summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore21
-rw-r--r--.travis.yml10
-rw-r--r--CMakeLists.txt119
-rw-r--r--README.txt69
-rw-r--r--build/android/Makefile458
-rw-r--r--build/android/jni/Android.mk72
-rw-r--r--build/android/libiconv_android.patch39
-rw-r--r--build/android/libiconv_stdio.patch13
-rw-r--r--build/android/src/net/minetest/minetest/MtNativeActivity.java6
-rw-r--r--builtin/common/misc_helpers.lua57
-rw-r--r--builtin/common/serialize.lua11
-rw-r--r--builtin/common/strict.lua5
-rw-r--r--builtin/fstk/ui.lua71
-rw-r--r--builtin/game/auth.lua1
-rw-r--r--builtin/game/chatcommands.lua109
-rw-r--r--builtin/game/detached_inventory.lua1
-rw-r--r--builtin/game/item.lua61
-rw-r--r--builtin/game/item_entity.lua113
-rw-r--r--builtin/game/misc.lua90
-rw-r--r--builtin/game/register.lua73
-rw-r--r--builtin/mainmenu/common.lua79
-rw-r--r--builtin/mainmenu/init.lua7
-rw-r--r--builtin/mainmenu/modmgr.lua8
-rw-r--r--builtin/mainmenu/store.lua44
-rw-r--r--builtin/mainmenu/tab_credits.lua87
-rw-r--r--builtin/mainmenu/tab_mods.lua5
-rw-r--r--builtin/mainmenu/tab_multiplayer.lua5
-rw-r--r--builtin/mainmenu/tab_server.lua27
-rw-r--r--builtin/mainmenu/tab_settings.lua111
-rw-r--r--builtin/mainmenu/tab_simple_main.lua19
-rw-r--r--builtin/mainmenu/tab_singleplayer.lua40
-rw-r--r--builtin/mainmenu/tab_texturepacks.lua18
-rw-r--r--builtin/mainmenu/textures.lua31
-rw-r--r--client/shaders/minimap_shader/opengl_fragment.glsl32
-rw-r--r--client/shaders/minimap_shader/opengl_vertex.glsl11
-rw-r--r--client/shaders/nodes_shader/opengl_fragment.glsl144
-rw-r--r--client/shaders/nodes_shader/opengl_vertex.glsl83
-rw-r--r--client/shaders/water_surface_shader/opengl_fragment.glsl62
-rw-r--r--client/shaders/water_surface_shader/opengl_vertex.glsl17
-rw-r--r--client/shaders/wielded_shader/opengl_fragment.glsl114
-rw-r--r--client/shaders/wielded_shader/opengl_vertex.glsl35
-rw-r--r--cmake/Modules/FindCURL.cmake58
-rw-r--r--cmake/Modules/FindGMP.cmake28
-rw-r--r--cmake/Modules/FindGettextLib.cmake104
-rw-r--r--cmake/Modules/FindIrrlicht.cmake49
-rw-r--r--cmake/Modules/FindJson.cmake37
-rw-r--r--cmake/Modules/FindLua.cmake25
-rw-r--r--cmake/Modules/FindOpenGLES2.cmake186
-rw-r--r--cmake/Modules/GenerateVersion.cmake22
-rw-r--r--cmake/Modules/misc.cmake21
-rw-r--r--doc/lua_api.txt604
-rw-r--r--doc/menu_lua_api.txt10
-rw-r--r--doc/minetest.6110
-rw-r--r--doc/minetestserver.677
-rw-r--r--doc/texture_overrides.txt35
-rw-r--r--games/minimal/mods/default/init.lua104
-rw-r--r--games/minimal/mods/default/mapgen.lua156
-rw-r--r--games/minimal/mods/default/textures/default_jungletree.pngbin210 -> 0 bytes
-rw-r--r--games/minimal/mods/default/textures/default_jungletree_top.pngbin205 -> 0 bytes
-rw-r--r--minetest.conf.example215
-rw-r--r--misc/Info.plist4
-rw-r--r--misc/minetest.desktop7
-rw-r--r--misc/winresource.rc29
-rw-r--r--po/be/minetest.po659
-rw-r--r--po/cs/minetest.po636
-rw-r--r--po/da/minetest.po833
-rw-r--r--po/de/minetest.po895
-rw-r--r--po/es/minetest.po890
-rw-r--r--po/et/minetest.po864
-rw-r--r--po/fr/minetest.po1033
-rw-r--r--po/hu/minetest.po1161
-rw-r--r--po/id/minetest.po903
-rw-r--r--po/it/minetest.po865
-rw-r--r--po/ja/minetest.po1046
-rw-r--r--po/ko/minetest.po675
-rw-r--r--po/ky/minetest.po852
-rw-r--r--po/lt/minetest.po774
-rw-r--r--po/minetest.pot659
-rw-r--r--po/nb/minetest.po697
-rw-r--r--po/nl/minetest.po952
-rw-r--r--po/pl/minetest.po956
-rw-r--r--po/pt/minetest.po958
-rw-r--r--po/pt_BR/minetest.po953
-rw-r--r--po/ro/minetest.po856
-rw-r--r--po/ru/minetest.po1053
-rw-r--r--po/tr/minetest.po1211
-rw-r--r--po/uk/minetest.po854
-rw-r--r--po/zh_CN/minetest.po1045
-rw-r--r--src/CMakeLists.txt510
-rw-r--r--src/activeobject.h19
-rw-r--r--src/areastore.cpp343
-rw-r--r--src/areastore.h196
-rw-r--r--src/ban.cpp49
-rw-r--r--src/ban.h5
-rw-r--r--src/camera.cpp96
-rw-r--r--src/camera.h5
-rw-r--r--src/cavegen.cpp564
-rw-r--r--src/cavegen.h54
-rw-r--r--src/cguittfont/CGUITTFont.cpp26
-rw-r--r--src/cguittfont/CMakeLists.txt1
-rw-r--r--src/chat.cpp9
-rw-r--r--src/client.cpp2104
-rw-r--r--src/client.h164
-rw-r--r--src/client/CMakeLists.txt6
-rw-r--r--src/client/clientlauncher.cpp726
-rw-r--r--src/client/clientlauncher.h129
-rw-r--r--src/client/inputhandler.h435
-rw-r--r--src/client/tile.cpp (renamed from src/tile.cpp)217
-rw-r--r--src/client/tile.h (renamed from src/tile.h)47
-rw-r--r--src/clientiface.cpp128
-rw-r--r--src/clientiface.h256
-rw-r--r--src/clientmap.cpp128
-rw-r--r--src/clientmedia.cpp28
-rw-r--r--src/clientobject.cpp7
-rw-r--r--src/clientobject.h3
-rw-r--r--src/clouds.cpp123
-rw-r--r--src/clouds.h13
-rw-r--r--src/cmake_config.h.in46
-rw-r--r--src/cmake_config_githash.h.in2
-rw-r--r--src/collision.cpp117
-rw-r--r--src/collision.h9
-rw-r--r--src/config.h101
-rw-r--r--src/constants.h10
-rw-r--r--src/content_abm.cpp1
-rw-r--r--src/content_cao.cpp198
-rw-r--r--src/content_cao.h10
-rw-r--r--src/content_cso.cpp4
-rw-r--r--src/content_mapblock.cpp301
-rw-r--r--src/content_mapnode.cpp24
-rw-r--r--src/content_mapnode.h3
-rw-r--r--src/content_sao.cpp568
-rw-r--r--src/content_sao.h55
-rw-r--r--src/convert_json.cpp17
-rw-r--r--src/craftdef.cpp993
-rw-r--r--src/craftdef.h158
-rw-r--r--src/database-dummy.cpp54
-rw-r--r--src/database-dummy.h21
-rw-r--r--src/database-leveldb.cpp54
-rw-r--r--src/database-leveldb.h25
-rw-r--r--src/database-redis.cpp160
-rw-r--r--src/database-redis.h24
-rw-r--r--src/database-sqlite3.cpp344
-rw-r--r--src/database-sqlite3.h44
-rw-r--r--src/database.cpp4
-rw-r--r--src/database.h30
-rw-r--r--src/debug.cpp28
-rw-r--r--src/debug.h37
-rw-r--r--src/defaultsettings.cpp41
-rw-r--r--src/drawscene.cpp29
-rw-r--r--src/drawscene.h16
-rw-r--r--src/dungeongen.cpp206
-rw-r--r--src/emerge.cpp83
-rw-r--r--src/emerge.h5
-rw-r--r--src/environment.cpp619
-rw-r--r--src/environment.h48
-rw-r--r--src/exceptions.h10
-rw-r--r--src/filecache.cpp2
-rw-r--r--src/filesys.cpp82
-rw-r--r--src/filesys.h41
-rw-r--r--src/fontengine.cpp20
-rw-r--r--src/fontengine.h2
-rw-r--r--src/game.cpp695
-rw-r--r--src/game.h3
-rw-r--r--src/gamedef.h29
-rw-r--r--src/gameparams.h (renamed from src/content_object.h)26
-rw-r--r--src/genericobject.cpp16
-rw-r--r--src/genericobject.h27
-rw-r--r--src/gettext.cpp10
-rw-r--r--src/gettext.h4
-rw-r--r--src/gettime.h4
-rw-r--r--src/gmp/CMakeLists.txt7
-rw-r--r--src/gmp/mini-gmp.c4130
-rw-r--r--src/gmp/mini-gmp.h256
-rw-r--r--src/guiChatConsole.cpp3
-rw-r--r--src/guiChatConsole.h3
-rw-r--r--src/guiEngine.cpp47
-rw-r--r--src/guiEngine.h4
-rw-r--r--src/guiFileSelectMenu.cpp6
-rw-r--r--src/guiFormSpecMenu.cpp543
-rw-r--r--src/guiFormSpecMenu.h54
-rw-r--r--src/guiKeyChangeMenu.cpp61
-rw-r--r--src/guiMainMenu.h31
-rw-r--r--src/guiPasswordChange.cpp17
-rw-r--r--src/guiTable.cpp6
-rw-r--r--src/guiVolumeChange.cpp1
-rw-r--r--src/guiscalingfilter.cpp169
-rw-r--r--src/guiscalingfilter.h52
-rw-r--r--src/httpfetch.cpp35
-rw-r--r--src/httpfetch.h5
-rw-r--r--src/hud.cpp26
-rw-r--r--src/hud.h12
-rw-r--r--src/imagefilters.cpp172
-rw-r--r--src/imagefilters.h46
-rw-r--r--src/intlGUIEditBox.cpp1509
-rw-r--r--src/intlGUIEditBox.h178
-rw-r--r--src/inventory.cpp62
-rw-r--r--src/inventory.h10
-rw-r--r--src/inventorymanager.cpp302
-rw-r--r--src/inventorymanager.h50
-rw-r--r--src/irr_v3d.h1
-rw-r--r--src/itemdef.cpp73
-rw-r--r--src/json/CMakeLists.txt17
-rw-r--r--src/jthread/CMakeLists.txt29
-rw-r--r--src/keycode.cpp17
-rw-r--r--src/light.cpp2
-rw-r--r--src/localplayer.cpp129
-rw-r--r--src/localplayer.h13
-rw-r--r--src/log.cpp49
-rw-r--r--src/log.h17
-rw-r--r--src/lua/CMakeLists.txt77
-rw-r--r--src/lua/src/CMakeLists.txt11
-rw-r--r--src/lua/src/lauxlib.c3
-rw-r--r--src/main.cpp1436
-rw-r--r--src/main.h61
-rw-r--r--src/mainmenumanager.h2
-rw-r--r--src/map.cpp474
-rw-r--r--src/map.h39
-rw-r--r--src/mapblock.cpp96
-rw-r--r--src/mapblock.h463
-rw-r--r--src/mapblock_mesh.cpp171
-rw-r--r--src/mapblock_mesh.h22
-rw-r--r--src/mapchunk.h77
-rw-r--r--src/mapgen.cpp188
-rw-r--r--src/mapgen.h73
-rw-r--r--src/mapgen_singlenode.cpp14
-rw-r--r--src/mapgen_singlenode.h4
-rw-r--r--src/mapgen_v5.cpp429
-rw-r--r--src/mapgen_v5.h36
-rw-r--r--src/mapgen_v6.cpp388
-rw-r--r--src/mapgen_v6.h37
-rw-r--r--src/mapgen_v7.cpp436
-rw-r--r--src/mapgen_v7.h33
-rw-r--r--src/mapnode.cpp35
-rw-r--r--src/mapnode.h10
-rw-r--r--src/mapsector.cpp39
-rw-r--r--src/mapsector.h34
-rw-r--r--src/mesh.cpp101
-rw-r--r--src/mesh.h6
-rw-r--r--src/mg_biome.cpp83
-rw-r--r--src/mg_biome.h43
-rw-r--r--src/mg_decoration.cpp84
-rw-r--r--src/mg_decoration.h75
-rw-r--r--src/mg_ore.cpp97
-rw-r--r--src/mg_ore.h51
-rw-r--r--src/mg_schematic.cpp370
-rw-r--r--src/mg_schematic.h117
-rw-r--r--src/minimap.cpp561
-rw-r--r--src/minimap.h157
-rw-r--r--src/mods.cpp9
-rw-r--r--src/network/CMakeLists.txt16
-rw-r--r--src/network/clientopcodes.cpp213
-rw-r--r--src/network/clientopcodes.h52
-rw-r--r--src/network/clientpackethandler.cpp1211
-rw-r--r--src/network/connection.cpp (renamed from src/connection.cpp)696
-rw-r--r--src/network/connection.h (renamed from src/connection.h)70
-rw-r--r--src/network/networkpacket.cpp520
-rw-r--r--src/network/networkpacket.h131
-rw-r--r--src/network/networkprotocol.h (renamed from src/clientserver.h)195
-rw-r--r--src/network/serveropcodes.cpp213
-rw-r--r--src/network/serveropcodes.h52
-rw-r--r--src/network/serverpackethandler.cpp2086
-rw-r--r--src/nodedef.cpp324
-rw-r--r--src/nodedef.h132
-rw-r--r--src/nodemetadata.cpp50
-rw-r--r--src/nodemetadata.h24
-rw-r--r--src/noise.cpp198
-rw-r--r--src/noise.h85
-rw-r--r--src/objdef.cpp184
-rw-r--r--src/objdef.h95
-rw-r--r--src/particles.cpp19
-rw-r--r--src/particles.h2
-rw-r--r--src/pathfinder.cpp25
-rw-r--r--src/player.cpp53
-rw-r--r--src/player.h139
-rw-r--r--src/porting.cpp525
-rw-r--r--src/porting.h13
-rw-r--r--src/profiler.cpp (renamed from src/test.h)11
-rw-r--r--src/profiler.h45
-rw-r--r--src/rollback_interface.cpp3
-rw-r--r--src/script/CMakeLists.txt9
-rw-r--r--src/script/common/CMakeLists.txt5
-rw-r--r--src/script/common/c_content.cpp189
-rw-r--r--src/script/common/c_content.h69
-rw-r--r--src/script/common/c_converter.cpp300
-rw-r--r--src/script/common/c_converter.h38
-rw-r--r--src/script/common/c_internal.cpp84
-rw-r--r--src/script/common/c_internal.h17
-rw-r--r--src/script/cpp_api/CMakeLists.txt8
-rw-r--r--src/script/cpp_api/s_async.cpp15
-rw-r--r--src/script/cpp_api/s_base.cpp156
-rw-r--r--src/script/cpp_api/s_base.h39
-rw-r--r--src/script/cpp_api/s_entity.cpp35
-rw-r--r--src/script/cpp_api/s_env.cpp6
-rw-r--r--src/script/cpp_api/s_internal.h1
-rw-r--r--src/script/cpp_api/s_inventory.cpp21
-rw-r--r--src/script/cpp_api/s_item.cpp20
-rw-r--r--src/script/cpp_api/s_mainmenu.cpp22
-rw-r--r--src/script/cpp_api/s_mainmenu.h15
-rw-r--r--src/script/cpp_api/s_node.cpp34
-rw-r--r--src/script/cpp_api/s_node.h5
-rw-r--r--src/script/cpp_api/s_nodemeta.cpp24
-rw-r--r--src/script/cpp_api/s_player.cpp79
-rw-r--r--src/script/cpp_api/s_player.h16
-rw-r--r--src/script/cpp_api/s_security.cpp604
-rw-r--r--src/script/cpp_api/s_security.h70
-rw-r--r--src/script/cpp_api/s_server.cpp16
-rw-r--r--src/script/lua_api/CMakeLists.txt6
-rw-r--r--src/script/lua_api/l_areastore.cpp401
-rw-r--r--src/script/lua_api/l_areastore.h70
-rw-r--r--src/script/lua_api/l_base.cpp40
-rw-r--r--src/script/lua_api/l_base.h4
-rw-r--r--src/script/lua_api/l_craft.cpp183
-rw-r--r--src/script/lua_api/l_env.cpp147
-rw-r--r--src/script/lua_api/l_env.h8
-rw-r--r--src/script/lua_api/l_internal.h13
-rw-r--r--src/script/lua_api/l_mainmenu.cpp115
-rw-r--r--src/script/lua_api/l_mainmenu.h2
-rw-r--r--src/script/lua_api/l_mapgen.cpp986
-rw-r--r--src/script/lua_api/l_mapgen.h35
-rw-r--r--src/script/lua_api/l_nodemeta.cpp27
-rw-r--r--src/script/lua_api/l_noise.cpp338
-rw-r--r--src/script/lua_api/l_noise.h54
-rw-r--r--src/script/lua_api/l_object.cpp584
-rw-r--r--src/script/lua_api/l_object.h58
-rw-r--r--src/script/lua_api/l_particles.cpp214
-rw-r--r--src/script/lua_api/l_server.cpp63
-rw-r--r--src/script/lua_api/l_server.h8
-rw-r--r--src/script/lua_api/l_settings.cpp2
-rw-r--r--src/script/lua_api/l_util.cpp90
-rw-r--r--src/script/lua_api/l_util.h12
-rw-r--r--src/script/lua_api/l_vmanip.cpp50
-rw-r--r--src/script/scripting_game.cpp14
-rw-r--r--src/script/scripting_game.h20
-rw-r--r--src/script/scripting_mainmenu.cpp2
-rw-r--r--src/server.cpp3389
-rw-r--r--src/server.h152
-rw-r--r--src/serverlist.cpp6
-rw-r--r--src/serverobject.cpp19
-rw-r--r--src/serverobject.h26
-rw-r--r--src/settings.cpp32
-rw-r--r--src/settings.h8
-rw-r--r--src/shader.cpp147
-rw-r--r--src/sky.cpp15
-rw-r--r--src/socket.cpp14
-rw-r--r--src/sound_openal.cpp2
-rw-r--r--src/staticobject.cpp8
-rw-r--r--src/staticobject.h9
-rw-r--r--src/subgame.cpp55
-rw-r--r--src/subgame.h5
-rw-r--r--src/test.cpp2217
-rw-r--r--src/touchscreengui.cpp42
-rw-r--r--src/touchscreengui.h9
-rw-r--r--src/treegen.cpp766
-rw-r--r--src/treegen.h5
-rw-r--r--src/unittest/CMakeLists.txt23
-rw-r--r--src/unittest/test.cpp638
-rw-r--r--src/unittest/test.h146
-rw-r--r--src/unittest/test_areastore.cpp129
-rw-r--r--src/unittest/test_collision.cpp180
-rw-r--r--src/unittest/test_compression.cpp172
-rw-r--r--src/unittest/test_connection.cpp348
-rw-r--r--src/unittest/test_filepath.cpp261
-rw-r--r--src/unittest/test_inventory.cpp143
-rw-r--r--src/unittest/test_mapnode.cpp56
-rw-r--r--src/unittest/test_nodedef.cpp66
-rw-r--r--src/unittest/test_noderesolver.cpp208
-rw-r--r--src/unittest/test_noise.cpp285
-rw-r--r--src/unittest/test_objdef.cpp106
-rw-r--r--src/unittest/test_profiler.cpp72
-rw-r--r--src/unittest/test_random.cpp274
-rw-r--r--src/unittest/test_schematic.cpp280
-rw-r--r--src/unittest/test_serialization.cpp386
-rw-r--r--src/unittest/test_settings.cpp204
-rw-r--r--src/unittest/test_socket.cpp151
-rw-r--r--src/unittest/test_utilities.cpp295
-rw-r--r--src/unittest/test_voxelalgorithms.cpp204
-rw-r--r--src/unittest/test_voxelmanipulator.cpp108
-rw-r--r--src/util/CMakeLists.txt6
-rw-r--r--src/util/auth.cpp126
-rw-r--r--src/util/auth.h37
-rw-r--r--src/util/base64.cpp (renamed from src/base64.cpp)0
-rw-r--r--src/util/base64.h (renamed from src/base64.h)0
-rw-r--r--src/util/container.h205
-rw-r--r--src/util/hex.h (renamed from src/hex.h)0
-rw-r--r--src/util/md32_common.h428
-rw-r--r--src/util/numeric.cpp179
-rw-r--r--src/util/numeric.h147
-rw-r--r--src/util/pointer.h8
-rw-r--r--src/util/serialize.cpp285
-rw-r--r--src/util/serialize.h462
-rw-r--r--src/util/sha1.cpp (renamed from src/sha1.cpp)0
-rw-r--r--src/util/sha1.h (renamed from src/sha1.h)0
-rw-r--r--src/util/sha2.h154
-rw-r--r--src/util/sha256.c404
-rw-r--r--src/util/srp.cpp1038
-rw-r--r--src/util/srp.h171
-rw-r--r--src/util/string.cpp181
-rw-r--r--src/util/string.h51
-rw-r--r--src/util/thread.h157
-rw-r--r--src/version.cpp20
-rw-r--r--src/version.h6
-rw-r--r--src/voxel.cpp138
-rw-r--r--src/voxel.h17
-rw-r--r--src/wieldmesh.cpp68
-rw-r--r--src/wieldmesh.h5
-rw-r--r--textures/base/pack/disable_img.pngbin150 -> 0 bytes
-rw-r--r--textures/base/pack/enable_img.pngbin160 -> 0 bytes
-rw-r--r--textures/base/pack/minimap_mask_round.pngbin0 -> 4081 bytes
-rw-r--r--textures/base/pack/minimap_mask_square.pngbin0 -> 1951 bytes
-rw-r--r--textures/base/pack/minimap_overlay_round.pngbin0 -> 20630 bytes
-rw-r--r--textures/base/pack/minimap_overlay_square.pngbin0 -> 2889 bytes
-rw-r--r--textures/base/pack/no_screenshot.pngbin2446 -> 476 bytes
-rw-r--r--textures/base/pack/player_marker.pngbin0 -> 3885 bytes
-rw-r--r--util/buildbot/toolchain_mingw64.cmake8
-rwxr-xr-xutil/bump_version.sh23
-rwxr-xr-xutil/travis/before_install.sh37
-rwxr-xr-xutil/travis/script.sh32
-rw-r--r--util/travis/toolchain_mingw.cmake.in23
418 files changed, 53826 insertions, 28052 deletions
diff --git a/.gitignore b/.gitignore
index 680a44158..7b2b030af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,16 @@
## Generic ignorable patterns and files
*~
.*.swp
+.*-swp
*bak*
tags
*.vim
*.orig
*.rej
+## Files related to minetest development cycle
+/*.patch
+
## Non-static Minetest directories
/bin/
/games/*
@@ -35,6 +39,8 @@ doc/doxygen_*
CMakeFiles/*
src/CMakeFiles/*
src/Makefile
+src/android_version_githash.h
+src/android_version.h
src/cmake_config.h
src/cmake_config_githash.h
src/cmake_install.cmake
@@ -43,6 +49,7 @@ src/script/common/CMakeFiles/*
src/script/cpp_api/CMakeFiles/*
src/script/lua_api/CMakeFiles/*
src/util/CMakeFiles/*
+src/unittest/CMakeFiles/*
src/jthread/CMakeFiles/*
src/jthread/Makefile
src/jthread/cmake_config.h
@@ -55,28 +62,38 @@ src/cguittfont/CMakeFiles/
src/cguittfont/libcguittfont.a
src/cguittfont/cmake_install.cmake
src/cguittfont/Makefile
+src/gmp/CMakeFiles/
+src/gmp/libgmp.a
src/json/CMakeFiles/
src/json/libjsoncpp.a
src/sqlite/CMakeFiles/*
src/sqlite/libsqlite3.a
+src/client/CMakeFiles/
+src/network/CMakeFiles/
CMakeCache.txt
CPackConfig.cmake
CPackSourceConfig.cmake
Makefile
cmake_install.cmake
locale/
+.directory
+.kdev4/
*.cbp
+*.kdev4
*.layout
*.o
-## Build variants
+## Android build files
build/android/assets
build/android/bin
build/android/Debug
build/android/deps
build/android/gen
-build/android/jni/src/*
+build/android/jni/src
build/android/libs
build/android/obj
+build/android/path.cfg
+build/android/and_env
+build/android/AndroidManifest.xml
timestamp
diff --git a/.travis.yml b/.travis.yml
index 4bce211f7..d17af5463 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,9 +3,9 @@ compiler:
- gcc
- clang
env:
- - WINDOWS=32
- - WINDOWS=64
- - WINDOWS=no
+ - PLATFORM=Win32
+ - PLATFORM=Win64
+ - PLATFORM=Linux
before_install: ./util/travis/before_install.sh
script: ./util/travis/script.sh
notifications:
@@ -13,7 +13,7 @@ notifications:
matrix:
fast_finish: true
exclude:
- - env: WINDOWS=32
+ - env: PLATFORM=Win32
compiler: clang
- - env: WINDOWS=64
+ - env: PLATFORM=Win64
compiler: clang
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 98d9aee0f..b9dfd8ace 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,69 +1,64 @@
cmake_minimum_required(VERSION 2.6)
+
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_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()
# This can be read from ${PROJECT_NAME} after project() is called
project(minetest)
+set(PROJECT_NAME_CAPITALIZED "Minetest")
-set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
-# Also remember to set PROTOCOL_VERSION in clientserver.h when releasing
+# Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing
set(VERSION_MAJOR 0)
set(VERSION_MINOR 4)
-set(VERSION_PATCH 12)
-set(VERSION_PATCH_ORIG ${VERSION_PATCH})
+set(VERSION_PATCH 13)
+set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
+
+# Change to false for releases
+set(DEVELOPMENT_BUILD FALSE)
+set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
if(VERSION_EXTRA)
- set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA})
-else()
- # Comment the following line during release
- #set(VERSION_PATCH ${VERSION_PATCH}-dev)
+ set(VERSION_STRING ${VERSION_STRING}-${VERSION_EXTRA})
+elseif(DEVELOPMENT_BUILD)
+ set(VERSION_STRING "${VERSION_STRING}-dev")
endif()
-set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
-MESSAGE(STATUS "*** Will build version ${VERSION_STRING} ***")
+message(STATUS "*** Will build version ${VERSION_STRING} ***")
-# Configuration options
+# Configuration options
+set(DEFAULT_RUN_IN_PLACE FALSE)
if(WIN32)
- set(RUN_IN_PLACE 1 CACHE BOOL "Run directly in source directory structure")
-else()
- set(RUN_IN_PLACE 0 CACHE BOOL "Run directly in source directory structure")
+ set(DEFAULT_RUN_IN_PLACE TRUE)
endif()
+set(RUN_IN_PLACE ${DEFAULT_RUN_IN_PLACE} CACHE BOOL
+ "Run directly in source directory structure")
-# RUN_IN_PLACE is exported as a #define value, ensure it's 1/0 instead of ON/OFF
-if(RUN_IN_PLACE)
- set(RUN_IN_PLACE 1)
-else()
- set(RUN_IN_PLACE 0)
-endif()
-set(BUILD_CLIENT 1 CACHE BOOL "Build client")
-if(WIN32)
- set(BUILD_SERVER 0 CACHE BOOL "Build server")
-else()
- set(BUILD_SERVER 1 CACHE BOOL "Build server")
-endif()
+set(BUILD_CLIENT TRUE CACHE BOOL "Build client")
+set(BUILD_SERVER FALSE CACHE BOOL "Build server")
+
-set(WARN_ALL 1 CACHE BOOL "Enable -Wall for Release build")
+set(WARN_ALL TRUE CACHE BOOL "Enable -Wall for Release build")
if(NOT CMAKE_BUILD_TYPE)
# Default to release
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE)
endif()
+
# Included stuff
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
-include(${CMAKE_SOURCE_DIR}/cmake/Modules/misc.cmake)
-# This is done here so that relative search paths are more reasnable
+
+# This is done here so that relative search paths are more reasonable
find_package(Irrlicht)
-#
+
# Installation
-#
if(WIN32)
set(SHAREDIR ".")
@@ -72,11 +67,13 @@ if(WIN32)
set(EXAMPLE_CONF_DIR ".")
set(LOCALEDIR "locale")
elseif(APPLE)
- set(SHAREDIR ".")
- set(BINDIR ".")
- set(DOCDIR "./doc/${PROJECT_NAME}")
+ set(BUNDLE_NAME ${PROJECT_NAME}.app)
+ set(BUNDLE_PATH "${BUNDLE_NAME}")
+ set(BINDIR ${BUNDLE_NAME}/Contents/MacOS)
+ set(SHAREDIR ${BUNDLE_NAME}/Contents/Resources)
+ set(DOCDIR "${SHAREDIR}/${PROJECT_NAME}")
set(EXAMPLE_CONF_DIR ${DOCDIR})
- set(LOCALEDIR "locale")
+ set(LOCALEDIR "${SHAREDIR}/locale")
elseif(UNIX) # Linux, BSD etc
if(RUN_IN_PLACE)
set(SHAREDIR ".")
@@ -106,42 +103,50 @@ if(NOT CUSTOM_SHAREDIR STREQUAL "")
set(SHAREDIR "${CUSTOM_SHAREDIR}")
message(STATUS "Using SHAREDIR=${SHAREDIR}")
endif()
+
set(CUSTOM_BINDIR "" CACHE STRING "Directory to install binaries into")
if(NOT CUSTOM_BINDIR STREQUAL "")
set(BINDIR "${CUSTOM_BINDIR}")
message(STATUS "Using BINDIR=${BINDIR}")
endif()
+
set(CUSTOM_DOCDIR "" CACHE STRING "Directory to install documentation into")
if(NOT CUSTOM_DOCDIR STREQUAL "")
set(DOCDIR "${CUSTOM_DOCDIR}")
message(STATUS "Using DOCDIR=${DOCDIR}")
endif()
+
set(CUSTOM_MANDIR "" CACHE STRING "Directory to install manpages into")
if(NOT CUSTOM_MANDIR STREQUAL "")
set(MANDIR "${CUSTOM_MANDIR}")
message(STATUS "Using MANDIR=${MANDIR}")
endif()
+
set(CUSTOM_EXAMPLE_CONF_DIR "" CACHE STRING "Directory to install example config file into")
if(NOT CUSTOM_EXAMPLE_CONF_DIR STREQUAL "")
set(EXAMPLE_CONF_DIR "${CUSTOM_EXAMPLE_CONF_DIR}")
message(STATUS "Using EXAMPLE_CONF_DIR=${EXAMPLE_CONF_DIR}")
endif()
+
set(CUSTOM_XDG_APPS_DIR "" CACHE STRING "Directory to install .desktop files into")
if(NOT CUSTOM_XDG_APPS_DIR STREQUAL "")
set(XDG_APPS_DIR "${CUSTOM_XDG_APPS_DIR}")
message(STATUS "Using XDG_APPS_DIR=${XDG_APPS_DIR}")
endif()
+
set(CUSTOM_ICONDIR "" CACHE STRING "Directory to install icons into")
if(NOT CUSTOM_ICONDIR STREQUAL "")
set(ICONDIR "${CUSTOM_ICONDIR}")
message(STATUS "Using ICONDIR=${ICONDIR}")
endif()
+
set(CUSTOM_LOCALEDIR "" CACHE STRING "Directory to install l10n files into")
if(NOT CUSTOM_LOCALEDIR STREQUAL "")
set(LOCALEDIR "${CUSTOM_LOCALEDIR}")
message(STATUS "Using LOCALEDIR=${LOCALEDIR}")
endif()
+
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/builtin" DESTINATION "${SHAREDIR}")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/client" DESTINATION "${SHAREDIR}")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/games/minimal" DESTINATION "${SHAREDIR}/games")
@@ -176,13 +181,18 @@ if(UNIX AND NOT APPLE)
install(FILES "misc/minetest-icon.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps")
endif()
-#
+if(APPLE)
+ install(FILES "misc/minetest-icon.icns" DESTINATION "${SHAREDIR}")
+ install(FILES "misc/Info.plist" DESTINATION "${BUNDLE_PATH}/Contents")
+endif()
+
+
# Subdirectories
# Be sure to add all relevant definitions above this
-#
add_subdirectory(src)
+
# CPack
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An InfiniMiner/Minecraft inspired game")
@@ -193,32 +203,17 @@ set(CPACK_PACKAGE_VENDOR "celeron55")
set(CPACK_PACKAGE_CONTACT "Perttu Ahola <celeron55@gmail.com>")
if(WIN32)
- # For some reason these aren't copied otherwise
- # NOTE: For some reason now it seems to work without these
- #if(BUILD_CLIENT)
- # install(FILES bin/minetest.exe DESTINATION bin)
- #endif()
- #if(BUILD_SERVER)
- # install(FILES bin/minetestserver.exe DESTINATION bin)
- #endif()
-
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-win64")
- else(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ else()
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-win32")
- endif(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ endif()
set(CPACK_GENERATOR ZIP)
-
- # This might be needed for some installer
- #set(CPACK_PACKAGE_EXECUTABLES bin/minetest.exe "Minetest" bin/minetestserver.exe "Minetest Server")
elseif(APPLE)
+ set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-osx")
- set(CPACK_PACKAGE_ICON ${CMAKE_CURRENT_SOURCE_DIR}/misc/minetest-icon.icns)
- set(CPACK_BUNDLE_NAME ${PROJECT_NAME})
- set(CPACK_BUNDLE_ICON ${CPACK_PACKAGE_ICON})
- set(CPACK_BUNDLE_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/misc/Info.plist)
- set(CPACK_GENERATOR "Bundle")
+ set(CPACK_GENERATOR ZIP)
else()
set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-linux")
set(CPACK_GENERATOR TGZ)
@@ -227,14 +222,16 @@ endif()
include(CPack)
+
# Add a target to generate API documentation with Doxygen
find_package(Doxygen)
if(DOXYGEN_FOUND)
- configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile.in
+ ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY)
add_custom_target(doc
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc
COMMENT "Generating API documentation with Doxygen" VERBATIM
)
-endif(DOXYGEN_FOUND)
+endif()
diff --git a/README.txt b/README.txt
index 774dc6639..06122dee1 100644
--- a/README.txt
+++ b/README.txt
@@ -1,4 +1,4 @@
-Minetest
+Minetest
========
An InfiniMiner/Minecraft inspired game.
@@ -78,9 +78,9 @@ $share = /usr/share/minetest
$user = ~/.minetest
OS X:
-$bin = ?
-$share = ?
-$user = ~/Library/Application Support/minetest
+$bin = Contents/MacOS
+$share = Contents/Resources
+$user = Contents/User OR ~/Library/Application Support/minetest
World directory
----------------
@@ -103,21 +103,19 @@ Compiling on GNU/Linux:
-----------------------
Install dependencies. Here's an example for Debian/Ubuntu:
-$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev zlib1g-dev libjsoncpp-dev
+$ sudo apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-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
You can install git for easily keeping your copy up to date.
If you dont want git, read below on how to get the source without git.
This is an example for installing git on Debian/Ubuntu:
-$ apt-get install git-core
+$ sudo apt-get install git-core
Download source (this is the URL to the latest of source repository, which might not work at all times) using git:
$ git clone --depth 1 https://github.com/minetest/minetest.git
$ cd minetest
Download minetest_game (otherwise only the "Minimal development test" game is available) using git:
-$ cd games/
-$ git clone --depth 1 https://github.com/minetest/minetest_game.git
-$ cd ..
+$ git clone --depth 1 https://github.com/minetest/minetest_game.git games/minetest_game
Download source, without using git:
$ wget https://github.com/minetest/minetest/archive/master.tar.gz
@@ -132,19 +130,21 @@ $ mv minetest_game-master minetest_game
$ cd ..
Build a version that runs directly from the source directory:
-$ cmake . -DRUN_IN_PLACE=1
-$ make -j2
+$ cmake . -DRUN_IN_PLACE=TRUE
+$ make -j <number of processors>
Run it:
-$ cd bin
-$ ./minetest
+$ ./bin/minetest
- Use cmake . -LH to see all CMake options and their current state
-- If you want to install it system-wide (or are making a distribution package), you will want to use -DRUN_IN_PLACE=0
-- You can build a bare server or a bare client by specifying -DBUILD_CLIENT=0 or -DBUILD_SERVER=0
+- If you want to install it system-wide (or are making a distribution package),
+ you will want to use -DRUN_IN_PLACE=FALSE
+- You can build a bare server by specifying -DBUILD_SERVER=TRUE
+- You can disable the client build by specifying -DBUILD_CLIENT=FALSE
- You can select between Release and Debug build by -DCMAKE_BUILD_TYPE=<Debug or Release>
- Debug build is slower, but gives much more useful output in a debugger
-- If you build a bare server, you don't need to have Irrlicht installed. In that case use -DIRRLICHT_SOURCE_DIR=/the/irrlicht/source
+- If you build a bare server, you don't need to have Irrlicht installed.
+ In that case use -DIRRLICHT_SOURCE_DIR=/the/irrlicht/source
CMake options
-------------
@@ -155,16 +155,19 @@ BUILD_SERVER - Build Minetest server
CMAKE_BUILD_TYPE - Type of build (Release vs. Debug)
Release - Release build
Debug - Debug build
+ SemiDebug - Partially optimized debug build
RelWithDebInfo - Release build with Debug information
MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible
ENABLE_CURL - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http
-ENABLE_FREETYPE - Build with Freetype2; Allows using TTF fonts
+ENABLE_FREETYPE - Build with FreeType2; Allows using TTF fonts
ENABLE_GETTEXT - Build with Gettext; Allows using translations
ENABLE_GLES - Search for Open GLES headers & libraries and use them
-ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB, which is much faster than SQLite, as map backend
-ENABLE_REDIS - Build with libhiredis; Enables use of redis map backend
+ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB map backend (faster than SQLite3)
+ENABLE_REDIS - Build with libhiredis; Enables use of Redis map backend
+ENABLE_SPATIAL - Build with LibSpatial; Speeds up AreaStores
ENABLE_SOUND - Build with OpenAL, libogg & libvorbis; in-game Sounds
-DISABLE_LUAJIT - Do not search for LuaJIT headers & library
+ENABLE_LUAJIT - Build with LuaJIT (much faster than non-JIT Lua)
+ENABLE_SYSTEM_GMP - Use GMP from system (much faster than bundled mini-gmp)
RUN_IN_PLACE - Create a portable install (worlds, settings etc. in current directory)
USE_GPROF - Enable profiling using GProf
VERSION_EXTRA - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
@@ -177,7 +180,7 @@ CURL_DLL - Only if building with cURL on Windows; path to
CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located
CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib
EGL_INCLUDE_DIR - Only if building with GLES; directory that contains egl.h
-EGL_egl_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
+EGL_LIBRARY - Only if building with GLES; path to libEGL.a/libEGL.so
FREETYPE_INCLUDE_DIR_freetype2 - Only if building with Freetype2; directory that contains an freetype directory with files such as ftimage.h in it
FREETYPE_INCLUDE_DIR_ft2build - Only if building with Freetype2; directory that contains ft2build.h
FREETYPE_LIBRARY - Only if building with Freetype2; path to libfreetype.a/libfreetype.so/freetype.lib
@@ -187,14 +190,16 @@ GETTEXT_ICONV_DLL - Only when building with Gettext on Windows; pa
GETTEXT_INCLUDE_DIR - Only when building with Gettext; directory that contains iconv.h
GETTEXT_LIBRARY - Only when building with Gettext on Windows; path to libintl.dll.a
GETTEXT_MSGFMT - Only when building with Gettext; path to msgfmt/msgfmt.exe
-IRRLICHT_DLL - path to Irrlicht.dll
-IRRLICHT_INCLUDE_DIR - directory that contains IrrCompileConfig.h
-IRRLICHT_LIBRARY - path to libIrrlicht.a/libIrrlicht.so/libIrrlicht.dll.a
+IRRLICHT_DLL - Only on Windows; path to Irrlicht.dll
+IRRLICHT_INCLUDE_DIR - Directory that contains IrrCompileConfig.h
+IRRLICHT_LIBRARY - Path to libIrrlicht.a/libIrrlicht.so/libIrrlicht.dll.a/Irrlicht.lib
LEVELDB_INCLUDE_DIR - Only when building with LevelDB; directory that contains db.h
LEVELDB_LIBRARY - Only when building with LevelDB; path to libleveldb.a/libleveldb.so/libleveldb.dll.a
LEVELDB_DLL - Only when building with LevelDB on Windows; path to libleveldb.dll
-REDIS_INCLUDE_DIR - Only when building with redis support; directory that contains hiredis.h
-REDIS_LIBRARY - Only when building with redis support; path to libhiredis.a/libhiredis.so
+REDIS_INCLUDE_DIR - Only when building with Redis; directory that contains hiredis.h
+REDIS_LIBRARY - Only when building with Redis; path to libhiredis.a/libhiredis.so
+SPATIAL_INCLUDE_DIR - Only when building with LibSpatial; directory that contains spatialindex/SpatialIndex.h
+SPATIAL_LIBRARY - Only when building with LibSpatial; path to libspatialindex_c.so/spatialindex-32.lib
LUA_INCLUDE_DIR - Only if you want to use LuaJIT; directory where luajit.h is located
LUA_LIBRARY - Only if you want to use LuaJIT; path to libluajit.a/libluajit.so
MINGWM10_DLL - Only if compiling with MinGW; path to mingwm10.dll
@@ -205,9 +210,9 @@ OPENAL_DLL - Only if building with sound on Windows; path t
OPENAL_INCLUDE_DIR - Only if building with sound; directory where al.h is located
OPENAL_LIBRARY - Only if building with sound; path to libopenal.a/libopenal.so/OpenAL32.lib
OPENGLES2_INCLUDE_DIR - Only if building with GLES; directory that contains gl2.h
-OPENGLES2_gl_LIBRARY - Only if building with GLES; path to libGLESv2.a/libGLESv2.so
-SQLITE3_INCLUDE_DIR - Only if you want to use SQLite from your OS; directory that contains sqlite3.h
-SQLITE3_LIBRARY - Only if you want to use the SQLite from your OS; path to libsqlite3.a/libsqlite3.so
+OPENGLES2_LIBRARY - Only if building with GLES; path to libGLESv2.a/libGLESv2.so
+SQLITE3_INCLUDE_DIR - Directory that contains sqlite3.h
+SQLITE3_LIBRARY - Path to libsqlite3.a/libsqlite3.so/sqlite3.lib
VORBISFILE_DLL - Only if building with sound on Windows; path to libvorbisfile-3.dll
VORBISFILE_LIBRARY - Only if building with sound; path to libvorbisfile.a/libvorbisfile.so/libvorbisfile.dll.a
VORBIS_DLL - Only if building with sound on Windows; path to libvorbis-0.dll
@@ -216,8 +221,8 @@ VORBIS_LIBRARY - Only if building with sound; path to libvorbis
XXF86VM_LIBRARY - Only on Linux; path to libXXf86vm.a/libXXf86vm.so
ZLIB_DLL - Only on Windows; path to zlib1.dll
ZLIBWAPI_DLL - Only on Windows; path to zlibwapi.dll
-ZLIB_INCLUDE_DIR - directory where zlib.h is located
-ZLIB_LIBRARY - path to libz.a/libz.so/zlibwapi.lib
+ZLIB_INCLUDE_DIR - Directory that contains zlib.h
+ZLIB_LIBRARY - Path to libz.a/libz.so/zlibwapi.lib
Compiling on Windows:
---------------------
@@ -339,7 +344,7 @@ set irrlichtpath="C:\tmp\irrlicht-1.7.2"
set builddir=%sourcedir%\bvc10
mkdir %builddir%
pushd %builddir%
-cmake %sourcedir% -G "Visual Studio 10" -DIRRLICHT_SOURCE_DIR=%irrlichtpath% -DRUN_IN_PLACE=1 -DCMAKE_INSTALL_PREFIX=%installpath%
+cmake %sourcedir% -G "Visual Studio 10" -DIRRLICHT_SOURCE_DIR=%irrlichtpath% -DRUN_IN_PLACE=TRUE -DCMAKE_INSTALL_PREFIX=%installpath%
if %errorlevel% neq 0 goto fail
"C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" ALL_BUILD.vcxproj /p:Configuration=Release
if %errorlevel% neq 0 goto fail
diff --git a/build/android/Makefile b/build/android/Makefile
index bfc24bc85..6d3791550 100644
--- a/build/android/Makefile
+++ b/build/android/Makefile
@@ -2,13 +2,6 @@
OS := $(shell uname)
-#automaticaly set number of jobs
-ifeq ($(OS),Linux)
- PARALLEL := $(shell grep -c ^processor /proc/cpuinfo)
-else
- PARALLEL := 1
-endif
-
# compile with GPROF
# GPROF = 1
@@ -22,43 +15,49 @@ ROOT = $(shell pwd)
GAMES_TO_COPY = minetest_game
+
+VERSION_MAJOR := $(shell cat $(ROOT)/../../CMakeLists.txt | \
+ grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | cut -f2 -d' ')
+VERSION_MINOR := $(shell cat $(ROOT)/../../CMakeLists.txt | \
+ grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | cut -f2 -d' ')
+VERSION_PATCH := $(shell cat $(ROOT)/../../CMakeLists.txt | \
+ grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | cut -f2 -d' ')
+
################################################################################
# Android Version code
# Increase for each build!
################################################################################
# Play Store actual version (16/03/15): 11
-ANDROID_VERSION_CODE = 12
+ANDROID_VERSION_CODE = 13
################################################################################
# toolchain config for arm old processors
################################################################################
-TARGET_HOST = arm-linux
-TARGET_ABI = armeabi
-TARGET_LIBDIR = armeabi
-TARGET_TOOLCHAIN = arm-linux-androideabi-
-TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfp
-TARGET_ARCH = arm
-CROSS_PREFIX = arm-linux-androideabi-
-COMPILER_VERSION = 4.8
-HAVE_LEVELDB = 1
-
-################################################################################
-# toolchain config for arm new processors
-################################################################################
#TARGET_HOST = arm-linux
-#TARGET_ABI = armeabi-v7a-hard
-#TARGET_LIBDIR = armeabi-v7a
+#TARGET_ABI = armeabi
+#TARGET_LIBDIR = armeabi
#TARGET_TOOLCHAIN = arm-linux-androideabi-
-#TARGET_CFLAGS_ADDON = -mfpu=vfpv3-d16 -D_NDK_MATH_NO_SOFTFP=1 \
-# -mfloat-abi=hard -march=armv7-a
-#TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON)
-#TARGET_LDFLAGS_ADDON = -Wl,--no-warn-mismatch -lm_hard
-#TARGET_ARCH = armv7
+#TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfp
+#TARGET_ARCH = arm
#CROSS_PREFIX = arm-linux-androideabi-
#COMPILER_VERSION = 4.8
#HAVE_LEVELDB = 1
################################################################################
+# toolchain config for arm new processors
+################################################################################
+TARGET_HOST = arm-linux
+TARGET_ABI = armeabi-v7a
+TARGET_LIBDIR = armeabi-v7a
+TARGET_TOOLCHAIN = arm-linux-androideabi-
+TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfpv3
+TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON)
+TARGET_ARCH = armv7
+CROSS_PREFIX = arm-linux-androideabi-
+COMPILER_VERSION = 4.8
+HAVE_LEVELDB = 1
+
+################################################################################
# toolchain config for little endian mips
################################################################################
#TARGET_HOST = mipsel-linux
@@ -110,7 +109,7 @@ IRRLICHT_TIMESTAMP = $(IRRLICHT_DIR)timestamp
IRRLICHT_TIMESTAMP_INT = $(ROOT)/deps/irrlicht_timestamp
IRRLICHT_URL_SVN = http://svn.code.sf.net/p/irrlicht/code/branches/ogl-es/
-OPENSSL_VERSION = 1.0.1j
+OPENSSL_VERSION = 1.0.1l
OPENSSL_BASEDIR = openssl-$(OPENSSL_VERSION)
OPENSSL_DIR = $(ROOT)/deps/$(OPENSSL_BASEDIR)/
OPENSSL_LIB = $(OPENSSL_DIR)/libssl.so.1.0.0
@@ -118,19 +117,33 @@ OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp
OPENSSL_TIMESTAMP_INT = $(ROOT)/deps/openssl_timestamp
OPENSSL_URL = http://www.openssl.org/source/openssl-$(OPENSSL_VERSION).tar.gz
-CURL_VERSION = 7.40.0
+CURL_VERSION = 7.41.0
CURL_DIR = $(ROOT)/deps/curl-$(CURL_VERSION)
CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a
CURL_TIMESTAMP = $(CURL_DIR)/timestamp
CURL_TIMESTAMP_INT = $(ROOT)/deps/curl_timestamp
CURL_URL_HTTP = http://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2
+GMP_VERSION = 6.0.0
+GMP_DIR = $(ROOT)/deps/gmp-$(GMP_VERSION)
+GMP_LIB = $(GMP_DIR)/usr/lib/libgmp.so
+GMP_TIMESTAMP = $(GMP_DIR)/timestamp
+GMP_TIMESTAMP_INT = $(ROOT)/deps/gmp_timestamp
+GMP_URL_HTTP = https://gmplib.org/download/gmp/gmp-$(GMP_VERSION).tar.bz2
+
FREETYPE_DIR = $(ROOT)/deps/freetype2-android/
FREETYPE_LIB = $(FREETYPE_DIR)/Android/obj/local/$(TARGET_ABI)/libfreetype2-static.a
FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp
FREETYPE_TIMESTAMP_INT = $(ROOT)/deps/freetype_timestamp
FREETYPE_URL_GIT = https://github.com/cdave1/freetype2-android
+ICONV_VERSION = 1.14
+ICONV_DIR = $(ROOT)/deps/libiconv/
+ICONV_LIB = $(ICONV_DIR)/lib/.libs/libiconv.so
+ICONV_TIMESTAMP = $(ICONV_DIR)timestamp
+ICONV_TIMESTAMP_INT = $(ROOT)/deps/iconv_timestamp
+ICONV_URL_HTTP = http://ftp.gnu.org/pub/gnu/libiconv/libiconv-$(ICONV_VERSION).tar.gz
+
SQLITE3_FOLDER = sqlite-amalgamation-3080704
SQLITE3_URL = http://www.sqlite.org/2014/$(SQLITE3_FOLDER).zip
@@ -154,22 +167,23 @@ endif
$(OPENAL_TIMESTAMP) $(OGG_TIMESTAMP) \
$(IRRLICHT_TIMESTAMP) $(CURL_TIMESTAMP) \
$(OPENSSL_TIMESTAMP) curl_binary \
- $(ROOT)/jni/src/android_version.h
-
+ $(ROOT)/jni/src/android_version.h \
+ $(ROOT)/jni/src/android_version_githash.h
+
debug : $(PATHCFGFILE)
export NDEBUG=; \
export BUILD_TYPE=debug; \
- $(MAKE) -j${PARALLEL} apk
-
+ $(MAKE) apk
+
all : debug release
-
+
release : $(PATHCFGFILE)
@export NDEBUG=1; \
export BUILD_TYPE=release; \
- $(MAKE) -j${PARALLEL} apk
+ $(MAKE) apk
reconfig: delconfig
- @$(MAKE) -j${PARALLEL} $(PATHCFGFILE)
+ @$(MAKE) $(PATHCFGFILE)
delconfig :
$(RM) ${PATHCFGFILE}
@@ -201,7 +215,7 @@ $(OPENAL_TIMESTAMP) : openal_download
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
touch ${OPENAL_TIMESTAMP}; \
fi
-
+
openal_download :
@if [ ! -d ${OPENAL_DIR} ] ; then \
echo "openal sources missing, downloading..."; \
@@ -209,7 +223,7 @@ openal_download :
cd ${ROOT}/deps ; \
git clone ${OPENAL_URL_GIT} || exit 1; \
fi
-
+
openal : $(OPENAL_LIB)
$(OPENAL_LIB): $(OPENAL_TIMESTAMP)
@@ -225,8 +239,8 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP)
echo "changed timestamp for openal detected building..."; \
cd ${OPENAL_DIR}; \
ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
- APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
- TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ APP_ABI=${TARGET_ABI} TARGET_ARCH_ABI=${TARGET_ABI} \
+ APP_PLATFORM=${APP_PLATFORM} TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
touch ${OPENAL_TIMESTAMP}; \
@@ -234,16 +248,16 @@ $(OPENAL_LIB): $(OPENAL_TIMESTAMP)
else \
echo "nothing to be done for openal"; \
fi
-
+
clean_openal :
$(RM) -rf ${OPENAL_DIR}
-
+
$(OGG_TIMESTAMP) : ogg_download
@LAST_MODIF=$$(find ${OGG_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
touch ${OGG_TIMESTAMP}; \
fi
-
+
ogg_download :
@if [ ! -d ${OGG_DIR} ] ; then \
echo "ogg sources missing, downloading..."; \
@@ -252,8 +266,9 @@ ogg_download :
git clone ${OGG_URL_GIT}|| exit 1; \
cd libvorbis-libogg-android ; \
patch -p1 < ../../libvorbis-libogg-fpu.patch || exit 1; \
+ sed -i 's-:-?-' jni/Application.mk; \
fi
-
+
ogg : $(OGG_LIB)
$(OGG_LIB): $(OGG_TIMESTAMP)
@@ -270,7 +285,7 @@ $(OGG_LIB): $(OGG_TIMESTAMP)
echo "changed timestamp for ogg detected building..."; \
cd ${OGG_DIR}; \
ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
- APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
+ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
@@ -282,13 +297,13 @@ $(OGG_LIB): $(OGG_TIMESTAMP)
clean_ogg :
$(RM) -rf ${OGG_DIR}
-
+
$(OPENSSL_TIMESTAMP) : openssl_download
@LAST_MODIF=$$(find ${OPENSSL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
touch ${OPENSSL_TIMESTAMP}; \
fi
-
+
openssl_download :
@if [ ! -d ${OPENSSL_DIR} ] ; then \
echo "openssl sources missing, downloading..."; \
@@ -299,7 +314,7 @@ openssl_download :
cd ${OPENSSL_BASEDIR}; \
patch -p1 < ../../openssl_arch.patch; \
fi
-
+
openssl : $(OPENSSL_LIB)
$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP)
@@ -318,7 +333,7 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
- --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \
+ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
CC=${CROSS_PREFIX}gcc ./Configure android-${TARGET_ARCH} no-idea no-seed -no-sha0 -DL_ENDIAN;\
CC=${CROSS_PREFIX}gcc ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make build_libs; \
@@ -347,7 +362,7 @@ leveldb_download :
cd ${ROOT}/deps ; \
git clone ${LEVELDB_URL_GIT} || exit 1; \
fi
-
+
leveldb : $(LEVELDB_LIB)
$(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
@@ -366,7 +381,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
- --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \
+ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
export CXX=${CROSS_PREFIX}g++; \
@@ -374,17 +389,17 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
export CPPFLAGS="$${CPPFLAGS} ${TARGET_CFLAGS_ADDON}"; \
export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}"; \
export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
- $(MAKE) -j${PARALLEL} -s || exit 1; \
+ $(MAKE) -s || exit 1; \
touch ${LEVELDB_TIMESTAMP}; \
touch ${LEVELDB_TIMESTAMP_INT}; \
$(RM) -rf $${TOOLCHAIN}; \
else \
echo "nothing to be done for leveldb"; \
fi
-
+
clean_leveldb :
$(RM) -rf deps/leveldb
-
+
$(FREETYPE_TIMESTAMP) : freetype_download
@LAST_MODIF=$$(find ${FREETYPE_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
@@ -398,13 +413,7 @@ freetype_download :
cd deps; \
git clone ${FREETYPE_URL_GIT} || exit 1; \
fi
-
-$(IRRLICHT_TIMESTAMP) : irrlicht_download
- @LAST_MODIF=$$(find ${IRRLICHT_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${IRRLICHT_TIMESTAMP}; \
- fi
-
+
freetype : $(FREETYPE_LIB)
$(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
@@ -424,7 +433,7 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
echo "changed timestamp for freetype detected building..."; \
cd ${FREETYPE_DIR}/Android/jni; \
ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
- APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} -j${PARALLEL} \
+ APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
@@ -433,10 +442,71 @@ $(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
else \
echo "nothing to be done for freetype"; \
fi
-
+
clean_freetype :
$(RM) -rf ${FREETYPE_DIR}
+$(ICONV_TIMESTAMP) : iconv_download
+ @LAST_MODIF=$$(find ${ICONV_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ICONV_TIMESTAMP}; \
+ fi
+
+iconv_download :
+ @if [ ! -d ${ICONV_DIR} ] ; then \
+ echo "iconv sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd ${ROOT}/deps; \
+ wget ${ICONV_URL_HTTP} || exit 1; \
+ tar -xzf libiconv-${ICONV_VERSION}.tar.gz || exit 1; \
+ rm libiconv-${ICONV_VERSION}.tar.gz; \
+ ln -s libiconv-${ICONV_VERSION} libiconv; \
+ cd ${ICONV_DIR}; \
+ patch -p1 < ${ROOT}/libiconv_android.patch; \
+ patch -p1 < ${ROOT}/libiconv_stdio.patch; \
+ fi
+
+iconv : $(ICONV_LIB)
+
+$(ICONV_LIB) : $(ICONV_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${ICONV_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ! -e ${ICONV_LIB} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${ICONV_TIMESTAMP} -nt ${ICONV_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ mkdir -p ${ICONV_DIR}; \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for iconv detected building..."; \
+ cd ${ICONV_DIR}; \
+ \
+ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-iconv; \
+ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
+ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
+ --install-dir=$${TOOLCHAIN}; \
+ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
+ export CC=${CROSS_PREFIX}gcc; \
+ export CXX=${CROSS_PREFIX}g++; \
+ export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
+ ./configure --host=${TARGET_HOST} || exit 1; \
+ sed -i 's/LIBICONV_VERSION_INFO) /LIBICONV_VERSION_INFO) -avoid-version /g' lib/Makefile; \
+ grep "iconv_LDFLAGS" src/Makefile; \
+ $(MAKE) -s || exit 1; \
+ touch ${ICONV_TIMESTAMP}; \
+ touch ${ICONV_TIMESTAMP_INT}; \
+ rm -rf ${TOOLCHAIN}; \
+ else \
+ echo "nothing to be done for iconv"; \
+ fi
+
+clean_iconv :
+ $(RM) -rf ${ICONV_DIR}
+
#Note: Texturehack patch is required for gpu's not supporting color format
# correctly. Known bad GPU:
# -geforce on emulator
@@ -454,6 +524,12 @@ irrlicht_download :
patch -p1 < ../../irrlicht-texturehack.patch || exit 1; \
fi
+$(IRRLICHT_TIMESTAMP) : irrlicht_download
+ @LAST_MODIF=$$(find ${IRRLICHT_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${IRRLICHT_TIMESTAMP}; \
+ fi
+
irrlicht : $(IRRLICHT_LIB)
$(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
@@ -473,7 +549,7 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
echo "changed timestamp for irrlicht detected building..."; \
cd deps/irrlicht/source/Irrlicht/Android; \
ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
- APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
+ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} \
TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
@@ -482,10 +558,10 @@ $(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
else \
echo "nothing to be done for irrlicht"; \
fi
-
+
clean_irrlicht :
$(RM) -rf deps/irrlicht
-
+
$(CURL_TIMESTAMP) : curl_download
@LAST_MODIF=$$(find ${CURL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
@@ -525,7 +601,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \
${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
--toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
- --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \
+ --install-dir=$${TOOLCHAIN}; \
export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
export CC=${CROSS_PREFIX}gcc; \
export CXX=${CROSS_PREFIX}g++; \
@@ -535,22 +611,22 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \
export LDFLAGS="$${LDFLAGS} -L${OPENSSL_DIR} ${TARGET_LDFLAGS_ADDON}"; \
./configure --host=${TARGET_HOST} --disable-shared --enable-static --with-ssl; \
- $(MAKE) -j${PARALLEL} -s || exit 1; \
+ $(MAKE) -s || exit 1; \
touch ${CURL_TIMESTAMP}; \
touch ${CURL_TIMESTAMP_INT}; \
$(RM) -rf $${TOOLCHAIN}; \
else \
echo "nothing to be done for curl"; \
fi
-
+
clean_curl :
$(RM) -rf deps/curl-${CURL_VERSION} \
$(RM) -f deps/curl
-
-
-curl_binary:
+
+
+curl_binary:
@if [ ! -d "deps/curl-${CURL_VERSION_BINARY}" ] ; then \
- echo "curl sources missing, downloading..."; \
+ echo "curl binary missing, downloading..."; \
mkdir -p ${ROOT}/deps; \
cd deps; \
wget http://curl.haxx.se/gknw.net/7.34.0/dist-android/curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz || exit 1;\
@@ -559,6 +635,64 @@ curl_binary:
rm curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz; \
fi
+$(GMP_TIMESTAMP) : gmp_download
+ @LAST_MODIF=$$(find ${GMP_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${GMP_TIMESTAMP}; \
+ fi
+
+gmp_download :
+ @if [ ! -d "${GMP_DIR}" ] ; then \
+ echo "gmp sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd deps; \
+ wget ${GMP_URL_HTTP} || exit 1; \
+ tar -xjf gmp-${GMP_VERSION}.tar.bz2 || exit 1; \
+ rm gmp-${GMP_VERSION}.tar.bz2; \
+ ln -s gmp-${GMP_VERSION} gmp; \
+ fi
+
+gmp : $(GMP_LIB)
+
+$(GMP_LIB): $(GMP_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${GMP_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ! -e ${GMP_LIB} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${GMP_TIMESTAMP} -nt ${GMP_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ mkdir -p ${GMP_DIR}; \
+ export PATH="$${PATH}:${SDKFOLDER}/platform-tools:${ANDROID_NDK}"; \
+ echo "changed timestamp for gmp detected building..."; \
+ cd deps/gmp-${GMP_VERSION}; \
+ export CROSS_PREFIX=${CROSS_PREFIX}; \
+ export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-gmp; \
+ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
+ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
+ --install-dir=$${TOOLCHAIN}; \
+ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
+ export CC=${CROSS_PREFIX}gcc; \
+ export CXX=${CROSS_PREFIX}g++; \
+ export LIBGMP_LDFLAGS="-avoid-version"; \
+ export LIBGMPXX_LDFLAGS="-avoid-version"; \
+ ./configure --disable-static --host=${TARGET_HOST} --prefix=/usr; \
+ $(MAKE) install DESTDIR=/${GMP_DIR} || exit 1; \
+ touch ${GMP_TIMESTAMP}; \
+ touch ${GMP_TIMESTAMP_INT}; \
+ $(RM) -rf $${TOOLCHAIN}; \
+ else \
+ echo "nothing to be done for gmp"; \
+ fi
+
+clean_gmp:
+ $(RM) -rf deps/gmp-${GMP_VERSION} \
+ $(RM) -f deps/gmp
+
sqlite3_download: deps/${SQLITE3_FOLDER}/sqlite3.c
deps/${SQLITE3_FOLDER}/sqlite3.c :
@@ -566,54 +700,21 @@ deps/${SQLITE3_FOLDER}/sqlite3.c :
wget ${SQLITE3_URL}; \
unzip ${SQLITE3_FOLDER}.zip; \
ln -s ${SQLITE3_FOLDER} sqlite
-
+
clean_sqlite3:
cd deps && $(RM) -rf ${SQLITE3_FOLDER} && $(RM) -f ${SQLITE3_FOLDER}.zip && \
$(RM) -f sqlite
$(ASSETS_TIMESTAMP) : $(IRRLICHT_LIB)
@mkdir -p ${ROOT}/deps; \
- LAST_MODIF=$$(find ${ROOT}/../../builtin -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../builtin/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- echo builtin changed $$LAST_MODIF; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../client -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../client/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../doc -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../doc/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../fonts -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../fonts/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../games -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../games/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../mods -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../mods/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../po -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../po/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
- LAST_MODIF=$$(find ${ROOT}/../../textures -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
- if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
- touch ${ROOT}/../../textures/timestamp; \
- touch ${ASSETS_TIMESTAMP}; \
- fi; \
+ for DIRNAME in {builtin,client,doc,fonts,games,mods,po,textures}; do \
+ LAST_MODIF=$$(find ${ROOT}/../../${DIRNAME} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ]; then \
+ touch ${ROOT}/../../${DIRNAME}/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ echo ${DIRNAME} changed $$LAST_MODIF; \
+ fi; \
+ done; \
LAST_MODIF=$$(find ${IRRLICHT_DIR}/media -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
touch ${IRRLICHT_DIR}/media/timestamp; \
@@ -629,7 +730,7 @@ $(ASSETS_TIMESTAMP) : $(IRRLICHT_LIB)
if [ ! -e $(ASSETS_TIMESTAMP) ] ; then \
touch $(ASSETS_TIMESTAMP); \
fi
-
+
assets : $(ASSETS_TIMESTAMP)
@REFRESH=0; \
if [ ! -e ${ASSETS_TIMESTAMP}.old ] ; then \
@@ -643,7 +744,7 @@ assets : $(ASSETS_TIMESTAMP)
fi; \
if [ $$REFRESH -ne 0 ] ; then \
echo "assets changed, refreshing..."; \
- $(MAKE) -j${PARALLEL} clean_assets; \
+ $(MAKE) clean_assets; \
mkdir -p ${ROOT}/assets/Minetest; \
cp ${ROOT}/../../minetest.conf.example ${ROOT}/assets/Minetest; \
cp ${ROOT}/../../README.txt ${ROOT}/assets/Minetest; \
@@ -677,15 +778,15 @@ assets : $(ASSETS_TIMESTAMP)
clean_assets :
@$(RM) -r assets
-
-apk: $(PATHCFGFILE) assets $(IRRLICHT_LIB) $(CURL_LIB) $(LEVELDB_TARGET) \
+
+apk: $(PATHCFGFILE) assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) $(LEVELDB_TARGET) \
$(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ROOT)/jni/src/android_version.h \
- sqlite3_download
- @export NDEBUG=$$NDEBUG; $(MAKE) -j${PARALLEL} manifest; \
+ $(ROOT)/jni/src/android_version_githash.h sqlite3_download
+ @export NDEBUG=$$NDEBUG; $(MAKE) manifest; \
export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
export ANDROID_HOME=${SDKFOLDER}; \
mkdir -p ${ROOT}/src; \
- ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} -j${PARALLEL} \
+ ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} \
GPROF=${GPROF} APP_ABI=${TARGET_ABI} HAVE_LEVELDB=${HAVE_LEVELDB} \
APP_PLATFORM=${APP_PLATFORM} \
TARGET_LIBDIR=${TARGET_LIBDIR} \
@@ -696,74 +797,77 @@ apk: $(PATHCFGFILE) assets $(IRRLICHT_LIB) $(CURL_LIB) $(LEVELDB_TARGET) \
echo "++ Success!" && \
echo "APK: bin/Minetest-$$BUILD_TYPE.apk" && \
echo "You can install it with \`adb install -r bin/Minetest-$$BUILD_TYPE.apk\`"
-
+
prep_srcdir :
- @rm ${ROOT}/jni/src; \
- ln -s ${ROOT}/../../src ${ROOT}/jni/src
-
+ @if [ ! -e ${ROOT}/jni/src ]; then \
+ ln -s ${ROOT}/../../src ${ROOT}/jni/src; \
+ fi
+
clean_apk : manifest
@export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}; \
export ANDROID_HOME=${SDKFOLDER}; \
ant clean
-
-install_debug :
+
+install_debug :
@export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}; \
adb install -r ${ROOT}/bin/Minetest-debug.apk
-
+
install :
@export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}; \
adb install -r ${ROOT}/bin/Minetest-release.apk
-
+
envpaths :
@echo "export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}" > and_env;\
echo "export ANDROID_HOME=${SDKFOLDER}" >> and_env;
-
-clean_all :
- @$(MAKE) -j${PARALLEL} clean_apk; \
- $(MAKE) clean_assets clean_irrlicht clean_leveldb clean_curl clean_openssl \
- clean_openal clean_ogg clean_manifest; \
+
+clean_all :
+ @$(MAKE) clean_apk; \
+ $(MAKE) clean_assets clean_iconv clean_irrlicht clean_leveldb clean_curl clean_openssl \
+ clean_openal clean_ogg clean_gmp clean_manifest; \
sleep 1; \
$(RM) -r gen libs obj deps bin Debug and_env
-
-$(ROOT)/jni/src/android_version.h :
- @echo "#define STR_HELPER(x) #x" \
- >${ROOT}/jni/src/android_version.h; \
- echo "#define STR(x) STR_HELPER(x)" \
- >> ${ROOT}/jni/src/android_version.h; \
- echo "#define VERSION_MAJOR $$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | awk '{print $$2;}')" \
- >> ${ROOT}/jni/src/android_version.h; \
- echo "#define VERSION_MINOR $$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | awk '{print $$2;}')" \
- >> ${ROOT}/jni/src/android_version.h; \
- echo "#define VERSION_PATCH $$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}')" \
- >> ${ROOT}/jni/src/android_version.h; \
- echo "#define VERSION_PATCH_ORIG $$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}')" \
- >> ${ROOT}/jni/src/android_version.h; \
+
+$(ROOT)/jni/src/android_version_githash.h : prep_srcdir
+ @export VERSION_FILE=${ROOT}/jni/src/android_version_githash.h; \
+ export VERSION_FILE_NEW=$${VERSION_FILE}.new; \
+ { \
+ echo "#ifndef ANDROID_MT_VERSION_GITHASH_H"; \
+ echo "#define ANDROID_MT_VERSION_GITHASH_H"; \
export GITHASH=$$(git rev-parse --short=8 HEAD); \
- if [ "x$$GITHASH" = "x" ] ; then \
- export GITHASH=""; \
- fi; \
- export GITTAG=$$(git describe --abbrev=0 --tags); \
- if [ "x$$GITTAG" = "x" ] ; then \
- export GITTAG=""; \
- fi; \
- echo "#define CMAKE_VERSION_GITHASH \"$$GITTAG-$$GITHASH-Android\"" \
- >> ${ROOT}/jni/src/android_version.h; \
- echo "#define CMAKE_VERSION_STRING STR(VERSION_MAJOR)\".\"STR(VERSION_MINOR)\
- \".\"STR(VERSION_PATCH)" \
- >> ${ROOT}/jni/src/android_version.h;
-
+ export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"; \
+ echo "#define VERSION_GITHASH \"$$VERSION_STR-$$GITHASH-Android\""; \
+ echo "#endif"; \
+ } > "$${VERSION_FILE_NEW}"; \
+ if ! cmp -s $${VERSION_FILE} $${VERSION_FILE_NEW}; then \
+ echo "android_version_githash.h changed, updating..."; \
+ mv "$${VERSION_FILE_NEW}" "$${VERSION_FILE}"; \
+ else \
+ rm "$${VERSION_FILE_NEW}"; \
+ fi
+
+
+$(ROOT)/jni/src/android_version.h : prep_srcdir
+ @export VERSION_FILE=${ROOT}/jni/src/android_version.h; \
+ export VERSION_FILE_NEW=$${VERSION_FILE}.new; \
+ { \
+ echo "#ifndef ANDROID_MT_VERSION_H"; \
+ echo "#define ANDROID_MT_VERSION_H"; \
+ echo "#define VERSION_MAJOR ${VERSION_MAJOR}"; \
+ echo "#define VERSION_MINOR ${VERSION_MINOR}"; \
+ echo "#define VERSION_PATCH ${VERSION_PATCH}"; \
+ echo "#define VERSION_STRING STR(VERSION_MAJOR)\".\"STR(VERSION_MINOR)\
+ \".\"STR(VERSION_PATCH)"; \
+ echo "#endif"; \
+ } > $${VERSION_FILE_NEW}; \
+ if ! cmp -s $${VERSION_FILE} $${VERSION_FILE_NEW}; then \
+ echo "android_version.h changed, updating..."; \
+ mv "$${VERSION_FILE_NEW}" "$${VERSION_FILE}"; \
+ else \
+ rm "$${VERSION_FILE_NEW}"; \
+ fi
+
manifest :
- @VERS_MAJOR=$$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | awk '{print $$2;}'); \
- VERS_MINOR=$$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | awk '{print $$2;}'); \
- VERS_PATCH=$$(cat ${ROOT}/../../CMakeLists.txt | \
- grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}'); \
- BASE_VERSION="$$VERS_MAJOR.$$VERS_MINOR.$$VERS_PATCH"; \
+ @BASE_VERSION="${VERS_MAJOR}.${VERS_MINOR}.${VERS_PATCH}"; \
if [ "${NDEBUG}x" != "x" ] ; then \
DBG=''; \
DBG_FLAG="android:debuggable=\"false\""; \
@@ -772,11 +876,11 @@ manifest :
DBG_FLAG="android:debuggable=\"true\""; \
fi; \
cat ${ROOT}/AndroidManifest.xml.template | \
- sed s/###ANDROID_VERSION###/${ANDROID_VERSION_CODE}/g | \
- sed s/###BASE_VERSION###/$$BASE_VERSION/g | \
+ sed "s/###ANDROID_VERSION###/${ANDROID_VERSION_CODE}/g" | \
+ sed "s/###BASE_VERSION###/$$BASE_VERSION/g" | \
sed -e "s@###DEBUG_BUILD###@$$DBG@g" | \
sed -e "s@###DEBUG_FLAG###@$$DBG_FLAG@g" >${ROOT}/AndroidManifest.xml
-
+
clean_manifest :
rm -rf ${ROOT}/AndroidManifest.xml
diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk
index 7546626b2..61ba1d0f0 100644
--- a/build/android/jni/Android.mk
+++ b/build/android/jni/Android.mk
@@ -25,6 +25,11 @@ LOCAL_SRC_FILES := deps/freetype2-android/Android/obj/local/$(TARGET_ARCH_ABI)/l
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_MODULE := iconv
+LOCAL_SRC_FILES := deps/libiconv/lib/.libs/libiconv.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
LOCAL_MODULE := openal
LOCAL_SRC_FILES := deps/openal-soft/libs/$(TARGET_LIBDIR)/libopenal.so
include $(PREBUILT_SHARED_LIBRARY)
@@ -40,6 +45,11 @@ LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libvorbis
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_MODULE := gmp
+LOCAL_SRC_FILES := deps/gmp/usr/lib/libgmp.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
LOCAL_MODULE := ssl
LOCAL_SRC_FILES := deps/openssl/libssl.a
include $(PREBUILT_STATIC_LIBRARY)
@@ -49,7 +59,6 @@ LOCAL_MODULE := crypto
LOCAL_SRC_FILES := deps/openssl/libcrypto.a
include $(PREBUILT_STATIC_LIBRARY)
-
include $(CLEAR_VARS)
LOCAL_MODULE := minetest
@@ -93,16 +102,18 @@ LOCAL_C_INCLUDES := \
jni/src/json \
jni/src/cguittfont \
deps/irrlicht/include \
+ deps/libiconv/include \
deps/freetype2-android/include \
deps/curl/include \
deps/openal-soft/jni/OpenAL/include \
deps/libvorbis-libogg-android/jni/include \
+ deps/gmp/usr/include \
deps/leveldb/include \
deps/sqlite/
LOCAL_SRC_FILES := \
+ jni/src/areastore.cpp \
jni/src/ban.cpp \
- jni/src/base64.cpp \
jni/src/camera.cpp \
jni/src/cavegen.cpp \
jni/src/chat.cpp \
@@ -113,7 +124,6 @@ LOCAL_SRC_FILES := \
jni/src/clientobject.cpp \
jni/src/clouds.cpp \
jni/src/collision.cpp \
- jni/src/connection.cpp \
jni/src/content_abm.cpp \
jni/src/content_cao.cpp \
jni/src/content_cso.cpp \
@@ -145,9 +155,12 @@ LOCAL_SRC_FILES := \
jni/src/guiKeyChangeMenu.cpp \
jni/src/guiPasswordChange.cpp \
jni/src/guiTable.cpp \
+ jni/src/guiscalingfilter.cpp \
jni/src/guiVolumeChange.cpp \
jni/src/httpfetch.cpp \
jni/src/hud.cpp \
+ jni/src/imagefilters.cpp \
+ jni/src/intlGUIEditBox.cpp \
jni/src/inventory.cpp \
jni/src/inventorymanager.cpp \
jni/src/itemdef.cpp \
@@ -171,18 +184,21 @@ LOCAL_SRC_FILES := \
jni/src/mg_decoration.cpp \
jni/src/mg_ore.cpp \
jni/src/mg_schematic.cpp \
+ jni/src/minimap.cpp \
jni/src/mods.cpp \
jni/src/nameidmapping.cpp \
jni/src/nodedef.cpp \
jni/src/nodemetadata.cpp \
jni/src/nodetimer.cpp \
jni/src/noise.cpp \
+ jni/src/objdef.cpp \
jni/src/object_properties.cpp \
jni/src/particles.cpp \
jni/src/pathfinder.cpp \
jni/src/player.cpp \
jni/src/porting_android.cpp \
jni/src/porting.cpp \
+ jni/src/profiler.cpp \
jni/src/quicktune.cpp \
jni/src/rollback.cpp \
jni/src/rollback_interface.cpp \
@@ -190,7 +206,6 @@ LOCAL_SRC_FILES := \
jni/src/server.cpp \
jni/src/serverlist.cpp \
jni/src/serverobject.cpp \
- jni/src/sha1.cpp \
jni/src/shader.cpp \
jni/src/sky.cpp \
jni/src/socket.cpp \
@@ -198,23 +213,58 @@ LOCAL_SRC_FILES := \
jni/src/sound_openal.cpp \
jni/src/staticobject.cpp \
jni/src/subgame.cpp \
- jni/src/test.cpp \
- jni/src/tile.cpp \
jni/src/tool.cpp \
jni/src/treegen.cpp \
jni/src/version.cpp \
jni/src/voxel.cpp \
jni/src/voxelalgorithms.cpp \
+ jni/src/util/auth.cpp \
+ jni/src/util/base64.cpp \
jni/src/util/directiontables.cpp \
jni/src/util/numeric.cpp \
jni/src/util/pointedthing.cpp \
jni/src/util/serialize.cpp \
+ jni/src/util/sha1.cpp \
jni/src/util/string.cpp \
+ jni/src/util/srp.cpp \
jni/src/util/timetaker.cpp \
+ jni/src/unittest/test.cpp \
+ jni/src/unittest/test_collision.cpp \
+ jni/src/unittest/test_compression.cpp \
+ jni/src/unittest/test_connection.cpp \
+ jni/src/unittest/test_filepath.cpp \
+ jni/src/unittest/test_inventory.cpp \
+ jni/src/unittest/test_mapnode.cpp \
+ jni/src/unittest/test_nodedef.cpp \
+ jni/src/unittest/test_noderesolver.cpp \
+ jni/src/unittest/test_noise.cpp \
+ jni/src/unittest/test_objdef.cpp \
+ jni/src/unittest/test_profiler.cpp \
+ jni/src/unittest/test_random.cpp \
+ jni/src/unittest/test_schematic.cpp \
+ jni/src/unittest/test_serialization.cpp \
+ jni/src/unittest/test_settings.cpp \
+ jni/src/unittest/test_socket.cpp \
+ jni/src/unittest/test_utilities.cpp \
+ jni/src/unittest/test_voxelalgorithms.cpp \
+ jni/src/unittest/test_voxelmanipulator.cpp \
jni/src/touchscreengui.cpp \
jni/src/database-leveldb.cpp \
jni/src/settings.cpp \
- jni/src/wieldmesh.cpp
+ jni/src/wieldmesh.cpp \
+ jni/src/client/clientlauncher.cpp \
+ jni/src/client/tile.cpp
+
+# intentionally kept out (we already build openssl itself): jni/src/util/sha256.c
+
+# Network
+LOCAL_SRC_FILES += \
+ jni/src/network/connection.cpp \
+ jni/src/network/networkpacket.cpp \
+ jni/src/network/clientopcodes.cpp \
+ jni/src/network/clientpackethandler.cpp \
+ jni/src/network/serveropcodes.cpp \
+ jni/src/network/serverpackethandler.cpp \
# lua api
LOCAL_SRC_FILES += \
@@ -222,6 +272,7 @@ LOCAL_SRC_FILES += \
jni/src/script/common/c_converter.cpp \
jni/src/script/common/c_internal.cpp \
jni/src/script/common/c_types.cpp \
+ jni/src/script/cpp_api/s_async.cpp \
jni/src/script/cpp_api/s_base.cpp \
jni/src/script/cpp_api/s_entity.cpp \
jni/src/script/cpp_api/s_env.cpp \
@@ -231,8 +282,9 @@ LOCAL_SRC_FILES += \
jni/src/script/cpp_api/s_node.cpp \
jni/src/script/cpp_api/s_nodemeta.cpp \
jni/src/script/cpp_api/s_player.cpp \
+ jni/src/script/cpp_api/s_security.cpp \
jni/src/script/cpp_api/s_server.cpp \
- jni/src/script/cpp_api/s_async.cpp \
+ jni/src/script/lua_api/l_areastore.cpp \
jni/src/script/lua_api/l_base.cpp \
jni/src/script/lua_api/l_craft.cpp \
jni/src/script/lua_api/l_env.cpp \
@@ -252,7 +304,7 @@ LOCAL_SRC_FILES += \
jni/src/script/lua_api/l_vmanip.cpp \
jni/src/script/scripting_game.cpp \
jni/src/script/scripting_mainmenu.cpp
-
+
#freetype2 support
LOCAL_SRC_FILES += \
jni/src/cguittfont/xCGUITTFont.cpp
@@ -303,7 +355,7 @@ LOCAL_SRC_FILES += \
# json
LOCAL_SRC_FILES += jni/src/json/jsoncpp.cpp
-LOCAL_SHARED_LIBRARIES := openal ogg vorbis
+LOCAL_SHARED_LIBRARIES := iconv openal ogg vorbis gmp
LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl ssl crypto android_native_app_glue $(PROFILER_LIBS)
ifeq ($(HAVE_LEVELDB), 1)
diff --git a/build/android/libiconv_android.patch b/build/android/libiconv_android.patch
new file mode 100644
index 000000000..4eca0a4ef
--- /dev/null
+++ b/build/android/libiconv_android.patch
@@ -0,0 +1,39 @@
+--- a/libcharset/lib/localcharset.c 2015-06-10 11:55:25.933870724 +0200
++++ b/libcharset/lib/localcharset.c 2015-06-10 11:55:39.578063493 +0200
+@@ -47,7 +47,7 @@
+
+ #if !defined WIN32_NATIVE
+ # include <unistd.h>
+-# if HAVE_LANGINFO_CODESET
++# if HAVE_LANGINFO_CODESET && !(defined __ANDROID__)
+ # include <langinfo.h>
+ # else
+ # if 0 /* see comment below */
+@@ -124,7 +124,7 @@ get_charset_aliases (void)
+ cp = charset_aliases;
+ if (cp == NULL)
+ {
+-#if !(defined DARWIN7 || defined VMS || defined WIN32_NATIVE || defined __CYGWIN__)
++#if !(defined DARWIN7 || defined VMS || defined WIN32_NATIVE || defined __CYGWIN__ || defined __ANDROID__)
+ const char *dir;
+ const char *base = "charset.alias";
+ char *file_name;
+@@ -338,6 +338,9 @@ get_charset_aliases (void)
+ "CP54936" "\0" "GB18030" "\0"
+ "CP65001" "\0" "UTF-8" "\0";
+ # endif
++# if defined __ANDROID__
++ cp = "*" "\0" "UTF-8" "\0";
++# endif
+ #endif
+
+ charset_aliases = cp;
+@@ -361,7 +364,7 @@ locale_charset (void)
+ const char *codeset;
+ const char *aliases;
+
+-#if !(defined WIN32_NATIVE || defined OS2)
++#if !(defined WIN32_NATIVE || defined OS2 || defined __ANDROID__)
+
+ # if HAVE_LANGINFO_CODESET
+
diff --git a/build/android/libiconv_stdio.patch b/build/android/libiconv_stdio.patch
new file mode 100644
index 000000000..9fa50f79a
--- /dev/null
+++ b/build/android/libiconv_stdio.patch
@@ -0,0 +1,13 @@
+--- a/srclib/stdio.in.h 2011-08-07 15:42:06.000000000 +0200
++++ b/srclib/stdio.in.h 2015-06-10 09:27:58.129056262 +0200
+@@ -695,8 +696,9 @@ _GL_CXXALIASWARN (gets);
+ /* It is very rare that the developer ever has full control of stdin,
+ so any use of gets warrants an unconditional warning. Assume it is
+ always declared, since it is required by C89. */
+-_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
++/*_GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");*/
++#define gets(a) fgets( a, sizeof(*(a)), stdin)
+ #endif
+
+
+#if @GNULIB_OBSTACK_PRINTF@ || @GNULIB_OBSTACK_PRINTF_POSIX@
diff --git a/build/android/src/net/minetest/minetest/MtNativeActivity.java b/build/android/src/net/minetest/minetest/MtNativeActivity.java
index 2bfcef93c..5c1f44d17 100644
--- a/build/android/src/net/minetest/minetest/MtNativeActivity.java
+++ b/build/android/src/net/minetest/minetest/MtNativeActivity.java
@@ -86,6 +86,12 @@ public class MtNativeActivity extends NativeActivity {
System.loadLibrary("vorbis");
System.loadLibrary("ssl");
System.loadLibrary("crypto");
+ System.loadLibrary("gmp");
+
+ // We don't have to load libminetest.so ourselves,
+ // but if we do, we get nicer logcat errors when
+ // loading fails.
+ System.loadLibrary("minetest");
}
private int m_MessagReturnCode;
diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index 39fca7d1e..bf672e6da 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -1,6 +1,11 @@
-- Minetest: builtin/misc_helpers.lua
--------------------------------------------------------------------------------
+-- Localize functions to avoid table lookups (better performance).
+local table_insert = table.insert
+local string_sub, string_find = string.sub, string.find
+
+--------------------------------------------------------------------------------
function basic_dump(o)
local tp = type(o)
if tp == "number" then
@@ -89,13 +94,13 @@ function dump2(o, name, dumped)
-- the form _G["table: 0xFFFFFFF"]
keyStr = string.format("_G[%q]", tostring(k))
-- Dump key table
- table.insert(t, dump2(k, keyStr, dumped))
+ table_insert(t, dump2(k, keyStr, dumped))
end
else
keyStr = basic_dump(k)
end
local vname = string.format("%s[%s]", name, keyStr)
- table.insert(t, dump2(v, vname, dumped))
+ table_insert(t, dump2(v, vname, dumped))
end
return string.format("%s = {}\n%s", name, table.concat(t))
end
@@ -130,7 +135,7 @@ function dump(o, indent, nested, level)
local t = {}
local dumped_indexes = {}
for i, v in ipairs(o) do
- table.insert(t, dump(v, indent, nested, level + 1))
+ table_insert(t, dump(v, indent, nested, level + 1))
dumped_indexes[i] = true
end
for k, v in pairs(o) do
@@ -139,7 +144,7 @@ function dump(o, indent, nested, level)
k = "["..dump(k, indent, nested, level + 1).."]"
end
v = dump(v, indent, nested, level + 1)
- table.insert(t, k.." = "..v)
+ table_insert(t, k.." = "..v)
end
end
nested[o] = nil
@@ -155,9 +160,6 @@ function dump(o, indent, nested, level)
end
--------------------------------------------------------------------------------
--- Localize functions to avoid table lookups (better performance).
-local table_insert = table.insert
-local str_sub, str_find = string.sub, string.find
function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
delim = delim or ","
max_splits = max_splits or -1
@@ -166,13 +168,13 @@ function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
local plain = not sep_is_pattern
max_splits = max_splits + 1
repeat
- local np, npe = str_find(str, delim, pos, plain)
+ local np, npe = string_find(str, delim, pos, plain)
np, npe = (np or (len+1)), (npe or (len+1))
if (not np) or (max_splits == 1) then
np = len + 1
npe = np
end
- local s = str_sub(str, pos, np - 1)
+ local s = string_sub(str, pos, np - 1)
if include_empty or (s ~= "") then
max_splits = max_splits - 1
table_insert(items, s)
@@ -183,9 +185,22 @@ function string.split(str, delim, include_empty, max_splits, sep_is_pattern)
end
--------------------------------------------------------------------------------
+function table.indexof(list, val)
+ for i = 1, #list do
+ if list[i] == val then
+ return i
+ end
+ end
+ return -1
+end
+
+assert(table.indexof({"foo", "bar"}, "foo") == 1)
+assert(table.indexof({"foo", "bar"}, "baz") == -1)
+
+--------------------------------------------------------------------------------
function file_exists(filename)
local f = io.open(filename, "r")
- if f==nil then
+ if f == nil then
return false
else
f:close()
@@ -298,8 +313,8 @@ function core.splittext(text,charlimit)
local current_idx = 1
- local start,stop = string.find(text," ",current_idx)
- local nl_start,nl_stop = string.find(text,"\n",current_idx)
+ local start,stop = string_find(text, " ", current_idx)
+ local nl_start,nl_stop = string_find(text, "\n", current_idx)
local gotnewline = false
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
@@ -309,7 +324,7 @@ function core.splittext(text,charlimit)
local last_line = ""
while start ~= nil do
if string.len(last_line) + (stop-start) > charlimit then
- table.insert(retval,last_line)
+ table_insert(retval, last_line)
last_line = ""
end
@@ -317,17 +332,17 @@ function core.splittext(text,charlimit)
last_line = last_line .. " "
end
- last_line = last_line .. string.sub(text,current_idx,stop -1)
+ last_line = last_line .. string_sub(text, current_idx, stop - 1)
if gotnewline then
- table.insert(retval,last_line)
+ table_insert(retval, last_line)
last_line = ""
gotnewline = false
end
current_idx = stop+1
- start,stop = string.find(text," ",current_idx)
- nl_start,nl_stop = string.find(text,"\n",current_idx)
+ start,stop = string_find(text, " ", current_idx)
+ nl_start,nl_stop = string_find(text, "\n", current_idx)
if nl_start ~= nil and (start == nil or nl_start < start) then
start = nl_start
@@ -338,11 +353,11 @@ function core.splittext(text,charlimit)
--add last part of text
if string.len(last_line) + (string.len(text) - current_idx) > charlimit then
- table.insert(retval,last_line)
- table.insert(retval,string.sub(text,current_idx))
+ table_insert(retval, last_line)
+ table_insert(retval, string_sub(text, current_idx))
else
- last_line = last_line .. " " .. string.sub(text,current_idx)
- table.insert(retval,last_line)
+ last_line = last_line .. " " .. string_sub(text, current_idx)
+ table_insert(retval, last_line)
end
return retval
diff --git a/builtin/common/serialize.lua b/builtin/common/serialize.lua
index 24b2a12ee..90b8b2ad6 100644
--- a/builtin/common/serialize.lua
+++ b/builtin/common/serialize.lua
@@ -115,11 +115,20 @@ function core.serialize(x)
function dump_val(x)
local tp = type(x)
if x == nil then return "nil"
- elseif tp == "number" then return string.format("%d", x)
elseif tp == "string" then return string.format("%q", x)
elseif tp == "boolean" then return x and "true" or "false"
elseif tp == "function" then
return string.format("loadstring(%q)", string.dump(x))
+ elseif tp == "number" then
+ -- Serialize integers with string.format to prevent
+ -- scientific notation, which doesn't preserve
+ -- precision and breaks things like node position
+ -- hashes. Serialize floats normally.
+ if math.floor(x) == x then
+ return string.format("%d", x)
+ else
+ return tostring(x)
+ end
elseif tp == "table" then
local vals = {}
local idx_dumped = {}
diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua
index c353bb913..c7b86461f 100644
--- a/builtin/common/strict.lua
+++ b/builtin/common/strict.lua
@@ -4,6 +4,11 @@
local WARN_INIT = false
+function core.global_exists(name)
+ return rawget(_G, name) ~= nil
+end
+
+
local function warn(message)
print(os.date("%H:%M:%S: WARNING: ")..message)
end
diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua
index 708ea19cf..de8ae4d2c 100644
--- a/builtin/fstk/ui.lua
+++ b/builtin/fstk/ui.lua
@@ -23,7 +23,7 @@ ui.default = nil
function ui.add(child)
--TODO check child
ui.childlist[child.name] = child
-
+
return child.name
end
@@ -33,7 +33,7 @@ function ui.delete(child)
if ui.childlist[child.name] == nil then
return false
end
-
+
ui.childlist[child.name] = nil
return true
end
@@ -54,17 +54,52 @@ end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
+local function wordwrap_quickhack(str)
+ local res = ""
+ local ar = str:split("\n")
+ for i = 1, #ar do
+ local text = ar[i]
+ -- Hack to add word wrapping.
+ -- TODO: Add engine support for wrapping in formspecs
+ while #text > 80 do
+ if res ~= "" then
+ res = res .. ","
+ end
+ res = res .. core.formspec_escape(string.sub(text, 1, 79))
+ text = string.sub(text, 80, #text)
+ end
+ if res ~= "" then
+ res = res .. ","
+ end
+ res = res .. core.formspec_escape(text)
+ end
+ return res
+end
+
--------------------------------------------------------------------------------
function ui.update()
local formspec = ""
-- handle errors
- if gamedata ~= nil and gamedata.errormessage ~= nil then
- formspec = "size[12,3.2]" ..
- "textarea[1,1;10,2;;ERROR: " ..
- core.formspec_escape(gamedata.errormessage) ..
- ";]"..
- "button[4.5,2.5;3,0.5;btn_error_confirm;" .. fgettext("Ok") .. "]"
+ if gamedata ~= nil and gamedata.reconnect_requested then
+ formspec = wordwrap_quickhack(gamedata.errormessage or "")
+ formspec = "size[12,5]" ..
+ "label[0.5,0;" .. fgettext("The server has requested a reconnect:") ..
+ "]textlist[0.2,0.8;11.5,3.5;;" .. formspec ..
+ "]button[6,4.6;3,0.5;btn_reconnect_no;" .. fgettext("Main menu") .. "]" ..
+ "button[3,4.6;3,0.5;btn_reconnect_yes;" .. fgettext("Reconnect") .. "]"
+ elseif gamedata ~= nil and gamedata.errormessage ~= nil then
+ formspec = wordwrap_quickhack(gamedata.errormessage)
+ local error_title
+ if string.find(gamedata.errormessage, "ModError") then
+ error_title = fgettext("An error occured in a Lua script, such as a mod:")
+ else
+ error_title = fgettext("An error occured:")
+ end
+ formspec = "size[12,5]" ..
+ "label[0.5,0;" .. error_title ..
+ "]textlist[0.2,0.8;11.5,3.5;;" .. formspec ..
+ "]button[4.5,4.6;3,0.5;btn_error_confirm;" .. fgettext("Ok") .. "]"
else
local active_toplevel_ui_elements = 0
for key,value in pairs(ui.childlist) do
@@ -77,7 +112,7 @@ function ui.update()
end
end
end
-
+
-- no need to show addons if there ain't a toplevel element
if (active_toplevel_ui_elements > 0) then
for key,value in pairs(ui.childlist) do
@@ -106,13 +141,6 @@ end
--------------------------------------------------------------------------------
function ui.handle_buttons(fields)
-
- if fields["btn_error_confirm"] then
- gamedata.errormessage = nil
- update_menu()
- return
- end
-
for key,value in pairs(ui.childlist) do
local retval = value:handle_buttons(fields)
@@ -127,7 +155,7 @@ end
--------------------------------------------------------------------------------
function ui.handle_events(event)
-
+
for key,value in pairs(ui.childlist) do
if value.handle_events ~= nil then
@@ -146,8 +174,15 @@ end
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
core.button_handler = function(fields)
- if fields["btn_error_confirm"] then
+ if fields["btn_reconnect_yes"] then
+ gamedata.reconnect_requested = false
+ gamedata.errormessage = nil
+ gamedata.do_reconnect = true
+ core.start()
+ return
+ elseif fields["btn_reconnect_no"] or fields["btn_error_confirm"] then
gamedata.errormessage = nil
+ gamedata.reconnect_requested = false
ui.update()
return
end
diff --git a/builtin/game/auth.lua b/builtin/game/auth.lua
index 93b009981..423eb3134 100644
--- a/builtin/game/auth.lua
+++ b/builtin/game/auth.lua
@@ -171,6 +171,7 @@ function core.register_authentication_handler(handler)
end
core.registered_auth_handler = handler
core.registered_auth_handler_modname = core.get_current_modname()
+ handler.mod_origin = core.registered_auth_handler_modname
end
function core.get_auth_handler()
diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua
index 91b685fdf..5d317de4b 100644
--- a/builtin/game/chatcommands.lua
+++ b/builtin/game/chatcommands.lua
@@ -10,6 +10,7 @@ function core.register_chatcommand(cmd, def)
def.params = def.params or ""
def.description = def.description or ""
def.privs = def.privs or {}
+ def.mod_origin = core.get_current_modname() or "??"
core.chatcommands[cmd] = def
end
@@ -37,6 +38,7 @@ core.register_on_chat_message(function(name, message)
end
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 success, message = cmd_def.func(name, param)
if message then
core.chat_send_player(name, message)
@@ -189,7 +191,7 @@ core.register_chatcommand("revoke", {
local revoke_privs = core.string_to_privs(revoke_priv_str)
local privs = core.get_player_privs(revoke_name)
for priv, _ in pairs(revoke_privs) do
- if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and
+ if priv ~= "interact" and priv ~= "shout" and
not core.check_player_privs(name, {privs=true}) then
return false, "Your privileges are insufficient."
end
@@ -229,21 +231,28 @@ core.register_chatcommand("setpassword", {
if not toname then
return false, "Name field required"
end
- local actstr = "?"
+ local act_str_past = "?"
+ local act_str_pres = "?"
if not raw_password then
core.set_player_password(toname, "")
- actstr = "cleared"
+ act_str_past = "cleared"
+ act_str_pres = "clears"
else
core.set_player_password(toname,
core.get_password_hash(toname,
raw_password))
- actstr = "set"
+ act_str_past = "set"
+ act_str_pres = "sets"
end
if toname ~= name then
core.chat_send_player(toname, "Your password was "
- .. actstr .. " by " .. name)
+ .. act_str_past .. " by " .. name)
end
- return true, "Password of player \"" .. toname .. "\" " .. actstr
+
+ core.log("action", name .. " " .. act_str_pres
+ .. " password of " .. toname .. ".")
+
+ return true, "Password of player \"" .. toname .. "\" " .. act_str_past
end,
})
@@ -257,6 +266,9 @@ core.register_chatcommand("clearpassword", {
return false, "Name field required"
end
core.set_player_password(toname, '')
+
+ core.log("action", name .. " clears password of " .. toname .. ".")
+
return true, "Password of player \"" .. toname .. "\" cleared"
end,
})
@@ -308,7 +320,7 @@ core.register_chatcommand("teleport", {
teleportee:setpos(p)
return true, "Teleporting to "..core.pos_to_string(p)
end
-
+
local teleportee = nil
local p = nil
local target_name = nil
@@ -345,7 +357,7 @@ core.register_chatcommand("teleport", {
return true, "Teleporting " .. teleportee_name
.. " to " .. core.pos_to_string(p)
end
-
+
local teleportee = nil
local p = nil
local teleportee_name = nil
@@ -367,7 +379,7 @@ core.register_chatcommand("teleport", {
.. " to " .. target_name
.. " at " .. core.pos_to_string(p)
end
-
+
return false, 'Invalid parameters ("' .. param
.. '") or player not found (see /help teleport)'
end,
@@ -404,13 +416,14 @@ core.register_chatcommand("set", {
})
core.register_chatcommand("deleteblocks", {
- params = "[here] [<pos1> <pos2>]",
+ params = "(here [radius]) | (<pos1> <pos2>)",
description = "delete map blocks contained in area pos1 to pos2",
privs = {server=true},
func = function(name, param)
local p1 = {}
local p2 = {}
- if param == "here" then
+ local args = param:split(" ")
+ if args[1] == "here" then
local player = core.get_player_by_name(name)
if player == nil then
core.log("error", "player is nil")
@@ -418,6 +431,12 @@ core.register_chatcommand("deleteblocks", {
end
p1 = player:getpos()
p2 = p1
+
+ if #args >= 2 then
+ local radius = tonumber(args[2]) or 0
+ p1 = vector.add(p1, radius)
+ p2 = vector.subtract(p2, radius)
+ end
else
local pos1, pos2 = unpack(param:split(") ("))
if pos1 == nil or pos2 == nil then
@@ -513,22 +532,29 @@ core.register_chatcommand("giveme", {
})
core.register_chatcommand("spawnentity", {
- params = "<EntityName>",
- description = "Spawn entity at your position",
+ params = "<EntityName> [<X>,<Y>,<Z>]",
+ description = "Spawn entity at given (or your) position",
privs = {give=true, interact=true},
func = function(name, param)
- local entityname = string.match(param, "(.+)$")
+ local entityname, p = string.match(param, "^([^ ]+) *(.*)$")
if not entityname then
return false, "EntityName required"
end
- core.log("action", ("/spawnentity invoked, entityname=%q")
- :format(entityname))
+ core.log("action", ("%s invokes /spawnentity, entityname=%q")
+ :format(name, entityname))
local player = core.get_player_by_name(name)
if player == nil then
core.log("error", "Unable to spawn entity, player is nil")
return false, "Unable to spawn entity, player is nil"
end
- local p = player:getpos()
+ if p == "" then
+ p = player:getpos()
+ else
+ p = core.string_to_pos(p)
+ if p == nil then
+ return false, "Invalid parameters ('" .. param .. "')"
+ end
+ end
p.y = p.y + 1
core.add_entity(p, entityname)
return true, ("%q spawned."):format(entityname)
@@ -662,19 +688,41 @@ core.register_chatcommand("status", {
})
core.register_chatcommand("time", {
- params = "<0...24000>",
+ params = "<0..23>:<0..59> | <0..24000>",
description = "set time of day",
- privs = {settime=true},
+ privs = {},
func = function(name, param)
if param == "" then
- return false, "Missing time."
- end
- local newtime = tonumber(param)
- if newtime == nil then
- return false, "Invalid time."
- end
- core.set_timeofday((newtime % 24000) / 24000)
- core.log("action", name .. " sets time " .. newtime)
+ local current_time = math.floor(core.get_timeofday() * 1440)
+ local minutes = current_time % 60
+ local hour = (current_time - minutes) / 60
+ return true, ("Current time is %d:%02d"):format(hour, minutes)
+ end
+ local player_privs = minetest.get_player_privs(name)
+ if not player_privs.settime then
+ return false, "You don't have permission to run this command " ..
+ "(missing privilege: settime)."
+ end
+ local hour, minute = param:match("^(%d+):(%d+)$")
+ if not hour then
+ local new_time = tonumber(param)
+ if not new_time then
+ return false, "Invalid time."
+ end
+ -- Backward compatibility.
+ core.set_timeofday((new_time % 24000) / 24000)
+ core.log("action", name .. " sets time to " .. new_time)
+ return true, "Time of day changed."
+ end
+ hour = tonumber(hour)
+ minute = tonumber(minute)
+ if hour < 0 or hour > 23 then
+ return false, "Invalid hour (must be between 0 and 23 inclusive)."
+ elseif minute < 0 or minute > 59 then
+ return false, "Invalid minute (must be between 0 and 59 inclusive)."
+ end
+ core.set_timeofday((hour * 60 + minute) / 1440)
+ core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute))
return true, "Time of day changed."
end,
})
@@ -732,7 +780,11 @@ core.register_chatcommand("kick", {
if not core.kick_player(tokick, reason) then
return false, "Failed to kick player " .. tokick
end
- core.log("action", name .. " kicked " .. tokick)
+ local log_reason = ""
+ if reason then
+ log_reason = " with reason \"" .. reason .. "\""
+ end
+ core.log("action", name .. " kicks " .. tokick .. log_reason)
return true, "Kicked " .. tokick
end,
})
@@ -788,4 +840,3 @@ core.register_chatcommand("last-login", {
return false, "Last login time is unknown"
end,
})
-
diff --git a/builtin/game/detached_inventory.lua b/builtin/game/detached_inventory.lua
index e8f03b56c..b5d106b04 100644
--- a/builtin/game/detached_inventory.lua
+++ b/builtin/game/detached_inventory.lua
@@ -13,6 +13,7 @@ function core.create_detached_inventory(name, callbacks)
stuff.on_put = callbacks.on_put
stuff.on_take = callbacks.on_take
end
+ stuff.mod_origin = core.get_current_modname() or "??"
core.detached_inventories[name] = stuff
return core.create_detached_inventory_raw(name)
end
diff --git a/builtin/game/item.lua b/builtin/game/item.lua
index e136d4f4c..6628a4081 100644
--- a/builtin/game/item.lua
+++ b/builtin/game/item.lua
@@ -106,7 +106,7 @@ function core.facedir_to_dir(facedir)
{x=0, y=1, z=0}})
--indexed into by a table of correlating facedirs
- [({[0]=1, 2, 3, 4,
+ [({[0]=1, 2, 3, 4,
5, 2, 6, 4,
6, 2, 5, 4,
1, 5, 3, 6,
@@ -238,7 +238,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
core.log("action", placer:get_player_name() .. " places node "
.. def.name .. " at " .. core.pos_to_string(place_to))
-
+
local oldnode = core.get_node(place_to)
local newnode = {name = def.name, param1 = 0, param2 = param2}
@@ -357,19 +357,37 @@ function core.item_drop(itemstack, dropper, pos)
return itemstack
end
-function core.item_eat(hp_change, replace_with_item)
- return function(itemstack, user, pointed_thing) -- closure
- for _, callback in pairs(core.registered_on_item_eats) do
- local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing)
- if result then
- return result
- end
+function core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
+ for _, callback in pairs(core.registered_on_item_eats) do
+ local result = callback(hp_change, replace_with_item, itemstack, user, pointed_thing)
+ if result then
+ return result
end
- if itemstack:take_item() ~= nil then
- user:set_hp(user:get_hp() + hp_change)
- itemstack:add_item(replace_with_item) -- note: replace_with_item is optional
+ end
+ if itemstack:take_item() ~= nil then
+ user:set_hp(user:get_hp() + hp_change)
+
+ if replace_with_item then
+ if itemstack:is_empty() then
+ itemstack:add_item(replace_with_item)
+ else
+ local inv = user:get_inventory()
+ if inv:room_for_item("main", {name=replace_with_item}) then
+ inv:add_item("main", replace_with_item)
+ else
+ local pos = user:getpos()
+ pos.y = math.floor(pos.y + 0.5)
+ core.add_item(pos, replace_with_item)
+ end
+ end
end
- return itemstack
+ end
+ return itemstack
+end
+
+function core.item_eat(hp_change, replace_with_item)
+ return function(itemstack, user, pointed_thing) -- closure
+ return core.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
end
end
@@ -425,7 +443,7 @@ function core.node_dig(pos, node, digger)
local wielded = digger:get_wielded_item()
local drops = core.get_node_drops(node.name, wielded:get_name())
-
+
local wdef = wielded:get_definition()
local tp = wielded:get_tool_capabilities()
local dp = core.get_dig_params(def.groups, tp)
@@ -438,7 +456,7 @@ function core.node_dig(pos, node, digger)
end
end
digger:set_wielded_item(wielded)
-
+
-- Handle drops
core.handle_node_drops(pos, drops, digger)
@@ -449,7 +467,7 @@ function core.node_dig(pos, node, digger)
-- Remove node and update
core.remove_node(pos)
-
+
-- Run callback
if def.after_dig_node then
-- Copy pos and node because callback can modify them
@@ -461,6 +479,15 @@ function core.node_dig(pos, node, digger)
-- Run script hook
local _, callback
for _, callback in ipairs(core.registered_on_dignodes) do
+ local origin = core.callback_origins[callback]
+ if origin then
+ core.set_last_run_mod(origin.mod)
+ --print("Running " .. tostring(callback) ..
+ -- " (a " .. origin.name .. " callback in " .. origin.mod .. ")")
+ else
+ --print("No data associated with callback")
+ end
+
-- Copy pos and node because callback can modify them
local pos_copy = {x=pos.x, y=pos.y, z=pos.z}
local node_copy = {name=node.name, param1=node.param1, param2=node.param2}
@@ -507,7 +534,7 @@ core.nodedef_default = {
on_dig = redef_wrapper(core, 'node_dig'), -- core.node_dig
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,
diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua
index d6781feca..6425a10aa 100644
--- a/builtin/game/item_entity.lua
+++ b/builtin/game/item_entity.lua
@@ -96,6 +96,56 @@ core.register_entity(":__builtin:item", {
self:set_item(self.itemstring)
end,
+ try_merge_with = function(self, own_stack, object, obj)
+ local stack = ItemStack(obj.itemstring)
+ if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then
+ local overflow = false
+ local count = stack:get_count() + own_stack:get_count()
+ local max_count = stack:get_stack_max()
+ if count > max_count then
+ overflow = true
+ count = count - max_count
+ else
+ self.itemstring = ''
+ end
+ local pos = object:getpos()
+ pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15
+ object:moveto(pos, false)
+ local s, c
+ local max_count = stack:get_stack_max()
+ local name = stack:get_name()
+ if not overflow then
+ obj.itemstring = name .. " " .. count
+ s = 0.2 + 0.1 * (count / max_count)
+ c = s
+ object:set_properties({
+ visual_size = {x = s, y = s},
+ collisionbox = {-c, -c, -c, c, c, c}
+ })
+ self.object:remove()
+ -- merging succeeded
+ return true
+ else
+ s = 0.4
+ c = 0.3
+ object:set_properties({
+ visual_size = {x = s, y = s},
+ collisionbox = {-c, -c, -c, c, c, c}
+ })
+ obj.itemstring = name .. " " .. max_count
+ s = 0.2 + 0.1 * (count / max_count)
+ c = s
+ self.object:set_properties({
+ visual_size = {x = s, y = s},
+ collisionbox = {-c, -c, -c, c, c, c}
+ })
+ self.itemstring = name .. " " .. count
+ end
+ end
+ -- merging didn't succeed
+ return false
+ end,
+
on_step = function(self, dtime)
self.age = self.age + dtime
if time_to_live > 0 and self.age > time_to_live then
@@ -105,58 +155,29 @@ core.register_entity(":__builtin:item", {
end
local p = self.object:getpos()
p.y = p.y - 0.5
- local nn = core.get_node(p).name
+ local node = core.get_node_or_nil(p)
+ local in_unloaded = (node == nil)
+ if in_unloaded then
+ -- Don't infinetly fall into unloaded map
+ self.object:setvelocity({x = 0, y = 0, z = 0})
+ self.object:setacceleration({x = 0, y = 0, z = 0})
+ self.physical_state = false
+ self.object:set_properties({physical = false})
+ return
+ end
+ local nn = node.name
-- If node is not registered or node is walkably solid and resting on nodebox
local v = self.object:getvelocity()
if not core.registered_nodes[nn] or core.registered_nodes[nn].walkable and v.y == 0 then
if self.physical_state then
local own_stack = ItemStack(self.object:get_luaentity().itemstring)
- for _,object in ipairs(core.get_objects_inside_radius(p, 0.8)) do
+ -- Merge with close entities of the same item
+ for _, object in ipairs(core.get_objects_inside_radius(p, 0.8)) do
local obj = object:get_luaentity()
- if obj and obj.name == "__builtin:item" and obj.physical_state == false then
- local stack = ItemStack(obj.itemstring)
- if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then
- local overflow = false
- local count = stack:get_count() + own_stack:get_count()
- local max_count = stack:get_stack_max()
- if count>max_count then
- overflow = true
- count = count - max_count
- else
- self.itemstring = ''
- end
- local pos=object:getpos()
- pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15
- object:moveto(pos, false)
- local s, c
- local max_count = stack:get_stack_max()
- local name = stack:get_name()
- if not overflow then
- obj.itemstring = name.." "..count
- s = 0.2 + 0.1 * (count / max_count)
- c = s
- object:set_properties({
- visual_size = {x = s, y = s},
- collisionbox = {-c, -c, -c, c, c, c}
- })
- self.object:remove()
- return
- else
- s = 0.4
- c = 0.3
- object:set_properties({
- visual_size = {x = s, y = s},
- collisionbox = {-c, -c, -c, c, c, c}
- })
- obj.itemstring = name.." "..max_count
- s = 0.2 + 0.1 * (count / max_count)
- c = s
- self.object:set_properties({
- visual_size = {x = s, y = s},
- collisionbox = {-c, -c, -c, c, c, c}
- })
- self.itemstring = name.." "..count
- end
+ if obj and obj.name == "__builtin:item"
+ and obj.physical_state == false then
+ if self:try_merge_with(own_stack, object, obj) then
+ return
end
end
end
diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua
index c31df541d..e3b7d82bc 100644
--- a/builtin/game/misc.lua
+++ b/builtin/game/misc.lua
@@ -4,40 +4,83 @@
-- Misc. API functions
--
-core.timers_to_add = {}
-core.timers = {}
-core.register_globalstep(function(dtime)
- for _, timer in ipairs(core.timers_to_add) do
- table.insert(core.timers, timer)
- end
- core.timers_to_add = {}
- local index = 1
- while index <= #core.timers do
- local timer = core.timers[index]
- timer.time = timer.time - dtime
+local timers = {}
+local mintime
+local function update_timers(delay)
+ mintime = false
+ local sub = 0
+ for index = 1, #timers do
+ index = index - sub
+ local timer = timers[index]
+ timer.time = timer.time - delay
if timer.time <= 0 then
+ core.set_last_run_mod(timer.mod_origin)
timer.func(unpack(timer.args or {}))
- table.remove(core.timers,index)
+ table.remove(timers, index)
+ sub = sub + 1
+ elseif mintime then
+ mintime = math.min(mintime, timer.time)
else
- index = index + 1
+ mintime = timer.time
end
end
+end
+
+local timers_to_add
+local function add_timers()
+ for _, timer in ipairs(timers_to_add) do
+ table.insert(timers, timer)
+ end
+ timers_to_add = false
+end
+
+local delay = 0
+core.register_globalstep(function(dtime)
+ if not mintime then
+ -- abort if no timers are running
+ return
+ end
+ if timers_to_add then
+ add_timers()
+ end
+ delay = delay + dtime
+ if delay < mintime then
+ return
+ end
+ update_timers(delay)
+ delay = 0
end)
function core.after(time, func, ...)
assert(tonumber(time) and type(func) == "function",
"Invalid core.after invocation")
- table.insert(core.timers_to_add, {time=time, func=func, args={...}})
+ if not mintime then
+ mintime = time
+ timers_to_add = {{
+ time = time+delay,
+ func = func,
+ args = {...},
+ mod_origin = core.get_last_run_mod(),
+ }}
+ return
+ end
+ mintime = math.min(mintime, time)
+ timers_to_add = timers_to_add or {}
+ timers_to_add[#timers_to_add+1] = {
+ time = time+delay,
+ func = func,
+ args = {...},
+ mod_origin = core.get_last_run_mod(),
+ }
end
function core.check_player_privs(name, privs)
local player_privs = core.get_player_privs(name)
local missing_privileges = {}
for priv, val in pairs(privs) do
- if val then
- if not player_privs[priv] then
- table.insert(missing_privileges, priv)
- end
+ if val
+ and not player_privs[priv] then
+ table.insert(missing_privileges, priv)
end
end
if #missing_privileges > 0 then
@@ -112,3 +155,14 @@ function core.record_protection_violation(pos, name)
end
end
+local raillike_ids = {}
+local raillike_cur_id = 0
+function core.raillike_group(name)
+ local id = raillike_ids[name]
+ if not id then
+ raillike_cur_id = raillike_cur_id + 1
+ raillike_ids[name] = raillike_cur_id
+ id = raillike_cur_id
+ end
+ return id
+end
diff --git a/builtin/game/register.lua b/builtin/game/register.lua
index f286113ec..d0e04bfc3 100644
--- a/builtin/game/register.lua
+++ b/builtin/game/register.lua
@@ -72,6 +72,7 @@ end
function core.register_abm(spec)
-- Add to core.registered_abms
core.registered_abms[#core.registered_abms+1] = spec
+ spec.mod_origin = core.get_current_modname() or "??"
end
function core.register_entity(name, prototype)
@@ -86,6 +87,7 @@ function core.register_entity(name, prototype)
-- Add to core.registered_entities
core.registered_entities[name] = prototype
+ prototype.mod_origin = core.get_current_modname() or "??"
end
function core.register_item(name, itemdef)
@@ -147,6 +149,8 @@ function core.register_item(name, itemdef)
end
-- END Legacy stuff
+ itemdef.mod_origin = core.get_current_modname() or "??"
+
-- Disable all further modifications
getmetatable(itemdef).__newindex = {}
@@ -326,6 +330,8 @@ function core.override_item(name, redefinition)
end
+core.callback_origins = {}
+
function core.run_callbacks(callbacks, mode, ...)
assert(type(callbacks) == "table")
local cb_len = #callbacks
@@ -338,6 +344,14 @@ function core.run_callbacks(callbacks, mode, ...)
end
local ret = nil
for i = 1, cb_len do
+ local origin = core.callback_origins[callbacks[i]]
+ if origin then
+ core.set_last_run_mod(origin.mod)
+ --print("Running " .. tostring(callbacks[i]) ..
+ -- " (a " .. origin.name .. " callback in " .. origin.mod .. ")")
+ else
+ --print("No data associated with callback")
+ end
local cb_ret = callbacks[i](...)
if mode == 0 and i == 1 then
@@ -370,13 +384,29 @@ end
local function make_registration()
local t = {}
- local registerfunc = function(func) table.insert(t, func) end
+ local registerfunc = function(func)
+ table.insert(t, func)
+ core.callback_origins[func] = {
+ mod = core.get_current_modname() or "??",
+ name = debug.getinfo(1, "n").name or "??"
+ }
+ --local origin = core.callback_origins[func]
+ --print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
+ end
return t, registerfunc
end
local function make_registration_reverse()
local t = {}
- local registerfunc = function(func) table.insert(t, 1, func) end
+ local registerfunc = function(func)
+ table.insert(t, 1, func)
+ core.callback_origins[func] = {
+ mod = core.get_current_modname() or "??",
+ name = debug.getinfo(1, "n").name or "??"
+ }
+ --local origin = core.callback_origins[func]
+ --print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func))
+ end
return t, registerfunc
end
@@ -398,13 +428,49 @@ local function make_registration_wrap(reg_fn_name, clear_fn_name)
local orig_clear_fn = core[clear_fn_name]
core[clear_fn_name] = function()
- list = {}
+ for k in pairs(list) do
+ list[k] = nil
+ end
return orig_clear_fn()
end
return list
end
+core.registered_on_player_hpchanges = { modifiers = { }, loggers = { } }
+
+function core.registered_on_player_hpchange(player, hp_change)
+ local last = false
+ for i = #core.registered_on_player_hpchanges.modifiers, 1, -1 do
+ local func = core.registered_on_player_hpchanges.modifiers[i]
+ hp_change, last = func(player, hp_change)
+ if type(hp_change) ~= "number" then
+ local debuginfo = debug.getinfo(func)
+ error("The register_on_hp_changes function has to return a number at " ..
+ debuginfo.short_src .. " line " .. debuginfo.linedefined)
+ end
+ if last then
+ break
+ end
+ end
+ for i, func in ipairs(core.registered_on_player_hpchanges.loggers) do
+ func(player, hp_change)
+ end
+ return hp_change
+end
+
+function core.register_on_player_hpchange(func, modifier)
+ if modifier then
+ table.insert(core.registered_on_player_hpchanges.modifiers, func)
+ else
+ table.insert(core.registered_on_player_hpchanges.loggers, func)
+ end
+ core.callback_origins[func] = {
+ mod = core.get_current_modname() or "??",
+ name = debug.getinfo(1, "n").name or "??"
+ }
+end
+
core.registered_biomes = make_registration_wrap("register_biome", "clear_registered_biomes")
core.registered_ores = make_registration_wrap("register_ore", "clear_registered_ores")
core.registered_decorations = make_registration_wrap("register_decoration", "clear_registered_decorations")
@@ -429,6 +495,7 @@ core.registered_on_crafts, core.register_on_craft = make_registration()
core.registered_craft_predicts, core.register_craft_predict = make_registration()
core.registered_on_protection_violation, core.register_on_protection_violation = make_registration()
core.registered_on_item_eats, core.register_on_item_eat = make_registration()
+core.registered_on_punchplayers, core.register_on_punchplayer = make_registration()
--
-- Compatibility for on_mapgen_init()
diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua
index f32d77f2a..6266d0220 100644
--- a/builtin/mainmenu/common.lua
+++ b/builtin/mainmenu/common.lua
@@ -40,6 +40,18 @@ local function render_client_count(n)
end
end
+local function configure_selected_world_params(idx)
+ local worldconfig = modmgr.get_worldconfig(
+ menudata.worldlist:get_list()[idx].path)
+
+ if worldconfig.creative_mode ~= nil then
+ core.setting_set("creative_mode", worldconfig.creative_mode)
+ end
+ if worldconfig.enable_damage ~= nil then
+ core.setting_set("enable_damage", worldconfig.enable_damage)
+ end
+end
+
--------------------------------------------------------------------------------
function image_column(tooltip, flagname)
return "image," ..
@@ -189,7 +201,6 @@ end
--------------------------------------------------------------------------------
function menu_handle_key_up_down(fields,textlist,settingname)
-
if fields["key_up"] then
local oldidx = core.get_textlist_index(textlist)
@@ -197,6 +208,8 @@ function menu_handle_key_up_down(fields,textlist,settingname)
local newidx = oldidx -1
core.setting_set(settingname,
menudata.worldlist:get_raw_index(newidx))
+
+ configure_selected_world_params(newidx)
end
return true
end
@@ -208,18 +221,26 @@ function menu_handle_key_up_down(fields,textlist,settingname)
local newidx = oldidx + 1
core.setting_set(settingname,
menudata.worldlist:get_raw_index(newidx))
+
+ configure_selected_world_params(newidx)
end
-
+
return true
end
-
+
return false
end
--------------------------------------------------------------------------------
function asyncOnlineFavourites()
- menudata.favorites = {}
+ 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
core.handle_async(
function(param)
return core.get_favorites("online")
@@ -227,32 +248,36 @@ function asyncOnlineFavourites()
nil,
function(result)
if core.setting_getbool("public_serverlist") then
- menudata.favorites = order_favorite_list(result)
+ local favs = order_favorite_list(result)
+ if favs[1] then
+ menudata.public_known = favs
+ menudata.favorites = menudata.public_known
+ end
core.event_handler("Refresh")
end
end
- )
+ )
end
--------------------------------------------------------------------------------
function text2textlist(xpos,ypos,width,height,tl_name,textlen,text,transparency)
local textlines = core.splittext(text,textlen)
-
+
local retval = "textlist[" .. xpos .. "," .. ypos .. ";"
.. width .. "," .. height .. ";"
.. tl_name .. ";"
-
+
for i=1, #textlines, 1 do
textlines[i] = textlines[i]:gsub("\r","")
retval = retval .. core.formspec_escape(textlines[i]) .. ","
end
-
+
retval = retval .. ";0;"
-
+
if transparency then
retval = retval .. "true"
end
-
+
retval = retval .. "]"
return retval
@@ -275,3 +300,35 @@ function is_server_protocol_compat_or_error(proto_min, proto_max)
return true
end
+--------------------------------------------------------------------------------
+function menu_worldmt(selected, setting, value)
+ local world = menudata.worldlist:get_list()[selected]
+ if world then
+ local filename = world.path .. DIR_DELIM .. "world.mt"
+ local world_conf = Settings(filename)
+
+ if value ~= nil then
+ if not world_conf:write() then
+ core.log("error", "Failed to write world config file")
+ end
+ world_conf:set(setting, value)
+ world_conf:write()
+ else
+ return world_conf:get(setting)
+ end
+ else
+ return nil
+ end
+end
+
+function menu_worldmt_legacy(selected)
+ local modes_names = {"creative_mode", "enable_damage", "server_announce"}
+ for _, mode_name in pairs(modes_names) do
+ local mode_val = menu_worldmt(selected, mode_name)
+ if mode_val ~= nil then
+ core.setting_set(mode_name, mode_val)
+ else
+ menu_worldmt(selected, mode_name, core.setting_get(mode_name))
+ end
+ end
+end
diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua
index d008ec8b0..176796bef 100644
--- a/builtin/mainmenu/init.lua
+++ b/builtin/mainmenu/init.lua
@@ -139,11 +139,8 @@ local function init_globals()
tv_main:add(tab_credits)
tv_main:set_global_event_handler(main_event_handler)
- if PLATFORM ~= "Android" then
- tv_main:set_fixed_size(true)
- else
- tv_main:set_fixed_size(false)
- end
+
+ tv_main:set_fixed_size(false)
if not (PLATFORM == "Android") then
tv_main:set_tab(core.setting_get("maintab_LAST"))
diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua
index f2938685e..89292ed52 100644
--- a/builtin/mainmenu/modmgr.lua
+++ b/builtin/mainmenu/modmgr.lua
@@ -17,7 +17,7 @@
--------------------------------------------------------------------------------
function get_mods(path,retval,modpack)
- local mods = core.get_dirlist(path, true)
+ local mods = core.get_dir_list(path, true)
for i=1, #mods, 1 do
if mods[i]:sub(1,1) ~= "." then
@@ -94,7 +94,7 @@ function modmgr.getbasefolder(temppath)
}
end
- local subdirs = core.get_dirlist(temppath,true)
+ local subdirs = core.get_dir_list(temppath, true)
--only single mod or modpack allowed
if #subdirs ~= 1 then
@@ -321,8 +321,10 @@ function modmgr.get_worldconfig(worldpath)
for key,value in pairs(worldfile:to_table()) do
if key == "gameid" then
worldconfig.id = value
- else
+ elseif key:sub(0, 9) == "load_mod_" then
worldconfig.global_mods[key] = core.is_yes(value)
+ else
+ worldconfig[key] = value
end
end
diff --git a/builtin/mainmenu/store.lua b/builtin/mainmenu/store.lua
index 999125d6e..ad861082d 100644
--- a/builtin/mainmenu/store.lua
+++ b/builtin/mainmenu/store.lua
@@ -122,35 +122,36 @@ end
function modstore.showdownloading(title)
local new_dlg = dialog_create("store_downloading",
function(data)
- return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") ..
- " " .. data.title .. " " ..
- fgettext("please wait...") .. "]"
+ return "size[6,2]label[0.25,0.75;" ..
+ fgettext("Downloading $1, please wait...", data.title) .. "]"
end,
function(this,fields)
if fields["btn_hidden_close_download"] ~= nil then
if fields["btn_hidden_close_download"].successfull then
modstore.lastmodentry = fields["btn_hidden_close_download"]
- modstore.successfulldialog()
+ modstore.successfulldialog(this)
else
+ this.parent:show()
+ this:delete()
modstore.lastmodtitle = ""
end
- this:delete()
return true
end
return false
end,
- nil,
- modstore.tv_store)
+ nil)
+ new_dlg:set_parent(modstore.tv_store)
+ modstore.tv_store:hide()
new_dlg.data.title = title
new_dlg:show()
end
--------------------------------------------------------------------------------
-- @function [parent=#modstore] successfulldialog
-function modstore.successfulldialog()
+function modstore.successfulldialog(downloading_dlg)
local new_dlg = dialog_create("store_downloading",
function(data)
local retval = ""
@@ -158,18 +159,16 @@ function modstore.successfulldialog()
if modstore.lastmodentry ~= nil then
retval = retval .. "label[0,0.25;" .. fgettext("Successfully installed:") .. "]"
retval = retval .. "label[3,0.25;" .. modstore.lastmodentry.moddetails.title .. "]"
-
-
retval = retval .. "label[0,0.75;" .. fgettext("Shortname:") .. "]"
retval = retval .. "label[3,0.75;" .. core.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]"
-
end
- retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]"
+ retval = retval .. "button[2.2,1.5;1.5,0.5;btn_confirm_mod_successfull;" .. fgettext("Ok") .. "]"
+ return retval
end,
function(this,fields)
if fields["btn_confirm_mod_successfull"] ~= nil then
this.parent:show()
- this:hide()
+ downloading_dlg:delete()
this:delete()
return true
@@ -177,10 +176,10 @@ function modstore.successfulldialog()
return false
end,
- nil,
- modstore.tv_store)
+ nil)
- new_dlg.data.title = title
+ new_dlg:set_parent(modstore.tv_store)
+ modstore.tv_store:hide()
new_dlg:show()
end
@@ -217,7 +216,9 @@ function modstore.handle_buttons(parent, fields, name, data)
end
if fields["btn_modstore_close"] then
+ local maintab = ui.find_by_name("maintab")
parent:hide()
+ maintab:show()
return true
end
@@ -228,12 +229,7 @@ function modstore.handle_buttons(parent, fields, name, data)
for i=1,#modstore.modlist_unsorted.data,1 do
if modstore.modlist_unsorted.data[i].id == modid then
local moddetails = modstore.modlist_unsorted.data[i].details
-
- if modstore.lastmodtitle ~= "" then
- modstore.lastmodtitle = modstore.lastmodtitle .. ", "
- end
-
- modstore.lastmodtitle = modstore.lastmodtitle .. moddetails.title
+ modstore.lastmodtitle = moddetails.title
if not core.handle_async(
function(param)
@@ -281,7 +277,7 @@ function modstore.handle_buttons(parent, fields, name, data)
texturename = modstore.modlist_unsorted.data[i].texturename
},
function(result)
- print("Result from async: " .. dump(result.successfull))
+ --print("Result from async: " .. dump(result.successfull))
if result.successfull then
modmgr.installmod(result.filename,result.moddetails.basename)
os.remove(result.filename)
@@ -299,7 +295,7 @@ function modstore.handle_buttons(parent, fields, name, data)
print("ERROR: async event failed")
gamedata.errormessage = "Failed to download " .. modstore.lastmodtitle
end
- parent:hide()
+
modstore.showdownloading(modstore.lastmodtitle)
return true
end
diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua
index 2a7aa26a5..33c84f58f 100644
--- a/builtin/mainmenu/tab_credits.lua
+++ b/builtin/mainmenu/tab_credits.lua
@@ -25,41 +25,56 @@ tab_credits = {
return "label[0.5,3.2;Minetest " .. core.get_version() .. "]" ..
"label[0.5,3.5;http://minetest.net]" ..
"image[0.5,1;" .. core.formspec_escape(logofile) .. "]" ..
- "textlist[3.5,-0.25;8.5,5.8;list_credits;" ..
- "#FFFF00" .. fgettext("Core Developers") .."," ..
- "Perttu Ahola (celeron55) <celeron55@gmail.com>,"..
- "Ryan Kwolek (kwolekr) <kwolekr@minetest.net>,"..
- "PilzAdam <pilzadam@minetest.net>," ..
- "Lisa Milne (darkrose) <lisa@ltmnet.com>,"..
- "Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>,"..
- "sfan5 <sfan5@live.de>,"..
- "kahrl <kahrl@gmx.net>,"..
- "sapier,"..
- "ShadowNinja <shadowninja@minetest.net>,"..
- "Nathanael Courant (Nore/Novatux) <nore@mesecons.net>,"..
- "BlockMen,"..
- "Craig Robbins (Zeno),"..
- ","..
- "#FFFF00" .. fgettext("Active Contributors") .. "," ..
- "TriBlade9 <triblade9@mail.com>,"..
- "SmallJoker <mk939@ymail.com>,"..
- "Zefram <zefram@fysh.org>,"..
- "," ..
- "#FFFF00" .. fgettext("Previous Contributors") .. "," ..
- "Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>,"..
- "Jurgen Doser (doserj) <jurgen.doser@gmail.com>,"..
- "Jeija <jeija@mesecons.net>,"..
- "MirceaKitsune <mirceakitsune@gmail.com>,"..
- "dannydark <the_skeleton_of_a_child@yahoo.co.uk>,"..
- "0gb.us <0gb.us@0gb.us>,"..
- "proller <proler@gmail.com>,"..
- "Ilya Zhuravlev (xyz) <xyz@minetest.net>,"..
- "Guiseppe Bilotta (Oblomov) <guiseppe.bilotta@gmail.com>,"..
- "Jonathan Neuschafer <j.neuschaefer@gmx.net>,"..
- "Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>,"..
- "Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>,"..
- "matttpt <matttpt@gmail.com>,"..
- "JacobF <queatz@gmail.com>,"..
- ";0;true]"
+ "tablecolumns[color;text]" ..
+ "tableoptions[background=#00000000;highlight=#00000000;border=false]" ..
+ "table[3.5,-0.25;8.5,5.8;list_credits;" ..
+ "#FFFF00," .. fgettext("Core Developers") .."," ..
+ ",Perttu Ahola (celeron55) <celeron55@gmail.com>,"..
+ ",Ryan Kwolek (kwolekr) <kwolekr@minetest.net>,"..
+ ",PilzAdam <pilzadam@minetest.net>," ..
+ ",sfan5 <sfan5@live.de>,"..
+ ",kahrl <kahrl@gmx.net>,"..
+ ",sapier,"..
+ ",ShadowNinja <shadowninja@minetest.net>,"..
+ ",Nathanael Courant (Nore/Ekdohibs) <nore@mesecons.net>,"..
+ ",BlockMen,"..
+ ",Craig Robbins (Zeno),"..
+ ",Loic Blot (nerzhul/nrz) <loic.blot@unix-experience.fr>,"..
+ ",Mat Gregory (paramat),"..
+ ",est31 <MTest31@outlook.com>," ..
+ ",,"..
+ "#FFFF00," .. fgettext("Active Contributors") .. "," ..
+ ",SmallJoker <mk939@ymail.com>," ..
+ ",Andrew Ward (rubenwardy) <rubenwardy@gmail.com>," ..
+ ",Aaron Suen <warr1024@gmail.com>," ..
+ ",Sokomine <wegwerf@anarres.dyndns.org>," ..
+ ",Břetislav Štec (TeTpaAka)," ..
+ ",Jean-Patrick G (kilbith) <jeanpatrick.guerrero@gmail.com>," ..
+ ",Rui <mrrst0914@gmail.com>," ..
+ ",Diego Martinez (kaeza) <kaeza@users.sf.net>," ..
+ ",," ..
+ "#FFFF00," .. fgettext("Previous Core Developers") .."," ..
+ ",Maciej Kasatkin (RealBadAngel) <maciej.kasatkin@o2.pl>,"..
+ ",Lisa Milne (darkrose) <lisa@ltmnet.com>," ..
+ ",proller," ..
+ ",Ilya Zhuravlev (xyz) <xyz@minetest.net>," ..
+ ",," ..
+ "#FFFF00," .. fgettext("Previous Contributors") .. "," ..
+ ",Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>,"..
+ ",Jurgen Doser (doserj) <jurgen.doser@gmail.com>,"..
+ ",Gregory Currie (gregorycu)," ..
+ ",Jeija <jeija@mesecons.net>,"..
+ ",MirceaKitsune <mirceakitsune@gmail.com>,"..
+ ",dannydark <the_skeleton_of_a_child@yahoo.co.uk>,"..
+ ",0gb.us <0gb.us@0gb.us>,"..
+ ",Guiseppe Bilotta (Oblomov) <guiseppe.bilotta@gmail.com>,"..
+ ",Jonathan Neuschafer <j.neuschaefer@gmx.net>,"..
+ ",Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>,"..
+ ",Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>,"..
+ ",matttpt <matttpt@gmail.com>,"..
+ ",JacobF <queatz@gmail.com>,"..
+ ",TriBlade9 <triblade9@mail.com>,"..
+ ",Zefram <zefram@fysh.org>,"..
+ ";1]"
end
}
diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua
index 901f14553..2ddc9b07c 100644
--- a/builtin/mainmenu/tab_mods.lua
+++ b/builtin/mainmenu/tab_mods.lua
@@ -36,7 +36,10 @@ local function get_formspec(tabview, name, tabdata)
-- "label[0.8,4.2;" .. fgettext("Add mod:") .. "]" ..
-- TODO Disabled due to upcoming release 0.4.8 and irrlicht messing up localization
-- "button[0.75,4.85;1.8,0.5;btn_mod_mgr_install_local;".. fgettext("Local install") .. "]" ..
- "button[0,4.85;5.25,0.5;btn_modstore;".. fgettext("Online mod repository") .. "]"
+
+-- TODO Disabled due to service being offline, and not likely to come online again, in this form
+-- "button[0,4.85;5.25,0.5;btn_modstore;".. fgettext("Online mod repository") .. "]"
+ ""
local selected_mod = nil
diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua
index f9ac78f17..570259718 100644
--- a/builtin/mainmenu/tab_multiplayer.lua
+++ b/builtin/mainmenu/tab_multiplayer.lua
@@ -202,8 +202,9 @@ local function main_button_handler(tabview, fields, name, tabdata)
return true
end
- if fields["btn_mp_connect"] ~= nil or
- fields["key_enter"] ~= nil then
+ if (fields["btn_mp_connect"] ~= nil or
+ fields["key_enter"] ~= nil) and fields["te_address"] ~= nil and
+ fields["te_port"] ~= nil then
gamedata.playername = fields["te_name"]
gamedata.password = fields["te_pwd"]
diff --git a/builtin/mainmenu/tab_server.lua b/builtin/mainmenu/tab_server.lua
index 1ae2a0656..d08eecc21 100644
--- a/builtin/mainmenu/tab_server.lua
+++ b/builtin/mainmenu/tab_server.lua
@@ -67,6 +67,9 @@ local function main_button_handler(this, fields, name, tabdata)
if fields["srv_worlds"] ~= nil then
local event = core.explode_textlist_event(fields["srv_worlds"])
+ local selected = core.get_textlist_index("srv_worlds")
+
+ menu_worldmt_legacy(selected)
if event.type == "DCL" then
world_doubleclick = true
@@ -84,16 +87,25 @@ local function main_button_handler(this, fields, name, tabdata)
if fields["cb_creative_mode"] then
core.setting_set("creative_mode", fields["cb_creative_mode"])
+ local selected = core.get_textlist_index("srv_worlds")
+ menu_worldmt(selected, "creative_mode", fields["cb_creative_mode"])
+
return true
end
if fields["cb_enable_damage"] then
core.setting_set("enable_damage", fields["cb_enable_damage"])
+ local selected = core.get_textlist_index("srv_worlds")
+ menu_worldmt(selected, "enable_damage", fields["cb_enable_damage"])
+
return true
end
if fields["cb_server_announce"] then
core.setting_set("server_announce", fields["cb_server_announce"])
+ local selected = core.get_textlist_index("srv_worlds")
+ menu_worldmt(selected, "server_announce", fields["cb_server_announce"])
+
return true
end
@@ -101,12 +113,12 @@ local function main_button_handler(this, fields, name, tabdata)
world_doubleclick or
fields["key_enter"] then
local selected = core.get_textlist_index("srv_worlds")
- if selected ~= nil then
+ gamedata.selected_world = menudata.worldlist:get_raw_index(selected)
+ if selected ~= nil and gamedata.selected_world ~= 0 then
gamedata.playername = fields["te_playername"]
gamedata.password = fields["te_passwd"]
gamedata.port = fields["te_serverport"]
gamedata.address = ""
- gamedata.selected_world = menudata.worldlist:get_raw_index(selected)
core.setting_set("port",gamedata.port)
if fields["te_serveraddr"] ~= nil then
@@ -115,12 +127,17 @@ local function main_button_handler(this, fields, name, tabdata)
--update last game
local world = menudata.worldlist:get_raw_element(gamedata.selected_world)
+ if world then
+ local game, index = gamemgr.find_by_gameid(world.gameid)
+ core.setting_set("menu_last_game", game.id)
+ end
- local game,index = gamemgr.find_by_gameid(world.gameid)
- core.setting_set("menu_last_game",game.id)
core.start()
- return true
+ else
+ gamedata.errormessage =
+ fgettext("No world created or selected!")
end
+ return true
end
if fields["world_create"] ~= nil then
diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua
index 881a63498..c17a39432 100644
--- a/builtin/mainmenu/tab_settings.lua
+++ b/builtin/mainmenu/tab_settings.lua
@@ -17,6 +17,17 @@
--------------------------------------------------------------------------------
+local leaves_style_labels = {
+ fgettext("Opaque Leaves"),
+ fgettext("Simple Leaves"),
+ fgettext("Fancy Leaves")
+}
+
+local leaves_style = {
+ {leaves_style_labels[1]..","..leaves_style_labels[2]..","..leaves_style_labels[3]},
+ {"opaque", "simple", "fancy"},
+}
+
local dd_filter_labels = {
fgettext("No Filter"),
fgettext("Bilinear Filter"),
@@ -39,6 +50,29 @@ local mipmap = {
{"", "mip_map", "anisotropic_filter"},
}
+local function getLeavesStyleSettingIndex()
+ local style = core.setting_get("leaves_style")
+ if (style == leaves_style[2][3]) then
+ return 3
+ elseif (style == leaves_style[2][2]) then
+ return 2
+ end
+ return 1
+end
+
+local dd_antialiasing_labels = {
+ fgettext("None"),
+ fgettext("2x"),
+ fgettext("4x"),
+ fgettext("8x"),
+}
+
+local antialiasing = {
+ {dd_antialiasing_labels[1]..","..dd_antialiasing_labels[2]..","..
+ dd_antialiasing_labels[3]..","..dd_antialiasing_labels[4]},
+ {"0", "2", "4", "8"}
+}
+
local function getFilterSettingIndex()
if (core.setting_get(filters[2][3]) == "true") then
return 3
@@ -59,16 +93,23 @@ local function getMipmapSettingIndex()
return 1
end
-local function video_driver_fname_to_name(selected_driver)
- local video_drivers = core.get_video_drivers()
-
- for i=1, #video_drivers do
- if selected_driver == video_drivers[i].friendly_name then
- return video_drivers[i].name:lower()
+local function getAntialiasingSettingIndex()
+ local antialiasing_setting = core.setting_get("fsaa")
+ for i = 1, #(antialiasing[2]) do
+ if antialiasing_setting == antialiasing[2][i] then
+ return i
end
end
+ return 1
+end
- return ""
+local function antialiasing_fname_to_name(fname)
+ for i = 1, #(dd_antialiasing_labels) do
+ if fname == dd_antialiasing_labels[i] then
+ return antialiasing[2][i]
+ end
+ end
+ return 0
end
local function dlg_confirm_reset_formspec(data)
@@ -159,50 +200,31 @@ local function scrollbar_to_gui_scale(value)
end
local function formspec(tabview, name, tabdata)
- local video_drivers = core.get_video_drivers()
- local current_video_driver = core.setting_get("video_driver"):lower()
-
- local driver_formspec_string = ""
- local driver_current_idx = 0
-
- for i=2, #video_drivers do
- driver_formspec_string = driver_formspec_string .. video_drivers[i].friendly_name
- if i ~= #video_drivers then
- driver_formspec_string = driver_formspec_string .. ","
- end
-
- if current_video_driver == video_drivers[i].name:lower() then
- driver_current_idx = i - 1
- end
- end
-
local tab_string =
- "box[0,0;3.5,3.9;#999999]" ..
+ "box[0,0;3.5,4.0;#999999]" ..
"checkbox[0.25,0;cb_smooth_lighting;".. fgettext("Smooth Lighting")
.. ";".. dump(core.setting_getbool("smooth_lighting")) .. "]"..
"checkbox[0.25,0.5;cb_particles;".. fgettext("Enable Particles") .. ";"
.. dump(core.setting_getbool("enable_particles")) .. "]"..
"checkbox[0.25,1;cb_3d_clouds;".. fgettext("3D Clouds") .. ";"
.. dump(core.setting_getbool("enable_3d_clouds")) .. "]"..
- "checkbox[0.25,1.5;cb_fancy_trees;".. fgettext("Fancy Trees") .. ";"
- .. dump(core.setting_getbool("new_style_leaves")) .. "]"..
- "checkbox[0.25,2.0;cb_opaque_water;".. fgettext("Opaque Water") .. ";"
+ "checkbox[0.25,1.5;cb_opaque_water;".. fgettext("Opaque Water") .. ";"
.. dump(core.setting_getbool("opaque_water")) .. "]"..
- "checkbox[0.25,2.5;cb_connected_glass;".. fgettext("Connected Glass") .. ";"
+ "checkbox[0.25,2.0;cb_connected_glass;".. fgettext("Connected Glass") .. ";"
.. dump(core.setting_getbool("connected_glass")) .. "]"..
- "checkbox[0.25,3.0;cb_node_highlighting;".. fgettext("Node Highlighting") .. ";"
+ "checkbox[0.25,2.5;cb_node_highlighting;".. fgettext("Node Highlighting") .. ";"
.. dump(core.setting_getbool("enable_node_highlighting")) .. "]"..
+ "dropdown[0.25,3.2;3.3;dd_leaves_style;" .. leaves_style[1][1] .. ";"
+ .. getLeavesStyleSettingIndex() .. "]" ..
"box[3.75,0;3.75,3.45;#999999]" ..
"label[3.85,0.1;".. fgettext("Texturing:") .. "]"..
"dropdown[3.85,0.55;3.85;dd_filters;" .. filters[1][1] .. ";"
.. getFilterSettingIndex() .. "]" ..
"dropdown[3.85,1.35;3.85;dd_mipmap;" .. mipmap[1][1] .. ";"
.. getMipmapSettingIndex() .. "]" ..
- "label[3.85,2.15;".. fgettext("Rendering:") .. "]"..
- "dropdown[3.85,2.6;3.85;dd_video_driver;"
- .. driver_formspec_string .. ";" .. driver_current_idx .. "]" ..
- "tooltip[dd_video_driver;" ..
- fgettext("Restart minetest for driver change to take effect") .. "]" ..
+ "label[3.85,2.15;".. fgettext("Antialiasing:") .. "]"..
+ "dropdown[3.85,2.6;3.85;dd_antialiasing;" .. antialiasing[1][1] .. ";"
+ .. getAntialiasingSettingIndex() .. "]" ..
"box[7.75,0;4,4;#999999]" ..
"checkbox[8,0;cb_shaders;".. fgettext("Shaders") .. ";"
.. dump(core.setting_getbool("enable_shaders")) .. "]"
@@ -353,10 +375,16 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
core.setting_set("touchscreen_threshold",fields["dd_touchthreshold"])
ddhandled = true
end
-
- if fields["dd_video_driver"] then
- core.setting_set("video_driver",
- video_driver_fname_to_name(fields["dd_video_driver"]))
+ if fields["dd_leaves_style"] == leaves_style_labels[3] then
+ core.setting_set("leaves_style", leaves_style[2][3])
+ ddhandled = true
+ end
+ if fields["dd_leaves_style"] == leaves_style_labels[2] then
+ core.setting_set("leaves_style", leaves_style[2][2])
+ ddhandled = true
+ end
+ if fields["dd_leaves_style"] == leaves_style_labels[1] then
+ core.setting_set("leaves_style", leaves_style[2][1])
ddhandled = true
end
if fields["dd_filters"] == dd_filter_labels[1] then
@@ -389,6 +417,11 @@ local function handle_settings_buttons(this, fields, tabname, tabdata)
core.setting_set("anisotropic_filter", "true")
ddhandled = true
end
+ if fields["dd_antialiasing"] then
+ core.setting_set("fsaa",
+ antialiasing_fname_to_name(fields["dd_antialiasing"]))
+ ddhandled = true
+ end
return ddhandled
end
diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua
index 995c72132..434113b5f 100644
--- a/builtin/mainmenu/tab_simple_main.lua
+++ b/builtin/mainmenu/tab_simple_main.lua
@@ -69,20 +69,18 @@ local function get_formspec(tabview, name, tabdata)
-- separator
retval = retval ..
- "box[-0.3,3.75;12.4,0.1;#FFFFFF]"
+ "box[-0.28,3.75;12.4,0.1;#FFFFFF]"
-- checkboxes
retval = retval ..
- "checkbox[1.0,3.9;cb_creative;".. fgettext("Creative Mode") .. ";" ..
+ "checkbox[8.0,3.9;cb_creative;".. fgettext("Creative Mode") .. ";" ..
dump(core.setting_getbool("creative_mode")) .. "]"..
- "checkbox[5.0,3.9;cb_damage;".. fgettext("Enable Damage") .. ";" ..
- dump(core.setting_getbool("enable_damage")) .. "]" ..
- "checkbox[8,3.9;cb_fly_mode;".. fgettext("Fly mode") .. ";" ..
- dump(core.setting_getbool("free_move")) .. "]"
+ "checkbox[8.0,4.4;cb_damage;".. fgettext("Enable Damage") .. ";" ..
+ dump(core.setting_getbool("enable_damage")) .. "]"
-- buttons
retval = retval ..
- "button[2.0,4.5;6,1.5;btn_start_singleplayer;" .. fgettext("Start Singleplayer") .. "]" ..
- "button[8.25,4.5;2.5,1.5;btn_config_sp_world;" .. fgettext("Config mods") .. "]"
+ "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
@@ -138,11 +136,6 @@ local function main_button_handler(tabview, fields, name, tabdata)
return true
end
- if fields["cb_fly_mode"] then
- core.setting_set("free_move", fields["cb_fly_mode"])
- return true
- end
-
if fields["btn_mp_connect"] ~= nil or
fields["key_enter"] ~= nil then
diff --git a/builtin/mainmenu/tab_singleplayer.lua b/builtin/mainmenu/tab_singleplayer.lua
index 9dc377a8f..a40918af9 100644
--- a/builtin/mainmenu/tab_singleplayer.lua
+++ b/builtin/mainmenu/tab_singleplayer.lua
@@ -23,9 +23,9 @@ local function current_game()
end
local function singleplayer_refresh_gamebar()
-
+
local old_bar = ui.find_by_name("game_button_bar")
-
+
if old_bar ~= nil then
old_bar:delete()
end
@@ -38,6 +38,17 @@ local function singleplayer_refresh_gamebar()
core.set_topleft_text(gamemgr.games[j].name)
core.setting_set("menu_last_game",gamemgr.games[j].id)
menudata.worldlist:set_filtercriteria(gamemgr.games[j].id)
+ local index = filterlist.get_current_index(menudata.worldlist,
+ tonumber(core.setting_get("mainmenu_last_selected_world")))
+ if not index or index < 1 then
+ local selected = core.get_textlist_index("sp_worlds")
+ if selected ~= nil and selected < #menudata.worldlist:get_list() then
+ index = selected
+ else
+ index = #menudata.worldlist:get_list()
+ end
+ end
+ menu_worldmt_legacy(index)
return true
end
end
@@ -76,7 +87,7 @@ end
local function get_formspec(tabview, name, tabdata)
local retval = ""
-
+
local index = filterlist.get_current_index(menudata.worldlist,
tonumber(core.setting_get("mainmenu_last_selected_world"))
)
@@ -105,14 +116,17 @@ local function main_button_handler(this, fields, name, tabdata)
if fields["sp_worlds"] ~= nil then
local event = core.explode_textlist_event(fields["sp_worlds"])
+ local selected = core.get_textlist_index("sp_worlds")
+
+ menu_worldmt_legacy(selected)
if event.type == "DCL" then
world_doubleclick = true
end
- if event.type == "CHG" then
+ if event.type == "CHG" and selected ~= nil then
core.setting_set("mainmenu_last_selected_world",
- menudata.worldlist:get_raw_index(core.get_textlist_index("sp_worlds")))
+ menudata.worldlist:get_raw_index(selected))
return true
end
end
@@ -123,11 +137,17 @@ local function main_button_handler(this, fields, name, tabdata)
if fields["cb_creative_mode"] then
core.setting_set("creative_mode", fields["cb_creative_mode"])
+ local selected = core.get_textlist_index("sp_worlds")
+ menu_worldmt(selected, "creative_mode", fields["cb_creative_mode"])
+
return true
end
if fields["cb_enable_damage"] then
core.setting_set("enable_damage", fields["cb_enable_damage"])
+ local selected = core.get_textlist_index("sp_worlds")
+ menu_worldmt(selected, "enable_damage", fields["cb_enable_damage"])
+
return true
end
@@ -135,12 +155,14 @@ local function main_button_handler(this, fields, name, tabdata)
world_doubleclick or
fields["key_enter"] then
local selected = core.get_textlist_index("sp_worlds")
+ gamedata.selected_world = menudata.worldlist:get_raw_index(selected)
- if selected ~= nil then
- gamedata.selected_world = menudata.worldlist:get_raw_index(selected)
- gamedata.singleplayer = true
-
+ if selected ~= nil and gamedata.selected_world ~= 0 then
+ gamedata.singleplayer = true
core.start()
+ else
+ gamedata.errormessage =
+ fgettext("No world created or selected!")
end
return true
end
diff --git a/builtin/mainmenu/tab_texturepacks.lua b/builtin/mainmenu/tab_texturepacks.lua
index d32c073ab..3fb7b8598 100644
--- a/builtin/mainmenu/tab_texturepacks.lua
+++ b/builtin/mainmenu/tab_texturepacks.lua
@@ -45,12 +45,12 @@ end
--------------------------------------------------------------------------------
local function get_formspec(tabview, name, tabdata)
-
+
local retval = "label[4,-0.25;".. fgettext("Select texture pack:") .. "]"..
"textlist[4,0.25;7.5,5.0;TPs;"
local current_texture_path = core.setting_get("texture_path")
- local list = filter_texture_pack_list(core.get_dirlist(core.get_texturepath(), true))
+ local list = filter_texture_pack_list(core.get_dir_list(core.get_texturepath(), true))
local index = tonumber(core.setting_get("mainmenu_last_selected_TP"))
if index == nil then index = 1 end
@@ -62,10 +62,18 @@ local function get_formspec(tabview, name, tabdata)
return retval
end
- local infofile = current_texture_path ..DIR_DELIM.."info.txt"
+ local infofile = current_texture_path ..DIR_DELIM.."description.txt"
+ -- This adds backwards compatibility for old texture pack description files named
+ -- "info.txt", and should be removed once all such texture packs have been updated
+ if not file_exists(infofile) then
+ infofile = current_texture_path ..DIR_DELIM.."info.txt"
+ if file_exists(infofile) then
+ minetest.log("info.txt is depreciated. description.txt should be used instead.");
+ end
+ end
local infotext = ""
local f = io.open(infofile, "r")
- if f==nil then
+ if not f then
infotext = fgettext("No information available")
else
infotext = f:read("*all")
@@ -94,7 +102,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
local index = core.get_textlist_index("TPs")
core.setting_set("mainmenu_last_selected_TP",
index)
- local list = filter_texture_pack_list(core.get_dirlist(core.get_texturepath(), true))
+ local list = filter_texture_pack_list(core.get_dir_list(core.get_texturepath(), true))
local current_index = core.get_textlist_index("TPs")
if current_index ~= nil and #list >= current_index then
local new_path = core.get_texturepath()..DIR_DELIM..list[current_index]
diff --git a/builtin/mainmenu/textures.lua b/builtin/mainmenu/textures.lua
index 56992c0c5..075f38ee0 100644
--- a/builtin/mainmenu/textures.lua
+++ b/builtin/mainmenu/textures.lua
@@ -129,7 +129,7 @@ function mm_texture.set_generic(identifier)
end
--------------------------------------------------------------------------------
-function mm_texture.set_game(identifier,gamedetails)
+function mm_texture.set_game(identifier, gamedetails)
if gamedetails == nil then
return false
@@ -137,15 +137,34 @@ function mm_texture.set_game(identifier,gamedetails)
if mm_texture.texturepack ~= nil then
local path = mm_texture.texturepack .. DIR_DELIM ..
- gamedetails.id .. "_menu_" .. identifier .. ".png"
- if core.set_background(identifier,path) then
+ gamedetails.id .. "_menu_" .. identifier .. ".png"
+ if core.set_background(identifier, path) then
return true
end
end
- local path = gamedetails.path .. DIR_DELIM .."menu" ..
- DIR_DELIM .. identifier .. ".png"
- if core.set_background(identifier,path) then
+ -- Find out how many randomized textures the subgame provides
+ local n = 0
+ local filename
+ local menu_files = core.get_dir_list(gamedetails.path .. DIR_DELIM .. "menu", false)
+ for i = 1, #menu_files do
+ filename = identifier .. "." .. i .. ".png"
+ if table.indexof(menu_files, filename) == -1 then
+ n = i - 1
+ break
+ end
+ end
+ -- Select random texture, 0 means standard texture
+ n = math.random(0, n)
+ if n == 0 then
+ filename = identifier .. ".png"
+ else
+ filename = identifier .. "." .. n .. ".png"
+ end
+
+ local path = gamedetails.path .. DIR_DELIM .. "menu" ..
+ DIR_DELIM .. filename
+ if core.set_background(identifier, path) then
return true
end
diff --git a/client/shaders/minimap_shader/opengl_fragment.glsl b/client/shaders/minimap_shader/opengl_fragment.glsl
new file mode 100644
index 000000000..fa4f9cb1a
--- /dev/null
+++ b/client/shaders/minimap_shader/opengl_fragment.glsl
@@ -0,0 +1,32 @@
+uniform sampler2D baseTexture;
+uniform sampler2D normalTexture;
+uniform vec3 yawVec;
+
+void main (void)
+{
+ vec2 uv = gl_TexCoord[0].st;
+
+ //texture sampling rate
+ const float step = 1.0 / 256.0;
+ float tl = texture2D(normalTexture, vec2(uv.x - step, uv.y + step)).r;
+ float t = texture2D(normalTexture, vec2(uv.x - step, uv.y - step)).r;
+ float tr = texture2D(normalTexture, vec2(uv.x + step, uv.y + step)).r;
+ float r = texture2D(normalTexture, vec2(uv.x + step, uv.y)).r;
+ float br = texture2D(normalTexture, vec2(uv.x + step, uv.y - step)).r;
+ float b = texture2D(normalTexture, vec2(uv.x, uv.y - step)).r;
+ float bl = texture2D(normalTexture, vec2(uv.x - step, uv.y - step)).r;
+ float l = texture2D(normalTexture, vec2(uv.x - step, uv.y)).r;
+ float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl);
+ float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr);
+ vec4 bump = vec4 (normalize(vec3 (dX, dY, 0.1)),1.0);
+ float height = 2.0 * texture2D(normalTexture, vec2(uv.x, uv.y)).r - 1.0;
+ vec4 base = texture2D(baseTexture, uv).rgba;
+ vec3 L = normalize(vec3(0.0, 0.75, 1.0));
+ float specular = pow(clamp(dot(reflect(L, bump.xyz), yawVec), 0.0, 1.0), 1.0);
+ float diffuse = dot(yawVec, bump.xyz);
+
+ vec3 color = (1.1 * diffuse + 0.05 * height + 0.5 * specular) * base.rgb;
+ vec4 col = vec4(color.rgb, base.a);
+ col *= gl_Color;
+ 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
new file mode 100644
index 000000000..06df5a3cf
--- /dev/null
+++ b/client/shaders/minimap_shader/opengl_vertex.glsl
@@ -0,0 +1,11 @@
+uniform mat4 mWorldViewProj;
+uniform mat4 mInvWorld;
+uniform mat4 mTransWorld;
+uniform mat4 mWorld;
+
+void main(void)
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_Position = mWorldViewProj * gl_Vertex;
+ gl_FrontColor = gl_BackColor = gl_Color;
+}
diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl
index 6dc96eb48..424d32177 100644
--- a/client/shaders/nodes_shader/opengl_fragment.glsl
+++ b/client/shaders/nodes_shader/opengl_fragment.glsl
@@ -1,6 +1,6 @@
uniform sampler2D baseTexture;
uniform sampler2D normalTexture;
-uniform sampler2D useNormalmap;
+uniform sampler2D textureFlags;
uniform vec4 skyBgColor;
uniform float fogDistance;
@@ -8,91 +8,137 @@ uniform vec3 eyePosition;
varying vec3 vPosition;
varying vec3 worldPosition;
+varying float area_enable_parallax;
varying vec3 eyeVec;
varying vec3 tsEyeVec;
varying vec3 lightVec;
varying vec3 tsLightVec;
-bool normalTexturePresent = false;
+bool normalTexturePresent = false;
const float e = 2.718281828459;
const float BS = 10.0;
-
-float intensity (vec3 color){
+
+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);
+float get_rgb_height(vec2 uv)
+{
+ return intensity(texture2D(baseTexture, uv).rgb);
}
-vec4 get_normal_map(vec2 uv){
+vec4 get_normal_map(vec2 uv)
+{
vec4 bump = texture2D(normalTexture, uv).rgba;
- bump.xyz = normalize(bump.xyz * 2.0 -1.0);
- bump.y = -bump.y;
+ bump.xyz = normalize(bump.xyz * 2.0 - 1.0);
return bump;
}
-void main (void)
+float find_intersection(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 = texture2D(normalTexture, dp + ds * depth).a;
+ if (h >= depth)
+ break;
+ depth -= depth_step;
+ }
+ return 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;
-
-#ifdef USE_NORMALMAPS
- if (texture2D(useNormalmap,vec2(1.0,1.0)).r > 0.0){
- normalTexturePresent = true;
- }
-#endif
+ get_texture_flags();
#ifdef ENABLE_PARALLAX_OCCLUSION
- if (normalTexturePresent){
- vec3 tsEye = normalize(tsEyeVec);
- float height = PARALLAX_OCCLUSION_SCALE * texture2D(normalTexture, uv).a - PARALLAX_OCCLUSION_BIAS;
- uv = uv + texture2D(normalTexture, uv).z * height * vec2(tsEye.x,-tsEye.y);
+ 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;
+
+#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
-#ifdef USE_NORMALMAPS
- if (normalTexturePresent){
+#if USE_NORMALMAPS == 1
+ if (normalTexturePresent) {
bump = get_normal_map(uv);
use_normalmap = true;
- }
+ }
#endif
-
-#ifdef GENERATE_NORMALMAPS
- if (use_normalmap == 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));
+
+ if (GENERATE_NORMALMAPS == 1 && 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);
+ bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0);
use_normalmap = true;
}
-#endif
-vec4 base = texture2D(baseTexture, uv).rgba;
-
+ vec4 base = texture2D(baseTexture, uv).rgba;
+
#ifdef ENABLE_BUMPMAPPING
- if (use_normalmap){
+ 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),0.5);
- float diffuse = dot(E,bump.xyz);
- /* Mathematic optimization
- * Original: color = 0.05*base.rgb + diffuse*base.rgb + 0.2*specular*base.rgb;
- * This optimization save 2 multiplications (orig: 4 multiplications + 3 additions
- * end: 2 multiplications + 3 additions)
- */
- color = (0.05 + diffuse + 0.2 * specular) * base.rgb;
+ 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;
}
@@ -104,7 +150,7 @@ vec4 base = texture2D(baseTexture, uv).rgba;
float alpha = gl_Color.a;
vec4 col = vec4(color.rgb, alpha);
col *= gl_Color;
- if(fogDistance != 0.0){
+ if (fogDistance != 0.0) {
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
alpha = mix(alpha, 0.0, d);
}
@@ -112,7 +158,7 @@ vec4 base = texture2D(baseTexture, uv).rgba;
#else
vec4 col = vec4(color.rgb, base.a);
col *= gl_Color;
- if(fogDistance != 0.0){
+ if (fogDistance != 0.0) {
float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
col = mix(col, skyBgColor, d);
}
diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl
index 07684f616..53d5c92d0 100644
--- a/client/shaders/nodes_shader/opengl_vertex.glsl
+++ b/client/shaders/nodes_shader/opengl_vertex.glsl
@@ -14,25 +14,40 @@ varying vec3 eyeVec;
varying vec3 lightVec;
varying vec3 tsEyeVec;
varying vec3 tsLightVec;
+varying float area_enable_parallax;
const float e = 2.718281828459;
const float BS = 10.0;
-float smoothCurve( float x ) {
- return x * x *( 3.0 - 2.0 * x );
+float smoothCurve(float x)
+{
+ return x * x * (3.0 - 2.0 * x);
}
-float triangleWave( float x ) {
- return abs( fract( x + 0.5 ) * 2.0 - 1.0 );
+float triangleWave(float x)
+{
+ return abs(fract(x + 0.5) * 2.0 - 1.0);
}
-float smoothTriangleWave( float x ) {
- return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0;
+float smoothTriangleWave(float x)
+{
+ return smoothCurve(triangleWave(x)) * 2.0 - 1.0;
}
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
-
-#if (MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_OPAQUE) && ENABLE_WAVING_WATER
+ //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
+
+#if ((MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_OPAQUE) && ENABLE_WAVING_WATER)
vec4 pos = gl_Vertex;
pos.y -= 2.0;
@@ -76,43 +91,33 @@ void main(void)
vPosition = gl_Position.xyz;
worldPosition = (mWorld * gl_Vertex).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);
- if (gl_Normal.x > 0.5) {
- // 1.0, 0.0, 0.0
- tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, -1.0));
- binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
- } else if (gl_Normal.x < -0.5) {
- // -1.0, 0.0, 0.0
- tangent = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
- binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
- } else if (gl_Normal.y > 0.5) {
- // 0.0, 1.0, 0.0
- tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
- binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
- } else if (gl_Normal.y < -0.5) {
- // 0.0, -1.0, 0.0
- tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
- binormal = normalize(gl_NormalMatrix * vec3( 0.0, 0.0, 1.0));
- } else if (gl_Normal.z > 0.5) {
- // 0.0, 0.0, 1.0
- tangent = normalize(gl_NormalMatrix * vec3( 1.0, 0.0, 0.0));
- binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
- } else if (gl_Normal.z < -0.5) {
- // 0.0, 0.0, -1.0
- tangent = normalize(gl_NormalMatrix * vec3(-1.0, 0.0, 0.0));
- binormal = normalize(gl_NormalMatrix * vec3( 0.0, -1.0, 0.0));
- }
- mat3 tbnMatrix = mat3( tangent.x, binormal.x, normal.x,
- tangent.y, binormal.y, normal.y,
- tangent.z, binormal.z, normal.z);
+ tangent = normalize(gl_NormalMatrix * gl_MultiTexCoord1.xyz);
+ binormal = normalize(gl_NormalMatrix * gl_MultiTexCoord2.xyz);
+
+ vec3 v;
lightVec = sunPosition - worldPosition;
- tsLightVec = lightVec * tbnMatrix;
- eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz;
- tsEyeVec = eyeVec * tbnMatrix;
+ 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);
vec4 color;
float day = gl_Color.r;
diff --git a/client/shaders/water_surface_shader/opengl_fragment.glsl b/client/shaders/water_surface_shader/opengl_fragment.glsl
index 6dc96eb48..75751e243 100644
--- a/client/shaders/water_surface_shader/opengl_fragment.glsl
+++ b/client/shaders/water_surface_shader/opengl_fragment.glsl
@@ -1,6 +1,6 @@
uniform sampler2D baseTexture;
uniform sampler2D normalTexture;
-uniform sampler2D useNormalmap;
+uniform sampler2D textureFlags;
uniform vec4 skyBgColor;
uniform float fogDistance;
@@ -14,41 +14,59 @@ varying vec3 tsEyeVec;
varying vec3 lightVec;
varying vec3 tsLightVec;
-bool normalTexturePresent = false;
+bool normalTexturePresent = false;
+bool texTileableHorizontal = false;
+bool texTileableVertical = false;
+bool texSeamless = false;
const float e = 2.718281828459;
const float BS = 10.0;
-
-float intensity (vec3 color){
+
+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){
+float get_rgb_height(vec2 uv)
+{
return intensity(texture2D(baseTexture,uv).rgb);
}
-vec4 get_normal_map(vec2 uv){
+vec4 get_normal_map(vec2 uv)
+{
vec4 bump = texture2D(normalTexture, uv).rgba;
bump.xyz = normalize(bump.xyz * 2.0 -1.0);
bump.y = -bump.y;
return bump;
}
-void main (void)
+void main(void)
{
vec3 color;
vec4 bump;
vec2 uv = gl_TexCoord[0].st;
bool use_normalmap = false;
-
-#ifdef USE_NORMALMAPS
- if (texture2D(useNormalmap,vec2(1.0,1.0)).r > 0.0){
- normalTexturePresent = true;
- }
-#endif
+ get_texture_flags();
#ifdef ENABLE_PARALLAX_OCCLUSION
- if (normalTexturePresent){
+ if (normalTexturePresent) {
vec3 tsEye = normalize(tsEyeVec);
float height = PARALLAX_OCCLUSION_SCALE * texture2D(normalTexture, uv).a - PARALLAX_OCCLUSION_BIAS;
uv = uv + texture2D(normalTexture, uv).z * height * vec2(tsEye.x,-tsEye.y);
@@ -56,14 +74,13 @@ void main (void)
#endif
#ifdef USE_NORMALMAPS
- if (normalTexturePresent){
+ if (normalTexturePresent) {
bump = get_normal_map(uv);
use_normalmap = true;
}
#endif
-
-#ifdef GENERATE_NORMALMAPS
- if (use_normalmap == false){
+
+ if (GENERATE_NORMALMAPS == 1 && use_normalmap == 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));
@@ -77,16 +94,15 @@ void main (void)
bump = vec4 (normalize(vec3 (dX, -dY, NORMALMAPS_STRENGTH)),1.0);
use_normalmap = true;
}
-#endif
vec4 base = texture2D(baseTexture, uv).rgba;
-
+
#ifdef ENABLE_BUMPMAPPING
- if (use_normalmap){
+ 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),0.5);
- float diffuse = dot(E,bump.xyz);
+ float specular = pow(clamp(dot(reflect(L, bump.xyz), E), 0.0, 1.0),0.5);
+ float diffuse = dot(E,bump.xyz);
/* Mathematic optimization
* Original: color = 0.05*base.rgb + diffuse*base.rgb + 0.2*specular*base.rgb;
* This optimization save 2 multiplications (orig: 4 multiplications + 3 additions
diff --git a/client/shaders/water_surface_shader/opengl_vertex.glsl b/client/shaders/water_surface_shader/opengl_vertex.glsl
index 6e70bbc36..7ae517d8a 100644
--- a/client/shaders/water_surface_shader/opengl_vertex.glsl
+++ b/client/shaders/water_surface_shader/opengl_vertex.glsl
@@ -18,20 +18,23 @@ varying vec3 tsLightVec;
const float e = 2.718281828459;
const float BS = 10.0;
-float smoothCurve( float x ) {
- return x * x *( 3.0 - 2.0 * x );
+float smoothCurve(float x)
+{
+ return x * x * (3.0 - 2.0 * x);
}
-float triangleWave( float x ) {
- return abs( fract( x + 0.5 ) * 2.0 - 1.0 );
+float triangleWave(float x)
+{
+ return abs(fract( x + 0.5 ) * 2.0 - 1.0);
}
-float smoothTriangleWave( float x ) {
- return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0;
+float smoothTriangleWave(float x)
+{
+ return smoothCurve(triangleWave( x )) * 2.0 - 1.0;
}
void main(void)
{
gl_TexCoord[0] = gl_MultiTexCoord0;
-
+
#if (MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_OPAQUE) && ENABLE_WAVING_WATER
vec4 pos = gl_Vertex;
pos.y -= 2.0;
diff --git a/client/shaders/wielded_shader/opengl_fragment.glsl b/client/shaders/wielded_shader/opengl_fragment.glsl
new file mode 100644
index 000000000..75dd1b674
--- /dev/null
+++ b/client/shaders/wielded_shader/opengl_fragment.glsl
@@ -0,0 +1,114 @@
+uniform sampler2D baseTexture;
+uniform sampler2D normalTexture;
+uniform sampler2D textureFlags;
+
+uniform vec4 skyBgColor;
+uniform float fogDistance;
+uniform vec3 eyePosition;
+
+varying vec3 vPosition;
+varying vec3 worldPosition;
+
+varying vec3 eyeVec;
+varying vec3 lightVec;
+
+bool normalTexturePresent = false;
+bool texTileableHorizontal = false;
+bool texTileableVertical = false;
+bool texSeamless = false;
+
+const float e = 2.718281828459;
+const float BS = 10.0;
+
+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 && 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 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, base.a);
+ col *= gl_Color;
+ if (fogDistance != 0.0) {
+ float d = max(0.0, min(vPosition.z / fogDistance * 1.5 - 0.6, 1.0));
+ col = mix(col, skyBgColor, d);
+ }
+ gl_FragColor = vec4(col.rgb, base.a);
+}
diff --git a/client/shaders/wielded_shader/opengl_vertex.glsl b/client/shaders/wielded_shader/opengl_vertex.glsl
new file mode 100644
index 000000000..a2ab9fa5f
--- /dev/null
+++ b/client/shaders/wielded_shader/opengl_vertex.glsl
@@ -0,0 +1,35 @@
+uniform mat4 mWorldViewProj;
+uniform mat4 mInvWorld;
+uniform mat4 mTransWorld;
+uniform mat4 mWorld;
+
+uniform float dayNightRatio;
+uniform vec3 eyePosition;
+uniform float animationTimer;
+
+varying vec3 vPosition;
+varying vec3 worldPosition;
+
+varying vec3 eyeVec;
+varying vec3 lightVec;
+varying vec3 tsEyeVec;
+varying vec3 tsLightVec;
+
+const float e = 2.718281828459;
+const float BS = 10.0;
+
+void main(void)
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_Position = mWorldViewProj * gl_Vertex;
+
+ vPosition = gl_Position.xyz;
+ 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;
+
+ gl_FrontColor = gl_BackColor = gl_Color;
+}
diff --git a/cmake/Modules/FindCURL.cmake b/cmake/Modules/FindCURL.cmake
index 975b8088c..2ec866ef9 100644
--- a/cmake/Modules/FindCURL.cmake
+++ b/cmake/Modules/FindCURL.cmake
@@ -1,47 +1,19 @@
-# - Find curl
-# Find the native CURL headers and libraries.
-#
-# CURL_INCLUDE_DIR - where to find curl/curl.h, etc.
-# CURL_LIBRARY - List of libraries when using curl.
-# CURL_FOUND - True if curl found.
+mark_as_advanced(CURL_LIBRARY CURL_INCLUDE_DIR)
-if( UNIX )
- FIND_PATH(CURL_INCLUDE_DIR NAMES curl.h
- PATHS
- /usr/local/include/curl
- /usr/include/curl
- )
+find_library(CURL_LIBRARY NAMES curl)
+find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
- FIND_LIBRARY(CURL_LIBRARY NAMES curl
- PATHS
- /usr/local/lib
- /usr/lib
- )
-else( UNIX )
- FIND_PATH(CURL_INCLUDE_DIR NAMES curl/curl.h) # Look for the header file.
- FIND_LIBRARY(CURL_LIBRARY NAMES curl) # Look for the library.
- FIND_FILE(CURL_DLL NAMES libcurl.dll
- PATHS
- "c:/windows/system32"
- DOC "Path of the cURL dll (for installation)")
- INCLUDE(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set CURL_FOUND to TRUE if
- FIND_PACKAGE_HANDLE_STANDARD_ARGS(CURL DEFAULT_MSG CURL_LIBRARY CURL_INCLUDE_DIR) # all listed variables are TRUE
-endif( UNIX )
+set(CURL_REQUIRED_VARS CURL_LIBRARY CURL_INCLUDE_DIR)
-if( WIN32 )
- if( CURL_LIBRARY AND CURL_INCLUDE_DIR AND CURL_DLL ) # libcurl.dll is required on Windows
- SET(CURL_FOUND TRUE)
- else( CURL_LIBRARY AND CURL_INCLUDE_DIR AND CURL_DLL )
- SET(CURL_FOUND FALSE)
- endif( CURL_LIBRARY AND CURL_INCLUDE_DIR AND CURL_DLL )
-else ( WIN32 )
- if( CURL_LIBRARY AND CURL_INCLUDE_DIR )
- SET(CURL_FOUND TRUE)
- else( CURL_LIBRARY AND CURL_INCLUDE_DIR )
- SET(CURL_FOUND FALSE)
- endif( CURL_LIBRARY AND CURL_INCLUDE_DIR )
-endif ( WIN32 )
+if(WIN32)
+ find_file(CURL_DLL NAMES libcurl-4.dll
+ PATHS
+ "C:/Windows/System32"
+ DOC "Path to the cURL DLL (for installation)")
+ mark_as_advanced(CURL_DLL)
+ set(CURL_REQUIRED_VARS ${CURL_REQUIRED_VARS} CURL_DLL)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(CURL DEFAULT_MSG ${CURL_REQUIRED_VARS})
-MESSAGE(STATUS "CURL_INCLUDE_DIR = ${CURL_INCLUDE_DIR}")
-MESSAGE(STATUS "CURL_LIBRARY = ${CURL_LIBRARY}")
-MESSAGE(STATUS "CURL_DLL = ${CURL_DLL}")
diff --git a/cmake/Modules/FindGMP.cmake b/cmake/Modules/FindGMP.cmake
new file mode 100644
index 000000000..bb48289c5
--- /dev/null
+++ b/cmake/Modules/FindGMP.cmake
@@ -0,0 +1,28 @@
+
+option(ENABLE_SYSTEM_GMP "Use GMP from system" TRUE)
+mark_as_advanced(GMP_LIBRARY GMP_INCLUDE_DIR)
+set(USE_SYSTEM_GMP FALSE)
+
+if(ENABLE_SYSTEM_GMP)
+ find_library(GMP_LIBRARY NAMES libgmp.so)
+ find_path(GMP_INCLUDE_DIR NAMES gmp.h)
+
+ if(GMP_LIBRARY AND GMP_INCLUDE_DIR)
+ message (STATUS "Using GMP provided by system.")
+ set(USE_SYSTEM_GMP TRUE)
+ else()
+ message (STATUS "Detecting GMP from system failed.")
+ endif()
+else()
+ message (STATUS "Detecting GMP from system disabled! (ENABLE_SYSTEM_GMP=0)")
+endif()
+
+if(NOT USE_SYSTEM_GMP)
+ message(STATUS "Using bundled mini-gmp library.")
+ set(GMP_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/gmp)
+ set(GMP_LIBRARY gmp)
+ add_subdirectory(gmp)
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GMP DEFAULT_MSG GMP_LIBRARY GMP_INCLUDE_DIR)
diff --git a/cmake/Modules/FindGettextLib.cmake b/cmake/Modules/FindGettextLib.cmake
index c6f731e04..cb1ce7b91 100644
--- a/cmake/Modules/FindGettextLib.cmake
+++ b/cmake/Modules/FindGettextLib.cmake
@@ -1,84 +1,78 @@
-# Package finder for gettext libs and include files
-SET(CUSTOM_GETTEXT_PATH "${PROJECT_SOURCE_DIR}/../../gettext"
+set(CUSTOM_GETTEXT_PATH "${PROJECT_SOURCE_DIR}/../../gettext"
CACHE FILEPATH "path to custom gettext")
-# by default
-SET(GETTEXT_FOUND FALSE)
-
-FIND_PATH(GETTEXT_INCLUDE_DIR
+find_path(GETTEXT_INCLUDE_DIR
NAMES libintl.h
PATHS "${CUSTOM_GETTEXT_PATH}/include"
- DOC "gettext include directory")
+ DOC "GetText include directory")
-FIND_PROGRAM(GETTEXT_MSGFMT
+find_program(GETTEXT_MSGFMT
NAMES msgfmt
PATHS "${CUSTOM_GETTEXT_PATH}/bin"
- DOC "path to msgfmt")
+ DOC "Path to msgfmt")
+
+set(GETTEXT_REQUIRED_VARS GETTEXT_INCLUDE_DIR GETTEXT_MSGFMT)
if(APPLE)
- FIND_LIBRARY(GETTEXT_LIBRARY
+ find_library(GETTEXT_LIBRARY
NAMES libintl.a
PATHS "${CUSTOM_GETTEXT_PATH}/lib"
- DOC "gettext *intl*.lib")
+ DOC "GetText library")
- FIND_LIBRARY(ICONV_LIBRARY
+ find_library(ICONV_LIBRARY
NAMES libiconv.dylib
PATHS "/usr/lib"
- DOC "iconv lib")
+ DOC "IConv library")
+ set(GETTEXT_REQUIRED_VARS ${GETTEXT_REQUIRED_VARS} GETTEXT_LIBRARY ICONV_LIBRARY)
endif(APPLE)
-# modern Linux, as well as Mac, seem to not need require special linking
-# they do not because gettext is part of glibc
-# TODO check the requirements on other BSDs and older Linux
-IF (WIN32)
- IF(MSVC)
- SET(GETTEXT_LIB_NAMES
+# Modern Linux, as well as OSX, does not require special linking because
+# GetText is part of glibc.
+# TODO: check the requirements on other BSDs and older Linux
+if(WIN32)
+ if(MSVC)
+ set(GETTEXT_LIB_NAMES
libintl.lib intl.lib libintl3.lib intl3.lib)
- ELSE()
- SET(GETTEXT_LIB_NAMES
+ else()
+ set(GETTEXT_LIB_NAMES
libintl.dll.a intl.dll.a libintl3.dll.a intl3.dll.a)
- ENDIF()
- FIND_LIBRARY(GETTEXT_LIBRARY
+ endif()
+ find_library(GETTEXT_LIBRARY
NAMES ${GETTEXT_LIB_NAMES}
PATHS "${CUSTOM_GETTEXT_PATH}/lib"
- DOC "gettext *intl*.lib")
- FIND_FILE(GETTEXT_DLL
+ DOC "GetText library")
+ find_file(GETTEXT_DLL
NAMES libintl.dll intl.dll libintl3.dll intl3.dll
PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib"
DOC "gettext *intl*.dll")
- FIND_FILE(GETTEXT_ICONV_DLL
+ find_file(GETTEXT_ICONV_DLL
NAMES libiconv2.dll
PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib"
DOC "gettext *iconv*.lib")
-ENDIF(WIN32)
+ set(GETTEXT_REQUIRED_VARS ${GETTEXT_REQUIRED_VARS} GETTEXT_DLL GETTEXT_ICONV_DLL)
+endif(WIN32)
+
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GetText DEFAULT_MSG ${GETTEXT_REQUIRED_VARS})
+
+
+if(GETTEXT_FOUND)
+ # BSD variants require special linkage as they don't use glibc
+ if(${CMAKE_SYSTEM_NAME} MATCHES "BSD")
+ set(GETTEXT_LIBRARY "intl")
+ endif()
-IF(GETTEXT_INCLUDE_DIR AND GETTEXT_MSGFMT)
- IF (WIN32)
- # in the Win32 case check also for the extra linking requirements
- IF(GETTEXT_LIBRARY AND GETTEXT_DLL AND GETTEXT_ICONV_DLL)
- SET(GETTEXT_FOUND TRUE)
- ENDIF()
- ELSE(WIN32)
- # *BSD variants require special linkage as they don't use glibc
- IF(${CMAKE_SYSTEM_NAME} MATCHES "BSD")
- SET(GETTEXT_LIBRARY "intl")
- ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "BSD")
- SET(GETTEXT_FOUND TRUE)
- ENDIF(WIN32)
-ENDIF()
+ set(GETTEXT_PO_PATH ${CMAKE_SOURCE_DIR}/po)
+ set(GETTEXT_MO_BUILD_PATH ${CMAKE_BINARY_DIR}/locale/<locale>/LC_MESSAGES)
+ set(GETTEXT_MO_DEST_PATH ${LOCALEDIR}/<locale>/LC_MESSAGES)
+ file(GLOB GETTEXT_AVAILABLE_LOCALES RELATIVE ${GETTEXT_PO_PATH} "${GETTEXT_PO_PATH}/*")
+ list(REMOVE_ITEM GETTEXT_AVAILABLE_LOCALES minetest.pot)
+ list(REMOVE_ITEM GETTEXT_AVAILABLE_LOCALES timestamp)
+ macro(SET_MO_PATHS _buildvar _destvar _locale)
+ string(REPLACE "<locale>" ${_locale} ${_buildvar} ${GETTEXT_MO_BUILD_PATH})
+ string(REPLACE "<locale>" ${_locale} ${_destvar} ${GETTEXT_MO_DEST_PATH})
+ endmacro()
+endif()
-IF(GETTEXT_FOUND)
- SET(GETTEXT_PO_PATH ${CMAKE_SOURCE_DIR}/po)
- SET(GETTEXT_MO_BUILD_PATH ${CMAKE_BINARY_DIR}/locale/<locale>/LC_MESSAGES)
- SET(GETTEXT_MO_DEST_PATH ${LOCALEDIR}/<locale>/LC_MESSAGES)
- FILE(GLOB GETTEXT_AVAILABLE_LOCALES RELATIVE ${GETTEXT_PO_PATH} "${GETTEXT_PO_PATH}/*")
- LIST(REMOVE_ITEM GETTEXT_AVAILABLE_LOCALES minetest.pot)
- MACRO(SET_MO_PATHS _buildvar _destvar _locale)
- STRING(REPLACE "<locale>" ${_locale} ${_buildvar} ${GETTEXT_MO_BUILD_PATH})
- STRING(REPLACE "<locale>" ${_locale} ${_destvar} ${GETTEXT_MO_DEST_PATH})
- ENDMACRO(SET_MO_PATHS)
-ELSE()
- SET(GETTEXT_INCLUDE_DIR "")
- SET(GETTEXT_LIBRARY "")
-ENDIF()
diff --git a/cmake/Modules/FindIrrlicht.cmake b/cmake/Modules/FindIrrlicht.cmake
index dce78fbaa..8dda15722 100644
--- a/cmake/Modules/FindIrrlicht.cmake
+++ b/cmake/Modules/FindIrrlicht.cmake
@@ -1,7 +1,8 @@
-#FindIrrlicht.cmake
+mark_as_advanced(IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR IRRLICHT_DLL)
set(IRRLICHT_SOURCE_DIR "" CACHE PATH "Path to irrlicht source directory (optional)")
+
# Find include directory
if(NOT IRRLICHT_SOURCE_DIR STREQUAL "")
@@ -24,65 +25,47 @@ if(NOT IRRLICHT_SOURCE_DIR STREQUAL "")
set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a)
endif()
- FIND_PATH(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
+ find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
PATHS
${IRRLICHT_SOURCE_DIR_INCLUDE}
NO_DEFAULT_PATH
)
- FIND_LIBRARY(IRRLICHT_LIBRARY NAMES ${IRRLICHT_LIBRARY_NAMES}
+ find_library(IRRLICHT_LIBRARY NAMES ${IRRLICHT_LIBRARY_NAMES}
PATHS
${IRRLICHT_SOURCE_DIR_LIBS}
NO_DEFAULT_PATH
)
else()
-
- FIND_PATH(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
+ find_path(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h
PATHS
/usr/local/include/irrlicht
/usr/include/irrlicht
)
- FIND_LIBRARY(IRRLICHT_LIBRARY NAMES libIrrlicht.a Irrlicht
+ find_library(IRRLICHT_LIBRARY NAMES libIrrlicht.so libIrrlicht.a Irrlicht
PATHS
/usr/local/lib
/usr/lib
)
endif()
-MESSAGE(STATUS "IRRLICHT_SOURCE_DIR = ${IRRLICHT_SOURCE_DIR}")
-MESSAGE(STATUS "IRRLICHT_INCLUDE_DIR = ${IRRLICHT_INCLUDE_DIR}")
-MESSAGE(STATUS "IRRLICHT_LIBRARY = ${IRRLICHT_LIBRARY}")
-# On windows, find the dll for installation
+# On Windows, find the DLL for installation
if(WIN32)
if(MSVC)
- FIND_FILE(IRRLICHT_DLL NAMES Irrlicht.dll
- PATHS
- "${IRRLICHT_SOURCE_DIR}/bin/Win32-VisualStudio"
- DOC "Path of the Irrlicht dll (for installation)"
- )
+ set(IRRLICHT_COMPILER "VisualStudio")
else()
- FIND_FILE(IRRLICHT_DLL NAMES Irrlicht.dll
- PATHS
- "${IRRLICHT_SOURCE_DIR}/bin/Win32-gcc"
- DOC "Path of the Irrlicht dll (for installation)"
- )
+ set(IRRLICHT_COMPILER "gcc")
endif()
- MESSAGE(STATUS "IRRLICHT_DLL = ${IRRLICHT_DLL}")
+ find_file(IRRLICHT_DLL NAMES Irrlicht.dll
+ PATHS
+ "${IRRLICHT_SOURCE_DIR}/bin/Win32-${IRRLICHT_COMPILER}"
+ DOC "Path of the Irrlicht dll (for installation)"
+ )
endif(WIN32)
-# handle the QUIETLY and REQUIRED arguments and set IRRLICHT_FOUND to TRUE if
-# all listed variables are TRUE
-INCLUDE(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(Irrlicht DEFAULT_MSG IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR)
-
-IF(IRRLICHT_FOUND)
- SET(IRRLICHT_LIBRARIES ${IRRLICHT_LIBRARY})
-ELSE(IRRLICHT_FOUND)
- SET(IRRLICHT_LIBRARIES)
-ENDIF(IRRLICHT_FOUND)
-
-MARK_AS_ADVANCED(IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR IRRLICHT_DLL)
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(Irrlicht DEFAULT_MSG IRRLICHT_LIBRARY IRRLICHT_INCLUDE_DIR)
diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake
index a9178a225..8e355b93f 100644
--- a/cmake/Modules/FindJson.cmake
+++ b/cmake/Modules/FindJson.cmake
@@ -1,18 +1,27 @@
-# Look for json, use our own if not found
+# Look for JSONCPP if asked to.
+# We use a bundled version by default because some distros ship versions of
+# JSONCPP that cause segfaults and other memory errors when we link with them.
+# See https://github.com/minetest/minetest/issues/1793
-#FIND_PATH(JSON_INCLUDE_DIR json.h)
+mark_as_advanced(JSON_LIBRARY JSON_INCLUDE_DIR)
+option(ENABLE_SYSTEM_JSONCPP "Enable using a system-wide JSONCPP. May cause segfaults and other memory errors!" FALSE)
-#FIND_LIBRARY(JSON_LIBRARY NAMES jsoncpp)
+if(ENABLE_SYSTEM_JSONCPP)
+ find_library(JSON_LIBRARY NAMES jsoncpp)
+ find_path(JSON_INCLUDE_DIR json/features.h)
-#IF(JSON_LIBRARY AND JSON_INCLUDE_DIR)
-# SET( JSON_FOUND TRUE )
-#ENDIF(JSON_LIBRARY AND JSON_INCLUDE_DIR)
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(JSONCPP DEFAULT_MSG JSON_LIBRARY JSON_INCLUDE_DIR)
+
+ if(JSONCPP_FOUND)
+ message(STATUS "Using system JSONCPP library.")
+ endif()
+endif()
+
+if(NOT JSONCPP_FOUND)
+ message(STATUS "Using bundled JSONCPP library.")
+ set(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json)
+ set(JSON_LIBRARY jsoncpp)
+ add_subdirectory(json)
+endif()
-#IF(JSON_FOUND)
-# MESSAGE(STATUS "Found system jsoncpp header file in ${JSON_INCLUDE_DIR}")
-# MESSAGE(STATUS "Found system jsoncpp library ${JSON_LIBRARY}")
-#ELSE(JSON_FOUND)
- SET(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json)
- SET(JSON_LIBRARY jsoncpp)
- MESSAGE(STATUS "Using project jsoncpp library")
-#ENDIF(JSON_FOUND)
diff --git a/cmake/Modules/FindLua.cmake b/cmake/Modules/FindLua.cmake
new file mode 100644
index 000000000..479dfcf41
--- /dev/null
+++ b/cmake/Modules/FindLua.cmake
@@ -0,0 +1,25 @@
+
+option(ENABLE_LUAJIT "Enable LuaJIT support" TRUE)
+mark_as_advanced(LUA_LIBRARY LUA_INCLUDE_DIR)
+set(USE_LUAJIT FALSE)
+
+if(ENABLE_LUAJIT)
+ find_library(LUA_LIBRARY luajit
+ NAMES luajit-5.1)
+ find_path(LUA_INCLUDE_DIR luajit.h
+ NAMES luajit.h
+ PATH_SUFFIXES luajit-2.0)
+ if(LUA_LIBRARY AND LUA_INCLUDE_DIR)
+ set(USE_LUAJIT TRUE)
+ endif()
+else()
+ message (STATUS "LuaJIT detection disabled! (ENABLE_LUAJIT=0)")
+endif()
+
+if(NOT USE_LUAJIT)
+ message(STATUS "LuaJIT not found, using bundled Lua.")
+ set(LUA_LIBRARY "lua")
+ set(LUA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lua/src")
+ add_subdirectory(lua)
+endif()
+
diff --git a/cmake/Modules/FindOpenGLES2.cmake b/cmake/Modules/FindOpenGLES2.cmake
index 42d31c898..a8622f3b2 100644
--- a/cmake/Modules/FindOpenGLES2.cmake
+++ b/cmake/Modules/FindOpenGLES2.cmake
@@ -16,115 +16,97 @@
# EGL_INCLUDE_DIR - the EGL include directory
# EGL_LIBRARIES - Link these to use EGL
-# win32, apple, android NOT TESED
-# linux tested and works
-
-IF (WIN32)
- IF (CYGWIN)
-
- FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h )
-
- FIND_LIBRARY(OPENGLES2_gl_LIBRARY libGLESv2 )
-
- ELSE (CYGWIN)
-
- IF(BORLAND)
- SET (OPENGLES2_gl_LIBRARY import32 CACHE STRING "OpenGL ES 2.x library for win32")
- ELSE(BORLAND)
- # todo
- # SET (OPENGLES_gl_LIBRARY ${SOURCE_DIR}/Dependencies/lib/release/libGLESv2.lib CACHE STRING "OpenGL ES 2.x library for win32"
- ENDIF(BORLAND)
-
- ENDIF (CYGWIN)
-
-ELSE (WIN32)
-
- IF (APPLE)
-
+# Win32, Apple, and Android are not tested!
+# Linux tested and works
+
+if(WIN32)
+ if(CYGWIN)
+ find_path(OPENGLES2_INCLUDE_DIR GLES2/gl2.h)
+ find_library(OPENGLES2_LIBRARY libGLESv2)
+ else()
+ if(BORLAND)
+ set(OPENGLES2_LIBRARY import32 CACHE STRING "OpenGL ES 2.x library for Win32")
+ else()
+ # TODO
+ # set(OPENGLES_LIBRARY ${SOURCE_DIR}/Dependencies/lib/release/libGLESv2.lib CACHE STRING "OpenGL ES 2.x library for win32"
+ endif()
+ endif()
+elseif(APPLE)
create_search_paths(/Developer/Platforms)
findpkg_framework(OpenGLES2)
- set(OPENGLES2_gl_LIBRARY "-framework OpenGLES")
-
- ELSE(APPLE)
-
- FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h
- /usr/openwin/share/include
- /opt/graphics/OpenGL/include /usr/X11R6/include
- /usr/include
- )
-
- FIND_LIBRARY(OPENGLES2_gl_LIBRARY
- NAMES GLESv2
- PATHS /opt/graphics/OpenGL/lib
- /usr/openwin/lib
- /usr/shlib /usr/X11R6/lib
- /usr/lib
- )
-
- IF (NOT BUILD_ANDROID)
- FIND_PATH(EGL_INCLUDE_DIR EGL/egl.h
- /usr/openwin/share/include
- /opt/graphics/OpenGL/include /usr/X11R6/include
- /usr/include
+ set(OPENGLES2_LIBRARY "-framework OpenGLES")
+else()
+ find_path(OPENGLES2_INCLUDE_DIR GLES2/gl2.h
+ PATHS /usr/openwin/share/include
+ /opt/graphics/OpenGL/include
+ /usr/X11R6/include
+ /usr/include
+ )
+
+ find_library(OPENGLES2_LIBRARY
+ NAMES GLESv2
+ PATHS /opt/graphics/OpenGL/lib
+ /usr/openwin/lib
+ /usr/shlib /usr/X11R6/lib
+ /usr/lib
+ )
+
+ if(NOT BUILD_ANDROID)
+ find_path(EGL_INCLUDE_DIR EGL/egl.h
+ PATHS /usr/openwin/share/include
+ /opt/graphics/OpenGL/include
+ /usr/X11R6/include
+ /usr/include
)
- FIND_LIBRARY(EGL_egl_LIBRARY
- NAMES EGL
- PATHS /opt/graphics/OpenGL/lib
+ find_library(EGL_LIBRARY
+ NAMES EGL
+ PATHS /opt/graphics/OpenGL/lib
/usr/openwin/lib
- /usr/shlib /usr/X11R6/lib
+ /usr/shlib
+ /usr/X11R6/lib
/usr/lib
)
- # On Unix OpenGL most certainly always requires X11.
- # Feel free to tighten up these conditions if you don't
- # think this is always true.
- # It's not true on OSX.
-
- IF (OPENGLES2_gl_LIBRARY)
- IF(NOT X11_FOUND)
- INCLUDE(FindX11)
- ENDIF(NOT X11_FOUND)
- IF (X11_FOUND)
- IF (NOT APPLE)
- SET (OPENGLES2_LIBRARIES ${X11_LIBRARIES})
- ENDIF (NOT APPLE)
- ENDIF (X11_FOUND)
- ENDIF (OPENGLES2_gl_LIBRARY)
- ENDIF ()
-
- ENDIF(APPLE)
-ENDIF (WIN32)
-
-#SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
-
-IF (BUILD_ANDROID)
- IF(OPENGLES2_gl_LIBRARY)
- SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
- SET( EGL_LIBRARIES)
- SET( OPENGLES2_FOUND "YES" )
- ENDIF(OPENGLES2_gl_LIBRARY)
-ELSE ()
-
- SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
-
- IF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY)
- SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES})
- SET( EGL_LIBRARIES ${EGL_egl_LIBRARY} ${EGL_LIBRARIES})
- SET( OPENGLES2_FOUND "YES" )
- ENDIF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY)
-
-ENDIF ()
-
-MARK_AS_ADVANCED(
- OPENGLES2_INCLUDE_DIR
- OPENGLES2_gl_LIBRARY
- EGL_INCLUDE_DIR
- EGL_egl_LIBRARY
+ # On Unix OpenGL usually requires X11.
+ # It doesn't require X11 on OSX.
+
+ if(OPENGLES2_LIBRARY)
+ if(NOT X11_FOUND)
+ include(FindX11)
+ endif()
+ if(X11_FOUND)
+ set(OPENGLES2_LIBRARIES ${X11_LIBRARIES})
+ endif()
+ endif()
+ endif()
+endif()
+
+set(OPENGLES2_LIBRARIES ${OPENGLES2_LIBRARIES} ${OPENGLES2_LIBRARY})
+
+if(BUILD_ANDROID)
+ if(OPENGLES2_LIBRARY)
+ set(EGL_LIBRARIES)
+ set(OPENGLES2_FOUND TRUE)
+ endif()
+else()
+ if(OPENGLES2_LIBRARY AND EGL_LIBRARY)
+ set(OPENGLES2_LIBRARIES ${OPENGLES2_LIBRARY} ${OPENGLES2_LIBRARIES})
+ set(EGL_LIBRARIES ${EGL_LIBRARY} ${EGL_LIBRARIES})
+ set(OPENGLES2_FOUND TRUE)
+ endif()
+endif()
+
+mark_as_advanced(
+ OPENGLES2_INCLUDE_DIR
+ OPENGLES2_LIBRARY
+ EGL_INCLUDE_DIR
+ EGL_LIBRARY
)
-IF(OPENGLES2_FOUND)
- MESSAGE(STATUS "Found system opengles2 library ${OPENGLES2_LIBRARIES}")
-ELSE ()
- SET(OPENGLES2_LIBRARIES "")
-ENDIF ()
+if(OPENGLES2_FOUND)
+ message(STATUS "Found system OpenGL ES 2 library: ${OPENGLES2_LIBRARIES}")
+else()
+ set(OPENGLES2_LIBRARIES "")
+endif()
+
diff --git a/cmake/Modules/GenerateVersion.cmake b/cmake/Modules/GenerateVersion.cmake
index 4a7f183da..ad0e38263 100644
--- a/cmake/Modules/GenerateVersion.cmake
+++ b/cmake/Modules/GenerateVersion.cmake
@@ -1,20 +1,26 @@
# Always run during 'make'
-if(VERSION_EXTRA)
- set(VERSION_GITHASH "${VERSION_STRING}")
-else(VERSION_EXTRA)
- execute_process(COMMAND git describe --always --tag --dirty
+if(DEVELOPMENT_BUILD)
+ execute_process(COMMAND git rev-parse --short HEAD
WORKING_DIRECTORY "${GENERATE_VERSION_SOURCE_DIR}"
OUTPUT_VARIABLE VERSION_GITHASH OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
-
if(VERSION_GITHASH)
- message(STATUS "*** Detected git version ${VERSION_GITHASH} ***")
- else()
- set(VERSION_GITHASH "${VERSION_STRING}")
+ set(VERSION_GITHASH "${VERSION_STRING}-${VERSION_GITHASH}")
+ execute_process(COMMAND git diff-index --quiet HEAD
+ WORKING_DIRECTORY "${GENERATE_VERSION_SOURCE_DIR}"
+ RESULT_VARIABLE IS_DIRTY)
+ if(IS_DIRTY)
+ set(VERSION_GITHASH "${VERSION_GITHASH}-dirty")
+ endif()
+ message(STATUS "*** Detected Git version ${VERSION_GITHASH} ***")
endif()
endif()
+if(NOT VERSION_GITHASH)
+ set(VERSION_GITHASH "${VERSION_STRING}")
+endif()
configure_file(
${GENERATE_VERSION_SOURCE_DIR}/cmake_config_githash.h.in
${GENERATE_VERSION_BINARY_DIR}/cmake_config_githash.h)
+
diff --git a/cmake/Modules/misc.cmake b/cmake/Modules/misc.cmake
deleted file mode 100644
index 0bd2e3fce..000000000
--- a/cmake/Modules/misc.cmake
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Random macros
-#
-
-# Not used ATM
-
-MACRO (GETDATETIME RESULT)
- IF (WIN32)
- EXECUTE_PROCESS(COMMAND "cmd" /C echo %date% %time% OUTPUT_VARIABLE ${RESULT})
- string(REGEX REPLACE "\n" "" ${RESULT} "${${RESULT}}")
- ELSEIF(UNIX)
- EXECUTE_PROCESS(COMMAND "date" "+%Y-%m-%d_%H:%M:%S" OUTPUT_VARIABLE ${RESULT})
- string(REGEX REPLACE "\n" "" ${RESULT} "${${RESULT}}")
- ELSE (WIN32)
- MESSAGE(SEND_ERROR "date not implemented")
- SET(${RESULT} "Unknown")
- ENDIF (WIN32)
-
- string(REGEX REPLACE " " "_" ${RESULT} "${${RESULT}}")
-ENDMACRO (GETDATETIME)
-
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index d2d885880..5372cb834 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1,4 +1,4 @@
-Minetest Lua Modding API Reference 0.4.12
+Minetest Lua Modding API Reference 0.4.13
=========================================
* More information at <http://www.minetest.net/>
* Developer Wiki: <http://dev.minetest.net/>
@@ -20,7 +20,8 @@ source code patches to <celeron55@gmail.com>.
Programming in Lua
------------------
-If you have any difficulty in understanding this, please read [Programming in Lua](http://www.lua.org/pil/).
+If you have any difficulty in understanding this, please read
+[Programming in Lua](http://www.lua.org/pil/).
Startup
-------
@@ -64,6 +65,15 @@ e.g.
The game directory can contain the file minetest.conf, which will be used
to set default settings when running the particular game.
+### Menu images
+
+Games can provide custom main menu images. They are put inside a `menu` directory inside the game directory.
+
+The images are named `$identifier.png`, where `$identifier` is one of `overlay,background,footer,header`.
+If you want to specify multiple images for one identifier, add additional images named like `$identifier.$n.png`, with an ascending number $n starting with 1,
+and a random image will be chosen from the provided ones.
+
+
Mod load path
-------------
Generic:
@@ -418,6 +428,11 @@ the global `minetest.registered_*` tables.
* `minetest.register_craftitem(name, item definition)`
* added to `minetest.registered_items[name]`
+* `minetest.register_biome(biome definition)`
+ * returns an integer uniquely identifying the registered biome
+ * added to `minetest.registered_biome` with the key of `biome.name`
+ * if `biome.name` is nil, the key is the returned ID
+
* `minetest.register_ore(ore definition)`
* returns an integer uniquely identifying the registered ore
* added to `minetest.registered_ores` with the key of `ore.name`
@@ -428,11 +443,25 @@ the global `minetest.registered_*` tables.
* added to `minetest.registered_decorations` with the key of `decoration.name`
* if `decoration.name` is nil, the key is the returned ID
+* `minetest.register_schematic(schematic definition)`
+ * returns an integer uniquely identifying the registered schematic
+ * added to `minetest.registered_schematic` with the key of `schematic.name`
+ * if `schematic.name` is nil, the key is the returned ID
+ * if the schematic is loaded from a file, schematic.name is set to the filename
+ * if the function is called when loading the mod, and schematic.name is a relative path,
+ * then the current mod path will be prepended to the schematic filename
+
+* `minetest.clear_registered_biomes()`
+ * clears all biomes currently registered
+
* `minetest.clear_registered_ores()`
- * clears all ores currently registered
+ * clears all ores currently registered
* `minetest.clear_registered_decorations()`
- * clears all decorations currently registered
+ * clears all decorations currently registered
+
+* `minetest.clear_registered_schematics()`
+ * clears all schematics currently registered
Note that in some cases you will stumble upon things that are not contained
in these tables (e.g. when a mod has been removed). Always check for
@@ -593,13 +622,14 @@ set to level from `param2`.
Meshes
------
If drawtype `mesh` is used, tiles should hold model materials textures.
-Only static meshes are implemented.
+Only static meshes are implemented.
For supported model formats see Irrlicht engine documentation.
Noise Parameters
----------------
-Noise Parameters, or commonly called "`NoiseParams`", define the properties of perlin noise.
+Noise Parameters, or commonly called "`NoiseParams`", define the properties of
+perlin noise.
### `offset`
Offset that the noise is translated by (i.e. added) after calculation.
@@ -679,24 +709,26 @@ All default ores are of the uniformly-distributed scatter type.
Randomly chooses a location and generates a cluster of ore.
If `noise_params` is specified, the ore will be placed if the 3D perlin noise at
-that point is greater than the `noise_threshold`, giving the ability to create a non-equal
-distribution of ore.
+that point is greater than the `noise_threshold`, giving the ability to create
+a non-equal distribution of ore.
### `sheet`
-Creates a sheet of ore in a blob shape according to the 2D perlin noise described by `noise_params`.
-The relative height of the sheet can be controlled by the same perlin noise as well, by specifying
-a non-zero `scale` parameter in `noise_params`.
+Creates a sheet of ore in a blob shape according to the 2D perlin noise
+described by `noise_params`. The relative height of the sheet can be
+controlled by the same perlin noise as well, by specifying a non-zero
+`scale` parameter in `noise_params`.
-**IMPORTANT**: The noise is not transformed by `offset` or `scale` when comparing against the noise
-threshold, but scale is used to determine relative height.
+**IMPORTANT**: The noise is not transformed by `offset` or `scale` when comparing
+against the noise threshold, but scale is used to determine relative height.
The height of the blob is randomly scattered, with a maximum height of `clust_size`.
`clust_scarcity` and `clust_num_ores` are ignored.
-This is essentially an improved version of the so-called "stratus" ore seen in some unofficial mods.
+This is essentially an improved version of the so-called "stratus" ore seen in
+some unofficial mods.
### `blob`
-Creates a deformed sphere of ore according to 3d perlin noise described by
+Creates a deformed sphere of ore according to 3d perlin noise described by
`noise_params`. The maximum size of the blob is `clust_size`, and
`clust_scarcity` has the same meaning as with the `scatter` type.
### `vein
@@ -741,64 +773,72 @@ The varying types of decorations that can be placed.
The default value is `simple`, and is currently the only type supported.
### `simple`
-Creates a 1 times `H` times 1 column of a specified node (or a random node from a list, if a
-decoration list is specified). Can specify a certain node it must spawn next to, such as water or
-lava, for example. Can also generate a decoration of random height between a specified lower and
-upper bound. This type of decoration is intended for placement of grass, flowers, cacti, papyri,
-and so on.
+Creates a 1 times `H` times 1 column of a specified node (or a random node from
+a list, if a decoration list is specified). Can specify a certain node it must
+spawn next to, such as water or lava, for example. Can also generate a
+decoration of random height between a specified lower and upper bound.
+This type of decoration is intended for placement of grass, flowers, cacti,
+papyri, and so on.
### `schematic`
-Copies a box of `MapNodes` from a specified schematic file (or raw description). Can specify a
-probability of a node randomly appearing when placed. This decoration type is intended to be used
-for multi-node sized discrete structures, such as trees, cave spikes, rocks, and so on.
+Copies a box of `MapNodes` from a specified schematic file (or raw description).
+Can specify a probability of a node randomly appearing when placed.
+This decoration type is intended to be used for multi-node sized discrete
+structures, such as trees, cave spikes, rocks, and so on.
Schematic specifier
--------------------
-A schematic specifier identifies a schematic by either a filename to a Minetest Schematic file (`.mts`)
-or through raw data supplied through Lua, in the form of a table. This table must specify two fields:
-
-* The `size` field is a 3D vector containing the dimensions of the provided schematic.
-* The `data` field is a flat table of MapNodes making up the schematic, in the order of `[z [y [x]]]`.
-
-**Important**: The default value for `param1` in MapNodes here is `255`, which represents "always place".
-
-In the bulk `MapNode` data, `param1`, instead of the typical light values, instead represents the
-probability of that node appearing in the structure.
-
-When passed to `minetest.create_schematic`, probability is an integer value ranging from `0` to `255`:
-
-* A probability value of `0` means that node will never appear (0% chance).
-* A probability value of `255` means the node will always appear (100% chance).
-* If the probability value `p` is greater than `0`, then there is a `(p / 256 * 100)`% chance that node
- will appear when the schematic is placed on the map.
-
-**Important note**: Node aliases cannot be used for a raw schematic provided when registering as a decoration.
+A schematic specifier identifies a schematic by either a filename to a
+Minetest Schematic file (`.mts`) or through raw data supplied through Lua,
+in the form of a table. This table specifies the following fields:
+
+* The `size` field is a 3D vector containing the dimensions of the provided schematic. (required)
+* The `yslice_prob` field is a table of {ypos, prob} which sets the `ypos`th vertical slice
+ of the schematic to have a `prob / 256 * 100` chance of occuring. (default: 255)
+* The `data` field is a flat table of MapNode tables making up the schematic,
+ in the order of `[z [y [x]]]`. (required)
+ Each MapNode table contains:
+ * `name`: the name of the map node to place (required)
+ * `prob` (alias `param1`): the probability of this node being placed (default: 255)
+ * `param2`: the raw param2 value of the node being placed onto the map (default: 0)
+ * `force_place`: boolean representing if the node should forcibly overwrite any
+ previous contents (default: false)
+
+About probability values:
+* A probability value of `0` or `1` means that node will never appear (0% chance).
+* A probability value of `254` or `255` means the node will always appear (100% chance).
+* If the probability value `p` is greater than `1`, then there is a
+ `(p / 256 * 100)` percent chance that node will appear when the schematic is
+ placed on the map.
Schematic attributes
--------------------
See section "Flag Specifier Format".
-Currently supported flags: `place_center_x`, `place_center_y`, `place_center_z`.
+Currently supported flags: `place_center_x`, `place_center_y`,
+ `place_center_z`, `force_placement`.
* `place_center_x`: Placement of this decoration is centered along the X axis.
* `place_center_y`: Placement of this decoration is centered along the Y axis.
* `place_center_z`: Placement of this decoration is centered along the Z axis.
+* `force_placement`: Schematic nodes other than "ignore" will replace existing nodes.
HUD element types
-----------------
The position field is used for all element types.
-To account for differing resolutions, the position coordinates are the percentage of the screen,
-ranging in value from `0` to `1`.
+To account for differing resolutions, the position coordinates are the percentage
+of the screen, ranging in value from `0` to `1`.
-The name field is not yet used, but should contain a description of what the HUD element represents.
-The direction field is the direction in which something is drawn.
+The name field is not yet used, but should contain a description of what the
+HUD element represents. The direction field is the direction in which something
+is drawn.
-`0` draws from left to right, `1` draws from right to left, `2` draws from top to bottom,
-and `3` draws from bottom to top.
+`0` draws from left to right, `1` draws from right to left, `2` draws from
+top to bottom, and `3` draws from bottom to top.
The `alignment` field specifies how the item will be aligned. It ranges from `-1` to `1`,
with `0` being the center, `-1` is moved to the left/up, and `1` is to the right/down.
@@ -812,7 +852,8 @@ items in the HUD.
Below are the specific uses for fields in each type; fields not listed for that type are ignored.
-**Note**: Future revisions to the HUD API may be incompatible; the HUD API is still in the experimental stages.
+**Note**: Future revisions to the HUD API may be incompatible; the HUD API is still
+in the experimental stages.
### `image`
Displays an image on the HUD.
@@ -876,15 +917,18 @@ For helper functions see "Vector helpers".
Flag Specifier Format
---------------------
-Flags using the standardized flag specifier format can be specified in either of two ways, by string or table.
+Flags using the standardized flag specifier format can be specified in either of
+two ways, by string or table.
-The string format is a comma-delimited set of flag names; whitespace and unrecognized flag fields are ignored.
-Specifying a flag in the string sets the flag, and specifying a flag prefixed by the string `"no"` explicitly
+The string format is a comma-delimited set of flag names; whitespace and
+unrecognized flag fields are ignored. Specifying a flag in the string sets the
+flag, and specifying a flag prefixed by the string `"no"` explicitly
clears the flag from whatever the default may be.
-In addition to the standard string flag format, the schematic flags field can also be a table of flag names
-to boolean values representing whether or not the flag is set. Additionally, if a field with the flag name
-prefixed with `"no"` is present, mapped to a boolean of any value, the specified flag is unset.
+In addition to the standard string flag format, the schematic flags field can
+also be a table of flag names to boolean values representing whether or not the
+flag is set. Additionally, if a field with the flag name prefixed with `"no"`
+is present, mapped to a boolean of any value, the specified flag is unset.
E.g. A flag field of value
@@ -1036,8 +1080,8 @@ Another example: Make red wool from white wool and red dye:
dropped as an item. If the node is wallmounted the wallmounted direction is
checked.
* `soil`: saplings will grow on nodes in this group
-* `connect_to_raillike`: makes nodes of raillike drawtype connect to
- other group members with same drawtype
+* `connect_to_raillike`: makes nodes of raillike drawtype with same group value
+ connect to each other
### Known damage and digging time defining groups
* `crumbly`: dirt, sand
@@ -1177,7 +1221,8 @@ Damage calculation:
damage = 0
foreach group in cap.damage_groups:
- damage += cap.damage_groups[group] * limit(actual_interval / cap.full_punch_interval, 0.0, 1.0)
+ damage += cap.damage_groups[group] * limit(actual_interval /
+ cap.full_punch_interval, 0.0, 1.0)
* (object.armor_groups[group] / 100.0)
-- Where object.armor_groups[group] is 0 for inexistent values
return damage
@@ -1185,7 +1230,7 @@ Damage calculation:
Client predicts damage based on damage groups. Because of this, it is able to
give an immediate response when an entity is damaged or dies; the response is
pre-defined somehow (e.g. by defining a sprite animation) (not implemented;
-TODO).
+TODO).
Currently a smoke puff will appear when an entity dies.
The group `immortal` completely disables normal damage.
@@ -1198,8 +1243,8 @@ On the Lua side, every punch calls:
entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
-This should never be called directly, because damage is usually not handled by the entity
-itself.
+This should never be called directly, because damage is usually not handled by
+the entity itself.
* `puncher` is the object performing the punch. Can be `nil`. Should never be
accessed unless absolutely required, to encourage interoperability.
@@ -1244,12 +1289,14 @@ Example stuff:
print(dump(meta:to_table()))
meta:from_table({
inventory = {
- main = {[1] = "default:dirt", [2] = "", [3] = "", [4] = "", [5] = "", [6] = "",
- [7] = "", [8] = "", [9] = "", [10] = "", [11] = "", [12] = "", [13] = "",
- [14] = "default:cobble", [15] = "", [16] = "", [17] = "", [18] = "",
- [19] = "", [20] = "default:cobble", [21] = "", [22] = "", [23] = "",
- [24] = "", [25] = "", [26] = "", [27] = "", [28] = "", [29] = "", [30] = "",
- [31] = "", [32] = ""}
+ main = {[1] = "default:dirt", [2] = "", [3] = "", [4] = "",
+ [5] = "", [6] = "", [7] = "", [8] = "", [9] = "",
+ [10] = "", [11] = "", [12] = "", [13] = "",
+ [14] = "default:cobble", [15] = "", [16] = "", [17] = "",
+ [18] = "", [19] = "", [20] = "default:cobble", [21] = "",
+ [22] = "", [23] = "", [24] = "", [25] = "", [26] = "",
+ [27] = "", [28] = "", [29] = "", [30] = "", [31] = "",
+ [32] = ""}
},
fields = {
formspec = "size[8,9]list[context;main;0,0;8,4;]list[current_player;main;0,5;8,4;]",
@@ -1302,6 +1349,17 @@ examples.
#### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]`
* Show an inventory list
+#### `listring[<inventory location>;<list name>]`
+* Allows to create a ring of inventory lists
+* Shift-clicking on items in one element of the ring
+* will send them to the next inventory list inside the ring
+* The first occurrence of an element inside the ring will
+* determine the inventory where items will be sent to
+
+#### `listring[]`
+* Shorthand for doing `listring[<inventory location>;<list name>]`
+* for the last two inventory lists added by list[...]
+
#### `listcolors[<slot_bg_normal>;<slot_bg_hover>]`
* Sets background color of slots as `ColorString`
* Sets background color of slots on mouse hovering
@@ -1569,6 +1627,16 @@ To specify the value of the alpha channel, append `#AA` to the end of the color
(e.g. `colorname#08`). For named colors the hexadecimal string representing the alpha
value must (always) be two hexadecimal digits.
+`ColorSpec`
+-----------
+A ColorSpec specifies a 32-bit color. It can be written in either:
+table form, each element ranging from 0..255 (a, if absent, defaults to 255):
+ `colorspec = {a=255, r=0, g=255, b=0}`
+numerical form, the raw integer value of an ARGB8 quad:
+ `colorspec = 0xFF00FF00`
+or string form, a ColorString (defined above):
+ `colorspec = "green"`
+
Vector helpers
--------------
@@ -1627,7 +1695,7 @@ Helper functions
### Utilities
-* `minetest.get_current_modname()`: returns a string
+* `minetest.get_current_modname()`: returns the currently loading mod's name, when we are 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
@@ -1636,36 +1704,41 @@ Helper functions
* Useful for storing custom data
* `minetest.is_singleplayer()`
* `minetest.features`
- * table containing API feature flags: `{foo=true, bar=true}`
+ * Table containing API feature flags: `{foo=true, bar=true}`
* `minetest.has_feature(arg)`: returns `boolean, missing_features`
* `arg`: string or table in format `{foo=true, bar=true}`
* `missing_features`: `{foo=true, bar=true}`
-* `minetest.get_player_information(playername)`
- * table containing information about player peer.
-
-Example of `minetest.get_player_information` return value:
-
- {
- address = "127.0.0.1", -- IP address of client
- ip_version = 4, -- IPv4 / IPv6
- min_rtt = 0.01, -- minimum round trip time
- max_rtt = 0.2, -- maximum round trip time
- avg_rtt = 0.02, -- average round trip time
- min_jitter = 0.01, -- minimum packet time jitter
- max_jitter = 0.5, -- maximum packet time jitter
- avg_jitter = 0.03, -- average packet time jitter
- connection_uptime = 200, -- seconds since client connected
-
- -- following information is available on debug build only!!!
- -- DO NOT USE IN MODS
- --ser_vers = 26, -- serialization version used by client
- --prot_vers = 23, -- protocol version used by client
- --major = 0, -- major version number
- --minor = 4, -- minor version number
- --patch = 10, -- patch version number
- --vers_string = "0.4.9-git", -- full version string
- --state = "Active" -- current client state
- }
+* `minetest.get_player_information(player_name)`: returns a table containing
+ information about player. Example return value:
+ {
+ address = "127.0.0.1", -- IP address of client
+ ip_version = 4, -- IPv4 / IPv6
+ min_rtt = 0.01, -- minimum round trip time
+ max_rtt = 0.2, -- maximum round trip time
+ avg_rtt = 0.02, -- average round trip time
+ min_jitter = 0.01, -- minimum packet time jitter
+ max_jitter = 0.5, -- maximum packet time jitter
+ avg_jitter = 0.03, -- average packet time jitter
+ connection_uptime = 200, -- seconds since client connected
+
+ -- following information is available on debug build only!!!
+ -- DO NOT USE IN MODS
+ --ser_vers = 26, -- serialization version used by client
+ --prot_vers = 23, -- protocol version used by client
+ --major = 0, -- major version number
+ --minor = 4, -- minor version number
+ --patch = 10, -- patch version number
+ --vers_string = "0.4.9-git", -- full version string
+ --state = "Active" -- current client state
+ }
+* `minetest.mkdir(path)`: returns success.
+ * Creates a directory specified by `path`, creating parent directories
+ if they don't exist.
+* `minetest.get_dir_list(path, [is_dir])`: returns list of entry names
+ * is_dir is one of:
+ * nil: return all entries,
+ * true: return only subdirectory names, or
+ * false: return only file names.
### Logging
* `minetest.debug(line)`
@@ -1722,6 +1795,23 @@ Call these functions only at load time!
* Called after a new player has been created
* `minetest.register_on_dieplayer(func(ObjectRef))`
* Called when a player dies
+* `minetest.register_on_punchplayer(func(player, hitter, time_from_last_punch, tool_capabilities, dir, damage))`
+ * Called when a player is punched
+ * `player` - ObjectRef - Player that was punched
+ * `hitter` - ObjectRef - Player that hit
+ * `time_from_last_punch`: Meant for disallowing spamming of clicks (can be nil)
+ * `tool_capabilities`: capability table of used tool (can be nil)
+ * `dir`: unit vector of direction of punch. Always defined. Points from
+ 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_player_hpchange(func(player, hp_change), modifier)`
+ * Called when the player gets damaged or healed
+ * `player`: ObjectRef of the player
+ * `hp_change`: the amount of change. Negative when it is damage.
+ * `modifier`: when true, the function should return the actual hp_change.
+ Note: modifiers only get a temporary hp_change that can be modified by later modifiers.
+ modifiers can return true as a second argument to stop the execution of further functions.
* `minetest.register_on_respawnplayer(func(ObjectRef))`
* Called when player is to be respawned
* Called _before_ repositioning of player occurs
@@ -1743,6 +1833,7 @@ Call these functions only at load time!
* `dug_too_fast`
* `minetest.register_on_chat_message(func(name, message))`
* 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_player_receive_fields(func(player, formname, fields))`
* Called when a button is pressed in player's inventory form
* Newest functions are called first
@@ -1777,8 +1868,12 @@ Call these functions only at load time!
### Setting-related
* `minetest.setting_set(name, value)`
+ * Setting names can't contain whitespace or any of `="{}#`.
+ * Setting values can't contain the sequence `\n"""`.
+ * Setting names starting with "secure." can't be set.
* `minetest.setting_get(name)`: returns string or `nil`
* `minetest.setting_setbool(name, value)`
+ * See documentation on `setting_set` for restrictions.
* `minetest.setting_getbool(name)`: returns boolean or `nil`
* `minetest.setting_get_pos(name)`: returns position or nil
* `minetest.setting_save()`, returns `nil`, save all settings to config file
@@ -1830,6 +1925,8 @@ and `minetest.auth_reload` call the authetification handler.
* `minetest.punch_node(pos)`
* Punch node with the same effects that a player would cause
+* `minetest.find_nodes_with_meta(pos1, pos2)`
+ * Get a table of positions of nodes that have metadata within a region {pos1, pos2}
* `minetest.get_meta(pos)`
* Get a `NodeMetaRef` at that position
* `minetest.get_node_timer(pos)`
@@ -1848,6 +1945,10 @@ and `minetest.auth_reload` call the authetification handler.
* `minetest.find_node_near(pos, radius, nodenames)`: returns pos or `nil`
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `minetest.find_nodes_in_area(minp, maxp, nodenames)`: returns a list of positions
+ * returns as second value a table with the count of the individual nodes found
+ * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
+* `minetest.find_nodes_in_area_under_air(minp, maxp, nodenames)`: returns a list of positions
+ * returned positions are nodes with a node air above
* `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
* `minetest.get_perlin(noiseparams)`
* `minetest.get_perlin(seeddiff, octaves, persistence, scale)`
@@ -1860,14 +1961,17 @@ and `minetest.auth_reload` call the authetification handler.
* `flags` is a flag field with the available flags: `dungeon`, `temple`, `cave_begin`,
`cave_end`, `large_cave_begin`, `large_cave_end`, `decoration`
* The second parameter is a list of IDS of decorations which notification is requested for
+* `get_gen_notify()`: returns a flagstring and a table with the deco_ids
* `minetest.get_mapgen_object(objectname)`
* Return requested mapgen object if available (see "Mapgen objects")
* `minetest.get_mapgen_params()` Returns mapgen parameters, a table containing
`mgname`, `seed`, `chunksize`, `water_level`, and `flags`.
* `minetest.set_mapgen_params(MapgenParams)`
* Set map generation parameters
- * Function cannot be called after the registration period; only initialization and `on_mapgen_init`
- * Takes a table as an argument with the fields `mgname`, `seed`, `water_level`, and `flags`.
+ * Function cannot be called after the registration period; only initialization
+ and `on_mapgen_init`
+ * Takes a table as an argument with the fields `mgname`, `seed`, `water_level`,
+ and `flags`.
* Leave field unset to leave that parameter unchanged
* `flags` contains a comma-delimited string of flags to set,
or if the prefix `"no"` is attached, clears instead.
@@ -1876,10 +1980,13 @@ and `minetest.auth_reload` call the authetification handler.
* Sets the noiseparams setting of `name` to the noiseparams table specified in `noiseparams`.
* `set_default` is an optional boolean (default: `true`) that specifies whether the setting
should be applied to the default config or current active config
-* `minetest.generate_ores(vm)`
- * Generate all registered ores within the VoxelManip specified by `vm`.
-* `minetest.generate_decorations(vm)`
- * Generate all registered decorations within the VoxelManip specified by `vm`.
+* `minetest.get_noiseparams(name)`: returns a table of the noiseparams for name
+* `minetest.generate_ores(vm, pos1, pos2)`
+ * Generate all registered ores within the VoxelManip `vm` and in the area from `pos1` to `pos2`.
+ * `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
+* `minetest.generate_decorations(vm, pos1, pos2)`
+ * Generate all registered decorations within the VoxelManip `vm` and in the area from `pos1` to `pos2`.
+ * `pos1` and `pos2` are optional and default to mapchunk minp and maxp.
* `minetest.clear_objects()`
* clear all objects in the environments
* `minetest.delete_area(pos1, pos2)`
@@ -1926,6 +2033,9 @@ and `minetest.auth_reload` call the authetification handler.
* `minetest.create_detached_inventory(name, callbacks)`: returns an `InvRef`
* callbacks: See "Detached inventory callbacks"
* Creates a detached inventory. If it already exists, it is cleared.
+* `minetest.do_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)`:
+ returns left over ItemStack
+ * See `minetest.item_eat` and `minetest.register_on_item_eat`
### Formspec
* `minetest.show_formspec(playername, formname, formspec)`
@@ -1977,6 +2087,8 @@ and `minetest.auth_reload` call the authetification handler.
`{ stack1, stack2, stack3, stack4, stack 5, stack 6, stack 7, stack 8, stack 9 }`
* `output.item` = `ItemStack`, if unsuccessful: empty `ItemStack`
* `output.time` = a number, if unsuccessful: `0`
+ * `output.replacements` = list of `ItemStack`s that couldn't be placed in
+ `decremented_input.items`
* `decremented_input` = like `input`
* `minetest.get_craft_recipe(output)`: returns input
* returns last registered recipe for output item (node)
@@ -2037,7 +2149,11 @@ These functions return the leftover itemstack.
* `minetest.item_drop(itemstack, dropper, pos)`
* Drop the item
* `minetest.item_eat(hp_change, replace_with_item)`
- * Eat the item. `replace_with_item` can be `nil`.
+ * Eat the item.
+ * `replace_with_item` is the itemstring which is added to the inventory.
+ If the player is eating a stack, then replace_with_item goes to a
+ different spot. Can be `nil`
+ * See `minetest.do_item_eat`
### Defaults for the `on_punch` and `on_dig` node definition callbacks
* `minetest.node_punch(pos, node, puncher, pointed_thing)`
@@ -2058,7 +2174,8 @@ These functions return the leftover itemstack.
* Optional: Variable number of arguments that are passed to `func`
### Server
-* `minetest.request_shutdown()`: request for server shutdown
+* `minetest.request_shutdown([message],[reconnect])`: request for server shutdown. Will display `message` to clients,
+ and `reconnect` == true displays a reconnect button.
* `minetest.get_server_status()`: returns server status string
### Bans
@@ -2075,7 +2192,7 @@ These functions return the leftover itemstack.
* `minetest.add_particlespawner(particlespawner definition)`
* Add a `ParticleSpawner`, an object that spawns an amount of particles over `time` seconds
- * Returns an `id`
+ * Returns an `id`, and -1 if adding didn't succeed
* `Deprecated: minetest.add_particlespawner(amount, time,
minpos, maxpos,
minvel, maxvel,
@@ -2094,12 +2211,15 @@ These functions return the leftover itemstack.
* Create a schematic from the volume of map specified by the box formed by p1 and p2.
* Apply the specified probability values to the specified nodes in `probability_list`.
* `probability_list` is an array of tables containing two fields, `pos` and `prob`.
- * `pos` is the 3D vector specifying the absolute coordinates of the node being modified,
+ * `pos` is the 3D vector specifying the absolute coordinates of the
+ node being modified,
* `prob` is the integer value from `0` to `255` of the probability (see: Schematic specifier).
- * If there are two or more entries with the same pos value, the last entry is used.
+ * If there are two or more entries with the same pos value, the
+ last entry is used.
* If `pos` is not inside the box formed by `p1` and `p2`, it is ignored.
* If `probability_list` equals `nil`, no probabilities are applied.
- * Slice probability works in the same manner, except takes a field called `ypos` instead which
+ * Slice probability works in the same manner, except takes a field
+ called `ypos` instead which
indicates the y position of the slice with a probability applied.
* If slice probability list equals `nil`, no slice probabilities are applied.
* Saves schematic in the Minetest Schematic format to filename.
@@ -2112,6 +2232,17 @@ These functions return the leftover itemstack.
* `force_placement` is a boolean indicating whether nodes other than `air` and
`ignore` are replaced by the schematic
+* `minetest.serialize_schematic(schematic, format, options)`
+ * Return the serialized schematic specified by schematic (see: Schematic specifier)
+ * in the `format` of either "mts" or "lua".
+ * "mts" - a string containing the binary MTS data used in the MTS file format
+ * "lua" - a string containing Lua code representing the schematic in table format
+ * `options` is a table containing the following optional parameters:
+ * If `lua_use_comments` is true and `format` is "lua", the Lua code generated will have (X, Z)
+ * position comments for every X row generated in the schematic data for easier reading.
+ * If `lua_num_indent_spaces` is a nonzero number and `format` is "lua", the Lua code generated
+ * will use that number of spaces as indentation instead of a tab character.
+
### Misc.
* `minetest.get_connected_players()`: returns list of `ObjectRefs`
* `minetest.hash_node_position({x=,y=,z=})`: returns an 48-bit integer
@@ -2122,6 +2253,10 @@ These functions return the leftover itemstack.
* Get rating of a group of an item. (`0` means: not in group)
* `minetest.get_node_group(name, group)`: returns a rating
* Deprecated: An alias for the former.
+* `minetest.raillike_group(name)`: returns a rating
+ * Returns rating of the connect_to_raillike group corresponding to name
+ * If name is not yet the name of a connect_to_raillike group, a new group id
+ * is created, with that name
* `minetest.get_content_id(name)`: returns an integer
* Gets the internal content ID of `name`
* `minetest.get_name_from_content_id(content_id)`: returns a string
@@ -2165,6 +2300,9 @@ These functions return the leftover itemstack.
* currently supported.
* `...` indicates method-specific arguments. Currently, no methods use this.
* `minetest.is_protected(pos, name)`: returns boolean
+ * Returns true, if player `name` shouldn't be abled to dig at `pos` or do other
+ actions, defineable by mods, due to some mod-defined ownership-like concept.
+ Returns false or nil, if the player is allowed to do such actions.
* This function should be overridden by protection mods and should be used to
check if a player can interact at a position.
* This function should call the old version of itself if the position is not
@@ -2196,29 +2334,35 @@ These functions return the leftover itemstack.
the floor or ceiling
* The first four options are mutually-exclusive; the last in the list takes
precedence over the first.
-
-
-
* `minetest.rotate_node(itemstack, placer, pointed_thing)`
- * calls `rotate_and_place()` with infinitestacks set according to the state of
+ * calls `rotate_and_place()` with infinitestacks set according to the state of
the creative mode setting, and checks for "sneak" to set the `invert_wall`
parameter.
* `minetest.forceload_block(pos)`
* forceloads the position `pos`.
* returns `true` if area could be forceloaded
+ * Please note that forceloaded areas are saved when the server restarts.
* `minetest.forceload_free_block(pos)`
* stops forceloading the position `pos`
-Please note that forceloaded areas are saved when the server restarts.
+* `minetest.request_insecure_environment()`: returns an environment containing
+ insecure functions if the calling mod has been listed as trusted in the
+ `secure.trusted_mods` setting or security is disabled, otherwise returns `nil`.
+ * Only works at init time.
+ * **DO NOT ALLOW ANY OTHER MODS TO ACCESS THE RETURNED ENVIRONMENT, STORE IT IN
+ A LOCAL VARIABLE!**
+
+* `minetest.global_exists(name)`
+ * Checks if a global variable has been set, without triggering a warning.
### Global objects
* `minetest.env`: `EnvRef` of the server environment and world.
* Any function in the minetest namespace can be called using the syntax
`minetest.env:somefunction(somearguments)`
instead of `minetest.somefunction(somearguments)`
- * Deprecated, but support is not to be dropped soon
+ * Deprecated, but support is not to be dropped soon
### Global tables
* `minetest.registered_items`
@@ -2244,7 +2388,7 @@ Class reference
---------------
### `NodeMetaRef`
-Node metadata: reference extra data and functionality stored in a node.
+Node metadata: reference extra data and functionality stored in a node.
Can be gotten via `minetest.get_meta(pos)`.
#### Methods
@@ -2260,7 +2404,7 @@ Can be gotten via `minetest.get_meta(pos)`.
* See "Node Metadata"
### `NoteTimerRef`
-Node Timers: a high resolution persistent per-node timer.
+Node Timers: a high resolution persistent per-node timer.
Can be gotten via `minetest.get_node_timer(pos)`.
#### Methods
@@ -2288,6 +2432,7 @@ This is basically a reference to a C++ `ServerActiveObject`
#### Methods
* `remove()`: remove object (after returning from Lua)
+ * Note: Doesn't work on players, use minetest.kick_player instead
* `getpos()`: returns `{x=num, y=num, z=num}`
* `setpos(pos)`; `pos`=`{x=num, y=num, z=num}`
* `moveto(pos, continuous=false)`: interpolated move
@@ -2304,17 +2449,23 @@ This is basically a reference to a C++ `ServerActiveObject`
* `get_wielded_item()`: returns an `ItemStack`
* `set_wielded_item(item)`: replaces the wielded item, returns `true` if successful
* `set_armor_groups({group1=rating, group2=rating, ...})`
-* `set_animation({x=1,y=1}, frame_speed=15, frame_blend=0)`
+* `get_armor_groups()`: returns a table with the armor group ratings
+* `set_animation({x=1,y=1}, frame_speed=15, frame_blend=0, frame_loop=true)`
+* `get_animation()`: returns range, frame_speed, frame_blend and frame_loop
* `set_attach(parent, bone, position, rotation)`
* `bone`: string
* `position`: `{x=num, y=num, z=num}` (relative)
* `rotation`: `{x=num, y=num, z=num}`
+* `get_attach()`: returns parent, bone, position, rotation or nil if it isn't attached
* `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}`
+* `get_bone_position(bone)`: returns position and rotation of the bone
* `set_properties(object property table)`
+* `get_properties()`: returns object property table
+* `is_player()`: returns true for players, false otherwise
##### LuaEntitySAO-only (no-op for other objects)
* `setvelocity({x=num, y=num, z=num})`
@@ -2332,8 +2483,8 @@ This is basically a reference to a C++ `ServerActiveObject`
* `get_luaentity()`
##### Player-only (no-op for other objects)
-* `is_player()`: true for players, false for others
* `get_player_name()`: returns `""` if is not a player
+* `get_player_velocity()`: returns `nil` if is not a player otherwise a table {x, y, z} representing the player's instantaneous velocity in nodes/s
* `get_look_dir()`: get camera direction as a unit vector
* `get_look_pitch()`: pitch in radians
* `get_look_yaw()`: yaw in radians (wraps around pretty randomly as of now)
@@ -2360,48 +2511,72 @@ This is basically a reference to a C++ `ServerActiveObject`
* `gravity`: multiplier to default gravity value (default: `1`)
* `sneak`: whether player can sneak (default: `true`)
* `sneak_glitch`: whether player can use the sneak glitch (default: `true`)
-* `hud_add(hud definition)`: add a HUD element described by HUD def, returns ID number on success
+* `get_physics_override()`: returns the table given to set_physics_override
+* `hud_add(hud definition)`: add a HUD element described by HUD def, returns ID
+ number on success
* `hud_remove(id)`: remove the HUD element of the specified id
* `hud_change(id, stat, value)`: change a value of a previously added HUD element
* element `stat` values: `position`, `name`, `scale`, `text`, `number`, `item`, `dir`
* `hud_get(id)`: gets the HUD element definition structure of the specified ID
* `hud_set_flags(flags)`: sets specified HUD flags to `true`/`false`
- * `flags`: (is visible) `hotbar`, `healthbar`, `crosshair`, `wielditem`
+ * `flags`: (is visible) `hotbar`, `healthbar`, `crosshair`, `wielditem`, `minimap`
* pass a table containing a `true`/`false` value of each flag to be set or unset
* if a flag equals `nil`, the flag is not modified
+ * note that setting `minimap` modifies the client's permission to view the minimap -
+ * the client may locally elect to not view the minimap
* `hud_get_flags()`: returns a table containing status of hud flags
- * returns `{ hotbar=true, healthbar=true, crosshair=true, wielditem=true, breathbar=true }`
+ * returns `{ hotbar=true, healthbar=true, crosshair=true, wielditem=true, breathbar=true, minimap=true }`
* `hud_set_hotbar_itemcount(count)`: sets number of items in builtin hotbar
* `count`: number of items, must be between `1` and `23`
+* `hud_get_hotbar_itemcount`: returns number of visible items
* `hud_set_hotbar_image(texturename)`
* sets background image for hotbar
+* `hud_get_hotbar_image`: returns texturename
* `hud_set_hotbar_selected_image(texturename)`
* sets image for selected item of hotbar
+* `hud_get_hotbar_selected_image`: returns texturename
* `hud_replace_builtin(name, hud_definition)`
* replace definition of a builtin hud element
* `name`: `"breath"` or `"health"`
* `hud_definition`: definition to replace builtin definition
* `set_sky(bgcolor, type, {texture names})`
- * `bgcolor`: `{r=0...255, g=0...255, b=0...255}` or `nil`, defaults to white
+ * `bgcolor`: ColorSpec, defaults to white
* Available types:
* `"regular"`: Uses 0 textures, `bgcolor` ignored
* `"skybox"`: Uses 6 textures, `bgcolor` used
* `"plain"`: Uses 0 textures, `bgcolor` used
* **Note**: currently does not work directly in `on_joinplayer`; use
`minetest.after(0)` in there.
+* `get_sky()`: returns bgcolor, type and a table with the textures
* `override_day_night_ratio(ratio or nil)`
* `0`...`1`: Overrides day-night ratio, controlling sunlight to a specific amount
* `nil`: Disables override, defaulting to sunlight based on day-night cycle
-* `set_local_animation({x=0, y=79}, {x=168, y=187}, {x=189, y=198}, {x=200, y=219}, frame_speed=30)`:
- set animation for player model in third person view
- * stand/idle animation key frames
- * walk animation key frames
- * dig animation key frames
- * walk+dig animation key frames
- * animation frame speed
+* `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 frame_speed
* `set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})`: defines offset value for camera per player
* 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_nametag_attributes()`
+ * returns a table with the attributes of the nametag of the player
+ * {
+ color = {a=0..255, r=0..255, g=0..255, b=0..255},
+ }
+* `set_nametag_attributes(attributes)`
+ * sets the attributes of the nametag of the player
+ * `attributes`:
+ {
+ color = ColorSpec,
+ }
### `InvRef`
An `InvRef` is a reference to an inventory.
@@ -2432,6 +2607,28 @@ An `InvRef` is a reference to an inventory.
* `get_location()`: returns a location compatible to `minetest.get_inventory(location)`
* returns `{type="undefined"}` in case location is not known
+### `AreaStore`
+A fast access data structure to store areas, and find areas near a given position or area.
+Every area has a `data` string attribute to store additional information.
+You can create an empty `AreaStore` by calling `AreaStore()`, or `AreaStore(type_name)`.
+If you chose the parameter-less constructor, a fast implementation will be automatically chosen for you.
+
+#### Methods
+* `get_area(id, include_borders, include_data)`: returns the area with the id `id`. (optional) Boolean values `include_borders` and `include_data` control what's copied.
+* `get_areas_for_pos(pos, include_borders, include_data)`: returns all areas that contain the position `pos`. (optional) Boolean values `include_borders` and `include_data` control what's copied.
+* `get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)`: returns all areas that contain all nodes inside the area specified by `edge1` and `edge2` (inclusive). If `accept_overlap` is true, also areas are returned that have nodes in common with the specified area. (optional) Boolean values `include_borders` and `include_data` control what's copied.
+* `insert_area(edge1, edge2, data)`: inserts an area into the store. Returns the id if successful, nil otherwise. The (inclusive) positions `edge1` and `edge2` describe the area, `data`
+is a string stored with the area.
+* `reserve(count)`: reserves resources for at most `count` many contained areas. Only needed for efficiency, and only some implementations profit.
+* `remove_area(id)`: removes the area with the given id from the store, returns success.
+* `set_cache_params(params)`: sets params for the included prefiltering cache. Calling invalidates the cache, so that its elements have to be newly generated.
+ * `params`:
+ {
+ enabled = boolean, -- whether to enable, default true
+ block_radius = number, -- the radius (in nodes) of the areas the cache generates prefiltered lists for, minimum 16, default 64
+ limit = number, -- the cache's size, minimum 20, default 1000
+ }
+
### `ItemStack`
An `ItemStack` is a stack of items.
@@ -2472,7 +2669,8 @@ an itemstring, a table or `nil`.
Returns taken `ItemStack`.
### `PseudoRandom`
-A pseudorandom number generator.
+A 16-bit pseudorandom number generator.
+Uses a well-known LCG algorithm introduced by K&R.
It can be created via `PseudoRandom(seed)`.
@@ -2482,10 +2680,23 @@ It can be created via `PseudoRandom(seed)`.
* `((max - min) == 32767) or ((max-min) <= 6553))` must be true
due to the simple implementation making bad distribution otherwise.
+### `PcgRandom`
+A 32-bit pseudorandom number generator.
+Uses PCG32, an algorithm of the permuted congruential generator family, offering very strong randomness.
+
+It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`.
+
+#### Methods
+* `next()`: return next integer random number [`-2147483648`...`2147483647`]
+* `next(min, max)`: return next integer random number [`min`...`max`]
+* `rand_normal_dist(min, max, num_trials=6)`: return normally distributed random number [`min`...`max`]
+ * This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1
+ * Increasing num_trials improves accuracy of the approximation
+
### `PerlinNoise`
A perlin noise generator.
It can be created via `PerlinNoise(seed, octaves, persistence, scale)`
-or `PerlinNoise(noiseparams)`.
+or `PerlinNoise(noiseparams)`.
Alternatively with `minetest.get_perlin(seeddiff, octaves, persistence, scale)`
or `minetest.get_perlin(noiseparams)`.
@@ -2503,14 +2714,30 @@ Format of `size` is `{x=dimx, y=dimy, z=dimz}`. The `z` conponent is ommitted
for 2D noise, and it must be must be larger than 1 for 3D noise (otherwise
`nil` is returned).
+For each of the functions with an optional `buffer` parameter: If `buffer` is not
+nil, this table will be used to store the result instead of creating a new table.
+
+
#### Methods
* `get2dMap(pos)`: returns a `<size.x>` times `<size.y>` 2D array of 2D noise
with values starting at `pos={x=,y=}`
* `get3dMap(pos)`: returns a `<size.x>` times `<size.y>` times `<size.z>` 3D array
of 3D noise with values starting at `pos={x=,y=,z=}`
-* `get2dMap_flat(pos)`: returns a flat `<size.x * size.y>` element array of 2D noise
+* `get2dMap_flat(pos, buffer)`: returns a flat `<size.x * size.y>` element array of 2D noise
with values starting at `pos={x=,y=}`
-* `get3dMap_flat(pos)`: Same as `get2dMap_flat`, but 3D noise
+* `get3dMap_flat(pos, buffer)`: Same as `get2dMap_flat`, but 3D noise
+* `calc2dMap(pos)`: Calculates the 2d noise map starting at `pos`. The result is stored internally.
+* `calc3dMap(pos)`: Calculates the 3d noise map starting at `pos`. The result is stored internally.
+* `getMapSlice(slice_offset, slice_size, buffer)`: In the form of an array, returns a slice of the
+ most recently computed noise results. The result slice begins at coordinates `slice_offset` and
+ takes a chunk of `slice_size`.
+ E.g. to grab a 2-slice high horizontal 2d plane of noise starting at buffer offset y = 20:
+ `noisevals = noise:getMapSlice({y=20}, {y=2})`
+ It is important to note that `slice_offset` offset coordinates begin at 1, and are relative to
+ the starting position of the most recently calculated noise.
+ To grab a single vertical column of noise starting at map coordinates x = 1023, y=1000, z = 1000:
+ `noise:calc3dMap({x=1000, y=1000, z=1000})`
+ `noisevals = noise:getMapSlice({x=24, z=1}, {x=1, z=1})`
### `VoxelManip`
An interface to the `MapVoxelManipulator` for Lua.
@@ -2519,36 +2746,44 @@ It can be created via `VoxelManip()` or `minetest.get_voxel_manip()`.
The map will be pre-loaded if two positions are passed to either.
#### Methods
-* `read_from_map(p1, p2)`: Reads a chunk of map from the map containing the region formed by `p1` and `p2`.
+* `read_from_map(p1, p2)`: Reads a chunk of map from the map containing the
+ region formed by `p1` and `p2`.
* returns actual emerged `pmin`, actual emerged `pmax`
* `write_to_map()`: Writes the data loaded from the `VoxelManip` back to the map.
* **important**: data must be set using `VoxelManip:set_data` before calling this
-* `get_node_at(pos)`: Returns a `MapNode` table of the node currently loaded in the `VoxelManip` at that position
-* `set_node_at(pos, node)`: Sets a specific `MapNode` in the `VoxelManip` at that position
-* `get_data()`: Gets the data read into the `VoxelManip` object
- * returns raw node data is in the form of an array of node content IDs
+* `get_node_at(pos)`: Returns a `MapNode` table of the node currently loaded in
+ the `VoxelManip` at that position
+* `set_node_at(pos, node)`: Sets a specific `MapNode` in the `VoxelManip` at
+ that position
+* `get_data(buffer)`: Gets the data read into the `VoxelManip` object
+ * returns raw node data in the form of an array of node content IDs
+ * if the param `buffer` is present, this table will be used to store the result instead
* `set_data(data)`: Sets the data contents of the `VoxelManip` object
* `update_map()`: Update map after writing chunk back to map.
- * To be used only by `VoxelManip` objects created by the mod itself; not a `VoxelManip` that was
- retrieved from `minetest.get_mapgen_object`
+ * To be used only by `VoxelManip` objects created by the mod itself;
+ not a `VoxelManip` that was retrieved from `minetest.get_mapgen_object`
* `set_lighting(light, p1, p2)`: Set the lighting within the `VoxelManip` to a uniform value
* `light` is a table, `{day=<0...15>, night=<0...15>}`
* To be used only by a `VoxelManip` object from `minetest.get_mapgen_object`
- * (`p1`, `p2`) is the area in which lighting is set; defaults to the whole area if left out
+ * (`p1`, `p2`) is the area in which lighting is set;
+ defaults to the whole area if left out
* `get_light_data()`: Gets the light data read into the `VoxelManip` object
* Returns an array (indices 1 to volume) of integers ranging from `0` to `255`
* Each value is the bitwise combination of day and night light values (`0` to `15` each)
* `light = day + (night * 16)`
-* `set_light_data(light_data)`: Sets the `param1` (light) contents of each node in the `VoxelManip`
+* `set_light_data(light_data)`: Sets the `param1` (light) contents of each node
+ in the `VoxelManip`
* expects lighting data in the same format that `get_light_data()` returns
* `get_param2_data()`: Gets the raw `param2` data read into the `VoxelManip` object
* `set_param2_data(param2_data)`: Sets the `param2` contents of each node in the `VoxelManip`
* `calc_lighting(p1, p2)`: Calculate lighting within the `VoxelManip`
* To be used only by a `VoxelManip` object from `minetest.get_mapgen_object`
- * (`p1`, `p2`) is the area in which lighting is set; defaults to the whole area if left out
+ * (`p1`, `p2`) is the area in which lighting is set; defaults to the whole area
+ if left out
* `update_liquids()`: Update liquid flow
-* `was_modified()`: Returns `true` or `false` if the data in the voxel manipulator had been modified since
- the last read from map, due to a call to `minetest.set_data()` on the loaded area elsewhere
+* `was_modified()`: Returns `true` or `false` if the data in the voxel manipulator
+ had been modified since the last read from map, due to a call to
+ `minetest.set_data()` on the loaded area elsewhere
* `get_emerged_area()`: Returns actual emerged minimum and maximum positions.
### `VoxelArea`
@@ -2557,10 +2792,12 @@ It can be created via `VoxelArea:new{MinEdge=pmin, MaxEdge=pmax}`.
The coordinates are *inclusive*, like most other things in Minetest.
#### Methods
-* `getExtent()`: returns a 3D vector containing the size of the area formed by `MinEdge` and `MaxEdge`
+* `getExtent()`: returns a 3D vector containing the size of the area formed by
+ `MinEdge` and `MaxEdge`
* `getVolume()`: returns the volume of the area formed by `MinEdge` and `MaxEdge`
* `index(x, y, z)`: returns the index of an absolute position in a flat array starting at `1`
- * useful for things like `VoxelManip`, raw Schematic specifiers, `PerlinNoiseMap:get2d`/`3dMap`, and so on
+ * useful for things like `VoxelManip`, raw Schematic specifiers,
+ `PerlinNoiseMap:get2d`/`3dMap`, and so on
* `indexp(p)`: same as above, except takes a vector
* `position(i)`: returns the absolute position vector corresponding to index `i`
* `contains(x, y, z)`: check if (`x`,`y`,`z`) is inside area formed by `MinEdge` and `MaxEdge`
@@ -2587,37 +2824,39 @@ It can be created via `Settings(filename)`.
Mapgen objects
--------------
-A mapgen object is a construct used in map generation. Mapgen objects can be used by an `on_generate`
-callback to speed up operations by avoiding unnecessary recalculations; these can be retrieved using the
-`minetest.get_mapgen_object()` function. If the requested Mapgen object is unavailable, or
-`get_mapgen_object()` was called outside of an `on_generate()` callback, `nil` is returned.
+A mapgen object is a construct used in map generation. Mapgen objects can be used
+by an `on_generate` callback to speed up operations by avoiding unnecessary
+recalculations; these can be retrieved using the `minetest.get_mapgen_object()`
+function. If the requested Mapgen object is unavailable, or `get_mapgen_object()`
+was called outside of an `on_generate()` callback, `nil` is returned.
The following Mapgen objects are currently available:
### `voxelmanip`
-This returns three values; the `VoxelManip` object to be used, minimum and maximum emerged position, in that
-order. All mapgens support this object.
+This returns three values; the `VoxelManip` object to be used, minimum and maximum
+emerged position, in that order. All mapgens support this object.
### `heightmap`
-Returns an array containing the y coordinates of the ground levels of nodes in the most recently
-generated chunk by the current mapgen.
+Returns an array containing the y coordinates of the ground levels of nodes in
+the most recently generated chunk by the current mapgen.
### `biomemap`
-Returns an array containing the biome IDs of nodes in the most recently generated chunk by the
-current mapgen.
+Returns an array containing the biome IDs of nodes in the most recently
+generated chunk by the current mapgen.
### `heatmap`
-Returns an array containing the temperature values of nodes in the most recently generated chunk by
-the current mapgen.
+Returns an array containing the temperature values of nodes in the most
+recently generated chunk by the current mapgen.
### `humiditymap`
-Returns an array containing the humidity values of nodes in the most recently generated chunk by the
-current mapgen.
+Returns an array containing the humidity values of nodes in the most recently
+generated chunk by the current mapgen.
### `gennotify`
-Returns a table mapping requested generation notification types to arrays of positions at which the
-corresponding generated structures are located at within the current chunk. To set the capture of positions
-of interest to be recorded on generate, use `minetest.set_gen_notify()`.
+Returns a table mapping requested generation notification types to arrays of
+positions at which the corresponding generated structures are located at within
+the current chunk. To set the capture of positions of interest to be recorded
+on generate, use `minetest.set_gen_notify()`.
Possible fields of the table returned are:
@@ -2629,7 +2868,8 @@ Possible fields of the table returned are:
* `large_cave_end`
* `decoration`
-Decorations have a key in the format of `"decoration#id"`, where `id` is the numeric unique decoration ID.
+Decorations have a key in the format of `"decoration#id"`, where `id` is the
+numeric unique decoration ID.
Registered entities
-------------------
@@ -2676,7 +2916,8 @@ L-system trees
angle, --num angle in deg
iterations, --num max # of iterations, usually 2 -5
random_level, --num factor to lower nr of iterations, usually 0 - 3
- trunk_type, --string single/double/crossed) type of trunk: 1 node, 2x2 nodes or 3x3 in cross shape
+ trunk_type, --string single/double/crossed) type of trunk: 1 node,
+ -- 2x2 nodes or 3x3 in cross shape
thin_branches, --boolean true -> use thin (1 node) branches
fruit, --string fruit node name
fruit_chance, --num chance (0-100) to replace leaves with fruit node
@@ -2856,8 +3097,13 @@ Definition tables
### Tile definition
* `"image.png"`
* `{name="image.png", animation={Tile Animation definition}}`
-* `{name="image.png", backface_culling=bool}`
- * backface culling only supported in special tiles
+* `{name="image.png", backface_culling=bool, tileable_vertical=bool,
+ tileable_horizontal=bool}`
+ * backface culling only supported in special tiles.
+ * 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
* deprecated, yet still supported field names:
* `image` (name)
@@ -2883,7 +3129,7 @@ Definition tables
^ List can be shortened to needed length ]]
alpha = 255,
use_texture_alpha = false, -- Use texture's alpha channel
- post_effect_color = {a=0, r=0, g=0, b=0}, -- If player is inside node
+ post_effect_color = "green#0F", -- If player is inside node, see "ColorSpec"
paramtype = "none", -- See "Nodes" --[[
^ paramtype = "light" allows light to propagate from or through the node with light value
^ falling by 1 per node. This line is essential for a light source node to spread its light. ]]
@@ -2952,7 +3198,7 @@ Definition tables
^ Called after destructing node when node was dug using
minetest.node_dig / minetest.dig_node
^ default: nil ]]
- can_dig = function(pos,player) --[[
+ can_dig = function(pos, [player]) --[[
^ returns true if node can be dug, or false if not
^ default: nil ]]
@@ -3087,6 +3333,10 @@ Definition tables
-- ^ Multiplier of the randomness contribution to the noise value at any
-- given point to decide if ore should be placed. Set to 0 for solid veins.
-- ^ This parameter is only valid for ore_type == "vein".
+ biomes = {"desert", "rainforest"}
+ -- ^ List of biomes in which this decoration occurs. Occurs in all biomes if this is omitted,
+ -- ^ and ignored if the Mapgen being used does not support biomes.
+ -- ^ Can be a list of (or a single) biome names, IDs, or definitions.
}
### Decoration definition (`register_decoration`)
@@ -3107,6 +3357,7 @@ Definition tables
biomes = {"Oceanside", "Hills", "Plains"},
-- ^ List of biomes in which this decoration occurs. Occurs in all biomes if this is omitted,
-- ^ and ignored if the Mapgen being used does not support biomes.
+ -- ^ Can be a list of (or a single) biome names, IDs, or definitions.
y_min = -31000
y_max = 31000
-- ^ Minimum and maximum `y` positions these decorations can be generated at.
@@ -3124,7 +3375,9 @@ Definition tables
-- ^ Number of nodes the decoration can be at maximum.
-- ^ If absent, the parameter 'height' is used as a constant.
spawn_by = "default:water",
- -- ^ Node that the decoration only spawns next to, in a 1-node square radius.
+ -- ^ Node that the decoration only spawns next to.
+ -- ^ The neighbours checked are the 8 nodes horizontally surrounding the lowest node of the
+ -- ^ decoration, and the 8 nodes horizontally surrounding the ground node below the decoration.
num_spawn_by = 1,
-- ^ Number of spawn_by nodes that must be surrounding the decoration position to occur.
-- ^ If absent or -1, decorations occur next to any nodes.
@@ -3133,13 +3386,16 @@ Definition tables
schematic = "foobar.mts",
-- ^ If schematic is a string, it is the filepath relative to the current working directory of the
-- ^ specified Minetest schematic file.
+ -- ^ - OR -, could be the ID of a previously registered schematic
-- ^ - OR -, could instead be a table containing two mandatory fields, size and data,
-- ^ and an optional table yslice_prob:
schematic = {
size = {x=4, y=6, z=4},
data = {
- {name="cobble", param1=255, param2=0},
- {name="dirt_with_grass", param1=255, param2=0},
+ {name="default:cobble", param1=255, param2=0},
+ {name="default:dirt_with_grass", param1=255, param2=0},
+ {name="ignore", param1=255, param2=0},
+ {name="air", param1=255, param2=0},
...
},
yslice_prob = {
@@ -3150,7 +3406,7 @@ Definition tables
},
-- ^ See 'Schematic specifier' for details.
replacements = {["oldname"] = "convert_to", ...},
- flags = "place_center_x, place_center_z",
+ flags = "place_center_x, place_center_y, place_center_z, force_placement",
-- ^ Flags for schematic decorations. See 'Schematic attributes'.
rotation = "90" -- rotate schematic 90 degrees on placement
-- ^ Rotation can be "0", "90", "180", "270", or "random".
diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt
index e5ba46d4c..7d46c6526 100644
--- a/doc/menu_lua_api.txt
+++ b/doc/menu_lua_api.txt
@@ -1,4 +1,4 @@
-Minetest Lua Mainmenu API Reference 0.4.12
+Minetest Lua Mainmenu API Reference 0.4.13
========================================
Introduction
@@ -31,8 +31,8 @@ core.start()
core.close()
Filesystem:
-core.get_scriptdir()
-^ returns directory of script
+core.get_builtin_path()
+^ returns path to builtin root
core.get_modpath() (possible in async calls)
^ returns path to global modpath
core.get_modstore_details(modid) (possible in async calls)
@@ -59,10 +59,6 @@ core.get_gamepath() (possible in async calls)
^ returns path to global gamepath
core.get_texturepath() (possible in async calls)
^ returns path to default textures
-core.get_dirlist(path,onlydirs) (possible in async calls)
-^ path to get subdirs from
-^ onlydirs should result contain only dirs?
-^ returns list of folders within path
core.create_dir(absolute_path) (possible in async calls)
^ absolute_path to directory to create (needs to be absolute)
^ returns true/false
diff --git a/doc/minetest.6 b/doc/minetest.6
index cd818e1d8..036cea6c9 100644
--- a/doc/minetest.6
+++ b/doc/minetest.6
@@ -1,12 +1,18 @@
-.\" Minetest man page
.TH minetest 6 "10 September 2013" "" ""
.SH NAME
-minetest \- Multiplayer infinite-world block sandbox
+minetest, minetestserver \- Multiplayer infinite-world block sandbox
.SH SYNOPSIS
.B minetest
-[ OPTION ... ]
+[\fB--server SERVER OPTIONS\fR | \fBCLIENT OPTIONS\fR]
+[\fBCOMMON OPTIONS\fR]
+[\fBWORLD PATH\fR]
+
+.B minetestserver
+[\fBSERVER OPTIONS\fR]
+[\fBCOMMON OPTIONS\fR]
+[\fBWORLD PATH\fR]
.SH DESCRIPTION
.B Minetest
@@ -14,79 +20,79 @@ is one of the first InfiniMiner/Minecraft(/whatever) inspired games (started Oct
.PP
The main design philosophy is to keep it technically simple, stable and portable. It will be kept lightweight enough to run on fairly old hardware.
-.SH OPTIONS
+.SH COMMON OPTIONS
.TP
-\-\-address <value>
-Address to connect to
+.B \-\-help
+Print allowed options and exit
.TP
-\-\-config <value>
+.B \-\-version
+Print version information and exit
+.TP
+.B \-\-config <value>
Load configuration from specified file
.TP
-\-\-disable\-unittests
-Disable unit tests
+.B \-\-logfile <value>
+Set logfile path (debug.txt)
.TP
-\-\-enable\-unittests
-Enable unit tests
+.B \-\-info
+Print more information to console
.TP
-\-\-gameid <value>
-Set gameid
+.B \-\-verbose
+Print even more information to console
.TP
-\-\-go
-Disable main menu
+.B \-\-trace
+Print enormous amounts of information to console
.TP
-\-\-help
-Show allowed options
+.B \-\-gameid <value>
+Set gameid
.TP
-\-\-version
-Show version information
+.B \-\-worldname <value>
+Set world path by name
.TP
-\-\-logfile <value>
-Set logfile path (debug.txt)
+.B \-\-world <value> | list
+Set world path or list worlds
.TP
-\-\-map\-dir <value>
+.B \-\-map\-dir <value>
Same as \-\-world (deprecated)
.TP
-\-\-name <value>
-Set player name
-.TP
-\-\-password <value>
-Set password
-.TP
-\-\-port <value>
+.B \-\-port <value>
Set network port (UDP) to use
.TP
-\-\-random\-input
-Enable random user input, for testing
-.TP
-\-\-server
-Run dedicated server
+.B \-\-run\-unittests
+Run unit tests and exit
+
+.SH CLIENT OPTIONS
.TP
-\-\-speedtests
-Run speed tests
+.B \-\-address <value>
+Address to connect to
.TP
-\-\-videomodes
-List available video modes
+.B \-\-go
+Disable main menu
.TP
-\-\-info
-Print more information to console
+.B \-\-name <value>
+Set player name
.TP
-\-\-verbose
-Print even more information to console
+.B \-\-password <value>
+Set password
.TP
-\-\-trace
-Print enormous amounts of information to console
+.B \-\-random\-input
+Enable random user input, for testing (client only)
.TP
-\-\-world <value>
-Set world path
+.B \-\-videomodes
+List available video modes (client only)
.TP
-\-\-migrate <value>
-Migrate from current map backend to another. Possible values are sqlite3
-and leveldb. Only works when using \-\-server.
+.B \-\-speedtests
+Run speed tests
-.SH ENVIRONMENT VARIABLES
+.SH SERVER OPTIONS
+.TP
+.B \-\-migrate <value>
+Migrate from current map backend to another. Possible values are sqlite3,
+leveldb, redis, and dummy.
+.SH ENVIRONMENT
.TP
-MINETEST_SUBGAME_PATH
+.B MINETEST_SUBGAME_PATH
Colon delimited list of directories to search for subgames.
.SH BUGS
@@ -103,5 +109,3 @@ Juhani Numminen <juhaninumminen0@gmail.com>.
.SH WWW
http://www.minetest.net/
-.SH "SEE ALSO"
-.BR minetestserver(6)
diff --git a/doc/minetestserver.6 b/doc/minetestserver.6
index 1d4a5f838..db5330d3c 100644
--- a/doc/minetestserver.6
+++ b/doc/minetestserver.6
@@ -1,77 +1,2 @@
-.\" Minetestserver man page
-.TH minetestserver 6 "10 September 2013" "" ""
+.so man6/minetest.6
-.SH NAME
-minetestserver \- Minetest server
-
-.SH SYNOPSIS
-.B minetestserver
-[ OPTION ... ]
-
-.SH DESCRIPTION
-.B Minetest
-is one of the first InfiniMiner/Minecraft(/whatever) inspired games (started October 2010), with a goal of taking the survival multiplayer gameplay to a slightly different direction.
-.PP
-The main design philosophy is to keep it technically simple, stable and portable. It will be kept lightweight enough to run on fairly old hardware.
-
-.SH OPTIONS
-.TP
-\-\-config <value>
-Load configuration from specified file
-.TP
-\-\-disable\-unittests
-Disable unit tests
-.TP
-\-\-enable\-unittests
-Enable unit tests
-.TP
-\-\-gameid <value>
-Set gameid
-.TP
-\-\-help
-Show allowed options
-.TP
-\-\-version
-Show version information
-.TP
-\-\-logfile <value>
-Set logfile path (debug.txt)
-.TP
-\-\-map\-dir <value>
-Same as \-\-world (deprecated)
-.TP
-\-\-port <value>
-Set network port (UDP) to use
-.TP
-\-\-info
-Print more information to console
-.TP
-\-\-verbose
-Print even more information to console
-.TP
-\-\-trace
-Print enormous amounts of information to console
-.TP
-\-\-world <value>
-Set world path
-.TP
-\-\-migrate <value>
-Migrate from current map backend to another. Possible values are sqlite3
-and leveldb.
-
-.SH BUGS
-Please report all bugs to Perttu Ahola <celeron55@gmail.com>.
-
-.SH AUTHOR
-.PP
-Perttu Ahola <celeron55@gmail.com>
-and contributors.
-.PP
-This man page was originally written by
-Juhani Numminen <juhaninumminen0@gmail.com>.
-
-.SH WWW
-http://www.minetest.net/
-
-.SH "SEE ALSO"
-.BR minetest(6)
diff --git a/doc/texture_overrides.txt b/doc/texture_overrides.txt
new file mode 100644
index 000000000..1a4e11a3c
--- /dev/null
+++ b/doc/texture_overrides.txt
@@ -0,0 +1,35 @@
+Texture Overrides
+=================
+
+You can override the textures of a node from a texture pack using
+texture overrides. To do this, create a file in a texture pack
+called override.txt
+
+Basic Format
+------------
+
+Each line in an override.txt file is a rule. It consists of
+
+ nodename face-selector texture
+
+For example,
+
+ default:dirt_with_grass sides default_stone.png
+
+You can use ^ operators as usual:
+
+ default:dirt_with_grass sides default_stone.png^[brighten
+
+Face Selectors
+--------------
+
+| face-selector | behavior |
+|---------------|---------------------------------------------------|
+| left | x- |
+| right | x+ |
+| front | z- |
+| back | z+ |
+| top | y+ |
+| bottom | y- |
+| sides | x-, x+, z-, z+ |
+| all | All faces. You can also use '*' instead of 'all'. |
diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua
index 01bf65b95..bff7860e3 100644
--- a/games/minimal/mods/default/init.lua
+++ b/games/minimal/mods/default/init.lua
@@ -452,9 +452,7 @@ minetest.register_craft({
}
})
---
--- Crafting (tool repair)
---
+-- Tool repair
minetest.register_craft({
type = "toolrepair",
additional_wear = -0.02,
@@ -478,12 +476,6 @@ minetest.register_craft({
minetest.register_craft({
type = "cooking",
- output = "default:coal_lump",
- recipe = "default:jungletree",
-})
-
-minetest.register_craft({
- type = "cooking",
output = "default:stone",
recipe = "default:cobble",
})
@@ -512,12 +504,6 @@ minetest.register_craft({
minetest.register_craft({
type = "fuel",
- recipe = "default:jungletree",
- burntime = 30,
-})
-
-minetest.register_craft({
- type = "fuel",
recipe = "default:junglegrass",
burntime = 2,
})
@@ -707,7 +693,7 @@ function default.node_sound_glass_defaults(table)
return table
end
---
+-- Register nodes
minetest.register_node("default:stone", {
description = "Stone",
@@ -736,7 +722,9 @@ minetest.register_node("default:stone_with_iron", {
minetest.register_node("default:dirt_with_grass", {
description = "Dirt with grass",
- tiles ={"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
+ tiles ={"default_grass.png", "default_dirt.png",
+ {name = "default_dirt.png^default_grass_side.png",
+ tileable_vertical = false}},
groups = {crumbly=3, soil=1},
drop = 'default:dirt',
sounds = default.node_sound_dirt_defaults({
@@ -746,7 +734,9 @@ minetest.register_node("default:dirt_with_grass", {
minetest.register_node("default:dirt_with_grass_footsteps", {
description = "Dirt with grass and footsteps",
- tiles ={"default_grass_footsteps.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"},
+ tiles ={"default_grass_footsteps.png", "default_dirt.png",
+ {name = "default_dirt.png^default_grass_side.png",
+ tileable_vertical = false}},
groups = {crumbly=3, soil=1},
drop = 'default:dirt',
sounds = default.node_sound_dirt_defaults({
@@ -798,6 +788,7 @@ minetest.register_node("default:clay", {
minetest.register_node("default:brick", {
description = "Brick",
tiles ={"default_brick.png"},
+ is_ground_content = false,
groups = {cracky=3},
drop = 'default:clay_brick 4',
sounds = default.node_sound_stone_defaults(),
@@ -806,13 +797,7 @@ minetest.register_node("default:brick", {
minetest.register_node("default:tree", {
description = "Tree",
tiles ={"default_tree_top.png", "default_tree_top.png", "default_tree.png"},
- groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1},
- sounds = default.node_sound_wood_defaults(),
-})
-
-minetest.register_node("default:jungletree", {
- description = "Jungle Tree",
- tiles ={"default_jungletree_top.png", "default_jungletree_top.png", "default_jungletree.png"},
+ is_ground_content = false,
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=1},
sounds = default.node_sound_wood_defaults(),
})
@@ -878,6 +863,7 @@ minetest.register_node("default:papyrus", {
minetest.register_node("default:bookshelf", {
description = "Bookshelf",
tiles ={"default_wood.png", "default_wood.png", "default_bookshelf.png"},
+ is_ground_content = false,
groups = {snappy=2,choppy=3,oddly_breakable_by_hand=2},
sounds = default.node_sound_wood_defaults(),
})
@@ -887,6 +873,7 @@ minetest.register_node("default:glass", {
drawtype = "glasslike",
tiles ={"default_glass.png"},
paramtype = "light",
+ is_ground_content = false,
sunlight_propagates = true,
groups = {snappy=2,cracky=3,oddly_breakable_by_hand=3},
sounds = default.node_sound_glass_defaults(),
@@ -899,6 +886,7 @@ minetest.register_node("default:fence_wood", {
inventory_image = "default_fence.png",
wield_image = "default_fence.png",
paramtype = "light",
+ is_ground_content = false,
selection_box = {
type = "fixed",
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
@@ -914,6 +902,7 @@ minetest.register_node("default:rail", {
inventory_image = "default_rail.png",
wield_image = "default_rail.png",
paramtype = "light",
+ is_ground_content = false,
walkable = false,
selection_box = {
type = "fixed",
@@ -930,6 +919,7 @@ minetest.register_node("default:ladder", {
wield_image = "default_ladder.png",
paramtype = "light",
paramtype2 = "wallmounted",
+ is_ground_content = false,
walkable = false,
climbable = true,
selection_box = {
@@ -946,6 +936,7 @@ minetest.register_node("default:ladder", {
minetest.register_node("default:wood", {
description = "Wood",
tiles ={"default_wood.png"},
+ is_ground_content = false,
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2},
sounds = default.node_sound_wood_defaults(),
})
@@ -960,6 +951,7 @@ minetest.register_node("default:mese", {
minetest.register_node("default:cloud", {
description = "Cloud",
tiles ={"default_cloud.png"},
+ is_ground_content = false,
sounds = default.node_sound_defaults(),
})
@@ -978,6 +970,7 @@ minetest.register_node("default:water_flowing", {
pointable = false,
diggable = false,
buildable_to = true,
+ is_ground_content = false,
drowning = 1,
liquidtype = "flowing",
liquid_alternative_flowing = "default:water_flowing",
@@ -1002,6 +995,7 @@ minetest.register_node("default:water_source", {
pointable = false,
diggable = false,
buildable_to = true,
+ is_ground_content = false,
drowning = 1,
liquidtype = "source",
liquid_alternative_flowing = "default:water_flowing",
@@ -1034,6 +1028,7 @@ minetest.register_node("default:lava_flowing", {
pointable = false,
diggable = false,
buildable_to = true,
+ is_ground_content = false,
drowning = 1,
liquidtype = "flowing",
liquid_alternative_flowing = "default:lava_flowing",
@@ -1062,6 +1057,7 @@ minetest.register_node("default:lava_source", {
pointable = false,
diggable = false,
buildable_to = true,
+ is_ground_content = false,
drowning = 1,
liquidtype = "source",
liquid_alternative_flowing = "default:lava_flowing",
@@ -1146,7 +1142,8 @@ minetest.register_node("default:chest", {
meta:set_string("formspec",
"size[8,9]"..
"list[current_name;main;0,0;8,4;]"..
- "list[current_player;main;0,5;8,4;]")
+ "list[current_player;main;0,5;8,4;]" ..
+ "listring[]")
meta:set_string("infotext", "Chest")
local inv = meta:get_inventory()
inv:set_size("main", 8*4)
@@ -1185,7 +1182,8 @@ minetest.register_node("default:chest_locked", {
meta:set_string("formspec",
"size[8,9]"..
"list[current_name;main;0,0;8,4;]"..
- "list[current_player;main;0,5;8,4;]")
+ "list[current_player;main;0,5;8,4;]" ..
+ "listring[]")
meta:set_string("infotext", "Locked Chest")
meta:set_string("owner", "")
local inv = meta:get_inventory()
@@ -1249,7 +1247,11 @@ default.furnace_inactive_formspec =
"list[current_name;fuel;2,3;1,1;]"..
"list[current_name;src;2,1;1,1;]"..
"list[current_name;dst;5,1;2,2;]"..
- "list[current_player;main;0,5;8,4;]"
+ "list[current_player;main;0,5;8,4;]" ..
+ "listring[current_name;dst]" ..
+ "listring[current_player;main]" ..
+ "listring[current_name;src]" ..
+ "listring[current_player;main]"
minetest.register_node("default:furnace", {
description = "Furnace",
@@ -1386,7 +1388,11 @@ minetest.register_abm({
"list[current_name;fuel;2,3;1,1;]"..
"list[current_name;src;2,1;1,1;]"..
"list[current_name;dst;5,1;2,2;]"..
- "list[current_player;main;0,5;8,4;]")
+ "list[current_player;main;0,5;8,4;]" ..
+ "listring[current_name;dst]" ..
+ "listring[current_player;main]" ..
+ "listring[current_name;src]" ..
+ "listring[current_player;main]")
return
end
@@ -1430,6 +1436,7 @@ minetest.register_abm({
minetest.register_node("default:cobble", {
description = "Cobble",
tiles ={"default_cobble.png"},
+ is_ground_content = false,
groups = {cracky=3},
sounds = default.node_sound_stone_defaults(),
})
@@ -1437,6 +1444,7 @@ minetest.register_node("default:cobble", {
minetest.register_node("default:mossycobble", {
description = "Mossy Cobble",
tiles ={"default_mossycobble.png"},
+ is_ground_content = false,
groups = {cracky=3},
sounds = default.node_sound_stone_defaults(),
})
@@ -1444,6 +1452,7 @@ minetest.register_node("default:mossycobble", {
minetest.register_node("default:steelblock", {
description = "Steel Block",
tiles ={"default_steel_block.png"},
+ is_ground_content = false,
groups = {snappy=1,bendy=2},
sounds = default.node_sound_stone_defaults(),
})
@@ -1489,6 +1498,7 @@ minetest.register_node("default:apple", {
tiles ={"default_apple.png"},
inventory_image = "default_apple.png",
paramtype = "light",
+ is_ground_content = false,
sunlight_propagates = true,
walkable = false,
groups = {fleshy=3,dig_immediate=3},
@@ -1496,6 +1506,9 @@ minetest.register_node("default:apple", {
sounds = default.node_sound_defaults(),
})
+--
+-- Grow tree function
+--
local c_air = minetest.get_content_id("air")
local c_ignore = minetest.get_content_id("ignore")
@@ -1568,13 +1581,19 @@ function default.grow_tree(data, a, pos, is_apple_tree, seed)
end
end
+--
+-- ABMs
+--
+
minetest.register_abm({
nodenames = {"default:sapling"},
interval = 10,
chance = 50,
action = function(pos, node)
- local is_soil = minetest.registered_nodes[minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name].groups.soil
- if is_soil == nil or is_soil == 0 then return end
+ if minetest.get_item_group(minetest.get_node(
+ {x = pos.x, y = pos.y - 1, z = pos.z}).name, "soil") == 0 then
+ return
+ end
print("A sapling grows into a tree at "..minetest.pos_to_string(pos))
local vm = minetest.get_voxel_manip()
local minp, maxp = vm:read_from_map({x=pos.x-16, y=pos.y, z=pos.z-16}, {x=pos.x+16, y=pos.y+16, z=pos.z+16})
@@ -1674,29 +1693,9 @@ minetest.register_craftitem("default:scorched_stuff", {
})
--
--- Aliases for the current map generator outputs
+-- Support old code
--
-minetest.register_alias("mapgen_air", "air")
-minetest.register_alias("mapgen_stone", "default:stone")
-minetest.register_alias("mapgen_tree", "default:tree")
-minetest.register_alias("mapgen_leaves", "default:leaves")
-minetest.register_alias("mapgen_apple", "default:apple")
-minetest.register_alias("mapgen_water_source", "default:water_source")
-minetest.register_alias("mapgen_dirt", "default:dirt")
-minetest.register_alias("mapgen_sand", "default:sand")
-minetest.register_alias("mapgen_gravel", "default:gravel")
-minetest.register_alias("mapgen_clay", "default:clay")
-minetest.register_alias("mapgen_lava_source", "default:lava_source")
-minetest.register_alias("mapgen_cobble", "default:cobble")
-minetest.register_alias("mapgen_mossycobble", "default:mossycobble")
-minetest.register_alias("mapgen_dirt_with_grass", "default:dirt_with_grass")
-minetest.register_alias("mapgen_junglegrass", "default:junglegrass")
-minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal")
-minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron")
-minetest.register_alias("mapgen_mese", "default:mese")
-
--- Support old code
function default.spawn_falling_node(p, nodename)
spawn_falling_node(p, nodename)
end
@@ -1795,3 +1794,4 @@ test_get_craft_result()
--dump2(minetest.registered_entities)
-- END
+
diff --git a/games/minimal/mods/default/mapgen.lua b/games/minimal/mods/default/mapgen.lua
index dd839b9a0..2082d5983 100644
--- a/games/minimal/mods/default/mapgen.lua
+++ b/games/minimal/mods/default/mapgen.lua
@@ -1,126 +1,136 @@
--- minetest/default/mapgen.lua
-
--
-- Aliases for map generator outputs
--
+
minetest.register_alias("mapgen_stone", "default:stone")
-minetest.register_alias("mapgen_tree", "default:tree")
-minetest.register_alias("mapgen_leaves", "default:leaves")
-minetest.register_alias("mapgen_apple", "default:apple")
-minetest.register_alias("mapgen_water_source", "default:water_source")
minetest.register_alias("mapgen_dirt", "default:dirt")
+minetest.register_alias("mapgen_dirt_with_grass", "default:dirt_with_grass")
minetest.register_alias("mapgen_sand", "default:sand")
-minetest.register_alias("mapgen_gravel", "default:gravel")
-minetest.register_alias("mapgen_clay", "default:clay")
+minetest.register_alias("mapgen_water_source", "default:water_source")
minetest.register_alias("mapgen_lava_source", "default:lava_source")
-minetest.register_alias("mapgen_cobble", "default:cobble")
-minetest.register_alias("mapgen_mossycobble", "default:mossycobble")
-minetest.register_alias("mapgen_dirt_with_grass", "default:dirt_with_grass")
+minetest.register_alias("mapgen_gravel", "default:gravel")
+
+minetest.register_alias("mapgen_tree", "default:tree")
+minetest.register_alias("mapgen_leaves", "default:leaves")
+minetest.register_alias("mapgen_apple", "default:apple")
minetest.register_alias("mapgen_junglegrass", "default:junglegrass")
-minetest.register_alias("mapgen_stone_with_coal", "default:stone_with_coal")
-minetest.register_alias("mapgen_stone_with_iron", "default:stone_with_iron")
-minetest.register_alias("mapgen_mese", "default:mese")
+
+minetest.register_alias("mapgen_cobble", "default:cobble")
minetest.register_alias("mapgen_stair_cobble", "stairs:stair_cobble")
+minetest.register_alias("mapgen_mossycobble", "default:mossycobble")
+
--
-- Ore generation
--
+
+-- Blob ore first to avoid other ores inside blobs
+
+minetest.register_ore({
+ ore_type = "blob",
+ ore = "default:clay",
+ wherein = {"default:sand"},
+ clust_scarcity = 24*24*24,
+ clust_size = 7,
+ y_min = -15,
+ y_max = 0,
+ noise_threshhold = 0,
+ noise_params = {
+ offset=0.35,
+ scale=0.2,
+ spread={x=5, y=5, z=5},
+ seed=-316,
+ octaves=1,
+ persist=0.5
+ },
+})
+
minetest.register_ore({
ore_type = "scatter",
ore = "default:stone_with_coal",
wherein = "default:stone",
clust_scarcity = 8*8*8,
- clust_num_ores = 5,
+ clust_num_ores = 8,
clust_size = 3,
- height_min = -31000,
- height_max = 64,
+ y_min = -31000,
+ y_max = 64,
})
minetest.register_ore({
ore_type = "scatter",
ore = "default:stone_with_iron",
wherein = "default:stone",
- clust_scarcity = 16*16*16,
- clust_num_ores = 5,
- clust_size = 3,
- height_min = -5,
- height_max = 7,
+ clust_scarcity = 12*12*12,
+ clust_num_ores = 3,
+ clust_size = 2,
+ y_min = -15,
+ y_max = 2,
})
minetest.register_ore({
ore_type = "scatter",
ore = "default:stone_with_iron",
wherein = "default:stone",
- clust_scarcity = 12*12*12,
+ clust_scarcity = 9*9*9,
clust_num_ores = 5,
clust_size = 3,
- height_min = -16,
- height_max = -5,
+ y_min = -63,
+ y_max = -16,
})
minetest.register_ore({
ore_type = "scatter",
ore = "default:stone_with_iron",
wherein = "default:stone",
- clust_scarcity = 9*9*9,
+ clust_scarcity = 7*7*7,
clust_num_ores = 5,
clust_size = 3,
- height_min = -31000,
- height_max = -17,
+ y_min = -31000,
+ y_max = -64,
})
-minetest.register_on_generated(function(minp, maxp, seed)
- -- Generate clay
- if maxp.y >= 2 and minp.y <= 0 then
- -- Assume X and Z lengths are equal
- local divlen = 4
- local divs = (maxp.x-minp.x)/divlen+1;
- for divx=0+1,divs-1-1 do
- for divz=0+1,divs-1-1 do
- local cx = minp.x + math.floor((divx+0.5)*divlen)
- local cz = minp.z + math.floor((divz+0.5)*divlen)
- if minetest.get_node({x=cx,y=1,z=cz}).name == "default:water_source" and
- minetest.get_node({x=cx,y=0,z=cz}).name == "default:sand" then
- local is_shallow = true
- local num_water_around = 0
- if minetest.get_node({x=cx-divlen*2,y=1,z=cz+0}).name == "default:water_source" then
- num_water_around = num_water_around + 1 end
- if minetest.get_node({x=cx+divlen*2,y=1,z=cz+0}).name == "default:water_source" then
- num_water_around = num_water_around + 1 end
- if minetest.get_node({x=cx+0,y=1,z=cz-divlen*2}).name == "default:water_source" then
- num_water_around = num_water_around + 1 end
- if minetest.get_node({x=cx+0,y=1,z=cz+divlen*2}).name == "default:water_source" then
- num_water_around = num_water_around + 1 end
- if num_water_around >= 2 then
- is_shallow = false
- end
- if is_shallow then
- for x1=-divlen,divlen do
- for z1=-divlen,divlen do
- if minetest.get_node({x=cx+x1,y=0,z=cz+z1}).name == "default:sand" then
- minetest.set_node({x=cx+x1,y=0,z=cz+z1}, {name="default:clay"})
- end
- end
- end
- end
- end
- end
- end
- end
-end)
--
--- Register biome for biome API
+-- Register biomes for biome API
--
+
+minetest.clear_registered_biomes()
+minetest.clear_registered_decorations()
+
minetest.register_biome({
- name = "Grassland",
- -- Will use defaults of omitted parameters
- y_min = -31000,
- y_max = 31000,
- heat_point = 50,
+ name = "default:grassland",
+ --node_dust = "",
+ node_top = "default:dirt_with_grass",
+ depth_top = 1,
+ node_filler = "default:dirt",
+ depth_filler = 1,
+ --node_stone = "",
+ --node_water_top = "",
+ --depth_water_top = ,
+ --node_water = "",
+ y_min = 5,
+ y_max = 31000,
+ heat_point = 50,
+ humidity_point = 50,
+})
+
+minetest.register_biome({
+ name = "default:grassland_ocean",
+ --node_dust = "",
+ node_top = "default:sand",
+ depth_top = 1,
+ node_filler = "default:sand",
+ depth_filler = 2,
+ --node_stone = "",
+ --node_water_top = "",
+ --depth_water_top = ,
+ --node_water = "",
+ y_min = -31000,
+ y_max = 4,
+ heat_point = 50,
humidity_point = 50,
})
diff --git a/games/minimal/mods/default/textures/default_jungletree.png b/games/minimal/mods/default/textures/default_jungletree.png
deleted file mode 100644
index a1fd36a51..000000000
--- a/games/minimal/mods/default/textures/default_jungletree.png
+++ /dev/null
Binary files differ
diff --git a/games/minimal/mods/default/textures/default_jungletree_top.png b/games/minimal/mods/default/textures/default_jungletree_top.png
deleted file mode 100644
index a13fdae4a..000000000
--- a/games/minimal/mods/default/textures/default_jungletree_top.png
+++ /dev/null
Binary files differ
diff --git a/minetest.conf.example b/minetest.conf.example
index 88e2db8ab..50893c578 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -48,6 +48,7 @@
#keymap_rangeselect = KEY_KEY_R
#keymap_freemove = KEY_KEY_K
#keymap_fastmove = KEY_KEY_J
+#keymap_cinematic = KEY_F8
#keymap_screenshot = KEY_F12
# If true, keymap_special1 instead of keymap_sneak is used for climbing down and descending
#aux1_descends = false
@@ -93,6 +94,9 @@
#random_input = false
# Timeout for client to remove unused map data from memory
#client_unload_unused_data_timeout = 600
+# Maximum number of mapblocks for client to be kept in memory
+# Set to -1 for unlimited amount
+#client_mapblock_limit = 5000
# Whether to fog out the end of the visible area
#enable_fog = true
# Whether to show the client debug info (has the same effect as hitting F5)
@@ -107,15 +111,18 @@
#liquid_queue_purge_time = 0
# Liquid update interval in seconds
#liquid_update = 1.0
-# Enable transparent leaf textures, disable for speed
-#new_style_leaves = true
+# Leaves style:
+# fancy - all faces visible
+# simple - only outer faces, if defined special_tiles are used
+# opaque - disable transparency
+#leaves_style = fancy
# Connects glass if supported by node
#connected_glass = false
# Enable smooth lighting with simple ambient occlusion.
# Disable for speed or for different looks.
#smooth_lighting = true
# Adjust the gamma encoding for the light tables. Valid values are in the range
-# 1.1 to 3.0 (inclusive); lower numbers are brighter. This setting is for the
+# 1.0 to 3.0 (inclusive); lower numbers are brighter. This setting is for the
# client only and is ignored by the server
#display_gamma = 1.8
# Path to texture directory. All textures are first searched from here.
@@ -127,6 +134,13 @@
#free_move = false
# Continuous forward movement (for testing)
#continuous_forward = false
+# Enable cinematic mode
+#cinematic = false
+# Camera smoothing - smooths rotation of camera. 0 is no smoothing.
+# Must be equal to or greater than 0, and less than 1.
+#camera_smoothing = 0.0
+# Camera smoothing when in cinematic mode
+#cinematic_camera_smoothing = 0.7
# Fast movement (keymap_special1)
#fast_move = false
# Invert mouse
@@ -134,6 +148,9 @@
# Enable/disable clouds
#enable_clouds = true
#cloud_height = 120
+# Radius of cloud area stated in number of 64 node cloud squares.
+# Values larger than 26 will start to produce sharp cutoffs at cloud area corners.
+#cloud_radius = 12
#enable_3d_clouds = true
# Use a cloud animation for the main menu background
#menu_clouds = true
@@ -164,6 +181,19 @@
#crosshair_alpha = 255
# Scale gui by a user specified value
#gui_scaling = 1.0
+# Use a nearest-neighbor-anti-alias filter to scale the GUI.
+# This will smooth over some of the rough edges, and blend
+# pixels when scaling down, at the cost of blurring some
+# edge pixels when images are scaled by non-integer sizes.
+#gui_scaling_filter = false
+# When gui_scaling_filter is true, all GUI images need to be
+# filtered in software, but some images are generated directly
+# to hardware (e.g. render-to-texture for nodes in inventory).
+# When gui_scaling_filter_txr2img is true, copy those images
+# from hardware to software for scaling. When false, fall back
+# to the old scaling method, for video drivers that don't
+# propery support downloading textures back from hardware.
+#gui_scaling_filter_txr2img = true
# Sensitivity multiplier
#mouse_sensitivity = 0.2
# Sound settings
@@ -185,43 +215,71 @@
#anisotropic_filter = false
#bilinear_filter = false
#trilinear_filter = false
+# Filtered textures can blend RGB values with fully-transparent neighbors,
+# which PNG optimizers usually discard, sometimes resulting in a dark or
+# light edge to transparent textures. Apply this filter to clean that up
+# at texture load time.
+#texture_clean_transparent = false
+# When using bilinear/trilinear/anisotropic filters, low-resolution textures
+# can be blurred, so automatically upscale them with nearest-neighbor
+# interpolation to preserve crisp pixels. This sets the minimum texture size
+# for the upscaled textures; higher values look sharper, but require more
+# memory. Powers of 2 are recommended. Setting this higher than 1 may not
+# have a visible effect unless bilinear/trilinear/anisotropic filtering is
+# enabled.
+#texture_min_size = 64
# Set to true to pre-generate all item visuals
#preload_item_visuals = false
# Set to true to enable shaders. Disable them if video_driver = direct3d9/8.
+
#enable_shaders = true
-# Set to true to enable textures bumpmapping. Requires shaders enabled.
#enable_bumpmapping = false
-# Set to true enables parallax occlusion mapping. Requires shaders enabled.
+# Set to true to enable textures bumpmapping. Requires shaders enabled.
#generate_normalmaps = false
# Set to true enables on the fly normalmap generation (Emboss effect).
# Requires bumpmapping enabled.
#normalmaps_strength = 0.6
# Strength of generated normalmaps
-#normalmaps_smooth = 1
+#normalmaps_smooth = 0
# Defines sampling step of texture (0 - 2).
# A higher value results in smoother normal maps.
+#parallax_occlusion_mode = 1
+# 0 = parallax occlusion with slope information (faster)
+# 1 = relief mapping (slower, more accurate)
#enable_parallax_occlusion = false
-# Scale of parallax occlusion effect
+# Set to true enables parallax occlusion mapping. Requires shaders enabled.
+#parallax_occlusion_iterations = 4
+# Number of parallax occlusion iterations
#parallax_occlusion_scale = 0.08
-# Bias of parallax occlusion effect, usually scale/2
+# Overall scale of parallax occlusion effect
#parallax_occlusion_bias = 0.04
-# Set to true enables waving water. Requires shaders enabled.
+# Overall bias of parallax occlusion effect, usually scale/2
#enable_waving_water = false
+# Set to true enables waving water. Requires shaders enabled.
# Parameters for waving water:
#water_wave_height = 1.0
#water_wave_length = 20.0
#water_wave_speed = 5.0
-# Set to true enables waving leaves. Requires shaders enabled.
#enable_waving_leaves = false
-# Set to true enables waving plants. Requires shaders enabled.
+# Set to true enables waving leaves. Requires shaders enabled.
#enable_waving_plants = false
-# Enables caching of facedir rotated meshes
+# Set to true enables waving plants. Requires shaders enabled.
#ambient_occlusion_gamma = 2.2
# The strength (darkness) of node ambient-occlusion shading.
# Lower is darker, Higher is lighter. The valid range of values for this
# setting is 0.25 to 4.0 inclusive. If the value is out of range it will be
# set to the nearest valid value.
-#enable_mesh_cache = true
+#enable_mesh_cache = false
+# Enables caching of facedir rotated meshes
+
+#enable_minimap = true
+# Enables minimap
+#minimap_shape_round = true
+# true - round shape, false - square
+#minimap_double_scan_height = true
+# true = 256, false = 128
+# useable to make minimap smoother on slower machines
+
# The time in seconds it takes between repeated
# right clicks when holding the right mouse button.
#repeat_rightclick_time = 0.25
@@ -264,17 +322,6 @@
#fallback_font_size = 15
#fallback_font_shadow = 1
#fallback_font_shadow_alpha = 128
-# Override language. When no value is provided (default) system language is used.
-# Check "locale" directory for the list of available translations.
-#language =
-#main_menu_script =
-#main_menu_game_mgr = 0
-#main_menu_mod_mgr = 1
-#modstore_download_url = https://forum.minetest.net/media/
-#modstore_listmods_url = https://forum.minetest.net/mmdb/mods/
-#modstore_details_url = https://forum.minetest.net/mmdb/mod/*/
-# Makes DirectX work with LuaJIT. Disable if it causes troubles.
-#high_precision_fpu = true
#
# Server stuff
@@ -344,6 +391,15 @@
# "log" = mimic and log backtrace of deprecated call (default for debug).
# "error" = abort on usage of deprecated call (suggested for mod developers).
#deprecated_lua_api_handling = legacy
+
+#kick_msg_shutdown = Server shutting down.
+# A message to be displayed to all clients when the server shuts down
+#kick_msg_crash = This server has experienced an internal error. You will now be disconnected.
+# A message to be displayed to all clients when the server crashes
+#ask_reconnect_on_crash = false
+# Whether to ask clients to reconnect after a (lua) crash.
+# Set this to true if your server is set up to restart automatically.
+
# Mod profiler
#mod_profiling = false
# Detailed mod profile data
@@ -364,6 +420,13 @@
#max_block_send_distance = 10
# From how far blocks are generated for clients, stated in mapblocks (16 nodes)
#max_block_generate_distance = 6
+# Where the map generator stops.
+# Please note:
+# * Limited to 31000 (setting above has no effect)
+# * The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks).
+# * Those groups have an offset of -32, -32 nodes from the origin.
+# * Only groups which are within the map_generation_limit are generated
+#map_generation_limit = 31000
# Number of extra blocks that can be loaded by /clearobjects at once.
# This is a trade-off between sqlite transaction overhead and
# memory consumption (4096=100MB, as a rule of thumb).
@@ -375,9 +438,6 @@
# Controls length of day/night cycle.
# 72=20min, 360=4min, 1=24hour, 0=day/night/whatever stays unchanged.
#time_speed = 72
-# Length of year in days for seasons change.
-# With default time_speed 365 days = 5 real days for year, 30 days = 10 real hours.
-#year_days = 30
#server_unload_unused_data_timeout = 29
# Maximum number of statically stored objects in a block
#max_objects_per_block = 49
@@ -416,12 +476,24 @@
# try reducing it, but don't reduce it to a number below double of targeted
# client number.
#max_packets_per_iteration = 1024
+
# Enable/disable IPv6
#enable_ipv6 = true
# Enable/disable running an IPv6 server. An IPv6 server may be restricted
# to IPv6 clients, depending on system configuration.
# Ignored if bind_address is set.
#ipv6_server = false
+#main_menu_script =
+#main_menu_game_mgr = 0
+#main_menu_mod_mgr = 1
+#modstore_download_url = https://forum.minetest.net/media/
+#modstore_listmods_url = https://forum.minetest.net/mmdb/mods/
+#modstore_details_url = https://forum.minetest.net/mmdb/mod/*/
+# Makes DirectX work with LuaJIT. Disable if it causes troubles.
+#high_precision_fpu = true
+# Override language. When no value is provided (default) system language is used.
+# Check "locale" directory for the list of available translations.
+#language =
#
# Physics stuff
@@ -454,19 +526,28 @@
# Global map generation attributes. Currently supported: trees, caves, flat, dungeons, light.
# Flags that are not specified in the flag string are not modified from the default.
# To explicitly turn off a flag, prepend "no" to the beginning, e.g. nolight.
-#mg_flags = trees, caves
+#mg_flags = trees, caves, dungeons, light
+# Enable/disable floating dungeons and dungeon slices
+#enable_floating_dungeons = true
+
# Map generation attributes specific to Mapgen V6.
-# Currently supported: biomeblend, jungles, mudflow.
-#mgv6_spflags = biomeblend, jungles, mudflow
+# Currently supported: jungles, biomeblend, mudflow, snowbiomes.
+# When snowbiomes are enabled jungles are enabled and the jungles flag is ignored.
+#mgv6_spflags = jungles, biomeblend, mudflow, snowbiomes
# Controls size of deserts and beaches in Mapgen V6
+# When snowbiomes are enabled 'mgv6_freq_desert' is ignored.
#mgv6_freq_desert = 0.45
#mgv6_freq_beach = 0.15
+# Map generation attributes specific to Mapgen V7.
+# Currently supported: mountains, ridges.
+#mgv7_spflags = mountains, ridges
+
# Perlin noise attributes for different map generation parameters.
# Noise parameters can be specified as a set of positional values:
# Offset, scale, (spread factors), seed offset, number of octaves, persistence, lacunarity.
#mgv6_np_terrain_base = -4, 20, (250, 250, 250), 82341, 5, 0.6, 2.0
-# Or the new group format can be used instead:
+# Or the new group format can be used instead, for example:
#mgv6_np_terrain_base = {
# offset = -4
# scale = 20
@@ -478,33 +559,20 @@
# flags = "defaults"
#}
# Only the group format supports noise flags which are needed for eased noise.
-# Mgv5 uses eased noise for np_cave1, np_cave2, np_ground and np_crumble, so these are shown in
-# group format, other noise parameters are shown in positional format to save space.
+# Mgv5 uses eased noise for np_ground so this is shown in group format,
+# other noise parameters are shown in positional format to save space.
+
+# Noise parameters for biome API temperature, humidity and biome blend
+#mg_biome_np_heat = 50, 50, (1000, 1000, 1000), 5349, 3, 0.5, 2.0
+#mg_biome_np_heat_blend = 0, 1.5, (8, 8, 8), 13, 2, 1.0, 2.0
+#mg_biome_np_humidity = 50, 50, (1000, 1000, 1000), 842, 3, 0.5, 2.0
+#mg_biome_np_humidity_blend = 0, 1.5, (8, 8, 8), 90003, 2, 1.0, 2.0
-#mgv5_spflags = blobs
#mgv5_np_filler_depth = 0, 1, (150, 150, 150), 261, 4, 0.7, 2.0
#mgv5_np_factor = 0, 1, (250, 250, 250), 920381, 3, 0.45, 2.0
#mgv5_np_height = 0, 10, (250, 250, 250), 84174, 4, 0.5, 2.0
-#mgv5_np_cave1 = {
-# offset = 0
-# scale = 6
-# spread = (50, 50, 50)
-# seed = 52534
-# octaves = 4
-# persistence = 0.5
-# lacunarity = 2.0
-# flags = "eased"
-#}
-#mgv5_np_cave2 = {
-# offset = 0
-# scale = 6
-# spread = (50, 50, 50)
-# seed = 10325
-# octaves = 4
-# persistence = 0.5
-# lacunarity = 2.0
-# flags = "eased"
-#}
+#mgv5_np_cave1 = 0, 12, (50, 50, 50), 52534, 4, 0.5, 2.0
+#mgv5_np_cave2 = 0, 12, (50, 50, 50), 10325, 4, 0.5, 2.0
#mgv5_np_ground = {
# offset = 0
# scale = 40
@@ -515,44 +583,33 @@
# lacunarity = 2.0
# flags = "eased"
#}
-#mgv5_np_crumble = {
-# offset = 0
-# scale = 1
-# spread = (20, 20, 20)
-# seed = 34413
-# octaves = 3
-# persistence = 1.3
-# lacunarity = 2.0
-# flags = "eased"
-#}
-#mgv5_np_wetness = 0, 1, (40, 40, 40), 32474, 4, 1.1, 2.0
-#mgv6_spflags = biomeblend, jungles, mudflow
#mgv6_np_terrain_base = -4, 20, (250, 250, 250), 82341, 5, 0.6, 2.0
#mgv6_np_terrain_higher = 20, 16, (500, 500, 500), 85039, 5, 0.6, 2.0
#mgv6_np_steepness = 0.85, 0.5, (125, 125, 125), -932, 5, 0.7, 2.0
#mgv6_np_height_select = 0.5, 1, (250, 250, 250), 4213, 5, 0.69, 2.0
#mgv6_np_mud = 4, 2, (200, 200, 200), 91013, 3, 0.55, 2.0
#mgv6_np_beach = 0, 1, (250, 250, 250), 59420, 3, 0.50, 2.0
-#mgv6_np_biome = 0, 1, (250, 250, 250), 9130, 3, 0.50, 2.0
+#mgv6_np_biome = 0, 1, (500, 500, 500), 9130, 3, 0.50, 2.0
#mgv6_np_cave = 6, 6, (250, 250, 250), 34329, 3, 0.50, 2.0
-#mgv6_np_humidity = 0.5, 0.5, (500, 500, 500), 72384, 4, 0.66, 2.0
+#mgv6_np_humidity = 0.5, 0.5, (500, 500, 500), 72384, 3, 0.50, 2.0
#mgv6_np_trees = 0, 1, (125, 125, 125), 2, 4, 0.66, 2.0
#mgv6_np_apple_trees = 0, 1, (100, 100, 100), 342902, 3, 0.45, 2.0
-#mgv7_spflags = mountains, ridges
-#mgv7_np_terrain_base = 4, 70, (300, 300, 300), 82341, 6, 0.7, 2.0
+#mgv7_np_terrain_base = 4, 70, (600, 600, 600), 82341, 5, 0.6, 2.0
#mgv7_np_terrain_alt = 4, 25, (600, 600, 600), 5934, 5, 0.6, 2.0
-#mgv7_np_terrain_persist = 0.6, 0.1, (500, 500, 500), 539, 3, 0.6, 2.0
-#mgv7_np_height_select = -0.5, 1, (250, 250, 250), 4213, 5, 0.69, 2.0
-#mgv7_np_filler_depth = 0, 1.2, (150, 150, 150), 261, 4, 0.7, 2.0
-#mgv7_np_mount_height = 100, 30, (500, 500, 500), 72449, 4, 0.6, 2.0
-#mgv7_np_ridge_uwater = 0, 1, (500, 500, 500), 85039, 4, 0.6, 2.0
-#mgv7_np_mountain = -0.6, 1, (250, 350, 250), 5333, 5, 0.68, 2.0
+#mgv7_np_terrain_persist = 0.6, 0.1, (2000, 2000, 2000), 539, 3, 0.6, 2.0
+#mgv7_np_height_select = -12, 24, (500, 500, 500), 4213, 6, 0.7, 2.0
+#mgv7_np_filler_depth = 0, 1.2, (150, 150, 150), 261, 3, 0.7, 2.0
+#mgv7_np_mount_height = 256, 112, (1000, 1000, 1000), 72449, 3, 0.6, 2.0
+#mgv7_np_ridge_uwater = 0, 1, (1000, 1000, 1000), 85039, 5, 0.6, 2.0
+#mgv7_np_mountain = -0.6, 1, (250, 350, 250), 5333, 5, 0.63, 2.0
#mgv7_np_ridge = 0, 1, (100, 100, 100), 6467, 4, 0.75, 2.0
#mgv7_np_cave1 = 0, 12, (100, 100, 100), 52534, 4, 0.5, 2.0
#mgv7_np_cave2 = 0, 12, (100, 100, 100), 10325, 4, 0.5, 2.0
-# Noise parameters for biome API temperature and humidity
-#mg_biome_np_heat = 50, 50, (500, 500, 500), 5349, 3, 0.5, 2.0
-#mg_biome_np_humidity = 50, 50, (500, 500, 500), 842, 3, 0.5, 2.0
+# Prevent mods from doing insecure things like running shell commands.
+#secure.enable_security = false
+# Comma-separated list of trusted mods that are allowed to access insecure
+# functions even when mod security is on (via request_insecure_environment()).
+#secure.trusted_mods =
diff --git a/misc/Info.plist b/misc/Info.plist
index 848ccfa30..1498ee474 100644
--- a/misc/Info.plist
+++ b/misc/Info.plist
@@ -5,9 +5,9 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
- <string>../Resources/minetest</string>
+ <string>minetest</string>
<key>CFBundleIconFile</key>
- <string>minetest.icns</string>
+ <string>minetest-icon.icns</string>
<key>CFBundleIdentifier</key>
<string>net.minetest.minetest</string>
</dict>
diff --git a/misc/minetest.desktop b/misc/minetest.desktop
index 896404789..4257cef37 100644
--- a/misc/minetest.desktop
+++ b/misc/minetest.desktop
@@ -2,8 +2,12 @@
Name=Minetest
GenericName=Minetest
Comment=Multiplayer infinite-world block sandbox
-Comment[fr]=Jeu multijoueurs de type bac à sable avec des mondes infinis
Comment[de]=Mehrspieler-Sandkastenspiel mit unendlichen Blockwelten
+Comment[es]=Juego sandbox multijugador con mundos infinitos
+Comment[fr]=Jeu multijoueurs de type bac à sable avec des mondes infinis
+Comment[ja]=マルãƒãƒ—レイã«å¯¾å¿œã—ãŸã€ç„¡é™ã®ä¸–ç•Œã®ãƒ–ロック型サンドボックスゲームã§ã™
+Comment[ru]=Игра-пеÑочница Ñ Ð±ÐµÐ·Ð³Ñ€Ð°Ð½Ð¸Ñ‡Ð½Ñ‹Ð¼ миром, ÑоÑтоÑщим из блоков
+Comment[tr]=Tek-Çok oyuncuyla küplerden sonsuz dünyalar inşa et
Exec=minetest
Icon=minetest-icon
Terminal=false
@@ -11,4 +15,3 @@ Type=Application
Categories=Game;
StartupNotify=false
Keywords=sandbox;world;mining;crafting;blocks;nodes;multiplayer;roleplaying;
-
diff --git a/misc/winresource.rc b/misc/winresource.rc
index ecb314c12..6b82e261b 100644
--- a/misc/winresource.rc
+++ b/misc/winresource.rc
@@ -5,10 +5,10 @@
#include "config.h"
#undef USE_CMAKE_CONFIG_H
-#if RUN_IN_PLACE == 1
- #define BUILDMODE "RUN_IN_PLACE=1\0"
+#if RUN_IN_PLACE
+ #define BUILDMODE "RUN_IN_PLACE=1"
#else
- #define BUILDMODE "RUN_IN_PLACE=0\0"
+ #define BUILDMODE "RUN_IN_PLACE=0"
#endif
LANGUAGE 0, SUBLANG_NEUTRAL
@@ -20,8 +20,8 @@ LANGUAGE 0, SUBLANG_NEUTRAL
//
1 VERSIONINFO
- FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH_ORIG,0
- PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH_ORIG,0
+ FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0
+ PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,0
FILEFLAGSMASK 0x3fL
#ifndef NDEBUG
FILEFLAGS 0x1L
@@ -36,16 +36,16 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
- VALUE "Comments", "\0"
- VALUE "CompanyName", "Minetest Community\0"
- VALUE "FileDescription", "Minetest engine core main application\0"
+ VALUE "Comments", ""
+ VALUE "CompanyName", PROJECT_NAME_C " community"
+ VALUE "FileDescription", PROJECT_NAME_C " engine"
VALUE "FileVersion", VERSION_STRING
- VALUE "InternalName", "Minetest engine\0"
- VALUE "LegalCopyright", "(c) 2014 celeron55\0"
- VALUE "LegalTrademarks", """Minetest"" is property of Minetest community, don't use the name for your application without permission!\0"
- VALUE "OriginalFilename", "minetest.exe\0"
- VALUE "PrivateBuild", VERSION_EXTRA_STRING
- VALUE "ProductName", "Minetest\0"
+ VALUE "InternalName", PROJECT_NAME
+ VALUE "LegalCopyright", "(c) 2011-2015 celeron55"
+ VALUE "LegalTrademarks", """Minetest"" is the property of the Minetest community, don't use it without permission!"
+ VALUE "OriginalFilename", "minetest.exe"
+ VALUE "PrivateBuild", VERSION_EXTRA
+ VALUE "ProductName", PROJECT_NAME_C
VALUE "ProductVersion", PRODUCT_VERSION_STRING
VALUE "SpecialBuild", BUILDMODE
END
@@ -55,3 +55,4 @@ BEGIN
VALUE "Translation", 0x409, 1200
END
END
+
diff --git a/po/be/minetest.po b/po/be/minetest.po
index 0e60035a0..daddc00ac 100644
--- a/po/be/minetest.po
+++ b/po/be/minetest.po
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-11-23 17:37+0100\n"
"Last-Translator: Selat <LongExampleTestName@gmail.com>\n"
"Language-Team: Belarusian\n"
@@ -15,57 +15,73 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr ""
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr ""
@@ -105,7 +121,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr ""
@@ -119,7 +135,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr ""
@@ -147,29 +163,29 @@ msgstr ""
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -177,47 +193,39 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr ""
-
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
msgstr ""
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -225,15 +233,19 @@ msgstr ""
msgid "Credits"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -274,12 +286,11 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+msgid "Address / Port :"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -288,7 +299,7 @@ msgid "Public Serverlist"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr ""
@@ -297,15 +308,30 @@ msgstr ""
msgid "Connect"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr ""
@@ -313,17 +339,18 @@ msgstr ""
msgid "Start Game"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr ""
@@ -331,6 +358,10 @@ msgstr ""
msgid "Public"
msgstr ""
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr ""
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -343,143 +374,172 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:140
-msgid "Fancy Trees"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr ""
@@ -495,50 +555,181 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr ""
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr ""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr ""
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr ""
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr ""
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr ""
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr ""
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr ""
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr ""
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr ""
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr ""
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr ""
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
+#: src/guiFormSpecMenu.cpp:2855
+msgid "Enter "
msgstr ""
-#: src/guiFormSpecMenu.cpp:2846
-msgid "Enter "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
msgstr ""
#: src/guiKeyChangeMenu.cpp:125
@@ -553,422 +744,398 @@ msgstr ""
msgid "Double tap \"jump\" to toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr ""
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr ""
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr ""
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr ""
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr ""
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr ""
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr ""
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr ""
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr ""
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr ""
-
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr ""
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr ""
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr ""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr ""
diff --git a/po/cs/minetest.po b/po/cs/minetest.po
index 563275b01..4fcaab18a 100644
--- a/po/cs/minetest.po
+++ b/po/cs/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-02-12 13:13+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2015-02-12 16:16+0100\n"
"Last-Translator: Jakub Vanek <vanek.jakub4@seznam.cz>\n"
"Language-Team: Czech <>\n"
@@ -17,57 +17,73 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
-#: builtin/fstk/ui.lua:67 builtin/mainmenu/store.lua:165
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "OK"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Nahrávám..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Svět:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Skrýt vnitřní"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Skrýt obsahy balíÄků"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48 builtin/mainmenu/tab_mods.lua:99
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Závislosti:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Uložit"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Zrušit"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Povolit balíÄek"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Zakázat balíÄek"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "povoleno"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Povolit vše"
@@ -107,7 +123,7 @@ msgstr "Varování: \"Minimal development test\" je zamýšlen pouze pro vývojÃ
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr "Stáhněte si z minetest.net podhru, například minetest_game."
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Svět s názvem \"$1\" už existuje"
@@ -121,7 +137,7 @@ msgstr "SkuteÄnÄ› chcete odstranit \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:79
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ano"
@@ -149,15 +165,15 @@ msgstr "Ne"
msgid "Rename Modpack:"
msgstr "PÅ™ejmenovat balíÄek modů:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Přijmout"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Instalace modu: ze souboru: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
@@ -165,15 +181,16 @@ msgstr ""
"\n"
"Instalace modu: špatný archiv nebo nepodporovaný typ souboru \"$1\""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Selhala instalace $1 do $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
-msgstr "Instalace modu: nenalezen vhodný adresář s přísluÅ¡ným názvem pro balíÄek $1"
+msgstr ""
+"Instalace modu: nenalezen vhodný adresář s přísluÅ¡ným názvem pro balíÄek $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Instalace modu: nenaÅ¡el jsem skuteÄné jméno modu: $1"
@@ -221,15 +238,20 @@ msgstr "Strana $1 z $2"
msgid "Credits"
msgstr "Autoři"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Vývojáři jádra"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Aktivní přispěvatelé"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Vývojáři jádra"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Bývalí přispěvatelé"
@@ -283,7 +305,7 @@ msgid "Public Serverlist"
msgstr "Seznam veřejných serverů"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Smazat"
@@ -307,15 +329,15 @@ msgstr "Poškození povoleno"
msgid "PvP enabled"
msgstr "PvP povoleno"
-#: builtin/mainmenu/tab_multiplayer.lua:247
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Klient"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nový"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Nastavit"
@@ -323,17 +345,18 @@ msgstr "Nastavit"
msgid "Start Game"
msgstr "Spustit hru"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Vyber svět:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:75
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Kreativní mód"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:77
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Povolit poškození"
@@ -357,163 +380,176 @@ msgstr "Port"
msgid "Server Port"
msgstr "Port serveru"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Nebyla vybrána podhra nebo název"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Server"
#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Neprůhledná voda"
+
+#: builtin/mainmenu/tab_settings.lua:22
+#, fuzzy
+msgid "Simple Leaves"
+msgstr "Vlnění listů"
+
+#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Ozdobné stromy"
+
+#: builtin/mainmenu/tab_settings.lua:32
msgid "No Filter"
msgstr "Žádný filtr"
-#: builtin/mainmenu/tab_settings.lua:22
+#: builtin/mainmenu/tab_settings.lua:33
msgid "Bilinear Filter"
msgstr "Bilineární filtr"
-#: builtin/mainmenu/tab_settings.lua:23
+#: builtin/mainmenu/tab_settings.lua:34
msgid "Trilinear Filter"
msgstr "Trilineární filtr"
-#: builtin/mainmenu/tab_settings.lua:32
+#: builtin/mainmenu/tab_settings.lua:43
msgid "No Mipmap"
msgstr "Žádné Mipmapy"
-#: builtin/mainmenu/tab_settings.lua:33
+#: builtin/mainmenu/tab_settings.lua:44
msgid "Mipmap"
msgstr "Mipmapa"
-#: builtin/mainmenu/tab_settings.lua:34
+#: builtin/mainmenu/tab_settings.lua:45
msgid "Mipmap + Aniso. Filter"
msgstr "Mipmapa + Anizo. filtr"
-#: builtin/mainmenu/tab_settings.lua:77
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr "Jste si jisti, že chcete resetovat místní svět?"
-#: builtin/mainmenu/tab_settings.lua:81
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr "Ne!!!"
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Plynulé osvětlení"
-#: builtin/mainmenu/tab_settings.lua:183
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Povolit Äástice"
-#: builtin/mainmenu/tab_settings.lua:185
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D mraky"
-#: builtin/mainmenu/tab_settings.lua:187
-msgid "Fancy Trees"
-msgstr "Ozdobné stromy"
-
-#: builtin/mainmenu/tab_settings.lua:189
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Neprůhledná voda"
-#: builtin/mainmenu/tab_settings.lua:191
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr "Propojené sklo"
-#: builtin/mainmenu/tab_settings.lua:193
+#: builtin/mainmenu/tab_settings.lua:212
msgid "Node Highlighting"
msgstr "Zvýraznění bloků"
-#: builtin/mainmenu/tab_settings.lua:196
+#: builtin/mainmenu/tab_settings.lua:217
msgid "Texturing:"
msgstr "Texturování:"
-#: builtin/mainmenu/tab_settings.lua:201
+#: builtin/mainmenu/tab_settings.lua:222
msgid "Rendering:"
msgstr "Renderování:"
-#: builtin/mainmenu/tab_settings.lua:205
+#: builtin/mainmenu/tab_settings.lua:226
msgid "Restart minetest for driver change to take effect"
msgstr "Aby se zmÄ›na ovladaÄe projevila, restartujte Minetest."
-#: builtin/mainmenu/tab_settings.lua:207
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shadery"
-#: builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Změnit nastavení kláves"
-#: builtin/mainmenu/tab_settings.lua:215
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr "Reset místního světa"
-#: builtin/mainmenu/tab_settings.lua:219
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr "Měřítko GUI"
-#: builtin/mainmenu/tab_settings.lua:223
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr "Měřítko aplikované na prvky menu: "
-#: builtin/mainmenu/tab_settings.lua:229
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr "Středový kurzor"
-#: builtin/mainmenu/tab_settings.lua:235
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr "Dosah dotyku (px)"
-#: builtin/mainmenu/tab_settings.lua:242 builtin/mainmenu/tab_settings.lua:256
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr "Bump mapování"
-#: builtin/mainmenu/tab_settings.lua:244 builtin/mainmenu/tab_settings.lua:257
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr "Generovat normálové mapy"
-#: builtin/mainmenu/tab_settings.lua:246 builtin/mainmenu/tab_settings.lua:258
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr "Parallax Occlusion"
-#: builtin/mainmenu/tab_settings.lua:248 builtin/mainmenu/tab_settings.lua:259
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr "Vlnění vody"
-#: builtin/mainmenu/tab_settings.lua:250 builtin/mainmenu/tab_settings.lua:260
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr "Vlnění listů"
-#: builtin/mainmenu/tab_settings.lua:252 builtin/mainmenu/tab_settings.lua:261
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr "Vlnění rostlin"
-#: builtin/mainmenu/tab_settings.lua:287
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Pro povolení shaderů musíte používat OpenGL ovladaÄ."
-#: builtin/mainmenu/tab_settings.lua:398
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Nastavení"
-#: builtin/mainmenu/tab_simple_main.lua:79
-msgid "Fly mode"
-msgstr "Létací režim"
-
-#: builtin/mainmenu/tab_simple_main.lua:83
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr "Start místní hry"
-#: builtin/mainmenu/tab_simple_main.lua:84
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr "Nastavení modů"
-#: builtin/mainmenu/tab_simple_main.lua:203
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr "Hlavní nabídka"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Hrát"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Místní hra"
@@ -529,43 +565,77 @@ msgstr "Informace nejsou dostupné"
msgid "Texturepacks"
msgstr "BalíÄky textur"
-#: src/client.cpp:2788
+#: src/client.cpp:1721
msgid "Loading textures..."
msgstr "NaÄítám textury..."
-#: src/client.cpp:2798
+#: src/client.cpp:1736
msgid "Rebuilding shaders..."
msgstr "Sestavuji shadery..."
-#: src/client.cpp:2805
+#: src/client.cpp:1743
msgid "Initializing nodes..."
msgstr "Inicializuji bloky..."
-#: src/client.cpp:2820
+#: src/client.cpp:1760
+#, fuzzy
+msgid "Initializing nodes"
+msgstr "Inicializuji bloky..."
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Textury věcí..."
-#: src/client.cpp:2845
+#: src/client.cpp:1793
msgid "Done!"
msgstr "Hotovo!"
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Hlavní nabídka"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Jméno hráÄe je příliÅ¡ dlouhé."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Chyba spojení (vyprÅ¡el Äas?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+"Nebyl vybrán žádný svět a nebyla poskytnuta žádná adresa. Nemám co dělat."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "Uvedená cesta ke světu neexistuje: "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Hru nebylo možné nahrát nebo najít \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Neplatná specifikace hry."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "no"
-#: src/game.cpp:1057 src/guiFormSpecMenu.cpp:2006
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
msgid "Proceed"
msgstr "PokraÄovat"
-#: src/game.cpp:1077
+#: src/game.cpp:1072
msgid "You died."
msgstr "Zemřel jsi."
-#: src/game.cpp:1078
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Znovu stvořit"
-#: src/game.cpp:1097
+#: src/game.cpp:1092
msgid ""
"Default Controls:\n"
"No menu visible:\n"
@@ -593,7 +663,7 @@ msgstr ""
"- stisk a přesun, klik druhým prstem\n"
" --> umístit samostatnou věc do přihrádky\n"
-#: src/game.cpp:1111
+#: src/game.cpp:1106
msgid ""
"Default Controls:\n"
"- WASD: move\n"
@@ -619,75 +689,71 @@ msgstr ""
"- MyÅ¡(koleÄko): vybrat pÅ™ihrádku\n"
"- T: chat\n"
-#: src/game.cpp:1130
+#: src/game.cpp:1125
msgid "Continue"
msgstr "PokraÄovat"
-#: src/game.cpp:1134
+#: src/game.cpp:1129
msgid "Change Password"
msgstr "Změnit heslo"
-#: src/game.cpp:1139
+#: src/game.cpp:1134
msgid "Sound Volume"
msgstr "Hlasitost"
-#: src/game.cpp:1141
+#: src/game.cpp:1136
msgid "Change Keys"
msgstr "Změnit klávesy"
-#: src/game.cpp:1144
+#: src/game.cpp:1139
msgid "Exit to Menu"
msgstr "Odejít do nabídky"
-#: src/game.cpp:1146
+#: src/game.cpp:1141
msgid "Exit to OS"
msgstr "UkonÄit hru"
-#: src/game.cpp:1809
+#: src/game.cpp:1841
msgid "Shutting down..."
msgstr "Vypínání..."
-#: src/game.cpp:1858
-msgid "Loading..."
-msgstr "Nahrávám..."
-
-#: src/game.cpp:1915
+#: src/game.cpp:1948
msgid "Creating server..."
msgstr "Vytvářím server..."
-#: src/game.cpp:1952
+#: src/game.cpp:1984
msgid "Creating client..."
msgstr "Vytvářím klienta..."
-#: src/game.cpp:2125
+#: src/game.cpp:2159
msgid "Resolving address..."
msgstr "Překládám adresu..."
-#: src/game.cpp:2216
+#: src/game.cpp:2261
msgid "Connecting to server..."
msgstr "Připojuji se k serveru..."
-#: src/game.cpp:2274
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definice věcí..."
-#: src/game.cpp:2279
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definice bloků..."
-#: src/game.cpp:2286
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Média..."
-#: src/game.cpp:2291
-msgid " KB/s"
-msgstr " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr ""
-#: src/game.cpp:2295
-msgid " MB/s"
-msgstr " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr ""
-#: src/game.cpp:4210
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -695,17 +761,18 @@ msgstr ""
"\n"
"Pro detaily se podívejte do debug.txt."
-#: src/guiFormSpecMenu.cpp:2797
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr "Zadejte "
-#: src/guiFormSpecMenu.cpp:2817
+#: src/guiFormSpecMenu.cpp:2875
msgid "ok"
msgstr "OK"
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
-msgstr "Nastavení kláves (Pokud tohle menu nebude v pořádku, odstraňte nastavení z "
+msgstr ""
+"Nastavení kláves (Pokud tohle menu nebude v pořádku, odstraňte nastavení z "
"minetest.conf)"
#: src/guiKeyChangeMenu.cpp:165
@@ -716,79 +783,84 @@ msgstr "\"Použít\" = slézt dolů"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Dvojstisk klávesy \"skok\" zapne létání"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Klávesa je již používána"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "stiskni klávesu"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Vpřed"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Vzad"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Doleva"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Doprava"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Použít"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Skok"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Plížit se"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Zahodit"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventář"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Chat"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Příkaz"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konzole"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Létání"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Turbo"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Turbo"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Duch"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Změna dohledu"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Vypsat hromádky"
@@ -812,407 +884,389 @@ msgstr "Změnit"
msgid "Passwords do not match!"
msgstr "Hesla se neshodují!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Hlasitost: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Odejít"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Levé tlaÄítko myÅ¡i"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "ProstÅ™ední tlaÄítko myÅ¡i"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Pravé tlaÄítko myÅ¡i"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X TlaÄítko 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Zpět"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "VyÄistit"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Vrátit"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabulátor"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X TlaÄítko 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Klávesa velkého písmene"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Nabídka"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pauza"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Změna režimu"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Další"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Předchozí"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Mezerník"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Dolů"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Spustit"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print Screen"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Vybrat"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Nahoru"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Pomoc"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Snapshot"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Levá klávesa Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Aplikace"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numerická klávesnice: 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numerická klávesnice: 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Pravá klávesa Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Spánek"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numerická klávesnice: 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numerická klávesnice: 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numerická klávesnice: 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numerická klávesnice: 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numerická klávesnice: 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numerická klávesnice: 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numerická klávesnice: *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numerická klávesnice: +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numerická klávesnice: -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numerická klávesnice: /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numerická klávesnice: 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numerická klávesnice: 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Levý Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Pravý Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Levý Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Levá klávesa Menu"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Pravý Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Pravá klávesa Menu"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Čárka"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Mínus"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "TeÄka"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Smazat EOF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Přiblížení"
-#: src/main.cpp:1688
-msgid "Main Menu"
-msgstr "Hlavní nabídka"
-
-#: src/main.cpp:1726
-msgid "Player name too long."
-msgstr "Jméno hráÄe je příliÅ¡ dlouhé."
-
-#: src/main.cpp:1764
-msgid "Connection error (timed out?)"
-msgstr "Chyba spojení (vyprÅ¡el Äas?)"
-
-#: src/main.cpp:1929
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Nebyl vybrán žádný svět a nebyla poskytnuta žádná adresa. Nemám co dělat."
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapování"
-#: src/main.cpp:1936
-msgid "Provided world path doesn't exist: "
-msgstr "Uvedená cesta ke světu neexistuje: "
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anizotropní filtrování"
-#: src/main.cpp:1945
-msgid "Could not find or load game \""
-msgstr "Hru nebylo možné nahrát nebo najít \""
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr ""
+#~ "Levý klik: Přesunout všechny předměty, Pravý klik: Přesunout jeden předmět"
-#: src/main.cpp:1963
-msgid "Invalid gamespec."
-msgstr "Neplatná specifikace hry."
+#~ msgid "Local install"
+#~ msgstr "Místní instalace"
-msgid "Downloading"
-msgstr "Stahuji"
+#~ msgid "Add mod:"
+#~ msgstr "Přidat mod:"
-#~ msgid "Game Name"
-#~ msgstr "Název hry"
+#~ msgid "MODS"
+#~ msgstr "MODY"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: Nepovedlo se zkopírovat mod \"$1\" do hry \"$2\""
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "BALÃÄŒKY TEXTUR"
-#~ msgid "GAMES"
-#~ msgstr "HRY"
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "HRA JEDNOHO HRÃÄŒE"
-#~ msgid "Games"
-#~ msgstr "Hry"
+#~ msgid "Finite Liquid"
+#~ msgstr "KoneÄná voda"
-#~ msgid "Mods:"
-#~ msgstr "Mody:"
+#~ msgid "Preload item visuals"
+#~ msgstr "PÅ™ednaÄíst textury pÅ™edmÄ›tů"
-#~ msgid "edit game"
-#~ msgstr "upravit hru"
+#~ msgid "SETTINGS"
+#~ msgstr "NASTAVENÃ"
-#~ msgid "new game"
-#~ msgstr "nová hra"
+#~ msgid "Password"
+#~ msgstr "Heslo"
-#~ msgid "EDIT GAME"
-#~ msgstr "UPRAVIT HRU"
+#~ msgid "Name"
+#~ msgstr "Jméno"
-#~ msgid "Remove selected mod"
-#~ msgstr "Odstranit vybraný mod"
+#~ msgid "START SERVER"
+#~ msgstr "MÃSTNÃ SERVER"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Přidat mod"
+#~ msgid "Favorites:"
+#~ msgstr "Oblíbené:"
#~ msgid "CLIENT"
#~ msgstr "KLIENT"
-#~ msgid "Favorites:"
-#~ msgstr "Oblíbené:"
-
-#~ msgid "START SERVER"
-#~ msgstr "MÃSTNÃ SERVER"
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Přidat mod"
-#~ msgid "Name"
-#~ msgstr "Jméno"
+#~ msgid "Remove selected mod"
+#~ msgstr "Odstranit vybraný mod"
-#~ msgid "Password"
-#~ msgstr "Heslo"
+#~ msgid "EDIT GAME"
+#~ msgstr "UPRAVIT HRU"
-#~ msgid "SETTINGS"
-#~ msgstr "NASTAVENÃ"
+#~ msgid "new game"
+#~ msgstr "nová hra"
-#~ msgid "Preload item visuals"
-#~ msgstr "PÅ™ednaÄíst textury pÅ™edmÄ›tů"
+#~ msgid "edit game"
+#~ msgstr "upravit hru"
-#~ msgid "Finite Liquid"
-#~ msgstr "KoneÄná voda"
+#~ msgid "Mods:"
+#~ msgstr "Mody:"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "HRA JEDNOHO HRÃÄŒE"
+#~ msgid "Games"
+#~ msgstr "Hry"
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "BALÃÄŒKY TEXTUR"
+#~ msgid "GAMES"
+#~ msgstr "HRY"
-#~ msgid "MODS"
-#~ msgstr "MODY"
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr: Nepovedlo se zkopírovat mod \"$1\" do hry \"$2\""
-#~ msgid "Add mod:"
-#~ msgstr "Přidat mod:"
+#~ msgid "Game Name"
+#~ msgstr "Název hry"
-#~ msgid "Local install"
-#~ msgstr "Místní instalace"
+#~ msgid "Downloading"
+#~ msgstr "Stahuji"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "Levý klik: Přesunout všechny předměty, Pravý klik: Přesunout jeden předmět"
+#~ msgid " MB/s"
+#~ msgstr " MB/s"
-#~ msgid "Anisotropic Filtering"
-#~ msgstr "Anizotropní filtrování"
+#~ msgid " KB/s"
+#~ msgstr " KB/s"
-#~ msgid "Mip-Mapping"
-#~ msgstr "Mip-Mapování"
+#~ msgid "Fly mode"
+#~ msgstr "Létací režim"
diff --git a/po/da/minetest.po b/po/da/minetest.po
index 35bce6c90..8405261b2 100644
--- a/po/da/minetest.po
+++ b/po/da/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-02-17 00:41+0200\n"
"Last-Translator: Rune Biskopstö Christensen <lakersforce@gmail.com>\n"
"Language-Team: \n"
@@ -18,62 +18,78 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Weblate 1.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr ""
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
#, fuzzy
msgid "World:"
msgstr "Vælg verden:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
#, fuzzy
msgid "Hide Game"
msgstr "Spil"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
#, fuzzy
msgid "Depends:"
msgstr "afhænger af:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Gem"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Anuller"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "Aktivér alle"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Deaktivér alle"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "aktiveret"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
#, fuzzy
msgid "Enable all"
msgstr "Aktivér alle"
@@ -114,7 +130,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
#, fuzzy
msgid "A world named \"$1\" already exists"
msgstr "Kan ikke skabe verden: en verden med dette navn eksisterer allerede"
@@ -129,7 +145,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ja"
@@ -158,30 +174,30 @@ msgstr "Nej"
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Accepter"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
#, fuzzy
msgid "Failed to install $1 to $2"
msgstr "Mislykkedes i at initialisere verden"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -189,49 +205,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Ned"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Verdens navn"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -239,15 +246,19 @@ msgstr ""
msgid "Credits"
msgstr "Skabt af"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -289,12 +300,13 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Adresse/port"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Navn/kodeord"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -303,7 +315,7 @@ msgid "Public Serverlist"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Slet"
@@ -312,15 +324,33 @@ msgstr "Slet"
msgid "Connect"
msgstr "Forbind"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Kreativ tilstand"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "aktiveret"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "aktiveret"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Ny"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Konfigurér"
@@ -329,17 +359,18 @@ msgstr "Konfigurér"
msgid "Start Game"
msgstr "Start spil / Forbind"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Vælg verden:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Kreativ tilstand"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Aktivér skade"
@@ -348,6 +379,10 @@ msgstr "Aktivér skade"
msgid "Public"
msgstr "Vis offentlig"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Navn/kodeord"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -360,151 +395,183 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Opakt (uigennemsigtigt) vand"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "\"Smarte\" træer"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Bi-lineær filtréring"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Tri-lineær filtréring"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Glat belysning"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Aktivér partikler"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D skyer"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "\"Smarte\" træer"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
#, fuzzy
msgid "Opaque Water"
msgstr "Opakt (uigennemsigtigt) vand"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Forbind"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-mapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Anisotropisk filtréring"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Bi-lineær filtréring"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Tri-lineær filtréring"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shadere"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Skift bindinger"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Enligspiller"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip-mapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Indstillinger"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Enligspiller"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Konfigurér"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Hovedmenu"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Afspil"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Enligspiller"
@@ -520,39 +587,171 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr ""
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Hovedmenu"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Forbindelses fejl (udløbelse af tidsfrist?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Ingen verden valgt og ingen adresse angivet. Ingen opgave at lave."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Kunne ikke finde eller indlæse spil \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Ugyldig spilspecifikationer."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Fortsæt"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Du døde."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Genopstå"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Fortsæt"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Skift kodeord"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr ""
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Skift bindinger"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Afslut til menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Afslut til operativsystemet"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -560,14 +759,14 @@ msgstr ""
"\n"
"Tjek debug.txt for detaljer."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Fortsæt"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -584,479 +783,441 @@ msgstr ""
"Tryk på \"hop\" hurtigt to gange for at skifte frem og tilbage mellem flyve-"
"tilstand"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Tast allerede i brug"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "Tryk på en tast"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Fremad"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Baglæns"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Venstre"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Højre"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Brug"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Hop"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Snige"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Slip"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Beholdning"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Snak"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Kommando"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konsol"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Omstil flyvning"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Omstil hurtig"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Omstil hurtig"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Omstil fylde"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Afstands vælg"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Udskriv stakke"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Gammelt kodeord"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Nyt kodeord"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Bekræft kodeord"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Skift"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Kodeordene er ikke ens!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr ""
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Venstre knap"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Midterste knap"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Højre knap"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X knap 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Tilbage"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Ryd"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabulator"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X knap 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
#, fuzzy
msgid "Capital"
msgstr "Store bogstaver"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pause"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Konvertér"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Endelig"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Tilstandsskift"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Næste"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Foregående"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Mellemrum"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Ned"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Eksekvér"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Udskriv"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Vælg"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Op"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Hjælp"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
#, fuzzy
msgid "Snapshot"
msgstr "Tilstandsbillede"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Venstre meta"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
#, fuzzy
msgid "Apps"
msgstr "Applikationer"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Højre meta"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sov"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Venstre Skift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Højre Skift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Venstre Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Venstre Menu"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Højre Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Højre Menu"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Komma"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Minus"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punktum"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Giv agt"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Udvisk Slut-PÃ¥-Fil"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Ryd"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Hovedmenu"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Forbindelses fejl (udløbelse af tidsfrist?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Ingen verden valgt og ingen adresse angivet. Ingen opgave at lave."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Kunne ikke finde eller indlæse spil \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Ugyldig spilspecifikationer."
-
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "Venstre klik: flyt alle enheder. Højre klik: flyt en enkelt enhed"
-
-#~ msgid "is required by:"
-#~ msgstr "er påkrævet af:"
-
-#~ msgid "Configuration saved. "
-#~ msgstr "Konfiguration gemt. "
-
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Advarsel: konfigurationen er ikke sammenhængende. "
-
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Kan ikke skabe verden: navnet indeholder ugyldige bogstaver"
-
-#~ msgid "Multiplayer"
-#~ msgstr "Flerspiller"
-
-#~ msgid "Advanced"
-#~ msgstr "Avanceret"
+#, fuzzy
+#~ msgid "Game Name"
+#~ msgstr "Spil"
-#~ msgid "Show Public"
-#~ msgstr "Vis offentlig"
+#, fuzzy
+#~ msgid "Games"
+#~ msgstr "Spil"
-#~ msgid "Show Favorites"
+#, fuzzy
+#~ msgid "Favorites:"
#~ msgstr "Vis favoritter"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Lad adresse-feltet være tomt for at starte en lokal server."
-
-#~ msgid "Create world"
-#~ msgstr "Skab verden"
-
-#~ msgid "Address required."
-#~ msgstr "Adresse påkrævet."
-
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Kan ikke slette verden: ingenting valgt"
+#, fuzzy
+#~ msgid "Password"
+#~ msgstr "Gammelt kodeord"
-#~ msgid "Files to be deleted"
-#~ msgstr "Filer som slettes"
+#~ msgid "Preload item visuals"
+#~ msgstr "For-indlæs elementernes grafik"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Kan ikke skabe verden: ingen spil fundet"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Advarsel: nogle modifikationer er endnu ikke konfigureret.\n"
+#~ "De vil blive aktiveret som standard når du gemmer konfigurationen. "
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Kan ikke konfigurere verden: ingenting valgt"
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Advarsel: nogle konfigurerede modifikationer mangler.\n"
+#~ "Deres indstillinger vil blive fjernet når du gemmer konfigurationen. "
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Mislykkedes i at slette alle verdenens filer"
+#~ msgid "Delete map"
+#~ msgstr "Slet mappen"
#~ msgid ""
#~ "Default Controls:\n"
@@ -1083,53 +1244,63 @@ msgstr "Ugyldig spilspecifikationer."
#~ "- ESC: denne menu\n"
#~ "- T: snak\n"
-#~ msgid "Delete map"
-#~ msgstr "Slet mappen"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Mislykkedes i at slette alle verdenens filer"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Advarsel: nogle konfigurerede modifikationer mangler.\n"
-#~ "Deres indstillinger vil blive fjernet når du gemmer konfigurationen. "
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Kan ikke konfigurere verden: ingenting valgt"
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Advarsel: nogle modifikationer er endnu ikke konfigureret.\n"
-#~ "De vil blive aktiveret som standard når du gemmer konfigurationen. "
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Kan ikke skabe verden: ingen spil fundet"
-#~ msgid "Exit to OS"
-#~ msgstr "Afslut til operativsystemet"
+#~ msgid "Files to be deleted"
+#~ msgstr "Filer som slettes"
-#~ msgid "Exit to Menu"
-#~ msgstr "Afslut til menu"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Kan ikke slette verden: ingenting valgt"
-#~ msgid "Change Password"
-#~ msgstr "Skift kodeord"
+#~ msgid "Address required."
+#~ msgstr "Adresse påkrævet."
-#~ msgid "Continue"
-#~ msgstr "Fortsæt"
+#~ msgid "Create world"
+#~ msgstr "Skab verden"
-#~ msgid "You died."
-#~ msgstr "Du døde."
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Lad adresse-feltet være tomt for at starte en lokal server."
-#~ msgid "Preload item visuals"
-#~ msgstr "For-indlæs elementernes grafik"
+#~ msgid "Show Favorites"
+#~ msgstr "Vis favoritter"
-#, fuzzy
-#~ msgid "Password"
-#~ msgstr "Gammelt kodeord"
+#~ msgid "Show Public"
+#~ msgstr "Vis offentlig"
-#, fuzzy
-#~ msgid "Favorites:"
-#~ msgstr "Vis favoritter"
+#~ msgid "Advanced"
+#~ msgstr "Avanceret"
-#, fuzzy
-#~ msgid "Games"
-#~ msgstr "Spil"
+#~ msgid "Multiplayer"
+#~ msgstr "Flerspiller"
+
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Kan ikke skabe verden: navnet indeholder ugyldige bogstaver"
+
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Advarsel: konfigurationen er ikke sammenhængende. "
+
+#~ msgid "Configuration saved. "
+#~ msgstr "Konfiguration gemt. "
+
+#~ msgid "is required by:"
+#~ msgstr "er påkrævet af:"
+
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "Venstre klik: flyt alle enheder. Højre klik: flyt en enkelt enhed"
+
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anisotropisk filtréring"
+
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-mapping"
#, fuzzy
-#~ msgid "Game Name"
-#~ msgstr "Spil"
+#~ msgid "Downloading"
+#~ msgstr "Ned"
diff --git a/po/de/minetest.po b/po/de/minetest.po
index 3789b6cc2..698f6920e 100644
--- a/po/de/minetest.po
+++ b/po/de/minetest.po
@@ -7,68 +7,87 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2013-12-29 14:29+0200\n"
-"Last-Translator: Pilz Adam <PilzAdam@gmx.de>\n"
-"Language-Team: Deutsch <>\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-08-09 21:51+0200\n"
+"Last-Translator: sfan5 <sfan5@live.de>\n"
+"Language-Team: German "
+"<https://hosted.weblate.org/projects/minetest/minetest/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 1.7-dev\n"
+"X-Generator: Weblate 2.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr "Ein Fehler ist in Lua aufgetreten (z.B. aufgrund eines Mods):"
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr "Ein Fehler ist aufgetreten:"
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
-msgstr "Ok"
+msgstr "OK"
+
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Lädt ..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+"Bitte versuchen Sie die Öffentliche Serverliste neu zu aktivieren, und "
+"prüfen Sie Ihre Internetverbindung."
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Welt:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
-msgstr "Spiel verstecken"
+msgstr "Spiel ausblenden"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
-msgstr "MP mods verstecken"
+msgstr "MP-Mods verstecken"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Abhängig von:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Speichern"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Abbrechen"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "MP aktivieren"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "MP deaktivieren"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "Aktiviert"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Alle an"
@@ -106,11 +125,13 @@ msgstr "Warnung: Die minimale Testversion ist für Entwickler gedacht."
#: builtin/mainmenu/dlg_create_world.lua:73
msgid "Download a subgame, such as minetest_game, from minetest.net"
-msgstr "Andere Spiele (wie minetest_game) können von minetest.net heruntergeladen werden"
+msgstr ""
+"Andere Spiele (wie minetest_game) können von minetest.net heruntergeladen "
+"werden"
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
-msgstr "Eine Welt namens \"$1\" existiert bereits"
+msgstr "Eine Welt namens „$1“ existiert bereits"
#: builtin/mainmenu/dlg_create_world.lua:116
msgid "No worldname given or no game selected"
@@ -118,11 +139,11 @@ msgstr "Kein Weltname gegeben oder kein Spiel ausgewählt"
#: builtin/mainmenu/dlg_delete_mod.lua:26
msgid "Are you sure you want to delete \"$1\"?"
-msgstr "\"$1\" wirklich löschen?"
+msgstr "„$1“ wirklich löschen?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ja"
@@ -132,15 +153,15 @@ msgstr "Nein, natürlich nicht!"
#: builtin/mainmenu/dlg_delete_mod.lua:41
msgid "Modmgr: failed to delete \"$1\""
-msgstr "Modmgr: Fehler beim löschen von \"$1\""
+msgstr "Modmgr: Fehler beim Löschen von „$1“"
#: builtin/mainmenu/dlg_delete_mod.lua:45
msgid "Modmgr: invalid modpath \"$1\""
-msgstr "Modmgr: Unzulässiger Modpfad \"$1\""
+msgstr "Modmgr: Unzulässiger Modpfad „$1“"
#: builtin/mainmenu/dlg_delete_world.lua:24
msgid "Delete World \"$1\"?"
-msgstr "Welt \"$1\" löschen?"
+msgstr "Welt „$1“ löschen?"
#: builtin/mainmenu/dlg_delete_world.lua:26
msgid "No"
@@ -150,95 +171,93 @@ msgstr "Nein"
msgid "Rename Modpack:"
msgstr "Modpack umbenennen:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Annehmen"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
-msgstr "Mod installieren: Datei: \"$1\""
+msgstr "Mod installieren: Datei: „$1“"
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
"\n"
-"Mod installieren: Nicht unterstützter Dateityp \"$1\" oder fehlerhaftes Archiv"
+"Mod installieren: Nicht unterstützter Dateityp „$1“ oder fehlerhaftes Archiv"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
-msgstr "Fehler beim installieren von $1 zu $2"
+msgstr "Fehler bei der Installation von $1 nach $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
-msgstr "Mod installieren: Kann keinen Ordnernamen für Modpack $1 finden"
+msgstr ""
+"Mod installieren: Geeigneter Ordnername für Modpack $1 konnte nicht gefunden "
+"werden"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
-msgstr "Mod installieren: Kann echten Namen für $1 nicht finden"
+msgstr "Mod installieren: Echter Modname für $1 konnte nicht gefunden werden"
#: builtin/mainmenu/store.lua:88
msgid "Unsorted"
msgstr "Unsortiert"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr "Suchen"
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr "Lade herunter"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr "Bitte warten..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr "$1 wird heruntergeladen, bitte warten ..."
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr "Erfolgreich installiert:"
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
msgstr "Kurzname:"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr "ok"
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Bewertung"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
-msgstr "erneut installieren"
+msgstr "Erneut installieren"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Installieren"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr "Schließen"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Seite $1 von $2"
#: builtin/mainmenu/tab_credits.lua:22
msgid "Credits"
-msgstr "Credits"
+msgstr "Mitwirkende"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
-msgstr "Kernentwickler"
+msgstr "Hauptentwickler"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Aktive Mitwirkende"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr "Frühere Hauptentwickler"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Frühere Mitwirkende"
@@ -248,7 +267,7 @@ msgstr "Installierte Mods:"
#: builtin/mainmenu/tab_mods.lua:39
msgid "Online mod repository"
-msgstr "Online-Mod-Speicher"
+msgstr "Online-Mod-Archiv"
#: builtin/mainmenu/tab_mods.lua:78
msgid "No mod description available"
@@ -256,7 +275,7 @@ msgstr "Keine Modbeschreibung verfügbar"
#: builtin/mainmenu/tab_mods.lua:82
msgid "Mod information:"
-msgstr "Mod Information:"
+msgstr "Modinformation:"
#: builtin/mainmenu/tab_mods.lua:93
msgid "Rename"
@@ -272,20 +291,19 @@ msgstr "Ausgewählte Mod deinstallieren"
#: builtin/mainmenu/tab_mods.lua:121
msgid "Select Mod File:"
-msgstr "Mod Datei auswählen:"
+msgstr "Mod-Datei auswählen:"
#: builtin/mainmenu/tab_mods.lua:165
msgid "Mods"
msgstr "Mods"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
-msgstr "Adresse / Port"
+msgid "Address / Port :"
+msgstr "Adresse/Port:"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
-msgstr "Name/Passwort"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
+msgstr "Name/Passwort:"
#: builtin/mainmenu/tab_multiplayer.lua:29
#: builtin/mainmenu/tab_simple_main.lua:30
@@ -293,7 +311,7 @@ msgid "Public Serverlist"
msgstr "Öffentliche Serverliste"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Entfernen"
@@ -302,15 +320,30 @@ msgstr "Entfernen"
msgid "Connect"
msgstr "Verbinden"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "Kreativmodus"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "Schaden aktiviert"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "Spielerkampf aktiviert"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Client"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Neu"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Konfigurieren"
@@ -318,17 +351,18 @@ msgstr "Konfigurieren"
msgid "Start Game"
msgstr "Spiel starten"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Welt wählen:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
-msgstr "Kreativitätsmodus"
+msgstr "Kreativmodus"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Schaden einschalten"
@@ -336,9 +370,13 @@ msgstr "Schaden einschalten"
msgid "Public"
msgstr "Öffentlich"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Name/Passwort"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
-msgstr ""
+msgstr "Bind-Adresse"
#: builtin/mainmenu/tab_server.lua:47
msgid "Port"
@@ -348,143 +386,172 @@ msgstr "Port"
msgid "Server Port"
msgstr "Serverport"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr "Kein Weltname angegeben oder kein Spiel ausgewählt!"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Server"
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr "Undurchs. Blätter"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr "Einfache Blätter"
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr "Schöne Blätter"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "Kein Filter"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "Bilinearer Filter"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "Trilinearer Filter"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr "Keine Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr "Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr "Mipmap u. Aniso. Filter"
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr "Sind Sie sicher, dass Sie die Einzelspielerwelt löschen wollen?"
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr "Nein!!!"
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Besseres Licht"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
-msgstr "Aktiviere Partikel"
+msgstr "Partikel aktivieren"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
-msgstr "3D Wolken"
+msgstr "3D-Wolken"
-#: builtin/mainmenu/tab_settings.lua:140
-msgid "Fancy Trees"
-msgstr "Schöne Bäume"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Undurchs. Wasser"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr "Verbundenes Glas"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr "Neustart nach Ändern des Treibers erforderlich"
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "Blöcke hervorheben"
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Anisotroper Filter"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "Texturierung:"
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Bi-Linearer Filter"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "Rendering:"
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Tri-Linearer Filter"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "Neustart nach Ändern des Treibers erforderlich"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shader"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Tasten ändern"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr "Einzelspielerwelt zurücksetzen"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr "GUI-Skalierfaktor"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
-msgstr "Skalierfaktor:"
+msgstr "Auf Menüelemente angewandter Skalierfaktor: "
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
-msgstr ""
+msgstr "Berührungsfreies Ziel"
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
-msgstr ""
+msgstr "Berührungsempfindlichkeit (px)"
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr "Bumpmapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
-msgstr "Generiere Normalmaps"
+msgstr "Normalmaps generieren"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr "Parallax Occlusion"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr "Wasserwellen"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr "Wehende Blätter"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr "Wogende Pflanzen"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
-msgstr "Um Shader zu benutzen muss der OpenGL Treiber benutzt werden."
+msgstr "Um Shader zu benutzen, muss der OpenGL-Treiber benutzt werden."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Einstellungen"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr "Flugmodus"
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
-msgstr "Starte Einzelspieler"
+msgstr "Einzelspieler starten"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr "Mods konfigurieren"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr "Hauptmenü"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Spielen"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Einzelspieler"
@@ -500,39 +567,193 @@ msgstr "Keine Informationen vorhanden"
msgid "Texturepacks"
msgstr "Texturpakete"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "Texturen laden ..."
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "Shader wiederherstellen ..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "Blöcke initialisieren ..."
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr "Blöcke initialisieren"
+
+#: src/client.cpp:1768
msgid "Item textures..."
-msgstr "Inventarbilder..."
+msgstr "Inventarbilder ..."
+
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "Fertig!"
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Hauptmenü"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Spielername zu lang."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Verbindungsfehler (Zeitüberschreitung?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Keine Welt ausgewählt und keine Adresse angegeben. Nichts zu tun."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "Angegebener Weltpfad existiert nicht: "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Kann Spiel nicht finden/laden \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Ungültige Spielspezif."
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "no"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Fortsetzen"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Sie sind gestorben."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Wiederbeleben"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+"Standardsteuerung:\n"
+"Kein Menü sichtbar:\n"
+"- einmal antippen: Knopf betätigen\n"
+"- doppelt antippen: bauen/benutzen\n"
+"- Finger wischen: umsehen\n"
+"Menü/Inventar sichtbar:\n"
+"- doppelt antippen (außen):\n"
+" -->schließen\n"
+"- Stapel berühren, Feld berühren:\n"
+" --> Stapel verschieben\n"
+"- berühren u. ziehen, mit 2. Finger antippen\n"
+" --> 1 Gegenstand ins Feld platzieren\n"
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Standard-Tastenbelegung:\n"
+"- WASD: Bewegen\n"
+"- Leertaste: Springen/Klettern\n"
+"- Umschalt: Kriechen/herunterklettern\n"
+"- Q: Item fallen lassen\n"
+"- I: Inventar\n"
+"- Maus: drehen/umschauen\n"
+"- Maus links: Abbauen/Schlagen\n"
+"- Maus rechts: Platzieren/Benutzen\n"
+"- Mausrad: Gegenstand auswählen\n"
+"- T: Chat\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Weiter"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Passwort ändern"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Tonlautstärke"
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "Tasten ändern"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Hauptmenü"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Programm beenden"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "Herunterfahren ..."
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr "Server erstellen ..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Client erstellen ..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Adresse auflösen ..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Zum Server verbinden ..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
-msgstr "Item-Definitionen..."
+msgstr "Item-Definitionen ..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
-msgstr "Node-Definitionen..."
+msgstr "Node-Definitionen ..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
-msgstr "Medien..."
+msgstr "Medien ..."
-#: src/game.cpp:2267
-msgid " KB/s"
-msgstr " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr "KiB/s"
-#: src/game.cpp:2271
-msgid " MB/s"
-msgstr " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr "MiB/s"
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -540,445 +761,431 @@ msgstr ""
"\n"
"Siehe debug.txt für Details."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Fortsetzen"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr "Enter "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "OK"
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
-msgstr "Steuerung"
+msgstr ""
+"Steuerung (Falls dieses Menü versagt, entfernen Sie Sachen aus minetest.conf)"
#: src/guiKeyChangeMenu.cpp:165
msgid "\"Use\" = climb down"
-msgstr "\"Benutzen\" = herunterklettern"
+msgstr "Benutzen = runterklettern"
#: src/guiKeyChangeMenu.cpp:180
msgid "Double tap \"jump\" to toggle fly"
-msgstr "Doppelt \"springen\" zum fliegen"
+msgstr "2×Sprungtaste zum Fliegen"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Taste bereits in Benutzung"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "Taste drücken"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Vorwärts"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Rückwärts"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Links"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Rechts"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Benutzen"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Springen"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Schleichen"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Wegwerfen"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventar"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Chat"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Befehl"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konsole"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
-msgstr "Fliegen umsch."
+msgstr "Flugmodus"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
-msgstr "Speed umsch."
+msgstr "Schnellmodus"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr "Kinomodus"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
-msgstr "Noclip umsch."
+msgstr "Geistmodus"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
-msgstr "Entfernung wählen"
+msgstr "Weite Sicht"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Stack ausgeben"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Altes Passwort"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Neues Passwort"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Passwort wiederholen"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Ändern"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Passwörter passen nicht zusammen!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
-msgstr "Soundlautstärke: "
+msgstr "Tonlautstärke: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Zurück"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Linke Taste"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Mittlere Taste"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Rechte Taste"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
-msgstr "X Knopf 1"
+msgstr "X-Knopf 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Rücktaste"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
-msgstr "löschen"
+msgstr "Clear"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
-msgstr "Enter"
+msgstr "Eingabe"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
-msgstr "X Knopf 2"
+msgstr "X-Knopf 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Feststellen"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Strg"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menü"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pause"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Umsch."
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
-msgstr "Konvertieren"
+msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
-msgstr "nicht konvertieren"
+msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Ende"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Pos1"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
-msgstr "Modus-Änderung"
+msgstr "Mode Change"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Bild runter"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Bild hoch"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Leertaste"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Runter"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Ausführen"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Druck"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
-msgstr "Selektiere"
+msgstr "Select"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Hoch"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Hilfe"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Einfg"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Druck"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Win links"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Apps"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Ziffernblock 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Ziffernblock 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Win rechts"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Schlaf"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Ziffernblock 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Ziffernblock 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Ziffernblock 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Ziffernblock 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Ziffernblock 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Ziffernblock 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Ziffernblock *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Ziffernblock +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Ziffernblock -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Ziffernblock /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Ziffernblock 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Ziffernblock 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Rollen"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Umsch. links"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Umsch. rechts"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Strg links"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Alt"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Strg rechts"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Alt Gr"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Komma"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Minus"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punkt"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
-msgstr "Attn."
+msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
-msgstr "Lösche OEF"
+msgstr "Erase OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Hauptmenü"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr "Spielername zu lang."
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Verbindungsfehler (Zeitüberschreitung?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Keine Welt ausgewählt und keine Adresse angegeben. Nichts zu tun."
+#~ msgid "Downloading"
+#~ msgstr "Lade herunter"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr "Angegebener Weltpfad existiert nicht: "
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapping"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Kann Spiel nicht finden/laden \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Invalide Spielspezif."
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anisotroper Filter"
#~ msgid "Left click: Move all items, Right click: Move single item"
#~ msgstr "Linksklick: Alle Items bewegen, Rechtsklick: Einzelnes Item bewegen"
@@ -1076,67 +1283,6 @@ msgstr "Invalide Spielspezif."
#~ "Warnung: Einige Mods sind noch nicht konfiguriert.\n"
#~ "Sie werden aktiviert wenn die Konfiguration gespeichert wird. "
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Standard Tastaturebelegung:\n"
-#~ "- WASD: Bewegen\n"
-#~ "- Leertaste: Springen/Klettern\n"
-#~ "- Umschalt: Kriechen/herunterklettern\n"
-#~ "- Q: Item droppen\n"
-#~ "- I: Inventar\n"
-#~ "- Maus: drehen/umschauen\n"
-#~ "- Maus links: Abbauen/Schlagen\n"
-#~ "- Maus rechts: Platzieren/Benutzen\n"
-#~ "- Mausrad: Item auswählen\n"
-#~ "- T: Chat\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Programm beenden"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Hauptmenü"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Sound Lautstärke"
-
-#~ msgid "Change Password"
-#~ msgstr "Passwort ändern"
-
-#~ msgid "Continue"
-#~ msgstr "Weiter"
-
-#~ msgid "You died."
-#~ msgstr "Sie sind gestorben."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Herunterfahren..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Verbinde zum Server..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Löse Adresse auf..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Erstelle Client..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Erstelle Server..."
-
-#~ msgid "Loading..."
-#~ msgstr "Lädt..."
-
#~ msgid "Local install"
#~ msgstr "Lokale Install."
@@ -1205,3 +1351,12 @@ msgstr "Invalide Spielspezif."
#~ msgid "Game Name"
#~ msgstr "Spielname"
+
+#~ msgid " MB/s"
+#~ msgstr " MB/s"
+
+#~ msgid " KB/s"
+#~ msgstr " KB/s"
+
+#~ msgid "Fly mode"
+#~ msgstr "Flugmodus"
diff --git a/po/es/minetest.po b/po/es/minetest.po
index a5380d597..29dca2f2d 100644
--- a/po/es/minetest.po
+++ b/po/es/minetest.po
@@ -7,68 +7,84 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2015-01-19 09:56+0100\n"
-"Last-Translator: Diego de las Heras <diegodelasheras@gmail.com>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-02-14 13:35+0100\n"
+"Last-Translator: Diego Martínez <lkaezadl3@yahoo.com>\n"
+"Language-Team: \n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Poedit 1.7.3\n"
+"X-Generator: Poedit 1.7.4\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr "Ha ocurrido un error en una script de Lua, como en un mod:"
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr "Ha ocurrido un error:"
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Aceptar"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Cargando..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr "Intenta re-habilitar la lista de servidores públicos y verifica tu conexión a Internet."
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Mundo:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Ocultar juego"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Ocultar contenido mp"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Dependencias:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Guardar"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Cancelar"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Activar paquete"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Desactivar paquete"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "Activado"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Activar todos"
@@ -110,7 +126,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr "Descarga un sub-juego, como minetest_game, desde minetest.net"
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Ya existe un mundo llamado \"$1\""
@@ -124,7 +140,7 @@ msgstr "¿Realmente desea borrar \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Sí"
@@ -152,15 +168,15 @@ msgstr "No"
msgid "Rename Modpack:"
msgstr "Renombrar paquete de mod:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Aceptar"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Instalar mod: Archivo: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
@@ -168,17 +184,17 @@ msgstr ""
"\n"
"Instalar mod: Formato de archivo \"$1\" no soportado o archivo corrupto"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Fallo al instalar $1 en $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Instalar mod: Imposible encontrar un nombre de archivo adecuado para el "
"paquete de mod $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Instalar mod: Imposible encontrar el nombre real del mod para: $1"
@@ -186,48 +202,40 @@ msgstr "Instalar mod: Imposible encontrar el nombre real del mod para: $1"
msgid "Unsorted"
msgstr "Sin ordenar"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr "Buscar"
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr "Descargando"
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr "Descargando $1, por favor espere..."
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr "por favor espere..."
-
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr "Instalado con éxito:"
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
msgstr "Nombre corto:"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr "aceptar"
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Clasificación"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Reinstalar"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Instalar"
# En el menú principal de mods pone repositorio no tienda.
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr "Cerrar repositorio"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Página $1 de $2"
@@ -235,15 +243,19 @@ msgstr "Página $1 de $2"
msgid "Credits"
msgstr "Créditos"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Desarrolladores principales"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Colaboradores activos"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr "Antiguos desarrolladores"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Antiguos colaboradores"
@@ -288,8 +300,7 @@ msgstr "Mods"
msgid "Address / Port :"
msgstr "Dirección / puerto:"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
+#: builtin/mainmenu/tab_multiplayer.lua:24
msgid "Name / Password :"
msgstr "Nombre / contraseña:"
@@ -299,7 +310,7 @@ msgid "Public Serverlist"
msgstr "Lista de servidores públicos"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Borrar"
@@ -308,15 +319,30 @@ msgstr "Borrar"
msgid "Connect"
msgstr "Conectar"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "Modo creativo"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "Daño activado"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "PvP activado"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Cliente"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nuevo"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Configurar"
@@ -324,17 +350,18 @@ msgstr "Configurar"
msgid "Start Game"
msgstr "Iniciar juego"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Selecciona un mundo:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Modo creativo"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Permitir daños"
@@ -342,6 +369,11 @@ msgstr "Permitir daños"
msgid "Public"
msgstr "Público"
+# Los dos puntos son intencionados.
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nombre / contraseña:"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr "Asociar dirección"
@@ -354,145 +386,173 @@ msgstr "Puerto"
msgid "Server Port"
msgstr "Puerto del servidor:"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr "No se ha dado un nombre al mundo o no se ha seleccionado uno"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Servidor"
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr "Hojas opacas"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr "Hojas simples"
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Ãrboles detallados"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "Sin filtro"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "Filtro bi-lineal"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "Filtro tri-lineal"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr "Sin Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr "Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr "Mipmap + Filtro aniso."
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr "¿Estás seguro de querer reiniciar el mundo de un jugador?"
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr "¡¡¡No!!!"
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Iluminación suave"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Habilitar partículas"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Nubes 3D"
-#: builtin/mainmenu/tab_settings.lua:141
-msgid "Fancy Trees"
-msgstr "Ãrboles detallados"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Agua opaca"
-#: builtin/mainmenu/tab_settings.lua:145
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr "Vidrios conectados"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr "Reinicia minetest para que los cambios en el controlador tengan efecto"
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "Resaltar nodos"
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtrado Anisotrópico"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "Texturizado:"
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtrado Bi-Lineal"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "Renderizado:"
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtrado Tri-Lineal"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "Reinicia minetest para que los cambios en el controlador tengan efecto"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Sombreadores"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Configurar teclas"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr "Reiniciar mundo de un jugador"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr "Factor de escala (GUI)"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr "Factor de escala aplicado a los elementos del menú: "
-#: builtin/mainmenu/tab_settings.lua:182
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr "Tocar para interactuar"
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr "Umbral táctil (px)"
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr "Mapeado de relieve"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr "Generar mapas normales"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr "Oclusión de paralaje"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr "Oleaje en el agua"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr "Movimiento de hojas"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr "Movimiento de plantas"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Para habilitar los sombreadores debe utilizar el controlador OpenGL."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Configuración"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr "Modo vuelo"
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr "Comenzar un jugador"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr "Configurar mods"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr "Principal"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Jugar"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Un jugador"
@@ -509,39 +569,195 @@ msgstr "Sin información disponible"
msgid "Texturepacks"
msgstr "Texturas"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "Cargando texturas..."
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "Reconstruyendo sombreadores..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "Inicializando nodos..."
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr "Inicializando nodos"
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Texturas de objetos..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "¡Completado!"
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menú principal"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Nombre de jugador demasiado largo."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Error de conexión (¿tiempo agotado?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+"No se seleccionó el mundo y no se ha especificado una dirección. Nada que "
+"hacer."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "La ruta del mundo especificada no existe: "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "No se puede encontrar o cargar el juego \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Juego especificado no válido."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Continuar"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Has muerto."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Revivir"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+"Controles predeterminados:\n"
+"Con el menú oculto:\n"
+"- toque simple: botón activar\n"
+"- toque doble: colocar/usar\n"
+"- deslizar dedo: mirar alrededor\n"
+"Con el menú/inventario visible:\n"
+"- toque doble (fuera):\n"
+" -->cerrar\n"
+"- toque en la pila de objetos:\n"
+" -->mover la pila\n"
+"- toque y arrastrar, toque con 2 dedos:\n"
+" -->colocar solamente un objeto\n"
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Controles predeterminados:\n"
+"- WASD: moverse\n"
+"- Espacio: saltar/subir\n"
+"- Mayús.: puntillas/bajar\n"
+"- Q: soltar objeto\n"
+"- I: inventario\n"
+"- Ratón: girar/mirar\n"
+"- Ratón izq.: cavar/golpear\n"
+"- Ratón der.: colocar/usar\n"
+"- Ratón rueda: elegir objeto\n"
+"- T: chat\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Continuar"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Cambiar contraseña"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volumen del sonido"
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "Configurar teclas"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Salir al menú"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Salir al S.O."
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "Cerrando..."
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr "Creando servidor..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Creando cliente..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Resolviendo dirección..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Conectando al servidor..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definiciones de objetos..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definiciones de nodos..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Media..."
-#: src/game.cpp:2267
-msgid " KB/s"
-msgstr " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
-msgstr " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -549,14 +765,14 @@ msgstr ""
"\n"
"Consulta debug.txt para obtener más detalles."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Continuar"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr "Ingresar "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "aceptar"
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -571,602 +787,408 @@ msgstr "\"Usar\" = Descender"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Pulsar dos veces \"saltar\" para volar"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "La tecla ya se está utilizando"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "pulsa una tecla"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Adelante"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Atrás"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Izquierda"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Derecha"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Usar"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Saltar"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Caminar"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Tirar"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventario"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Chat"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Comando"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Consola"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Activar volar"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Activar rápido"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr "Activar cinemático"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Activar noclip"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Seleccionar distancia"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Imprimir pilas"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Contraseña anterior"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Contraseña nueva"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Confirmar contraseña"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Cambiar"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "¡Las contraseñas no coinciden!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Volúmen del sonido: "
# Es en el menú de sonido. Salir suena muy fuerte.
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Cerrar"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Botón izquierdo"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Botón central"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Botón derecho"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X Button 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Atrás"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Limpiar"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Retorno"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabulador"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X Button 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Bloq Mayús"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menú"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pausa"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convertir"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "No convertir"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Fin"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Inicio"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Cambio de modo"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Siguiente"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Anterior"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Espacio"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Abajo"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Ejecutar"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Captura"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Seleccionar"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Arriba"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Ayuda"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Introducir"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Captura de pantalla"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Win izq."
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Aplicaciones"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Win der."
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Suspender"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Bloq Núm"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Bloq Despl"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Shift izq."
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Shift der."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Control izq."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Menú izq."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Control der."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Menú der."
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Coma"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Menos"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punto"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Más"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Borrar OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "Limpiar OEM"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menú principal"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr "Nombre de jugador demasiado largo."
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Error de conexión (¿tiempo agotado?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-"No se seleccionó el mundo y no se ha especificado una dirección. Nada que "
-"hacer."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr "La ruta del mundo especificada no existe: "
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "No se puede encontrar o cargar el juego \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Juego especificado no válido."
-
-msgid ""
-"Default Controls:\n"
-"- WASD: move\n"
-"- Space: jump/climb\n"
-"- Shift: sneak/go down\n"
-"- Q: drop item\n"
-"- I: inventory\n"
-"- Mouse: turn/look\n"
-"- Mouse left: dig/punch\n"
-"- Mouse right: place/use\n"
-"- Mouse wheel: select item\n"
-"- T: chat\n"
-msgstr ""
-"Controles predeterminados:\n"
-"- WASD: moverse\n"
-"- Espacio: saltar/subir\n"
-"- Mayús.: puntillas/bajar\n"
-"- Q: soltar objeto\n"
-"- I: inventario\n"
-"- Ratón: girar/mirar\n"
-"- Ratón izq.: cavar/golpear\n"
-"- Ratón der.: colocar/usar\n"
-"- Ratón rueda: elegir objeto\n"
-"- T: chat\n"
-
-msgid "is required by:"
-msgstr "es requerido por:"
-
-msgid "Configuration saved. "
-msgstr "Configuración guardada. "
-
-msgid "Warning: Configuration not consistent. "
-msgstr "Advertencia: La configuración no es coherente."
-
-msgid "Cannot create world: Name contains invalid characters"
-msgstr "No se puede crear el mundo: El nombre contiene caracteres no válidos"
-
-msgid "Multiplayer"
-msgstr "Multijugador"
-
-msgid "Advanced"
-msgstr "Avanzado"
-
-msgid "Show Public"
-msgstr "Mostrar público"
-
-msgid "Show Favorites"
-msgstr "Mostrar favoritos"
-
-msgid "Leave address blank to start a local server."
-msgstr "Dejar la dirección en blanco para iniciar un servidor local."
-
-msgid "Address required."
-msgstr "Requiere una dirección."
-
-msgid "Cannot delete world: Nothing selected"
-msgstr "No se puede eliminar el mundo: Ninguno seleccionado"
-
-msgid "Files to be deleted"
-msgstr "Archivos que se eliminarán"
-
-msgid "Cannot create world: No games found"
-msgstr "No se puede crear el mundo: No se encontraron juegos"
-
-msgid "Cannot configure world: Nothing selected"
-msgstr "No se puede configurar el mundo: Ninguno seleccionado"
-
-msgid "Failed to delete all world files"
-msgstr "No se pudo eliminar todos los archivos del mundo"
-
-msgid ""
-"Warning: Some configured mods are missing.\n"
-"Their setting will be removed when you save the configuration. "
-msgstr ""
-"Advertencia: Algunos mods configurados faltan.\n"
-"Sus ajustes se eliminarán al guardar la configuración. "
-
-msgid ""
-"Warning: Some mods are not configured yet.\n"
-"They will be enabled by default when you save the configuration. "
-msgstr ""
-"Advertencia: Algunos mods todavía no están configurados.\n"
-"Se habilitarán de forma predeterminada al guardar la configuración. "
-
-msgid "Exit to OS"
-msgstr "Salir al S.O."
-
-msgid "Exit to Menu"
-msgstr "Salir al menú"
-
-msgid "Sound Volume"
-msgstr "Volumen del sonido"
-
-msgid "Change Password"
-msgstr "Cambiar contraseña"
-
-msgid "Continue"
-msgstr "Continuar"
-
-msgid "You died."
-msgstr "Has muerto."
-
-msgid "Shutting down..."
-msgstr "Cerrando..."
-
-msgid "Connecting to server..."
-msgstr "Conectando al servidor..."
-
-msgid "Resolving address..."
-msgstr "Resolviendo dirección..."
-
-msgid "Creating client..."
-msgstr "Creando cliente..."
-
-msgid "Creating server...."
-msgstr "Creando servidor..."
-
-msgid "Loading..."
-msgstr "Cargando..."
-
-msgid "Local install"
-msgstr "Instalación local"
-
-msgid "Preload item visuals"
-msgstr "Precarga elementos visuales"
-
-msgid "Password"
-msgstr "Contraseña"
-
-msgid "Name"
-msgstr "Nombre"
-
-msgid "Favorites:"
-msgstr "Favoritos:"
-
-msgid "<<-- Add mod"
-msgstr "<<-- Añadir mod"
-
-msgid "Remove selected mod"
-msgstr "Eliminar el mod seleccionado"
+#~ msgid " MB/s"
+#~ msgstr " MB/s"
-msgid "Mods:"
-msgstr "Mods:"
+#~ msgid " KB/s"
+#~ msgstr " KB/s"
-msgid "Games"
-msgstr "Juegos"
-
-msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-msgstr "Gamemgr: Imposible copiar el mod \"$1\" al juego \"$2\""
-
-msgid "Game Name"
-msgstr "Nombre del juego"
-
-msgid "Change Keys"
-msgstr "Configurar teclas"
-
-msgid "Name/Password"
-msgstr "Nombre / contraseña:"
-
-msgid "Node Highlighting"
-msgstr "Resaltar nodos"
-
-msgid "Texturing:"
-msgstr "Texturizado:"
-
-msgid "Rendering:"
-msgstr "Renderizado:"
-
-msgid "Rebuilding shaders..."
-msgstr "Reconstruyendo sombreadores..."
-
-msgid "Initializing nodes..."
-msgstr "Inicializando nodos..."
-
-msgid "Done!"
-msgstr "¡Completado!"
+#~ msgid "Fly mode"
+#~ msgstr "Modo vuelo"
diff --git a/po/et/minetest.po b/po/et/minetest.po
index 00a919550..561b69a43 100644
--- a/po/et/minetest.po
+++ b/po/et/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-12-18 21:28+0200\n"
"Last-Translator: Jabo Babo <bb7b@gmx.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,62 +18,78 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "kinnitama"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr ""
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
#, fuzzy
msgid "World:"
msgstr "Vali maailm:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
#, fuzzy
msgid "Hide Game"
msgstr "Mäng"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
#, fuzzy
msgid "Depends:"
msgstr "Vajab:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Salvesta"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Tühista"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "Lülita kõik sisse"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Lülita kõik välja"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "Sisse lülitatud"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
#, fuzzy
msgid "Enable all"
msgstr "Lülita kõik sisse"
@@ -115,7 +131,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
#, fuzzy
msgid "A world named \"$1\" already exists"
msgstr "Maailma loomine ebaõnnestus: Samanimeline maailm on juba olemas"
@@ -130,7 +146,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Jah"
@@ -159,30 +175,30 @@ msgstr "Ei"
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Nõustu"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
#, fuzzy
msgid "Failed to install $1 to $2"
msgstr "Maailma initsialiseerimine ebaõnnestus"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -190,49 +206,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Alla"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Maailma nimi"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -240,15 +247,20 @@ msgstr ""
msgid "Credits"
msgstr "Tänuavaldused"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Põhiline arendaja"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Co-arendaja"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Põhiline arendaja"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Early arendajad"
@@ -290,12 +302,13 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "IP/Port"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nimi/Parool"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -305,7 +318,7 @@ msgid "Public Serverlist"
msgstr "Avatud serverite nimekiri:"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Kustuta"
@@ -314,15 +327,33 @@ msgstr "Kustuta"
msgid "Connect"
msgstr "Liitu"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Kujunduslik mängumood"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "Sisse lülitatud"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "Sisse lülitatud"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Uus"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Konfigureeri"
@@ -330,17 +361,18 @@ msgstr "Konfigureeri"
msgid "Start Game"
msgstr "Alusta mängu"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Vali maailm:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Kujunduslik mängumood"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Lülita valu sisse"
@@ -348,6 +380,10 @@ msgstr "Lülita valu sisse"
msgid "Public"
msgstr "Avalik"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nimi/Parool"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -360,151 +396,184 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "No nimi või no mäng valitud"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Läbipaistmatu vesi"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Uhked puud"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Bi-lineaarsed Filtreerimine"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Tri-Linear Filtreerimine"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Ilus valgustus"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Lülita osakesed sisse"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D pilved"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Uhked puud"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
#, fuzzy
msgid "Opaque Water"
msgstr "Läbipaistmatu vesi"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Liitu"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Väga hea kvaliteet"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Anisotroopne Filtreerimine"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Bi-lineaarsed Filtreerimine"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Tri-Linear Filtreerimine"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Varjutajad"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Vaheta nuppe"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Üksikmäng"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Väga hea kvaliteet"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Aktiveerimiseks varjud, nad vajavad OpenGL draiver."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Sätted"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Üksikmäng"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Konfigureeri"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Menüü"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Mängi"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Üksikmäng"
@@ -521,39 +590,171 @@ msgstr "Informatsioon ei ole kättesaadav"
msgid "Texturepacks"
msgstr "Vali graafika:"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr ""
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menüü"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Ãœhenduse viga (Aeg otsas?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Pole valitud ei maailma ega IP aadressi. Pole midagi teha."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Ei leia ega suuda jätkata mängu \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Vale mängu ID."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Jätka"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Sa surid."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Ärka ellu"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Jätka"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Vaheta parooli"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Hääle volüüm"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Vaheta nuppe"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Välju menüüsse"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Välju mängust"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -561,14 +762,14 @@ msgstr ""
"\n"
"Vaata debug.txt info jaoks."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Jätka"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -583,478 +784,463 @@ msgstr "\"Tegevus\" = Roni alla"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Topeltklõpsa \"Hüppamist\" et sisse lülitada lendamine"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Nupp juba kasutuses"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "Vajuta nuppu"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Edasi"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Tagasi"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Vasakule"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Paremale"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Tegevus"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Hüppamine"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Hiilimine"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Viska maha"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Seljakott"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Jututuba"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Käsklus"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konsool"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Lülita lendamine sisse"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Lülita kiirus sisse"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Lülita kiirus sisse"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Lülita läbi seinte minek sisse"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Kauguse valik"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Prindi kogused"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Vana parool"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Uus parool"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Kinnita parooli"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Muuda"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Paroolid ei ole samad!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Hääle Volüüm: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Välju"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Vasak nupp"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Keskmine nupp"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Parem nupp"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X Nuppp 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Tagasi"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Tühjenda"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Reavahetus"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X Nupp 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "CTRL"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menüü"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Paus"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift,"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Konverteeri"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Põgene"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Viimane"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Konverteerimatta"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Lõpeta"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Kodu"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Moodi vahetamine"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Järgmine"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Eelnev"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Tühik"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Alla"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Soorita"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Prindi"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Vali"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Ãœles"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Abi"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Sisesta"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Mängupilt"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Vasak Windowsi nupp"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Aplikatsioonid"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numbrilaual 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numbrilaual 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Parem Windowsi nupp"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Maga"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numbrilaual 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numbrilaual 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numbrilaual 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numbrilaual 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numbrilaual 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numbrilaual 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numbrilaual *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numbrilaual +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numbrilaual -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numbrilaual /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numbrilaual 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numbrilaual 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Numbrilaual Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll lukk"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Vasak Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Parem Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Vasak CTRL"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Vasak Menüü"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Parem CTRL"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Parem Menüü"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Koma"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Miinus"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punkt"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Pluss"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Kustuta OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Tühi"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Suumi"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menüü"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Ãœhenduse viga (Aeg otsas?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Pole valitud ei maailma ega IP aadressi. Pole midagi teha."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Ei leia ega suuda jätkata mängu \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Vale mängu ID."
-
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr ""
-#~ "Vasak hiireklõps: Liiguta kõiki asju, Parem hiireklõps: Liiguta üksikut "
-#~ "asja"
-
-#~ msgid "is required by:"
-#~ msgstr "Seda vajavad:"
+#, fuzzy
+#~ msgid "Game Name"
+#~ msgstr "Mäng"
-#~ msgid "Configuration saved. "
-#~ msgstr "Konfiguratsioon salvestatud. "
+#~ msgid "GAMES"
+#~ msgstr "MÄNGUD"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Hoiatus: Konfiguratsioon pole kindel."
+#~ msgid "Games"
+#~ msgstr "Mängud"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Maailma loomine ebaõnnestus: Nimes esineb keelatud tähti"
+#, fuzzy
+#~ msgid "edit game"
+#~ msgstr "Muuda mängu"
-#~ msgid "Multiplayer"
-#~ msgstr "Mitmikmäng"
+#~ msgid "new game"
+#~ msgstr "uus mängu"
-#~ msgid "Advanced"
-#~ msgstr "Arenenud sätted"
+#~ msgid "EDIT GAME"
+#~ msgstr "MUUDA MÄNGU"
-#~ msgid "Show Public"
-#~ msgstr "Näita avalikke"
+#, fuzzy
+#~ msgid "Remove selected mod"
+#~ msgstr "Eemalda valitud muutus"
-#~ msgid "Show Favorites"
-#~ msgstr "Näita lemmikuid"
+#, fuzzy
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Lisama muutus"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Jäta IP lahter tühjaks et alustada LAN serverit."
+#~ msgid "Favorites:"
+#~ msgstr "Lemmikud:"
-#~ msgid "Create world"
-#~ msgstr "Loo maailm"
+#~ msgid "Name"
+#~ msgstr "Nimi"
-#~ msgid "Address required."
-#~ msgstr "IP on vajalkik."
+#~ msgid "Password"
+#~ msgstr "Parool"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Maailma kustutamine ebaõnnestus: Maailma pole valitud"
+#~ msgid "SETTINGS"
+#~ msgstr "Seaded"
-#~ msgid "Files to be deleted"
-#~ msgstr "Failid mida kustutada"
+#~ msgid "Preload item visuals"
+#~ msgstr "Lae asjade visuaale"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Maailma loomine ebaõnnestus: Mängu ei leitud"
+#, fuzzy
+#~ msgid "Finite Liquid"
+#~ msgstr "Löppev vedelik"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Maailma konfigureerimine ebaõnnestus: Pole midagi valitud"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Hoiatus: Mõned modifikatsioonid pole sätitud veel.\n"
+#~ "Need lülitatakse sisse kohe pärast sätete salvestamist."
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Kõigi maailma failide kustutamine ebaõnnestus"
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Hoiatus: Mõned konfigureeritud modifikatsioonid on kaotsi läinud.\n"
+#~ "Nende sätted kustutatakse kui salvestada konfiguratsioon."
#~ msgid ""
#~ "Default Controls:\n"
@@ -1081,81 +1267,65 @@ msgstr "Vale mängu ID."
#~ "- ESC: Menüü\n"
#~ "- T: Jututupa\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Hoiatus: Mõned konfigureeritud modifikatsioonid on kaotsi läinud.\n"
-#~ "Nende sätted kustutatakse kui salvestada konfiguratsioon."
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Hoiatus: Mõned modifikatsioonid pole sätitud veel.\n"
-#~ "Need lülitatakse sisse kohe pärast sätete salvestamist."
-
-#~ msgid "Exit to OS"
-#~ msgstr "Välju mängust"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Kõigi maailma failide kustutamine ebaõnnestus"
-#~ msgid "Exit to Menu"
-#~ msgstr "Välju menüüsse"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Maailma konfigureerimine ebaõnnestus: Pole midagi valitud"
-#~ msgid "Sound Volume"
-#~ msgstr "Hääle volüüm"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Maailma loomine ebaõnnestus: Mängu ei leitud"
-#~ msgid "Change Password"
-#~ msgstr "Vaheta parooli"
+#~ msgid "Files to be deleted"
+#~ msgstr "Failid mida kustutada"
-#~ msgid "Continue"
-#~ msgstr "Jätka"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Maailma kustutamine ebaõnnestus: Maailma pole valitud"
-#~ msgid "You died."
-#~ msgstr "Sa surid."
+#~ msgid "Address required."
+#~ msgstr "IP on vajalkik."
-#, fuzzy
-#~ msgid "Finite Liquid"
-#~ msgstr "Löppev vedelik"
+#~ msgid "Create world"
+#~ msgstr "Loo maailm"
-#~ msgid "Preload item visuals"
-#~ msgstr "Lae asjade visuaale"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Jäta IP lahter tühjaks et alustada LAN serverit."
-#~ msgid "SETTINGS"
-#~ msgstr "Seaded"
+#~ msgid "Show Favorites"
+#~ msgstr "Näita lemmikuid"
-#~ msgid "Password"
-#~ msgstr "Parool"
+#~ msgid "Show Public"
+#~ msgstr "Näita avalikke"
-#~ msgid "Name"
-#~ msgstr "Nimi"
+#~ msgid "Advanced"
+#~ msgstr "Arenenud sätted"
-#~ msgid "Favorites:"
-#~ msgstr "Lemmikud:"
+#~ msgid "Multiplayer"
+#~ msgstr "Mitmikmäng"
-#, fuzzy
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Lisama muutus"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Maailma loomine ebaõnnestus: Nimes esineb keelatud tähti"
-#, fuzzy
-#~ msgid "Remove selected mod"
-#~ msgstr "Eemalda valitud muutus"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Hoiatus: Konfiguratsioon pole kindel."
-#~ msgid "EDIT GAME"
-#~ msgstr "MUUDA MÄNGU"
+#~ msgid "Configuration saved. "
+#~ msgstr "Konfiguratsioon salvestatud. "
-#~ msgid "new game"
-#~ msgstr "uus mängu"
+#~ msgid "is required by:"
+#~ msgstr "Seda vajavad:"
-#, fuzzy
-#~ msgid "edit game"
-#~ msgstr "Muuda mängu"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr ""
+#~ "Vasak hiireklõps: Liiguta kõiki asju, Parem hiireklõps: Liiguta üksikut "
+#~ "asja"
-#~ msgid "Games"
-#~ msgstr "Mängud"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anisotroopne Filtreerimine"
-#~ msgid "GAMES"
-#~ msgstr "MÄNGUD"
+#~ msgid "Mip-Mapping"
+#~ msgstr "Väga hea kvaliteet"
#, fuzzy
-#~ msgid "Game Name"
-#~ msgstr "Mäng"
+#~ msgid "Downloading"
+#~ msgstr "Alla"
diff --git a/po/fr/minetest.po b/po/fr/minetest.po
index 8b65af0ff..b9666ccdf 100644
--- a/po/fr/minetest.po
+++ b/po/fr/minetest.po
@@ -7,68 +7,86 @@ msgid ""
msgstr ""
"Project-Id-Version: 0.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2014-12-14 17:34+0100\n"
-"Last-Translator: Calinou <calinou@opmbx.org>\n"
-"Language-Team: Français <>\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-07-17 22:49+0200\n"
+"Last-Translator: Jean-Patrick G. <jeanpatrick.guerrero@gmail.com>\n"
+"Language-Team: French "
+"<https://hosted.weblate.org/projects/minetest/minetest/fr/>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
-"X-Generator: Weblate 1.7-dev\n"
+"X-Generator: Weblate 2.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr "Une erreur est survenue avec un script Lua, comme un mod :"
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr "Une erreur est survenue :"
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "OK"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Chargement..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+"Rechargez la liste des serveurs publics et vérifiez votre connexion Internet."
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Sélectionner un monde :"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Cacher le jeu"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
-msgstr "Cacher le contenu de packs de mods"
+msgstr "Cacher le pack de mods"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod :"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Dépend de :"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Enregistrer"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Annuler"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Activer le pack de mods"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Désactiver le pack de mods"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "activé"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Tout activer"
@@ -98,7 +116,7 @@ msgstr "Vous n'avez pas de sous-jeux installés."
#: builtin/mainmenu/dlg_create_world.lua:69
msgid "Download one from minetest.net"
-msgstr ""
+msgstr "Téléchargez-en un depuis minetest.net"
#: builtin/mainmenu/dlg_create_world.lua:72
msgid "Warning: The minimal development test is meant for developers."
@@ -106,9 +124,9 @@ msgstr "Avertissement : le jeu minimal est fait pour les développeurs."
#: builtin/mainmenu/dlg_create_world.lua:73
msgid "Download a subgame, such as minetest_game, from minetest.net"
-msgstr "Téléchargez un sosu-jeu, comme minetest_game, depuis minetest.net"
+msgstr "Téléchargez un sous-jeu, comme minetest_game, depuis minetest.net"
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Le monde \"$1\" existe déjà"
@@ -118,11 +136,11 @@ msgstr "Nom du monde manquant ou aucun jeu sélectionné"
#: builtin/mainmenu/dlg_delete_mod.lua:26
msgid "Are you sure you want to delete \"$1\"?"
-msgstr "Êtes-vous sûr de supprimer \"$1\" ?"
+msgstr "Êtes-vous sûr de vouloir supprimer \"$1\" ?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Oui"
@@ -150,15 +168,15 @@ msgstr "Non"
msgid "Rename Modpack:"
msgstr "Renommer le pack de mods :"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Accepter"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Installer un mod : fichier : \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
@@ -166,17 +184,17 @@ msgstr ""
"\n"
"Installer un mod : type de fichier non supporté \"$1\" ou archive cassée"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "N'a pas pu installer $1 à $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Installer un mod : impossible de trouver un nom de dossier valide pour le "
"pack de mods $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Installer un mod : impossible de trouver le vrai nom du mod pour : $1"
@@ -184,47 +202,39 @@ msgstr "Installer un mod : impossible de trouver le vrai nom du mod pour : $1"
msgid "Unsorted"
msgstr "Non trié"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr "Rechercher"
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr "Télécharement"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr "veuillez patienter..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr "Téléchargement de $1, veuillez patienter..."
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr "Installé avec succès :"
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
-msgstr "Nom court :"
+msgstr "Nom :"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr "ok"
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Note"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Réinstaller"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Installer"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr "Fermer le store"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Page $1 sur $2"
@@ -232,15 +242,19 @@ msgstr "Page $1 sur $2"
msgid "Credits"
msgstr "Crédits"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Développeurs principaux"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Contributeurs actifs"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr "Anciens développeurs"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Anciens contributeurs"
@@ -266,11 +280,11 @@ msgstr "Renommer"
#: builtin/mainmenu/tab_mods.lua:95
msgid "Uninstall selected modpack"
-msgstr "Désinstaller le pack de mods sélectionné"
+msgstr "Désinstaller le pack de mods"
#: builtin/mainmenu/tab_mods.lua:106
msgid "Uninstall selected mod"
-msgstr "Désinstaller le mod sélectionné"
+msgstr "Désinstaller le mod"
#: builtin/mainmenu/tab_mods.lua:121
msgid "Select Mod File:"
@@ -281,13 +295,12 @@ msgid "Mods"
msgstr "Mods"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
-msgstr "Adresse / Port"
+msgid "Address / Port :"
+msgstr "Adresse / Port :"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
-msgstr "Nom / Mot de passe"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
+msgstr "Nom / Mot de passe :"
#: builtin/mainmenu/tab_multiplayer.lua:29
#: builtin/mainmenu/tab_simple_main.lua:30
@@ -295,7 +308,7 @@ msgid "Public Serverlist"
msgstr "Liste de serveurs publics"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Supprimer"
@@ -304,15 +317,30 @@ msgstr "Supprimer"
msgid "Connect"
msgstr "Rejoindre"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "Mode créatif"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "Dégâts activés"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "Combat activé"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Client"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nouveau"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Configurer"
@@ -320,17 +348,18 @@ msgstr "Configurer"
msgid "Start Game"
msgstr "Démarrer"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Sélectionner un monde :"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Mode créatif"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Activer les dégâts"
@@ -338,6 +367,10 @@ msgstr "Activer les dégâts"
msgid "Public"
msgstr "Public"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nom / Mot de passe"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr "Adresse à assigner"
@@ -350,147 +383,174 @@ msgstr "Port"
msgid "Server Port"
msgstr "Port du serveur"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr "Aucun monde créé ou sélectionné !"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Serveur"
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr "Arbres minimaux"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr "Arbres simples"
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr "Arbres détaillés"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "Aucun filtrage"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "Filtrage bilinéaire"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "Filtrage trilinéaire"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr "Sans MIP map"
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr "MIP mapping"
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr "MIP map + anisotropie"
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
-msgstr "Êtes-vous sûr de remettre à zéro votre monde solo ?"
+msgstr "Êtes-vous sûr de vouloir réinitialiser votre monde ?"
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr "Non !"
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Lumière douce"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
-msgstr "Activer les particules"
+msgstr "Particules"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Nuages 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-
-msgid "Fancy Trees"
-msgstr "Arbres détaillés"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Eau opaque"
-#: builtin/mainmenu/tab_settings.lua:144
-
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr "Verre connecté"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr "Redémarrez Minetest pour que le changement de pilote prenne effet"
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "Eclairage des nodes"
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtrage anisotrope"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "Textures :"
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtrage bilinéaire"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "Affichage :"
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtrage trilinéaire"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "Redémarrez Minetest pour que le changement du pilote prenne effet"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shaders"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Changer les touches"
-#: builtin/mainmenu/tab_settings.lua:167
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
-msgstr "Remettre le monde solo à zéro"
+msgstr "Réinitialiser le monde"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
-msgstr "Échelle des menus"
+msgstr "Taille des menus"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
-msgstr "Échelle appliquée aux menus :"
+msgstr "Taille appliquée aux menus : "
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
+#, fuzzy
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
+#, fuzzy
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr "Bump mapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
-msgstr "Générer des normal maps"
+msgstr "Normal mapping"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr "Occlusion parallaxe"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
-msgstr "Liquides animés"
+msgstr "Liquides mouvants"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
-msgstr "Feuilles animées"
+msgstr "Feuilles mouvantes"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
-msgstr "Plantes animées"
+msgstr "Plantes mouvantes"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Pour activer les shaders, le pilote OpenGL doit être utilisé."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Réglages"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr "Voler"
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr "Démarrer la partie solo"
-#: builtin/mainmenu/tab_simple_main.lua:72
-
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr "Configurer les mods"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr "Menu principal"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Jouer"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Solo"
@@ -506,39 +566,183 @@ msgstr "Pas d'information disponible"
msgid "Texturepacks"
msgstr "Packs de textures"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "Chargement des textures..."
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "Construction des shaders..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "Initialisation des nodes..."
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr "Initialisation des nodes"
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Textures d'objets..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "Terminé !"
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menu principal"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Nom du joueur trop long."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Erreur de connexion (perte de connexion ?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Pas de monde sélectionné et pas d'adresse fournie. Rien à faire."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "Le chemin du monde spécifié n'existe pas : "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Le jeu \" n'a pas pu être trouvé"
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "gamespec invalide."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
+#, fuzzy
msgid "needs_fallback_font"
msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Procéder"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Vous êtes mort."
+
+#: src/game.cpp:1073
msgid "Respawn"
-msgstr "Réapparaître"
+msgstr "Ressusciter"
+
+#: src/game.cpp:1092
+#, fuzzy
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Contrôles:\n"
+"- ZQSD : se déplacer\n"
+"- Espace : sauter/grimper\n"
+"- Maj. : marcher prudemment/descendre\n"
+"- A : lâcher l'objet en main\n"
+"- I : inventaire\n"
+"- Souris : tourner/regarder\n"
+"- Souris gauche : creuser/attaquer\n"
+"- Souris droite : placer/utiliser\n"
+"- Molette souris : sélectionner objet\n"
+"- T : discuter\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Continuer"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Changer mot de passe"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volume du son"
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "Changer les touches"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Quitter vers le menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Quitter le jeu"
-#: src/game.cpp:2250
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "Fermeture du jeu..."
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr "Création du serveur..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Création du client..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Résolution de l'adresse..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Connexion au serveur..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Définitions d'objets..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Définitions des blocs..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Média..."
-#: src/game.cpp:2267
-msgid " KB/s"
-msgstr " Ko/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr "Ko/s"
-#: src/game.cpp:2271
-msgid " MB/s"
-msgstr " Mo/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr "Mo/s"
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -546,14 +750,14 @@ msgstr ""
"\n"
"Voir debug.txt pour plus d'information."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Procéder"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr "Entrer "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "ok"
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr "Raccourcis"
@@ -564,480 +768,495 @@ msgstr "\"Use\" = descendre (escalade)"
#: src/guiKeyChangeMenu.cpp:180
msgid "Double tap \"jump\" to toggle fly"
-msgstr "Double appui sur \"saut\" pour voler"
+msgstr "Double-appui sur \"saut\" pour voler"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Touche déjà utilisée"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "appuyez sur une touche"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Avancer"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Reculer"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Gauche"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Droite"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Utiliser"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Sauter"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Marcher"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
-msgstr "Lâcher"
+msgstr "Jeter"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventaire"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
-msgstr "Messagerie"
+msgstr "Chatter"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Commande"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Console"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Voler"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Mode rapide"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr "Mode cinématique"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
-msgstr "Mode noclip"
+msgstr "Mode sans collision"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
-msgstr "Distance de rendu"
+msgstr "Distance d'affichage"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Imprimer stacks"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Ancien mot de passe"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Nouveau mot de passe"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Confirmer mot de passe"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Changer"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Les mots de passe ne correspondent pas !"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
-msgstr "Volume du son :"
+msgstr "Volume du son : "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Quitter"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Bouton gauche"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Bouton du milieu"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Bouton droit"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Bouton X 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Retour en arrière"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Vider"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Retour"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabulation"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Bouton X 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
-msgstr "Verr. maj."
+msgstr "Verr Maj"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Contrôle"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
+#, fuzzy
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pause"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
-msgstr "Majuscule"
+msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convertir"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Échap"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
+#, fuzzy
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
+#, fuzzy
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
+#, fuzzy
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Fin"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Origine"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Changer de mode"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Suivant"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Précédent"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Espace"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Bas"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Exécuter"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Imprimer"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Sélectionner"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Haut"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Aide"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insérer"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Capture d'écran"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Windows gauche"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Applications"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Pavé num. 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Pavé num. 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Windows droite"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Mise en veille"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Pavé num. 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Pavé num. 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Pavé num. 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Pavé num. 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Pavé num. 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Pavé num. 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Pavé num. *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Pavé num. +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Pavé num. -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Pavé num. /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Pavé num. 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Pavé num. 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
-msgstr "Verr. pavé num."
+msgstr "Verr Num"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Verr. défilement"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
-msgstr "Majuscule gauche"
+msgstr "Shift gauche"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
-msgstr "Majuscule droite"
+msgstr "Shift droite"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Contrôle gauche"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Menu gauche"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Contrôle droite"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Menu droite"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Virgule"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Moins"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Point"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attente"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "Vider sélection"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Écraser l'OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
+#, fuzzy
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
+#, fuzzy
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoomer"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menu principal"
+#~ msgid "Game Name"
+#~ msgstr "Nom du jeu"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr "Nom du joueur trop long."
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr : Impossible de copier le mod \"$1\" dans le jeu \"$2\""
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Erreur de connexion (perte de connexion ?)"
+#~ msgid "GAMES"
+#~ msgstr "JEUX"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Pas de monde sélectionné et pas d'adresse fournie. Rien à faire."
+#~ msgid "Games"
+#~ msgstr "Jeux"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr "Le chemin du monde spécifié n'existe pas :"
+#~ msgid "Mods:"
+#~ msgstr "Mods :"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Le jeu \" n'a pas pu être trouvé"
+#~ msgid "edit game"
+#~ msgstr "éditer le jeu"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "gamespec invalide."
+#~ msgid "new game"
+#~ msgstr "nouveau jeu"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr ""
-#~ "Clic gauche : déplacer tous les objets -- Clic droit : déplacer un objet"
+#~ msgid "EDIT GAME"
+#~ msgstr "MODIFIER LE JEU"
-#~ msgid "is required by:"
-#~ msgstr "est requis par :"
+#~ msgid "Remove selected mod"
+#~ msgstr "Supprimer le mod sélectionné"
-#~ msgid "Configuration saved. "
-#~ msgstr "Configuration enregistrée. "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Ajouter un mod"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Attention : configuration incorrecte. "
+#~ msgid "CLIENT"
+#~ msgstr "CLIENT"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr ""
-#~ "Impossible de créer le monde : le nom contient des caractères invalides"
+#~ msgid "Favorites:"
+#~ msgstr "Favoris :"
-#~ msgid "Multiplayer"
-#~ msgstr "Multijoueur"
+#~ msgid "START SERVER"
+#~ msgstr "DÉMARRER LE SERVEUR"
-#~ msgid "Advanced"
-#~ msgstr "Avancé"
+#~ msgid "Name"
+#~ msgstr "Nom"
-#~ msgid "Show Public"
-#~ msgstr "Voir les serveurs publics"
+#~ msgid "Password"
+#~ msgstr "Mot de passe"
-#~ msgid "Show Favorites"
-#~ msgstr "Voir les serveurs favoris"
+#~ msgid "SETTINGS"
+#~ msgstr "PARAMÈTRES"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Laisser l'adresse vide pour lancer un serveur local."
+#~ msgid "Preload item visuals"
+#~ msgstr "Précharger les objets"
-#~ msgid "Create world"
-#~ msgstr "Créer un monde"
+#~ msgid "Finite Liquid"
+#~ msgstr "Liquides limités"
-#~ msgid "Address required."
-#~ msgstr "Adresse requise."
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "PARTIE SOLO"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Impossible de supprimer le monde : rien n'est sélectionné"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "PACKS DE TEXTURES"
-#~ msgid "Files to be deleted"
-#~ msgstr "Fichiers à supprimer"
+#~ msgid "MODS"
+#~ msgstr "MODS"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Impossible de créer le monde : aucun jeu n'est présent"
+#~ msgid "Add mod:"
+#~ msgstr "Ajouter un mod :"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Impossible de configurer ce monde : aucune sélection active"
+#~ msgid "Local install"
+#~ msgstr "Installation locale"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Tous les fichiers du monde n'ont pu être supprimés"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Attention : certains mods ne sont pas encore configurés.\n"
+#~ "Ils seront activés par défaut quand vous enregistrerez la configuration. "
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Attention : certains mods configurés sont introuvables.\n"
+#~ "Leurs réglages seront effacés quand vous enregistrerez la configuration. "
+
+#~ msgid "Delete map"
+#~ msgstr "Supprimer la carte"
#~ msgid ""
#~ "Default Controls:\n"
@@ -1064,149 +1283,73 @@ msgstr "gamespec invalide."
#~ "- Échap : ce menu\n"
#~ "- T : discuter\n"
-#~ msgid "Delete map"
-#~ msgstr "Supprimer la carte"
-
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Attention : certains mods configurés sont introuvables.\n"
-#~ "Leurs réglages seront effacés quand vous enregistrerez la configuration. "
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Attention : certains mods ne sont pas encore configurés.\n"
-#~ "Ils seront activés par défaut quand vous enregistrerez la configuration. "
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Contrôles:\n"
-#~ "- ZQSD : se déplacer\n"
-#~ "- Espace : sauter/grimper\n"
-#~ "- Maj. : marcher prudemment/descendre\n"
-#~ "- A : lâcher l'objet en main\n"
-#~ "- I : inventaire\n"
-#~ "- Souris : tourner/regarder\n"
-#~ "- Souris gauche : creuser/attaquer\n"
-#~ "- Souris droite : placer/utiliser\n"
-#~ "- Molette souris : sélectionner objet\n"
-#~ "- T : discuter\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Quitter le jeu"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Quitter vers le menu"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volume du son"
-
-#~ msgid "Change Password"
-#~ msgstr "Changer mot de passe"
-
-#~ msgid "Continue"
-#~ msgstr "Continuer"
-
-#~ msgid "You died."
-#~ msgstr "Vous êtes mort."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Quitter le jeu..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Connexion au serveur..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Résolution de l'adresse..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Création du client..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Création du serveur..."
-
-#~ msgid "Loading..."
-#~ msgstr "Chargement..."
-
-#~ msgid "Local install"
-#~ msgstr "Installation locale"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Tous les fichiers du monde n'ont pu être supprimés"
-#~ msgid "Add mod:"
-#~ msgstr "Ajouter un mod :"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Impossible de configurer ce monde : aucune sélection active"
-#~ msgid "MODS"
-#~ msgstr "MODS"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Impossible de créer le monde : aucun jeu n'est présent"
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "PACKS DE TEXTURES"
+#~ msgid "Files to be deleted"
+#~ msgstr "Fichiers à supprimer"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "PARTIE SOLO"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Impossible de supprimer le monde : rien n'est sélectionné"
-#~ msgid "Finite Liquid"
-#~ msgstr "Liquides limités"
+#~ msgid "Address required."
+#~ msgstr "Adresse requise."
-#~ msgid "Preload item visuals"
-#~ msgstr "Précharger les objets"
+#~ msgid "Create world"
+#~ msgstr "Créer un monde"
-#~ msgid "SETTINGS"
-#~ msgstr "PARAMÈTRES"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Laisser l'adresse vide pour lancer un serveur local."
-#~ msgid "Password"
-#~ msgstr "Mot de passe"
+#~ msgid "Show Favorites"
+#~ msgstr "Voir les serveurs favoris"
-#~ msgid "Name"
-#~ msgstr "Nom"
+#~ msgid "Show Public"
+#~ msgstr "Voir les serveurs publics"
-#~ msgid "START SERVER"
-#~ msgstr "DÉMARRER LE SERVEUR"
+#~ msgid "Advanced"
+#~ msgstr "Avancé"
-#~ msgid "Favorites:"
-#~ msgstr "Favoris :"
+#~ msgid "Multiplayer"
+#~ msgstr "Multijoueur"
-#~ msgid "CLIENT"
-#~ msgstr "CLIENT"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr ""
+#~ "Impossible de créer le monde : le nom contient des caractères invalides"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Ajouter un mod"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Attention : configuration incorrecte. "
-#~ msgid "Remove selected mod"
-#~ msgstr "Supprimer le mod sélectionné"
+#~ msgid "Configuration saved. "
+#~ msgstr "Configuration enregistrée. "
-#~ msgid "EDIT GAME"
-#~ msgstr "MODIFIER LE JEU"
+#~ msgid "is required by:"
+#~ msgstr "est requis par :"
-#~ msgid "new game"
-#~ msgstr "nouveau jeu"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr ""
+#~ "Clic gauche : déplacer tous les objets -- Clic droit : déplacer un objet"
-#~ msgid "edit game"
-#~ msgstr "éditer le jeu"
+#~ msgid " MB/s"
+#~ msgstr " Mo/s"
-#~ msgid "Mods:"
-#~ msgstr "Mods :"
+#~ msgid " KB/s"
+#~ msgstr " Ko/s"
-#~ msgid "Games"
-#~ msgstr "Jeux"
+#~ msgid "Fly mode"
+#~ msgstr "Voler"
-#~ msgid "GAMES"
-#~ msgstr "JEUX"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Filtrage anisotrope"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr : Impossible de copier le mod \"$1\" dans le jeu \"$2\""
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-mapping"
-#~ msgid "Game Name"
-#~ msgstr "Nom du jeu"
+#~ msgid "Downloading"
+#~ msgstr "Téléchargement"
diff --git a/po/hu/minetest.po b/po/hu/minetest.po
index d49bc782b..3db7b54e9 100644
--- a/po/hu/minetest.po
+++ b/po/hu/minetest.po
@@ -7,88 +7,103 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2013-08-11 03:05+0200\n"
-"Last-Translator: Sasikaa Lacikaa <sasikaa@gmail.com>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-08-05 13:29+0200\n"
+"Last-Translator: Kisbenedek Márton <martonkisbenedek@gmail.com>\n"
+"Language-Team: Hungarian "
+"<https://hosted.weblate.org/projects/minetest/minetest/hu/>\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Weblate 1.4-dev\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 2.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr "Hiba történt:"
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
+msgstr "OK"
+
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Betöltés..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
msgstr ""
+"Próbáld újra engedélyezni a nyilvános szerverlistát és ellenőrizd az "
+"internetkapcsolatot."
-#: builtin/mainmenu/dlg_config_world.lua:26
-#, fuzzy
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
-msgstr "Világ kiválasztása:"
+msgstr "Világ:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
-#, fuzzy
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
-msgstr "Játék"
+msgstr "Játék elrejtése"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
-msgstr ""
+msgstr "Modpakk tartalom elrejtése"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
+#, fuzzy
msgid "Mod:"
-msgstr ""
+msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
-#, fuzzy
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
-msgstr "Attól függ:"
+msgstr "Függ ettől:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Mentés"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Mégse"
-#: builtin/mainmenu/dlg_config_world.lua:68
-#, fuzzy
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
-msgstr "Összes engedélyezve"
+msgstr "Modpakk engedélyezése"
-#: builtin/mainmenu/dlg_config_world.lua:70
-#, fuzzy
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
-msgstr "Összes tiltva"
+msgstr "Modpakk letiltása"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "Engedélyezve"
-#: builtin/mainmenu/dlg_config_world.lua:82
-#, fuzzy
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
-msgstr "Összes engedélyezve"
+msgstr "Összes engedélyezése"
#: builtin/mainmenu/dlg_create_world.lua:50
msgid "World name"
msgstr "Világ neve"
#: builtin/mainmenu/dlg_create_world.lua:53
+#, fuzzy
msgid "Seed"
-msgstr ""
+msgstr "Seed"
#: builtin/mainmenu/dlg_create_world.lua:56
msgid "Mapgen"
-msgstr ""
+msgstr "Térkép generátor"
#: builtin/mainmenu/dlg_create_world.lua:59
msgid "Game"
@@ -100,55 +115,54 @@ msgstr "Létrehozás"
#: builtin/mainmenu/dlg_create_world.lua:68
msgid "You have no subgames installed."
-msgstr ""
+msgstr "Nincsenek al-játékok telepítve."
#: builtin/mainmenu/dlg_create_world.lua:69
msgid "Download one from minetest.net"
-msgstr ""
+msgstr "Letöltés a minetest.net-ről"
#: builtin/mainmenu/dlg_create_world.lua:72
msgid "Warning: The minimal development test is meant for developers."
msgstr ""
+"Figyelmeztetés: A \"minimal development test\" csak fejlesztőknek ajánlott."
#: builtin/mainmenu/dlg_create_world.lua:73
msgid "Download a subgame, such as minetest_game, from minetest.net"
-msgstr ""
+msgstr "Tölts le egy al-játékot (pl. minetest_game) a minetest.net-ről"
-#: builtin/mainmenu/dlg_create_world.lua:97
-#, fuzzy
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
-msgstr "Nem sikerült a viág létrehozása: A világ neve már használva van"
+msgstr "\"$1\" nevű világ már létezik"
#: builtin/mainmenu/dlg_create_world.lua:116
msgid "No worldname given or no game selected"
-msgstr ""
+msgstr "Nincs megadva a világ neve, vagy nincs kiválasztva játék"
#: builtin/mainmenu/dlg_delete_mod.lua:26
msgid "Are you sure you want to delete \"$1\"?"
-msgstr ""
+msgstr "Biztosan törölni akarod: \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Igen"
#: builtin/mainmenu/dlg_delete_mod.lua:28
msgid "No of course not!"
-msgstr ""
+msgstr "Persze, hogy nem!"
#: builtin/mainmenu/dlg_delete_mod.lua:41
msgid "Modmgr: failed to delete \"$1\""
-msgstr ""
+msgstr "Modmgr: \"$1\" törlése meghiúsult"
#: builtin/mainmenu/dlg_delete_mod.lua:45
msgid "Modmgr: invalid modpath \"$1\""
-msgstr ""
+msgstr "Modmgr: érvénytelen mod útvonal: \"$1\""
#: builtin/mainmenu/dlg_delete_world.lua:24
-#, fuzzy
msgid "Delete World \"$1\"?"
-msgstr "Világ törlése"
+msgstr "\"$1\" világ törlése?"
#: builtin/mainmenu/dlg_delete_world.lua:26
msgid "No"
@@ -156,155 +170,148 @@ msgstr "Nem"
#: builtin/mainmenu/dlg_rename_modpack.lua:26
msgid "Rename Modpack:"
-msgstr ""
+msgstr "Modpakk átnevezése:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
-msgstr "Elfogadva"
+msgstr "Elfogad"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
-msgstr ""
+msgstr "Mod telepítés: fájl: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
+"\n"
+"Mod telepítés: \"$1\" nem támogatott fájltípus, vagy hibás archívum"
-#: builtin/mainmenu/modmgr.lua:363
-#, fuzzy
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
-msgstr "A világ betöltése közben hiba"
+msgstr "$1 telepítése meghiúsult"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
+"Mod telepítése: nem található megfelelő mappanév ehhez a modpakk-hoz: $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
-msgstr ""
+msgstr "Mod telepítése: nem található megfelelő mod név ehhez: $1"
#: builtin/mainmenu/store.lua:88
msgid "Unsorted"
-msgstr ""
+msgstr "Rendezetlen"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
-msgstr ""
+msgstr "Keresés"
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Le"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr ""
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr "$1 letöltése, kérlek várj..."
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
-msgstr ""
+msgstr "Sikeresen telepítve:"
-#: builtin/mainmenu/store.lua:163
-#, fuzzy
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
-msgstr "Világ neve"
+msgstr "Rövid név:"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
-msgstr ""
+msgstr "Értékelés"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
-msgstr ""
+msgstr "Újratelepítés"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
-msgstr ""
+msgstr "Telepítés"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
-msgstr ""
+msgstr "Ãruház bezárása"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
-msgstr ""
+msgstr "Oldal $1 ennyiből: $2"
#: builtin/mainmenu/tab_credits.lua:22
msgid "Credits"
-msgstr "Stáblista"
+msgstr "Készítők"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
-msgstr ""
+msgstr "Belső fejlesztők"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
-msgstr ""
+msgstr "Tevékeny hozzájárulók"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr "Korábbi belső fejlesztők"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
-msgstr ""
+msgstr "Korábbi hozzájárulók"
#: builtin/mainmenu/tab_mods.lua:30
msgid "Installed Mods:"
-msgstr ""
+msgstr "Telepített modok:"
#: builtin/mainmenu/tab_mods.lua:39
msgid "Online mod repository"
-msgstr ""
+msgstr "Online mod tároló"
#: builtin/mainmenu/tab_mods.lua:78
msgid "No mod description available"
-msgstr ""
+msgstr "Nincs elérhető mod leírás"
#: builtin/mainmenu/tab_mods.lua:82
msgid "Mod information:"
-msgstr ""
+msgstr "Mod ismertető:"
#: builtin/mainmenu/tab_mods.lua:93
msgid "Rename"
-msgstr ""
+msgstr "Ãtnevez"
#: builtin/mainmenu/tab_mods.lua:95
msgid "Uninstall selected modpack"
-msgstr ""
+msgstr "Kiválasztott modpakk törlése"
#: builtin/mainmenu/tab_mods.lua:106
msgid "Uninstall selected mod"
-msgstr ""
+msgstr "Kiválasztott mod törlése"
#: builtin/mainmenu/tab_mods.lua:121
-#, fuzzy
msgid "Select Mod File:"
-msgstr "Világ kiválasztása:"
+msgstr "Mod fájl kiválasztása:"
#: builtin/mainmenu/tab_mods.lua:165
msgid "Mods"
-msgstr ""
+msgstr "Modok"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
-msgstr "Cím/Port"
+msgid "Address / Port :"
+msgstr "Cím / Port:"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
-msgstr "Név/jelszó"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
+msgstr "Név / jelszó:"
#: builtin/mainmenu/tab_multiplayer.lua:29
#: builtin/mainmenu/tab_simple_main.lua:30
-#, fuzzy
msgid "Public Serverlist"
-msgstr "Publikus Szerver Lista:"
+msgstr "Nyilvános szerverlista"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Törlés"
@@ -313,264 +320,470 @@ msgstr "Törlés"
msgid "Connect"
msgstr "Csatlakozás"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "Kreatív mód"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "Sérülés engedélyezve"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "PvP engedélyezve"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
-msgstr ""
+msgstr "Kliens"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Új"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Beállítás"
#: builtin/mainmenu/tab_server.lua:29
-#, fuzzy
msgid "Start Game"
-msgstr "Játék indítása / Csatlakozás"
+msgstr "Játék indítása"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Világ kiválasztása:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Kreatív mód"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Sérülés engedélyezése"
#: builtin/mainmenu/tab_server.lua:35
msgid "Public"
-msgstr "Publikus"
+msgstr "Nyilvános"
+
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Név/jelszó"
#: builtin/mainmenu/tab_server.lua:45
+#, fuzzy
msgid "Bind Address"
-msgstr ""
+msgstr "Bind Address"
#: builtin/mainmenu/tab_server.lua:47
msgid "Port"
-msgstr ""
+msgstr "Port"
#: builtin/mainmenu/tab_server.lua:51
msgid "Server Port"
-msgstr ""
+msgstr "Szerver port"
+
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr "Nincs létrehozva, vagy kiválasztva világ!"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
-msgstr ""
+msgstr "Szerver"
+
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr "ÃttetszÅ‘ levelek"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr "Egyszerű levelek"
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr "Szép levelek"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "Nincs szűrés"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "Bi-lineáris szűrés"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "Tri-lineáris szűrés"
+
+#: builtin/mainmenu/tab_settings.lua:43
+#, fuzzy
+msgid "No Mipmap"
+msgstr "No Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:44
+#, fuzzy
+msgid "Mipmap"
+msgstr "Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:45
+#, fuzzy
+msgid "Mipmap + Aniso. Filter"
+msgstr "Mipmap + Aniso. Filter"
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
-msgstr ""
+msgstr "Biztosan visszaállítod az egyjátékos világod?"
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
-msgstr ""
+msgstr "Nem!!!"
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Simított megvilágítás"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Részecskék engedélyezése"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
-msgstr "3D Felhők"
+msgstr "3D felhők"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Szép fák"
-
-#: builtin/mainmenu/tab_settings.lua:142
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
-msgstr "Ãtlátszó víz"
+msgstr "ÃttetszÅ‘ víz"
-#: builtin/mainmenu/tab_settings.lua:144
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
-msgstr "Csatlakozás"
-
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
+msgstr "Tiszta (csatlakozó) üveg"
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "Node kiemelés"
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Anzisztrópikus szűrés"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "Textúrázás:"
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Bi-Linear Szűrés"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "Renderelés:"
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Tri-Linear Szűrés"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "A driver változások életbe lépéséhez indítsd újra a Minetestet"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shaderek"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
-msgstr "Gomb választása"
+msgstr "Gombok változtatása"
-#: builtin/mainmenu/tab_settings.lua:167
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
-msgstr "Egyjátékos mód"
+msgstr "Egyjátékos világ visszaállítása"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
-msgstr ""
+msgstr "Felhasználói felület méretaránya"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
-msgstr ""
+msgstr "A méretarány alkalmazva a menü elemekre: "
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
+#, fuzzy
msgid "Touch free target"
-msgstr ""
+msgstr "Touch free target"
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
+#, fuzzy
msgid "Touchthreshold (px)"
-msgstr ""
+msgstr "Touchthreshold (px)"
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
-msgstr "Mip-mapping"
+msgstr "Bumpmapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
+#, fuzzy
msgid "Generate Normalmaps"
-msgstr ""
+msgstr "Generate Normalmaps"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
+#, fuzzy
msgid "Parallax Occlusion"
-msgstr ""
+msgstr "Parallax Occlusion"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
-msgstr ""
+msgstr "Hullámzó víz"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
-msgstr ""
+msgstr "Hullámzó levelek"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
-msgstr ""
+msgstr "Hullámzó növények"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
-msgstr ""
+msgstr "A shaderek engedélyezéséhez OpenGL driver használata szükséges."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Beállítások"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
-#, fuzzy
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
-msgstr "Egyjátékos mód"
+msgstr "Egyjátékos mód indítása"
-#: builtin/mainmenu/tab_simple_main.lua:72
-#, fuzzy
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
-msgstr "Beállítás"
+msgstr "Modok beállítása"
-#: builtin/mainmenu/tab_simple_main.lua:191
-#, fuzzy
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
-msgstr "Fő menü"
+msgstr "Főmenü"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Játék"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Egyjátékos mód"
#: builtin/mainmenu/tab_texturepacks.lua:49
msgid "Select texture pack:"
-msgstr ""
+msgstr "Textúra pakk kiválasztása:"
#: builtin/mainmenu/tab_texturepacks.lua:69
msgid "No information available"
-msgstr ""
+msgstr "Nincs elérhető információ"
#: builtin/mainmenu/tab_texturepacks.lua:114
msgid "Texturepacks"
-msgstr ""
+msgstr "Textúra pakkok"
+
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "Textúrák betöltése..."
-#: src/client.cpp:2726
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "Shaderek újraépítése..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "Csomópontok inicializálása..."
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr "Csomópontok inicializálása"
+
+#: src/client.cpp:1768
msgid "Item textures..."
-msgstr "Tárgy textúrák..."
+msgstr "Elem textúrák..."
+
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "Kész!"
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Főmenü"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Túl hosszú játékosnév."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Csatlakozási hiba (idő lejárt?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Nincs világ kiválasztva és nincs cím megadva. Nincs mit tenni."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "A megadott világ útvonala nem létezik: "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Nem lehet betölteni, vagy nem található játék \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Nem valós játék spec."
#: src/fontengine.cpp:70 src/fontengine.cpp:226
+#, fuzzy
msgid "needs_fallback_font"
-msgstr ""
+msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Folytatás"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Meghaltál."
+
+#: src/game.cpp:1073
msgid "Respawn"
-msgstr "Újra éledés"
+msgstr "Újraéledés"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+"Alapértelmezett irányítás:\n"
+"Nem látható menü:\n"
+"- egy érintés: gomb aktiválás\n"
+"- dupla érintés: helyez/használat\n"
+"- ujj csúsztatás: körbenéz\n"
+"Menü/Inventory látható:\n"
+"- dupla érintés (külső):\n"
+" -->bezár\n"
+"- stack, vagy slot érintése:\n"
+" --> stack mozgatása\n"
+"- érint&megfogás, érintés 2. ujjal\n"
+" --> egy elem slotba helyezése\n"
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Alapértelmezett irányítás:\n"
+"- WASD: Mozgás\n"
+"- Space: Ugrás/Mászás\n"
+"- Shift: Lopakodás/Lefele menés\n"
+"- Q: Tárgyak eldobása\n"
+"- I: Invertory\n"
+"- Egér: Forgás/Nézelődés\n"
+"- Egér Bal-gomb: Ãsás/Ãœtés\n"
+"- Egér jobb-gomb: Helyezés/Használat\n"
+"- Egér görgő: Tárgyak kiválasztása\n"
+"- T: Beszélgetés\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Tovább"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Jelszó változtatás"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Hangerő"
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "Gombok változtatása"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Kilépés a menübe"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Kilépés az OP-rendszerbe"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "Leállítás..."
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr "Szerver létrehozása..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Kliens létrehozása..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Cím feloldása..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Csatlakozás a szerverhez..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
-msgstr ""
+msgstr "Elem definíciók..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
-msgstr ""
+msgstr "Csomópont definíciók..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
-msgstr ""
+msgstr "Média..."
-#: src/game.cpp:2267
-msgid " KB/s"
-msgstr ""
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr "KiB/mp"
-#: src/game.cpp:2271
-msgid " MB/s"
-msgstr ""
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr "MiB/mp"
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
+"\n"
+"Részletekért tekintsd meg a debug.txt fájlt."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Elfogadva"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
-msgstr ""
+msgstr "Belépés "
+
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "Ok"
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
-"Gombkiosztás. (Amíg ez a menü fejlesztve van, el van távolítva a minetest."
-"conf-ból)"
+"Gombkiosztás. (Ha elfuserálod ezt a menüt, távolíts el néhány cuccot a "
+"minetest.conf-ból)"
#: src/guiKeyChangeMenu.cpp:165
msgid "\"Use\" = climb down"
@@ -578,558 +791,494 @@ msgstr "\"Használat\" = Lemászás"
#: src/guiKeyChangeMenu.cpp:180
msgid "Double tap \"jump\" to toggle fly"
-msgstr "Duplán nyomd meg az \"ugrás\" gombot ahhoz hogy repülj"
+msgstr "Az \"ugrás\" gomb duplán a repüléshez"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "A gomb már használatban van"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "Nyomj meg egy gombot"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
-msgstr "Vissza"
+msgstr "Előre"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
-msgstr "Előre"
+msgstr "Vissza"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Bal"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Jobb"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
-msgstr "Használni"
+msgstr "Használat"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Ugrás"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Lopakodás"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
-msgstr "Dobás"
+msgstr "Eldobás"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
+#, fuzzy
msgid "Inventory"
-msgstr "Invertory"
+msgstr "Inventory"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Beszélgetés"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Parancs"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konzol"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Repülés bekapcsolása"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Gyorsaság bekapcsolása"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Toggle Cinematic"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
-msgstr "váltás noclip-re"
+msgstr "Váltás noclip-re"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
-msgstr "Távolság választása"
+msgstr "Látótávolság választása"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Stacks nyomtatása"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Régi jelszó"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Új jelszó"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
-msgstr "Jelszó visszaigazolás"
+msgstr "Jelszó megerősítés"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Változtat"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
-msgstr "Nem eggyeznek a jelszavak!"
+msgstr "Nem egyeznek a jelszavak!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Hangerő: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Kilépés"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Bal gomb"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Középső gomb"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Jobb gomb"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X gomb 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Vissza"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Törlés"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabulátor"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X Gomb 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
-msgstr "Kapital"
+msgstr "Nagybetű"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
+#, fuzzy
msgid "Control"
-msgstr "Controll"
+msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
+#, fuzzy
msgid "Kana"
-msgstr "Kanaa"
+msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menü"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
-msgstr "Pillanat-álj"
+msgstr "Szünet"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
+#, fuzzy
msgid "Shift"
-msgstr "váltás"
+msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Konvertálás"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Kilépés"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Befejezés"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junjaa"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanjii"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nem konvertált"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Vége"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Otthon"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Mód váltás"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Következő"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
-msgstr "Priorr"
+msgstr "Elsődleges"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
-msgstr "Hely"
+msgstr "Szóköz"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Le"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
-msgstr "Tömörít"
+msgstr "Végrehajt"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Nyomtat"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Kiválaszt"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Fel"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Segítség"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Beilleszt"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
-msgstr "Verzió"
+msgstr "Pillanatkép"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Bal Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
-msgstr "Prog"
+msgstr "Alkalmazások"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numerikus billentyű 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numerikus billentyű 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Jobb Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Alvás"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numerikus billentyű 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numerikus billentyű 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numerikus billentyű 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numerikus billentyű 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numerikus billentyű 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numerikus billentyű 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numerikus billentyű *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numerikus billentyű +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numerikus billentyű -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numerikus billentyű /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numerikus billentyű 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numerikus billentyű 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
-msgstr "Numerikus lock"
+msgstr "Numlock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
-msgstr "Skroll Lock"
+msgstr "ScrollLock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Bal Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Jobb Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Bal Controll"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Bal menü"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
-msgstr "Jobb Controll"
+msgstr "Jobb Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Jobb menü"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Vessző"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
-msgstr "Minusz"
+msgstr "Mínusz"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
-msgstr "tizedes"
+msgstr "Pont"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plusz"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
+#, fuzzy
msgid "Attn"
-msgstr "Attnn"
+msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
+#, fuzzy
msgid "CrSel"
-msgstr "CrSell"
+msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
+#, fuzzy
msgid "Erase OEF"
-msgstr "Erase OEFF"
+msgstr "Erase OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
+#, fuzzy
msgid "ExSel"
-msgstr "ExSell"
+msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
+#, fuzzy
msgid "OEM Clear"
-msgstr "OEM Clearr"
+msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
+#, fuzzy
msgid "PA1"
-msgstr "PA11"
+msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Nagyítás"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Fő menü"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Csatlakozási hiba (Idő lejárt?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Nincs kiválasztott világ, nincs cím. Nincs mit tenni."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Nem található, vagy nem betöltött játék \""
+#, fuzzy
+#~ msgid "Game Name"
+#~ msgstr "Játék"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Nem valós játék spec."
+#, fuzzy
+#~ msgid "Games"
+#~ msgstr "Játék"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "Ball gomb: Tárgyak mozgatása, Jobb gomb: egy tárgyat mozgat"
+#~ msgid "Favorites:"
+#~ msgstr "Kedvencek:"
-#~ msgid "is required by:"
-#~ msgstr "kell neki:"
+#, fuzzy
+#~ msgid "Password"
+#~ msgstr "Régi jelszó"
-#~ msgid "Configuration saved. "
-#~ msgstr "Beállítások mentve. "
+#~ msgid "Preload item visuals"
+#~ msgstr "Előretöltött tárgy láthatóság"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Figyelem: A beállítások nem egyformák. "
+#, fuzzy
+#~ msgid "Finite Liquid"
+#~ msgstr "Végtelen folyadék"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Nem sikerült a világ létrehozása: A névben nem jó karakterek vannak"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Hiba az összes világ törlése közben"
-#~ msgid "Multiplayer"
-#~ msgstr "Többjátékos mód"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Nem sikerült a világ beállítása: Nincs kiválasztva"
-#~ msgid "Advanced"
-#~ msgstr "Haladó"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Nem sikerült a világot létrehozni: Nem található a játék"
-#~ msgid "Show Public"
-#~ msgstr "Publikus mutatása"
+#~ msgid "Files to be deleted"
+#~ msgstr "A fájl törölve lett"
-#~ msgid "Show Favorites"
-#~ msgstr "Kedvencek mutatása"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Nem törölhető a világ: Nincs kiválasztva"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Hagyd el a nevét, hogy helyi szervert indíts."
+#~ msgid "Address required."
+#~ msgstr "Cím szükséges."
#~ msgid "Create world"
#~ msgstr "Világ létrehozása"
-#~ msgid "Address required."
-#~ msgstr "Cím szükséges."
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Hagyd el a nevét, hogy helyi szervert indíts."
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Nem törölhető a világ: Nincs kiválasztva"
+#~ msgid "Show Favorites"
+#~ msgstr "Kedvencek mutatása"
-#~ msgid "Files to be deleted"
-#~ msgstr "A fájl törölve lett"
+#~ msgid "Show Public"
+#~ msgstr "Publikus mutatása"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Nem sikerült a világot létrehozni: Nem található a játék"
+#~ msgid "Advanced"
+#~ msgstr "Haladó"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Nem sikerült a világ beállítása: Nincs kiválasztva"
+#~ msgid "Multiplayer"
+#~ msgstr "Többjátékos mód"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Hiba az összes világ törlése közben"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Nem sikerült a világ létrehozása: A névben nem jó karakterek vannak"
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Alap beállítások:\n"
-#~ "- WASD: Mozgás\n"
-#~ "- Space: Ugrás/Mászás\n"
-#~ "- Shift: Lopakodás/Lefele menés\n"
-#~ "- Q: Tárgyak eldobása\n"
-#~ "- I: Invertory\n"
-#~ "- Egér: Forgás/Nézelődés\n"
-#~ "- Egér Bal-gomb: Ãsás/ütés\n"
-#~ "- Egér jobb-gomb: elhejezés/használat\n"
-#~ "- Egér görgő: Tárgyak kiválasztása\n"
-#~ "- T: Beszélgetés\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Kilépés az OP-rendszerbe"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Kilépés a menübe"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Hangerő"
-
-#~ msgid "Change Password"
-#~ msgstr "Jelszó változtatás"
-
-#~ msgid "Continue"
-#~ msgstr "Tovább"
-
-#~ msgid "You died."
-#~ msgstr "Meghaltál."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Dolog leállítása..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Csatlakozás a szerverhez..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Cím létrehozása..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Kliens létrehozása..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Szerver létrehozása..."
-
-#~ msgid "Loading..."
-#~ msgstr "Betöltés..."
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Figyelem: A beállítások nem egyformák. "
-#, fuzzy
-#~ msgid "Finite Liquid"
-#~ msgstr "Végtelen folyadék"
+#~ msgid "Configuration saved. "
+#~ msgstr "Beállítások mentve. "
-#~ msgid "Preload item visuals"
-#~ msgstr "Előretöltött tárgy láthatóság"
+#~ msgid "is required by:"
+#~ msgstr "kell neki:"
-#, fuzzy
-#~ msgid "Password"
-#~ msgstr "Régi jelszó"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "Ball gomb: Tárgyak mozgatása, Jobb gomb: egy tárgyat mozgat"
-#~ msgid "Favorites:"
-#~ msgstr "Kedvencek:"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anzisztrópikus szűrés"
-#, fuzzy
-#~ msgid "Games"
-#~ msgstr "Játék"
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-mapping"
#, fuzzy
-#~ msgid "Game Name"
-#~ msgstr "Játék"
+#~ msgid "Downloading"
+#~ msgstr "Le"
diff --git a/po/id/minetest.po b/po/id/minetest.po
index fd6cba2c3..1cacddc2e 100644
--- a/po/id/minetest.po
+++ b/po/id/minetest.po
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2014-10-30 19:27+0700\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-03-02 00:21+0700\n"
"Last-Translator: Muhammad Rifqi Priyo Susanto "
"<muhammadrifqipriyosusanto@gmail.com>\n"
"Language-Team: Bahasa Indonesia <>\n"
@@ -18,61 +18,77 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Oke"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr ""
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Dunia:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr ""
"Sembunyikan\n"
"permainan"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
"Sembunyikan\n"
"konten pm"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Bergantung pada:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Simpan"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Batal"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Aktifkan PM"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Nonaktifkan PM"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "diaktifkan"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Aktifkan semua"
@@ -98,21 +114,21 @@ msgstr "Buat"
#: builtin/mainmenu/dlg_create_world.lua:68
msgid "You have no subgames installed."
-msgstr ""
+msgstr "Kamu tidak punya sub-permainan terpasang"
#: builtin/mainmenu/dlg_create_world.lua:69
msgid "Download one from minetest.net"
-msgstr ""
+msgstr "Unduh satu dari minetest.net"
#: builtin/mainmenu/dlg_create_world.lua:72
msgid "Warning: The minimal development test is meant for developers."
-msgstr ""
+msgstr "Peringatan: minimal development test ditujukan untuk pengembang."
#: builtin/mainmenu/dlg_create_world.lua:73
msgid "Download a subgame, such as minetest_game, from minetest.net"
-msgstr ""
+msgstr "Unduh sebuah sub-permainan, seperti minetest_game, dari minetest.net"
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Dunia bernama \"$1\" telah ada"
@@ -126,7 +142,7 @@ msgstr "Kamu yakin ingin menghapus \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ya"
@@ -136,11 +152,11 @@ msgstr "Tentu tidak!"
#: builtin/mainmenu/dlg_delete_mod.lua:41
msgid "Modmgr: failed to delete \"$1\""
-msgstr "Manajer mod: gagal untuk menghapus \"$1\""
+msgstr "Pengelola mod: gagal untuk menghapus \"$1\""
#: builtin/mainmenu/dlg_delete_mod.lua:45
msgid "Modmgr: invalid modpath \"$1\""
-msgstr "Manajer mod: jalur mod tidak sah"
+msgstr "Pengelola mod: jalur mod tidak sah"
#: builtin/mainmenu/dlg_delete_world.lua:24
msgid "Delete World \"$1\"?"
@@ -152,85 +168,78 @@ msgstr "Tidak"
#: builtin/mainmenu/dlg_rename_modpack.lua:26
msgid "Rename Modpack:"
-msgstr "Ganti Nama Paket Mod"
+msgstr "Ganti Nama Paket Mod:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
-msgstr "Terima"
+msgstr "Setuju"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
-msgstr "Pasang Mod: berkas: \"$1\""
+msgstr "Pemasangan Mod: berkas: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
"\n"
-"Pemasangan Mod: tipe berkas tidak didukung \"$1\""
+"Pemasangan Mod: tipe berkas tidak didukung \"$1\" atau kerusakan arsip"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Gagal untuk memasang $1 ke $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Pemasangan Mod: tidak dapat mencari nama folder yang sesuai untuk paket mod "
"$1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Pemasangan Mod: tidak dapat mencari nama yang sebenarnya dari: $1"
#: builtin/mainmenu/store.lua:88
msgid "Unsorted"
-msgstr ""
+msgstr "Tidak diurutkan"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
-msgstr ""
+msgstr "Cari"
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr ""
+#: builtin/mainmenu/store.lua:126
+#, fuzzy
+msgid "Downloading $1, please wait..."
+msgstr "mohon tunggu..."
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
-msgstr ""
+msgstr "Berhasil dipasang:"
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
-msgstr "Nama dunia"
+msgstr "Nama pendek:"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Peringkat"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Pasang ulang"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Pasang"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
-msgstr ""
+msgstr "Tutup toko"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Halaman $1 dari $2"
@@ -238,15 +247,20 @@ msgstr "Halaman $1 dari $2"
msgid "Credits"
msgstr "Penghargaan"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Pengembang Inti"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Kontributor Aktif"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Pengembang Inti"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Kontributor Sebelumnya"
@@ -287,12 +301,13 @@ msgid "Mods"
msgstr "Mod"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Alamat/Port"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nama/Kata sandi"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -301,7 +316,7 @@ msgid "Public Serverlist"
msgstr "Daftar Server Publik"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Hapus"
@@ -310,15 +325,33 @@ msgstr "Hapus"
msgid "Connect"
msgstr "Sambung"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Mode Kreatif"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "diaktifkan"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "diaktifkan"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Klien"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Baru"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Konfigurasi"
@@ -326,17 +359,18 @@ msgstr "Konfigurasi"
msgid "Start Game"
msgstr "Mulai Permainan"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Pilih Dunia:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Mode Kreatif"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Aktifkan Kerusakan"
@@ -344,162 +378,200 @@ msgstr "Aktifkan Kerusakan"
msgid "Public"
msgstr "Publik"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nama/Kata sandi"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
#: builtin/mainmenu/tab_server.lua:47
msgid "Port"
-msgstr ""
+msgstr "Port"
#: builtin/mainmenu/tab_server.lua:51
msgid "Server Port"
msgstr "Port Server"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Tidak ada nama atau permainan yang dipilih"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Server"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Air Buram"
+
+#: builtin/mainmenu/tab_settings.lua:22
+#, fuzzy
+msgid "Simple Leaves"
+msgstr "Daun Melambai"
+
#: builtin/mainmenu/tab_settings.lua:23
-msgid "Are you sure to reset your singleplayer world?"
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Pohon Indah"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
-msgid "No!!!"
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Bi-Linear Filtering"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Tri-Linear Filtering"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
+msgid "Are you sure to reset your singleplayer world?"
+msgstr "Yakin ingin mengaturulang dunia anda?"
+
+#: builtin/mainmenu/tab_settings.lua:102
+msgid "No!!!"
+msgstr "Tidak!!!"
+
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Pencahayaan Halus"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Aktifkan Partikel"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Awan 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Pohon mewah"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Air Buram"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
-msgstr "Sambung"
+msgstr "Kaca Tersambung"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Anisotropic Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Bi-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Tri-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "Mulai ulang minetest untuk beralih ke driver yang dipilih"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shaders"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Ubah tombol"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
-msgstr "Pemain Tunggal"
+msgstr "Atur ulang dunia pemain tunggal"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
-msgstr ""
+msgstr "Skala antarmuka"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
-msgstr ""
+msgstr "Faktor skala yang diatur untuk elemen menu: "
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
-msgstr ""
+msgstr "Bebas sentuhan"
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
-msgstr ""
+msgstr "Batas sentuhan (px)"
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
-msgstr "Mip-Mapping"
+msgstr "Bumpmapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
-msgstr ""
+msgstr "Gunakan Normalmaps"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
-msgstr ""
+msgstr "Parallax Occlusion"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
-msgstr ""
+msgstr "Air Berombak"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
-msgstr ""
+msgstr "Daun Melambai"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
-msgstr ""
+msgstr "Tanaman Berayun"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Untuk mengaktifkan shaders OpenGL driver harus digunakan."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Pengaturan"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
-msgstr "Pemain Tunggal"
+msgstr "Mulai Pemain Tunggal"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
-msgstr "Konfigurasi"
+msgstr "Konfigurasi mod"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
-msgstr "Menu Utama"
+msgstr "Beranda"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Mainkan"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Pemain Tunggal"
@@ -516,39 +588,175 @@ msgstr "Tidak ada informasi tersedia"
msgid "Texturepacks"
msgstr "Paket Tekstur"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Tekstur barang..."
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Tekstur barang..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menu Utama"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Nama pemain terlalu panjang."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Koneksi rusak (terlalu lama?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Tidak ada dunia yang dipilih dan tidak ada alamat yang diberikan."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "Jalur dunia yang diberikan tidak ada: "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Tidak dapat mencari atau memuat permainan \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Spesifikasi permainan tidak sah."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Lanjut"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr ""
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Bangkit"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr ""
+
+#: src/game.cpp:1129
+#, fuzzy
+msgid "Change Password"
+msgstr "Kata Sandi Baru"
+
+#: src/game.cpp:1134
+#, fuzzy
+msgid "Sound Volume"
+msgstr "Volume Suara: "
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Ubah tombol"
+
+#: src/game.cpp:1139
+#, fuzzy
+msgid "Exit to Menu"
+msgstr "Right Menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr ""
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definisi barang..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definisi node..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Media..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -556,18 +764,19 @@ msgstr ""
"\n"
"Cek debug.txt untuk detail."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Lanjut"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
-msgstr ""
+msgstr "Masuk "
+
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "oke"
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
-"Kontrol. (Jika menu ini kacau, hapus pengaturan kontrol dari minetest.conf)"
+"Pengaturan tombol. (Jika menu ini kacau, hapus pengaturan kontrol dari "
+"minetest.conf)"
#: src/guiKeyChangeMenu.cpp:165
msgid "\"Use\" = climb down"
@@ -575,555 +784,421 @@ msgstr "\"Pakai\" = turun"
#: src/guiKeyChangeMenu.cpp:180
msgid "Double tap \"jump\" to toggle fly"
-msgstr "Tekan ganda \"lompat\" untuk beralih terbang"
+msgstr ""
+"Tekan ganda \"lompat\" untuk\n"
+"beralih terbang"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Tombol telah terpakai"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "tekan tombol"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Maju"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Mundur"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Kiri"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Kanan"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Pakai"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Lompat"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Menyelinap"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Jatuhkan"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventaris"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Obrolan"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Perintah"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konsol"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Terbang"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Gerak cepat"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Gerak cepat"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Tembus blok"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Jarak pandang"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Cetak tumpukan"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Kata Sandi Lama"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Kata Sandi Baru"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Konfirmasi Kata Sandi"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Ubah"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Kata sandi tidak cocok!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Volume Suara: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Keluar"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Klik Kiri"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Klik Tengah"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Klik Kanan"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Tombol X 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Backspace"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Bersihkan"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Tombol X 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Ctrl"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Alt"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pause"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Mode Change"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Page Up"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Page Down"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Spasi"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Turun"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Execute"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Select"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Atas"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Help"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Snapshot"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Start Kiri"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Tombol Menu"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Start Kanan"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sleep"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Shift Kiri"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Shift Kanan"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Ctrl Kiri"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Left Menu"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Ctrl Kanan"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Right Menu"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Koma"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Kurang"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Titik"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Tambah"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Erase OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menu Utama"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Koneksi rusak (terlalu lama?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Tidak ada dunia yang dipilih dan tidak ada alamat yang diberikan."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Tidak dapat mencari atau memuat permainan \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Spesifikasi permainan tidak sah."
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Kontrol Bawaan:\n"
-#~ "- WASD: gerak\n"
-#~ "- Space: lompat/panjat\n"
-#~ "- Shift: menyelinap/turun\n"
-#~ "- Q: jatuhkan barang\n"
-#~ "- I: inventaris\n"
-#~ "- Mouse: tengok/lihat\n"
-#~ "- Mouse left: gali/pukul\n"
-#~ "- Mouse right: taruh/pakai\n"
-#~ "- Mouse wheel: pilih barang\n"
-#~ "- T: obrolan\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Akhiri Program"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Menu Utama"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volume Suara"
-
-#~ msgid "Change Password"
-#~ msgstr "Ubah Kata Sandi"
-
-#~ msgid "Continue"
-#~ msgstr "Lanjut"
-
-#~ msgid "You died."
-#~ msgstr "Kamu mati."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Mematikan..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Menyambung ke server..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Menyelesaikan alamat..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Membuat klien..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Membuat server...."
-
-#~ msgid "Loading..."
-#~ msgstr "Memuat..."
-
-#~ msgid "Local install"
-#~ msgstr "Pemasangan lokal"
-
-#~ msgid "Add mod:"
-#~ msgstr "Tambah mod:"
-
-#~ msgid "MODS"
-#~ msgstr "MOD"
-
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "PAKET TEKSTUR"
-
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "PEMAIN TUNGGAL"
-
-#~ msgid "Finite Liquid"
-#~ msgstr "Cairan Terbatas"
-
-#~ msgid "Preload item visuals"
-#~ msgstr "Pramuat gambaran barang"
-
-#~ msgid "SETTINGS"
-#~ msgstr "PENGATURAN"
-
-#~ msgid "Password"
-#~ msgstr "Kata Sandi"
-
-#~ msgid "Name"
-#~ msgstr "Nama"
-
-#~ msgid "START SERVER"
-#~ msgstr "MULAI SERVER"
-
-#~ msgid "Favorites:"
-#~ msgstr "Favorit:"
-
-#~ msgid "CLIENT"
-#~ msgstr "KLIEN"
-
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Tambah mod"
-
-#~ msgid "Remove selected mod"
-#~ msgstr "Cabut mod terpilih"
-
-#~ msgid "EDIT GAME"
-#~ msgstr "SUNTING PERMAINAN"
-
-#~ msgid "new game"
-#~ msgstr "permainan baru"
-
-#~ msgid "edit game"
-#~ msgstr "sunting permainan"
+#~ msgid " MB/s"
+#~ msgstr " MB/detik"
-#~ msgid "Mods:"
-#~ msgstr "Mod:"
+#~ msgid " KB/s"
+#~ msgstr " KB/detik"
-#~ msgid "Games"
-#~ msgstr "Permainan"
+#~ msgid "Fly mode"
+#~ msgstr "Mode terbang"
-#~ msgid "GAMES"
-#~ msgstr "PERMAINAN"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anisotropic Filtering"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr ""
-#~ "Manajer Permainan: Tidak dapat menyalin mod \"$1\" ke permainan \"$2\""
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapping"
-#~ msgid "Game Name"
-#~ msgstr "Nama Permainan"
+#~ msgid "Downloading"
+#~ msgstr "Mengunduh"
diff --git a/po/it/minetest.po b/po/it/minetest.po
index ef8ebc58e..ae9614944 100644
--- a/po/it/minetest.po
+++ b/po/it/minetest.po
@@ -7,67 +7,85 @@ msgid ""
msgstr ""
"Project-Id-Version: Minetest 0.4.9\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2014-06-01 19:05+0100\n"
-"Last-Translator: Enki <sohayl@users.sourceforge.net>\n"
-"Language-Team: \n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-07-07 14:43+0200\n"
+"Last-Translator: Michal ÄŒihaÅ™ <michal@cihar.com>\n"
+"Language-Team: Italian <https://hosted.weblate.org/projects/minetest/"
+"minetest/it/>\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: Poedit 1.5.4\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 2.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Ok"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Caricamento..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Mondo:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Nasc. gioco"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Nasc. cont. pacchetti"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod.:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Dipendenze:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Salvare"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Annullare"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Att. pacch."
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Disatt. pacch."
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "attivata"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Attivarli tutti"
@@ -107,7 +125,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Esiste già un mondo chiamato \"$1\""
@@ -123,7 +141,7 @@ msgstr "Siete certi di volere cancellare \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Sì"
@@ -151,15 +169,15 @@ msgstr "No"
msgid "Rename Modpack:"
msgstr "Rinominare il pacchetto moduli:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Accettare"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Installare un modulo: file: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -168,17 +186,17 @@ msgstr ""
"\n"
"Installare un modulo: tipo di file non supportato \"$1\""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "L'installazione di $1 in $2 è fallita"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Installare un modulo: impossibile trovare un nome di cartella appropriato "
"per il pacchetto moduli $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
"Installare un modulo: impossibile trovare il vero nome del modulo per: $1"
@@ -187,48 +205,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Nome del mondo"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Valutazione"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Reinstallare"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Installare"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Pagina $1 di $2"
@@ -236,15 +246,20 @@ msgstr "Pagina $1 di $2"
msgid "Credits"
msgstr "Riconoscimenti"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Sviluppatori principali"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Contributori attivi"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Sviluppatori principali"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Contributori precedenti"
@@ -285,12 +300,13 @@ msgid "Mods"
msgstr "Moduli"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Indirizzo/Porta"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nome/Password"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -299,7 +315,7 @@ msgid "Public Serverlist"
msgstr "Elenco dei server pubblici"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Cancellare"
@@ -308,15 +324,33 @@ msgstr "Cancellare"
msgid "Connect"
msgstr "Connettere"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Modalità creativa"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "attivata"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "attivata"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Client"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nuovo"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Configurare"
@@ -324,17 +358,18 @@ msgstr "Configurare"
msgid "Start Game"
msgstr "Avviare il gioco"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Selezionare il mondo:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Modalità creativa"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Abilitare il danno"
@@ -342,6 +377,10 @@ msgstr "Abilitare il danno"
msgid "Public"
msgstr "Pubblico"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nome/Password"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -354,150 +393,185 @@ msgstr ""
msgid "Server Port"
msgstr "Porta del server"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr ""
+"Non è stato fornito nessun nome del mondo oppure non è stato selezionato "
+"nessun gioco"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Server"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Acqua opaca"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Alberi migliori"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Filtro Bi-Lineare"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Filtro Tri-Lineare"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Illuminazione armoniosa"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Abilitare le particelle"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Nuvole 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Alberi migliori"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Acqua opaca"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Connettere"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtro anisotropico"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtro Bi-Lineare"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtro Tri-Lineare"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shader"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Cambiare i tasti"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Giocatore singolo"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip-Mapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "E' necessario usare i driver OpenGL per abilitare gli shader."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Impostazioni"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Giocatore singolo"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Configurare"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Menù principale"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Giocare"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Giocatore singolo"
@@ -514,39 +588,188 @@ msgstr "Nessuna informazione disponibile"
msgid "Texturepacks"
msgstr "Pacch. immagini"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Caricamento..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "Risoluzione dell'indirizzo..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Immagini degli oggetti..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menù principale"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Errore di connessione (scaduta?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+"Non è stato selezionato nessun mondo e non è stato fornito nessun indirizzo. "
+"Niente da fare."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Impossibile trovare o caricare il gioco \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Specifica del gioco non valida."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Procedere"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Siete morti."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Riapparire"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Controlli predefiniti:\n"
+"- WASD: muoversi\n"
+"- Space: saltare/arrampicarsi\n"
+"- Shift: strisciare/scendere\n"
+"- Q: scartare l'oggetto\n"
+"- I: inventario\n"
+"- Mouse: girarsi/guardare\n"
+"- Tasto sinistro del mouse: scavare/colpire\n"
+"- Tasto destro del mouse: posizionare/usare\n"
+"- Rotella del mouse: scegliere l'oggetto\n"
+"- T: chat\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Continuare"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Cambiare la password"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volume del suono"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Cambiare i tasti"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Tornare al menù"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Tornare al S.O."
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "Spegnimento della roba..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Creazione del server..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Creazione del client..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Risoluzione dell'indirizzo..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Connessione al server..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definizioni degli oggetti..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definizioni dei cubi..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Media..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -554,14 +777,14 @@ msgstr ""
"\n"
"Controllare debug.txt per i dettagli."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Procedere"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -576,554 +799,474 @@ msgstr "\"Usare\" = scendere"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Volare On/Off = due volte \"saltare\""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Tasto già in uso"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "premere il tasto"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Avanti"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Indietro"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Sinistra"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Destra"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Usare"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Saltare"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Strisciare"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Scartare"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventario"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Chat"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Comando"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Console"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Volare On/Off"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Correre On/Off"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Correre On/Off"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Fantasma On/Off"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Selez. ad area"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Stampa stack"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Vecchia password"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Nuova password"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Confermare la password"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Cambiare"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Le password non coincidono!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
-msgstr "Volume suono:"
+msgstr "Volume suono: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Uscire"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Tasto sinistro"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Tasto centrale"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Tasto destro"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Pulsante X 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Backspace"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Canc"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Invio"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Pulsante X 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Bloc Maiusc"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menù"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pausa"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Maiusc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Fine"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Fine"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Inizio"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Cambio di modalità"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Successivo"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Precedente"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Spazio"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Pag. giù"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Eseguire"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Stamp"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Selezionare"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Pag. su"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Aiuto"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Ins"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Istantanea"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Finestre a sinistra"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Applicazioni"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Tast. num. 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Tast. num. 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Finestre a destra"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sospensione"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Tast. num. 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Tast. num. 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Tast. num. 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Tast. num. 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Tast. num. 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Tast. num. 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Tast. num. *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Tast. num. +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Tast. num. -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Tast. num. /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Tast. num. 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Tast. num. 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Bloc Num"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Bloc Scorr"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Maiusc sx"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Maiusc dx"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Ctrl sx"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Menù a sinistra"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Ctrl dx"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Menù a destra"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Virgola"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Meno"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punto"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Più"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Erase OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menù principale"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Errore di connessione (scaduta?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-"Non è stato selezionato nessun mondo e non è stato fornito nessun indirizzo. "
-"Niente da fare."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Impossibile trovare o caricare il gioco \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Specifica del gioco non valida."
+#~ msgid "Game Name"
+#~ msgstr "Nome del gioco"
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Controlli predefiniti:\n"
-#~ "- WASD: muoversi\n"
-#~ "- Space: saltare/arrampicarsi\n"
-#~ "- Shift: strisciare/scendere\n"
-#~ "- Q: scartare l'oggetto\n"
-#~ "- I: inventario\n"
-#~ "- Mouse: girarsi/guardare\n"
-#~ "- Tasto sinistro del mouse: scavare/colpire\n"
-#~ "- Tasto destro del mouse: posizionare/usare\n"
-#~ "- Rotella del mouse: scegliere l'oggetto\n"
-#~ "- T: chat\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Tornare al S.O."
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Tornare al menù"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volume del suono"
-
-#~ msgid "Change Password"
-#~ msgstr "Cambiare la password"
-
-#~ msgid "Continue"
-#~ msgstr "Continuare"
-
-#~ msgid "You died."
-#~ msgstr "Siete morti."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Spegnimento della roba..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Connessione al server..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Risoluzione dell'indirizzo..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Creazione del client..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Creazione del server..."
-
-#~ msgid "Loading..."
-#~ msgstr "Caricamento..."
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gestore del gioco: impossibile il modulo \"$1\" nel gioco \"$2\""
-#~ msgid "Local install"
-#~ msgstr "Installazione locale"
+#~ msgid "GAMES"
+#~ msgstr "GIOCHI"
-#~ msgid "Add mod:"
-#~ msgstr "Aggiungere un modulo:"
+#~ msgid "Games"
+#~ msgstr "Giochi"
-#~ msgid "MODS"
-#~ msgstr "MODULI"
+#~ msgid "Mods:"
+#~ msgstr "Moduli:"
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "PACCH. DI IMM."
+#~ msgid "edit game"
+#~ msgstr "modificare il gioco"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "GIOC. SING."
+#~ msgid "new game"
+#~ msgstr "nuovo gioco"
-#~ msgid "Finite Liquid"
-#~ msgstr "Liquido limitato"
+#~ msgid "EDIT GAME"
+#~ msgstr "MODIFICARE IL GIOCO"
-#~ msgid "Preload item visuals"
-#~ msgstr "Precaricare le immagini"
+#~ msgid "Remove selected mod"
+#~ msgstr "Rimuovere il modulo selezionato"
-#~ msgid "SETTINGS"
-#~ msgstr "IMPOSTAZIONI"
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Aggiungere il modulo"
-#~ msgid "Password"
-#~ msgstr "Password"
+#~ msgid "CLIENT"
+#~ msgstr "CLIENT"
-#~ msgid "Name"
-#~ msgstr "Nome"
+#~ msgid "Favorites:"
+#~ msgstr "Preferiti:"
#~ msgid "START SERVER"
#~ msgstr "AVVIO SERVER"
-#~ msgid "Favorites:"
-#~ msgstr "Preferiti:"
+#~ msgid "Name"
+#~ msgstr "Nome"
-#~ msgid "CLIENT"
-#~ msgstr "CLIENT"
+#~ msgid "Password"
+#~ msgstr "Password"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Aggiungere il modulo"
+#~ msgid "SETTINGS"
+#~ msgstr "IMPOSTAZIONI"
-#~ msgid "Remove selected mod"
-#~ msgstr "Rimuovere il modulo selezionato"
+#~ msgid "Preload item visuals"
+#~ msgstr "Precaricare le immagini"
-#~ msgid "EDIT GAME"
-#~ msgstr "MODIFICARE IL GIOCO"
+#~ msgid "Finite Liquid"
+#~ msgstr "Liquido limitato"
-#~ msgid "new game"
-#~ msgstr "nuovo gioco"
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "GIOC. SING."
-#~ msgid "edit game"
-#~ msgstr "modificare il gioco"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "PACCH. DI IMM."
-#~ msgid "Mods:"
-#~ msgstr "Moduli:"
+#~ msgid "MODS"
+#~ msgstr "MODULI"
-#~ msgid "Games"
-#~ msgstr "Giochi"
+#~ msgid "Add mod:"
+#~ msgstr "Aggiungere un modulo:"
-#~ msgid "GAMES"
-#~ msgstr "GIOCHI"
+#~ msgid "Local install"
+#~ msgstr "Installazione locale"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gestore del gioco: impossibile il modulo \"$1\" nel gioco \"$2\""
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Filtro anisotropico"
-#~ msgid "Game Name"
-#~ msgstr "Nome del gioco"
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapping"
diff --git a/po/ja/minetest.po b/po/ja/minetest.po
index 910be7826..fb68d7304 100644
--- a/po/ja/minetest.po
+++ b/po/ja/minetest.po
@@ -1,726 +1,1176 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-11-23 17:37+0100\n"
-"PO-Revision-Date: 2014-12-28 15:48+0900\n"
-"Last-Translator: Rui Takeda <mrrst0914@gmail.com>\n"
-"Language-Team: Japanese\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-07-17 14:33+0200\n"
+"Last-Translator: Rui <mrrst0914@gmail.com>\n"
+"Language-Team: Japanese "
+"<https://hosted.weblate.org/projects/minetest/minetest/ja/>\n"
+"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Poedit 1.6.5\n"
-"Language: ja_JP\n"
-"X-Poedit-SourceCharset: UTF-8\n"
+"X-Generator: Weblate 2.4-dev\n"
-msgid "Game Name"
-msgstr "ゲームå"
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr "以下ã®Modã®Luaスクリプト内ã§ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ:"
-msgid "Create"
-msgstr "作æˆ"
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ:"
-msgid "Cancel"
-msgstr "キャンセル"
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
+msgid "Ok"
+msgstr "決定"
-msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-msgstr "Gamemgr: \"$1\"ã®Modã‚’ \"$2\" ã«ï½ºï¾‹ï¾Ÿï½°ã§ãã¾ã›ã‚“"
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "読ã¿è¾¼ã¿ä¸­..."
-msgid "Games"
-msgstr "ゲーム"
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr "インターãƒãƒƒãƒˆæŽ¥ç¶šã‚’確èªã—ã€å…¬é–‹ã‚µãƒ¼ãƒãƒ¼ãƒªã‚¹ãƒˆã‚’å†æœ‰åŠ¹åŒ–ã—ã¦ãã ã•ã„。"
+
+#: builtin/mainmenu/dlg_config_world.lua:29
+msgid "World:"
+msgstr "ワールド:"
-msgid "Mods:"
-msgstr "Mod:"
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
+msgid "Hide Game"
+msgstr "ゲームをéžè¡¨ç¤º"
-msgid "edit game"
-msgstr "ゲーム編集"
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
+msgid "Hide mp content"
+msgstr "Modパックをéžè¡¨ç¤º"
-msgid "new game"
-msgstr "æ–°è¦ã‚²ãƒ¼ãƒ "
+#: builtin/mainmenu/dlg_config_world.lua:49
+msgid "Mod:"
+msgstr "Modå:"
-msgid "Remove selected mod"
-msgstr "é¸æŠžã—ãŸModを削除"
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
+msgid "Depends:"
+msgstr "ä¾å­˜Mod:"
-msgid "Ok"
-msgstr "決定"
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
+msgid "Save"
+msgstr "ä¿å­˜"
+#: builtin/mainmenu/dlg_config_world.lua:55
+#: builtin/mainmenu/dlg_create_world.lua:64
+#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
+#: src/keycode.cpp:223
+msgid "Cancel"
+msgstr "キャンセル"
+
+#: builtin/mainmenu/dlg_config_world.lua:71
+msgid "Enable MP"
+msgstr "有効化"
+
+#: builtin/mainmenu/dlg_config_world.lua:73
+msgid "Disable MP"
+msgstr "無効化"
+
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
+msgid "enabled"
+msgstr "有効化"
+
+#: builtin/mainmenu/dlg_config_world.lua:85
+msgid "Enable all"
+msgstr "ã™ã¹ã¦æœ‰åŠ¹åŒ–"
+
+#: builtin/mainmenu/dlg_create_world.lua:50
msgid "World name"
msgstr "ワールドå"
+#: builtin/mainmenu/dlg_create_world.lua:53
msgid "Seed"
-msgstr "シード値"
+msgstr "Seed値"
+#: builtin/mainmenu/dlg_create_world.lua:56
msgid "Mapgen"
-msgstr "マップ生æˆ"
+msgstr "ワールドタイプ"
+#: builtin/mainmenu/dlg_create_world.lua:59
msgid "Game"
msgstr "ゲーム"
-msgid "Delete World \"$1\"?"
-msgstr "\"$1\"ã®ï¾œï½°ï¾™ï¾„゙を削除ã—ã¾ã™ã‹ï¼Ÿ"
+#: builtin/mainmenu/dlg_create_world.lua:63
+msgid "Create"
+msgstr "作æˆ"
+
+#: builtin/mainmenu/dlg_create_world.lua:68
+msgid "You have no subgames installed."
+msgstr "ゲームãŒã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•ã‚Œã¦ã„ã¾ã›ã‚“。"
+
+#: builtin/mainmenu/dlg_create_world.lua:69
+msgid "Download one from minetest.net"
+msgstr "minetest.netã‹ã‚‰å†ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã—ã¦ãã ã•ã„"
+
+#: builtin/mainmenu/dlg_create_world.lua:72
+msgid "Warning: The minimal development test is meant for developers."
+msgstr "警告: Minimal development testã¯é–‹ç™ºè€…ã®ãŸã‚ã®ã‚²ãƒ¼ãƒ ã§ã™ã€‚"
+
+#: builtin/mainmenu/dlg_create_world.lua:73
+msgid "Download a subgame, such as minetest_game, from minetest.net"
+msgstr "minetest.netã‹ã‚‰minetest_gameã®ã‚²ãƒ¼ãƒ ã‚’ダウンロードã—ã¦ãã ã•ã„"
+
+#: builtin/mainmenu/dlg_create_world.lua:99
+msgid "A world named \"$1\" already exists"
+msgstr "ワールドå\"$1\"ã¯ã™ã§ã«ä½¿ç”¨ã•ã‚Œã¦ã„ã¾ã™"
+
+#: builtin/mainmenu/dlg_create_world.lua:116
+msgid "No worldname given or no game selected"
+msgstr "ワールドåãŒå…¥åŠ›ã•ã‚Œã¦ã„ãªã„ã‹ã€ã‚²ãƒ¼ãƒ ãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
+#: builtin/mainmenu/dlg_delete_mod.lua:26
+msgid "Are you sure you want to delete \"$1\"?"
+msgstr "本当ã«\"$1\"を削除ã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:27
+#: builtin/mainmenu/dlg_delete_world.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "ã¯ã„"
+#: builtin/mainmenu/dlg_delete_mod.lua:28
+msgid "No of course not!"
+msgstr "ã„ã„ãˆï¼"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:41
+msgid "Modmgr: failed to delete \"$1\""
+msgstr "Modマãƒãƒ¼ã‚¸ãƒ£: \"$1\"ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:45
+msgid "Modmgr: invalid modpath \"$1\""
+msgstr "Modマãƒãƒ¼ã‚¸ãƒ£: Mod\"$1\"ã®å ´æ‰€ãŒä¸æ˜Žã§ã™"
+
+#: builtin/mainmenu/dlg_delete_world.lua:24
+msgid "Delete World \"$1\"?"
+msgstr "ワールド\"$1\"を削除ã—ã¾ã™ã‹ï¼Ÿ"
+
+#: builtin/mainmenu/dlg_delete_world.lua:26
msgid "No"
msgstr "ã„ã„ãˆ"
-msgid "A world named \"$1\" already exists"
-msgstr "\"$1\"ã¨ã„ã†åå‰ã®ï¾œï½°ï¾™ï¾„゙を作æˆã§ãã¾ã›ã‚“。åŒåã®ï¾œï½°ï¾™ï¾„゙ãŒå­˜åœ¨ã—ã¦ã„ã¾ã™"
+#: builtin/mainmenu/dlg_rename_modpack.lua:26
+msgid "Rename Modpack:"
+msgstr "åå‰ã‚’変更:"
-msgid "No worldname given or no game selected"
-msgstr "ワールドåãŒå…¥åŠ›ã•ã‚Œã¦ã„ãªã„ã‹ã€ï½¹ï¾žï½°ï¾‘ãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
+msgid "Accept"
+msgstr "決定"
-msgid "To enable shaders the OpenGL driver needs to be used."
-msgstr "シェーダーを有効ã«ã™ã‚‹ã«ã¯ã€OpenGLã®ï¾„゙ライバãŒå¿…è¦ã§ã™"
+#: builtin/mainmenu/modmgr.lua:344
+msgid "Install Mod: file: \"$1\""
+msgstr "Modインストール: ファイル: \"$1\""
-msgid "Address/Port"
-msgstr "アドレス/ãƒãƒ¼ãƒˆ"
+#: builtin/mainmenu/modmgr.lua:345
+msgid ""
+"\n"
+"Install Mod: unsupported filetype \"$1\" or broken archive"
+msgstr ""
+"\n"
+"Modインストール: ファイル\"$1\"ã¯éžå¯¾å¿œã®å½¢å¼ã‹ã€å£Šã‚Œã¦ã„ã¾ã™"
-msgid "Name/Password"
-msgstr "åå‰/パスワード"
+#: builtin/mainmenu/modmgr.lua:365
+msgid "Failed to install $1 to $2"
+msgstr "$2ã¸$1をインストールã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: builtin/mainmenu/modmgr.lua:368
+msgid "Install Mod: unable to find suitable foldername for modpack $1"
+msgstr "Modインストール: Modパック$1ã«é©ã—ãŸãƒ•ã‚©ãƒ«ãƒ€åを見ã¤ã‘ã‚‹ã“ã¨ãŒã§ãã¾ã›ã‚“ã§ã—ãŸ"
+
+#: builtin/mainmenu/modmgr.lua:388
+msgid "Install Mod: unable to find real modname for: $1"
+msgstr "Modインストール: $1ã®æœ¬æ¥ã®ModåãŒä¸æ˜Žã§ã™"
+
+#: builtin/mainmenu/store.lua:88
+msgid "Unsorted"
+msgstr "未分類"
+
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
+msgid "Search"
+msgstr "検索"
+
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr "$1をダウンロードã—ã¦ã„ã¾ã™ã€‚ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„..."
+
+#: builtin/mainmenu/store.lua:160
+msgid "Successfully installed:"
+msgstr "インストールãŒå®Œäº†ã—ã¾ã—ãŸ:"
+
+#: builtin/mainmenu/store.lua:162
+msgid "Shortname:"
+msgstr "çœç•¥å:"
+
+#: builtin/mainmenu/store.lua:472
+msgid "Rating"
+msgstr "評価"
+
+#: builtin/mainmenu/store.lua:497
+msgid "re-Install"
+msgstr "å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«"
+
+#: builtin/mainmenu/store.lua:499
+msgid "Install"
+msgstr "インストール"
+
+#: builtin/mainmenu/store.lua:518
+msgid "Close store"
+msgstr "é–‰ã˜ã‚‹"
+
+#: builtin/mainmenu/store.lua:526
+msgid "Page $1 of $2"
+msgstr "ページ $1 / $2"
+#: builtin/mainmenu/tab_credits.lua:22
+msgid "Credits"
+msgstr "クレジット"
+
+#: builtin/mainmenu/tab_credits.lua:31
+msgid "Core Developers"
+msgstr "開発者"
+
+#: builtin/mainmenu/tab_credits.lua:47
+msgid "Active Contributors"
+msgstr "開発å”力者"
+
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr "以å‰ã®é–‹ç™ºè€…"
+
+#: builtin/mainmenu/tab_credits.lua:59
+msgid "Previous Contributors"
+msgstr "以å‰ã®é–‹ç™ºå”力者"
+
+#: builtin/mainmenu/tab_mods.lua:30
+msgid "Installed Mods:"
+msgstr "インストール済ã¿ã®Mod:"
+
+#: builtin/mainmenu/tab_mods.lua:39
+msgid "Online mod repository"
+msgstr "オンラインã§Modを検索"
+
+#: builtin/mainmenu/tab_mods.lua:78
+msgid "No mod description available"
+msgstr "Modã®èª¬æ˜ŽãŒã‚ã‚Šã¾ã›ã‚“"
+
+#: builtin/mainmenu/tab_mods.lua:82
+msgid "Mod information:"
+msgstr "Modã®æƒ…å ±:"
+
+#: builtin/mainmenu/tab_mods.lua:93
+msgid "Rename"
+msgstr "åå‰ã‚’変更"
+
+#: builtin/mainmenu/tab_mods.lua:95
+msgid "Uninstall selected modpack"
+msgstr "é¸æŠžã—ãŸModパックを削除"
+
+#: builtin/mainmenu/tab_mods.lua:106
+msgid "Uninstall selected mod"
+msgstr "é¸æŠžã—ãŸModを削除"
+
+#: builtin/mainmenu/tab_mods.lua:121
+msgid "Select Mod File:"
+msgstr "Modファイルをé¸æŠž:"
+
+#: builtin/mainmenu/tab_mods.lua:165
+msgid "Mods"
+msgstr "Mod"
+
+#: builtin/mainmenu/tab_multiplayer.lua:23
+msgid "Address / Port :"
+msgstr "アドレスã¨ãƒãƒ¼ãƒˆ:"
+
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
+msgstr "åå‰ã¨ãƒ‘スワード:"
+
+#: builtin/mainmenu/tab_multiplayer.lua:29
+#: builtin/mainmenu/tab_simple_main.lua:30
msgid "Public Serverlist"
-msgstr "公開ã•ã‚Œã¦ã„るサーバーリスト"
+msgstr "公開サーãƒãƒ¼ãƒªã‚¹ãƒˆ"
+#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "削除"
+#: builtin/mainmenu/tab_multiplayer.lua:38
+#: builtin/mainmenu/tab_simple_main.lua:34
msgid "Connect"
msgstr "接続"
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "クリエイティブモード"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "ダメージ有効"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "PvP有効"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
+msgid "Client"
+msgstr "クライアント"
+
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "作æˆ"
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "設定"
+#: builtin/mainmenu/tab_server.lua:29
msgid "Start Game"
msgstr "ゲームスタート"
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
-msgstr "ワールドé¸æŠž:"
+msgstr "ワールドをé¸æŠž:"
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
-msgstr "クリエイティブモード"
+msgstr "クリエイティブモード"
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
-msgstr "ダメージ有効"
+msgstr "ダメージ有効"
+#: builtin/mainmenu/tab_server.lua:35
msgid "Public"
-msgstr "公開ã™ã‚‹"
+msgstr "公開サーãƒãƒ¼"
-msgid "Name"
-msgstr "åå‰"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "åå‰ã¨ãƒ‘スワード"
-msgid "Password"
-msgstr "パスワード"
+#: builtin/mainmenu/tab_server.lua:45
+msgid "Bind Address"
+msgstr "ãƒã‚¤ãƒ³ãƒ‰ã‚¢ãƒ‰ãƒ¬ã‚¹"
-msgid "Server Port"
+#: builtin/mainmenu/tab_server.lua:47
+msgid "Port"
msgstr "ãƒãƒ¼ãƒˆ"
-msgid "Fancy Trees"
-msgstr "ãã‚Œã„ãªæœ¨"
+#: builtin/mainmenu/tab_server.lua:51
+msgid "Server Port"
+msgstr "サーãƒãƒ¼ã®ãƒãƒ¼ãƒˆ"
+
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr "ワールドãŒä½œæˆã¾ãŸã¯é¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“ï¼"
+#: builtin/mainmenu/tab_server.lua:191
+msgid "Server"
+msgstr "サーãƒãƒ¼"
+
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr "ä¸é€æ˜Žãªè‘‰"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr "シンプルãªè‘‰"
+
+#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr "綺麗ãªè‘‰"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "フィルタ無ã—"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "ãƒã‚¤ãƒªãƒ‹ã‚¢ãƒ•ã‚£ãƒ«ã‚¿"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "トリリニアフィルタ"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr "ミップマップ無ã—"
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr "ミップマップ"
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr "異方性フィルタ"
+
+#: builtin/mainmenu/tab_settings.lua:98
+msgid "Are you sure to reset your singleplayer world?"
+msgstr "シングルプレイヤーã®ãƒ¯ãƒ¼ãƒ«ãƒ‰ã‚’リセットã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+
+#: builtin/mainmenu/tab_settings.lua:102
+msgid "No!!!"
+msgstr "ã„ã„ãˆï¼ï¼ï¼"
+
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
-msgstr "自然ãªå…‰"
+msgstr "滑らã‹ãªå…‰"
+
+#: builtin/mainmenu/tab_settings.lua:204
+msgid "Enable Particles"
+msgstr "パーティクル有効化"
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
-msgstr "立体ãªé›²"
+msgstr "3Dã®é›²"
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
-msgstr "ä¸é€æ˜Žãªæ°´é¢"
+msgstr "ä¸é€æ˜Žãªæ°´"
+
+#: builtin/mainmenu/tab_settings.lua:210
+msgid "Connected Glass"
+msgstr "ガラスを繋ã’ã‚‹"
-msgid "Mip-Mapping"
-msgstr "ï¾ï½¯ï¾Œï¾Ÿï¾ï½¯ï¾Œï¾Ÿ"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "ノードをãƒã‚¤ãƒ©ã‚¤ãƒˆ"
-msgid "Anisotropic Filtering"
-msgstr "異方性フィルタリï¾ï½¸ï¾ž"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "テクスãƒãƒ£ãƒªãƒ³ã‚°:"
-msgid "Bi-Linear Filtering"
-msgstr "バイリニアフィルタリï¾ï½¸ï¾ž"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "レンダリング:"
-msgid "Tri-Linear Filtering"
-msgstr "トリリニアフィルタリï¾ï½¸ï¾ž"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "ドライãƒãƒ¼ã‚’変更ã™ã‚‹ãŸã‚Minetestã‚’å†èµ·å‹•ã—ã¾ã™"
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
-msgstr "シェーダー"
+msgstr "シェーダー"
-msgid "Connected Glass"
-msgstr "ガラスを繋ã’ã‚‹"
+#: builtin/mainmenu/tab_settings.lua:233
+msgid "Change keys"
+msgstr "æ“作変更"
-msgid "Enable Particles"
-msgstr "破片ã®æœ‰åŠ¹åŒ–"
+#: builtin/mainmenu/tab_settings.lua:236
+msgid "Reset singleplayer world"
+msgstr "シングルプレイヤーã®ãƒ¯ãƒ¼ãƒ«ãƒ‰ã‚’リセット"
-msgid "Finite Liquid"
-msgstr "液体ã®åˆ¶é™"
+#: builtin/mainmenu/tab_settings.lua:240
+msgid "GUI scale factor"
+msgstr "メニューã®å¤§ãã•"
-msgid "Change keys"
-msgstr "キー割当ã¦å¤‰æ›´"
+#: builtin/mainmenu/tab_settings.lua:244
+msgid "Scaling factor applied to menu elements: "
+msgstr "メニューã®å¤§ãã•ã¨ã—ã¦è¨­å®šã™ã‚‹æ•°å€¤: "
-msgid "Play"
-msgstr "ゲームスタート"
+#: builtin/mainmenu/tab_settings.lua:250
+msgid "Touch free target"
+msgstr "タッãƒä½ç½®ã‚’自由ã«ã™ã‚‹"
-msgid "Select texture pack:"
-msgstr "テクスãƒãƒ£ãƒ‘ックをé¸æŠž:"
+#: builtin/mainmenu/tab_settings.lua:256
+msgid "Touchthreshold (px)"
+msgstr "タッãƒã®ã—ãã„値(ピクセル)"
-msgid "No information available"
-msgstr "情報ãŒã‚ã‚Šã¾ã›ã‚“"
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
+msgid "Bumpmapping"
+msgstr "ãƒãƒ³ãƒ—マッピング"
-msgid "Core Developers"
-msgstr "開発者"
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
+msgid "Generate Normalmaps"
+msgstr "ノーマルマップã®ç”Ÿæˆ"
-msgid "Active Contributors"
-msgstr "貢献者"
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
+msgid "Parallax Occlusion"
+msgstr "視差é®è”½ãƒžãƒƒãƒ”ング"
-msgid "Previous Contributors"
-msgstr "以å‰ã®è²¢çŒ®è€…"
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
+msgid "Waving Water"
+msgstr "æºã‚Œã‚‹æ°´"
-msgid "Singleplayer"
-msgstr "シングルプレイ"
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
+msgid "Waving Leaves"
+msgstr "æºã‚Œã‚‹è‘‰"
-msgid "Client"
-msgstr "クライアント"
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
+msgid "Waving Plants"
+msgstr "æºã‚Œã‚‹è‰èŠ±"
-msgid "Server"
-msgstr "マルãƒãƒ—レイ"
+#: builtin/mainmenu/tab_settings.lua:308
+msgid "To enable shaders the OpenGL driver needs to be used."
+msgstr "シェーダーを有効ã«ã™ã‚‹ã«ã¯OpenGLを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "設定"
-msgid "Texturepacks"
-msgstr "テクスãƒãƒ£ãƒ‘ック"
+#: builtin/mainmenu/tab_simple_main.lua:82
+msgid "Start Singleplayer"
+msgstr "シングルプレイ開始"
-msgid "Mods"
-msgstr "Mod"
+#: builtin/mainmenu/tab_simple_main.lua:83
+msgid "Config mods"
+msgstr "Mod設定"
-msgid "Credits"
-msgstr "クレジット"
+#: builtin/mainmenu/tab_simple_main.lua:201
+msgid "Main"
+msgstr "メイン"
-msgid "Installed Mods:"
-msgstr "インストール済ã¿ã®Mod:"
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
+msgid "Play"
+msgstr "プレイ"
-msgid "Online mod repository"
-msgstr "オンラインã§æ¤œç´¢"
+#: builtin/mainmenu/tab_singleplayer.lua:246
+msgid "Singleplayer"
+msgstr "シングルプレイヤー"
-msgid "No mod description available"
-msgstr "Modã®èª¬æ˜ŽãŒã‚ã‚Šã¾ã›ã‚“"
+#: builtin/mainmenu/tab_texturepacks.lua:49
+msgid "Select texture pack:"
+msgstr "テクスãƒãƒ£ãƒ‘ックをé¸æŠž:"
-msgid "Mod information:"
-msgstr "Modã®æƒ…å ±:"
+#: builtin/mainmenu/tab_texturepacks.lua:69
+msgid "No information available"
+msgstr "情報ãŒã‚ã‚Šã¾ã›ã‚“"
-msgid "Rename"
-msgstr "åå‰ã‚’変更"
+#: builtin/mainmenu/tab_texturepacks.lua:114
+msgid "Texturepacks"
+msgstr "テクスãƒãƒ£ãƒ‘ック"
-msgid "Uninstall selected modpack"
-msgstr "é¸æŠžã—ãŸModパックを削除"
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "テクスãƒãƒ£èª­ã¿è¾¼ã¿ä¸­..."
-msgid "Uninstall selected mod"
-msgstr "é¸æŠžã—ãŸModを削除"
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "シェーダー構築中..."
-msgid "Rename Modpack:"
-msgstr "Modパックã®åå‰ã‚’変更"
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "ノードã®è¨­å®šä¸­..."
-msgid "Accept"
-msgstr "了承"
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr "ノードを設定中"
-msgid "World:"
-msgstr "ワールド:"
+#: src/client.cpp:1768
+msgid "Item textures..."
+msgstr "テクスãƒãƒ£ã‚’設定中..."
-msgid "Hide Game"
-msgstr "内部Mod"
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "完了ï¼"
-msgid "Hide mp content"
-msgstr "Modパックã®ç°¡ç•¥åŒ–"
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "メインメニュー"
-msgid "Mod:"
-msgstr "Modå:"
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "åå‰ãŒé•·éŽãŽã¾ã™ã€‚"
-msgid "Depends:"
-msgstr "ä¾å­˜Mod:"
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "接続失敗(ã¾ãŸã¯ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆï¼‰"
-msgid "Save"
-msgstr "ä¿å­˜"
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "ワールドãŒé¸æŠžã•ã‚Œã¦ã„ãªã„アドレスã§ã™ã€‚続行ã§ãã¾ã›ã‚“。"
-msgid "Enable MP"
-msgstr "有効化"
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "ワールドãŒå­˜åœ¨ã—ã¾ã›ã‚“: "
-msgid "Disable MP"
-msgstr "無効化"
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "ゲーム\"ã®èª­ã¿è¾¼ã¿ãŒã§ãã¾ã›ã‚“"
-msgid "enabled"
-msgstr "有効化"
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "無効ãªgamespecã§ã™ã€‚"
-msgid "Enable all"
-msgstr "å…¨ã¦æœ‰åŠ¹åŒ–"
+#: src/fontengine.cpp:70 src/fontengine.cpp:226
+msgid "needs_fallback_font"
+msgstr "yes"
-msgid "Modmgr: failed to delete \"$1\""
-msgstr "Modmgr: \"$1\"ã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ"
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "決定"
-msgid "Modmgr: invalid modpath \"$1\""
-msgstr "Modmgr: \"$1\"ã¯ç„¡åŠ¹ãªModパスã§ã™"
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "æ­»ã«ã¾ã—ãŸã€‚"
-msgid "Are you sure you want to delete \"$1\"?"
-msgstr "\"$1\"を削除ã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+#: src/game.cpp:1073
+msgid "Respawn"
+msgstr "リスãƒãƒ¼ãƒ³"
-msgid "No of course not!"
-msgstr "é•ã„ã¾ã™ï¼"
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+"基本æ“作:\n"
+"タッãƒæ“作:\n"
+"- シングルタップ: ブロックã®ç ´å£Š\n"
+"- ダブルタップ: 設置ã€ä½¿ç”¨\n"
+"- スライド: 見回ã™\n"
+"メニューã€ã‚¤ãƒ³ãƒ™ãƒ³ãƒˆãƒªã®æ“作:\n"
+"- メニューã®å¤–をダブルタップ:\n"
+" --> é–‰ã˜ã‚‹\n"
+"- アイテムをタッãƒ:\n"
+" --> アイテムã®ç§»å‹•\n"
+"- タッãƒã—ã¦ãƒ‰ãƒ©ãƒƒã‚°ã€äºŒæœ¬æŒ‡ã‚¿ãƒƒãƒ—:\n"
+" --> アイテムを一ã¤ã‚¹ãƒ­ãƒƒãƒˆã«ç½®ã\n"
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"基本æ“作:\n"
+"- WASD: 移動\n"
+"- スペース: ジャンプ、登る\n"
+"- Shift: スニーク、é™ã‚Šã‚‹\n"
+"- Q: アイテムをè½ã¨ã™\n"
+"- I: インベントリ\n"
+"- マウス: 見回ã™\n"
+"- 左クリック: 破壊ã€ãƒ‘ンãƒ\n"
+"- å³ã‚¯ãƒªãƒƒã‚¯: 設置ã€ä½¿ç”¨\n"
+"- ホイール: アイテムé¸æŠž\n"
+"- T: ãƒãƒ£ãƒƒãƒˆç”»é¢\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "å†é–‹"
-msgid "Page $1 of $2"
-msgstr "ページ $1/$2"
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "パスワード変更"
-msgid "Rating"
-msgstr "評価"
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "音é‡"
-msgid "re-Install"
-msgstr "å†ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«"
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "æ“作変更"
-msgid "Install"
-msgstr "インストール"
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "タイトル"
-msgid "Item textures..."
-msgstr "アイテムã®ï¾ƒï½¸ï½½ï¾ï½¬è¨­å®šä¸­..."
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "終了"
-msgid "Loading..."
-msgstr "ロード中..."
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "終了中..."
+#: src/game.cpp:1948
msgid "Creating server..."
-msgstr "サーバー構築中..."
+msgstr "サーãƒãƒ¼èµ·å‹•ä¸­..."
+#: src/game.cpp:1984
msgid "Creating client..."
-msgstr "クライアï¾ï¾„作æˆä¸­..."
+msgstr "クライアント起動中..."
+#: src/game.cpp:2159
msgid "Resolving address..."
-msgstr "アドレス解決中..."
+msgstr "アドレス解決中..."
+#: src/game.cpp:2261
msgid "Connecting to server..."
-msgstr "サーバー接続中..."
+msgstr "サーãƒãƒ¼æŽ¥ç¶šä¸­..."
+#: src/game.cpp:2317
msgid "Item definitions..."
-msgstr "アイテム定義中..."
+msgstr "アイテム定義中..."
+#: src/game.cpp:2322
msgid "Node definitions..."
-msgstr "ノード定義中..."
+msgstr "ノード定義中..."
+#: src/game.cpp:2329
msgid "Media..."
-msgstr "..."
+msgstr "通信中..."
-msgid "Shutting down..."
-msgstr "終了中..."
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr "KB/秒"
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr "MB/秒"
+
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
"\n"
-"詳細ã¯debug.txtを御覧ãã ã•ã„。"
-
-msgid "You died."
-msgstr "You died!"
+"詳細ã¯debug.txtã‚’ã”覧ãã ã•ã„。"
-msgid "Respawn"
-msgstr "Respawn"
+#: src/guiFormSpecMenu.cpp:2855
+msgid "Enter "
+msgstr "エンター "
-msgid "Proceed"
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
msgstr "決定"
+#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
-msgstr "キーãƒã‚¤ãƒ³ãƒ‰"
+msgstr "æ“作ã®è¨­å®šã§ã™ã€‚(変更ã«å¤±æ•—ã—ãŸå ´åˆã€minetest.confã‹ã‚‰è©²å½“ã™ã‚‹è¨­å®šã‚’削除ã—ã¦ãã ã•ã„)"
+#: src/guiKeyChangeMenu.cpp:165
msgid "\"Use\" = climb down"
-msgstr "「使ã†ã€ã‚­ãƒ¼ã§é™ã‚Šã‚‹"
+msgstr "「使用ã€ã§é™ã‚Šã‚‹"
+#: src/guiKeyChangeMenu.cpp:180
msgid "Double tap \"jump\" to toggle fly"
-msgstr "ジャンプã®äºŒå›žæŠ¼ã—ã§é£›è¡Œ"
+msgstr "「ジャンプã€äºŒå›žæŠ¼ã—ã§é£›è¡Œãƒ¢ãƒ¼ãƒ‰"
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
-msgstr "æ—¢ã«ä½¿ã‚ã‚Œã¦ã„るキーã§ã™"
+msgstr "キーãŒé‡è¤‡ã—ã¦ã„ã¾ã™"
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "キー入力待ã¡"
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "å‰é€²"
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "後退"
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
-msgstr "å·¦ã¸é€²ã‚€"
+msgstr "å·¦"
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
-msgstr "å³ã¸é€²ã‚€"
+msgstr "å³"
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
-msgstr "使ã†"
+msgstr "使用"
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "ジャンプ"
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
-msgstr "ã“ã£ãり進む"
+msgstr "スニーク"
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "è½ã¨ã™"
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "インベントリ"
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "ãƒãƒ£ãƒƒãƒˆ"
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "コマンド"
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "コンソール"
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
-msgstr "飛行"
+msgstr "飛行モード"
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
-msgstr "高速移動"
+msgstr "高速移動モード"
+
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr "映画風カメラ"
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
-msgstr "ã™ã‚ŠæŠœã‘"
+msgstr "ã™ã‚ŠæŠœã‘モード"
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
-msgstr "視野切り替ãˆ"
+msgstr "視野範囲変更"
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "スタックã®è¡¨ç¤º"
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "å¤ã„パスワード"
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "æ–°ã—ã„パスワード"
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "パスワードã®ç¢ºèª"
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "変更"
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
-msgstr "パスワードãŒä¸€è‡´ã—ã¾ã›ã‚“!"
-
-msgid "Continue"
-msgstr "続ã‘ã‚‹"
-
-msgid "Change Password"
-msgstr "パスワード変更"
-
-msgid "Sound Volume"
-msgstr "音é‡"
-
-msgid "Exit to Menu"
-msgstr "タイトル"
-
-msgid "Exit to OS"
-msgstr "終了"
-
-msgid ""
-"Default Controls:\n"
-"- WASD: move\n"
-"- Space: jump/climb\n"
-"- Shift: sneak/go down\n"
-"- Q: drop item\n"
-"- I: inventory\n"
-"- Mouse: turn/look\n"
-"- Mouse left: dig/punch\n"
-"- Mouse right: place/use\n"
-"- Mouse wheel: select item\n"
-"- T: chat\n"
-msgstr ""
-"基本æ“作:\n"
-"WASD:移動\n"
-"ï½½ï¾ï¾Ÿï½°ï½½:ジャï¾ï¾Œï¾Ÿ/登る\n"
-"シフト:å¿ã³æ­©ã/é™ã‚Šã‚‹\n"
-"Q:アイテムをè½ã¨ã™\n"
-"I:インベントリ\n"
-"ï¾ï½³ï½½:見回ã™\n"
-"左クリック:掘る/パï¾ï¾\n"
-"å³ï½¸ï¾˜ï½¯ï½¸:使ã†\n"
-"ï¾ï½³ï½½ï¾Žï½²ï½°ï¾™:アイテムé¸æŠž\n"
-"T:ï¾ï½¬ï½¯ï¾„\n"
+msgstr "パスワードãŒä¸€è‡´ã—ã¾ã›ã‚“ï¼"
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
-msgstr "音é‡"
+msgstr "音é‡: "
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
-msgstr "戻る"
+msgstr "é–‰ã˜ã‚‹"
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "左ボタン"
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "中ボタン"
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "å³ãƒœã‚¿ãƒ³"
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Xボタン1"
+#: src/keycode.cpp:224
msgid "Back"
-msgstr "Back"
+msgstr "戻るキー"
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "消ã™"
+#: src/keycode.cpp:224
msgid "Return"
msgstr "エンター"
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "タブ"
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Xボタン2"
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
+#: src/keycode.cpp:225
msgid "Control"
msgstr "コントロール"
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "ã‹ãª"
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "メニュー"
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "ãƒãƒ¼ã‚º"
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "変æ›"
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Finalキー"
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junjaキー"
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "åŠè§’/全角"
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "無変æ›"
+#: src/keycode.cpp:227
msgid "End"
msgstr "終了"
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "モード変更"
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Page Down"
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Page Up"
+#: src/keycode.cpp:227
msgid "Space"
msgstr "スペース"
+#: src/keycode.cpp:228
msgid "Down"
msgstr "下"
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "実行キー"
+#: src/keycode.cpp:228
msgid "Print"
msgstr "å°åˆ·ã‚­ãƒ¼"
+#: src/keycode.cpp:228
msgid "Select"
msgstr "é¸æŠžã‚­ãƒ¼"
+#: src/keycode.cpp:228
msgid "Up"
msgstr "上"
+#: src/keycode.cpp:229
msgid "Help"
msgstr "ヘルプ"
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Snapshot"
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "å·¦Windows"
+#: src/keycode.cpp:233
msgid "Apps"
-msgstr "Apps"
+msgstr "アプリ"
+#: src/keycode.cpp:233
msgid "Numpad 0"
-msgstr "Numpad 0"
+msgstr "テンキー 0"
+#: src/keycode.cpp:233
msgid "Numpad 1"
-msgstr "Numpad 1"
+msgstr "テンキー 1"
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "å³Windows"
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "スリープ"
+#: src/keycode.cpp:234
msgid "Numpad 2"
-msgstr "Numpad 2"
+msgstr "テンキー 2"
+#: src/keycode.cpp:234
msgid "Numpad 3"
-msgstr "Numpad 3"
+msgstr "テンキー 3"
+#: src/keycode.cpp:234
msgid "Numpad 4"
-msgstr "Numpad 4"
+msgstr "テンキー 4"
+#: src/keycode.cpp:234
msgid "Numpad 5"
-msgstr "Numpad 5"
+msgstr "テンキー 5"
+#: src/keycode.cpp:234
msgid "Numpad 6"
-msgstr "Numpad 6"
+msgstr "テンキー 6"
+#: src/keycode.cpp:234
msgid "Numpad 7"
-msgstr "Numpad 7"
+msgstr "テンキー 7"
+#: src/keycode.cpp:235
msgid "Numpad *"
-msgstr "Numpad *"
+msgstr "テンキー *"
+#: src/keycode.cpp:235
msgid "Numpad +"
-msgstr "Numpad +"
+msgstr "テンキー +"
+#: src/keycode.cpp:235
msgid "Numpad -"
-msgstr "Numpad -"
+msgstr "テンキー -"
+#: src/keycode.cpp:235
msgid "Numpad /"
-msgstr "Numpad /"
+msgstr "テンキー /"
+#: src/keycode.cpp:235
msgid "Numpad 8"
-msgstr "Numpad 8"
+msgstr "テンキー 8"
+#: src/keycode.cpp:235
msgid "Numpad 9"
-msgstr "Numpad 9"
+msgstr "テンキー 9"
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "å·¦Shift"
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "å³Shift"
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "å·¦Ctrl"
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "左メニュー"
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "å³Ctrl"
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "å³ãƒ¡ãƒ‹ãƒ¥ãƒ¼"
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "読点"
+#: src/keycode.cpp:243
msgid "Minus"
-msgstr "ー"
+msgstr "-"
+#: src/keycode.cpp:243
msgid "Period"
msgstr "."
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "プラス"
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ":"
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Erase OEF"
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "ズーム"
-msgid "needs_fallback_font"
-msgstr "yes"
-
-msgid "Main Menu"
-msgstr "メインメニュー"
-
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-"ワールドãŒé¸æŠžã§ãã¦ã„ãªã„ã‹ã€ï½±ï¾„゙レスãŒå…¥åŠ›ã•ã‚Œã¦ã„ã¾ã›ã‚“。ãã®ãŸã‚実行ã•ã‚Œã¾ã›"
-"ん。"
-
-msgid "Could not find or load game \""
-msgstr "読ã¿è¾¼ã¿ã‹æ¤œç´¢ã«å¤±æ•—: \""
-
-msgid "Invalid gamespec."
-msgstr "無効ãªgamespecã§ã™"
-
-msgid "Connection error (timed out?)"
-msgstr "接続エラー(タイムアウト)"
-
-msgid "Bumpmapping"
-msgstr "バï¾ï¾Œï¾Ÿï¾ï½¯ï¾‹ï¾Ÿï¾ï½¸ï¾ž"
-
-msgid "Generate Normalmaps"
-msgstr "法線ï¾ï½¯ï¾‹ï¾Ÿï¾ï½¸ï¾ž"
-
-msgid "Parallax Occlusion"
-msgstr "視差オクルージョï¾ï¾ï½¯ï¾‹ï¾Ÿï¾ï½¸ï¾ž"
-
-msgid "Waving Water"
-msgstr "æºã‚Œã‚‹æ°´"
-
-msgid "Waving Leaves"
-msgstr "æºã‚Œã‚‹è‘‰"
-
-msgid "Waving Plants"
-msgstr "æºã‚Œã‚‹è‰èŠ±"
+#~ msgid " MB/s"
+#~ msgstr " MB/秒"
-msgid "GUI scale factor"
-msgstr "メニューã®å¤§ãã•"
+#~ msgid " KB/s"
+#~ msgstr " KB/秒"
-msgid "Unsorted"
-msgstr "未分類"
-
-msgid "Search"
-msgstr "検索"
-
-msgid "Close store"
-msgstr "é–‰ã˜ã‚‹"
-
-msgid "Change Keys"
-msgstr "æ“作変更"
+#~ msgid "Fly mode"
+#~ msgstr "飛行モード"
diff --git a/po/ko/minetest.po b/po/ko/minetest.po
index 26117589b..9cb920bd0 100644
--- a/po/ko/minetest.po
+++ b/po/ko/minetest.po
@@ -3,71 +3,89 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
-#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-07-08 23:30+0200\n"
+"Last-Translator: Tae Lim Kook <tkook11@gmail.com>\n"
+"Language-Team: Korean <https://hosted.weblate.org/projects/minetest/minetest/"
+"ko/>\n"
+"Language: ko\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Weblate 2.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
+msgstr "확ì¸"
+
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
-msgstr ""
+msgstr "저장"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
-msgstr ""
+msgstr "취소"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr ""
@@ -107,7 +125,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr ""
@@ -121,7 +139,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr ""
@@ -149,29 +167,29 @@ msgstr ""
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -179,47 +197,39 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
msgstr ""
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -227,15 +237,19 @@ msgstr ""
msgid "Credits"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -276,12 +290,11 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+msgid "Address / Port :"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -290,7 +303,7 @@ msgid "Public Serverlist"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr ""
@@ -299,15 +312,30 @@ msgstr ""
msgid "Connect"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr ""
@@ -315,17 +343,18 @@ msgstr ""
msgid "Start Game"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr ""
@@ -333,6 +362,10 @@ msgstr ""
msgid "Public"
msgstr ""
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr ""
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -345,143 +378,172 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:140
-msgid "Fancy Trees"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr ""
@@ -497,50 +559,181 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr ""
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr ""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr ""
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "yes"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr ""
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr ""
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr ""
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr ""
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr ""
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr ""
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr ""
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr ""
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr ""
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
+#: src/guiFormSpecMenu.cpp:2855
+msgid "Enter "
msgstr ""
-#: src/guiFormSpecMenu.cpp:2846
-msgid "Enter "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
msgstr ""
#: src/guiKeyChangeMenu.cpp:125
@@ -555,422 +748,398 @@ msgstr ""
msgid "Double tap \"jump\" to toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr ""
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr ""
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr ""
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr ""
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr ""
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr ""
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr ""
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr ""
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr ""
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr ""
-
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr ""
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr ""
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr ""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr ""
diff --git a/po/ky/minetest.po b/po/ky/minetest.po
index 47da737bd..7eb4ec2c3 100644
--- a/po/ky/minetest.po
+++ b/po/ky/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-06-01 18:09+0200\n"
"Last-Translator: Chynggyz Jumaliev <translatorky@lavabit.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,62 +18,78 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 1.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Жүктөлүүдө..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
#, fuzzy
msgid "World:"
msgstr "Дүйнөнү тандаңыз:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
#, fuzzy
msgid "Hide Game"
msgstr "Оюн"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
#, fuzzy
msgid "Depends:"
msgstr "көз карандылыктары:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Сактоо"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Жокко чыгаруу"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "Баарын күйгүзүү"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Баарын өчүрүү"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "күйгүзүлгөн"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
#, fuzzy
msgid "Enable all"
msgstr "Баарын күйгүзүү"
@@ -114,7 +130,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr ""
@@ -128,7 +144,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ооба"
@@ -157,30 +173,30 @@ msgstr "Жок"
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Кабыл алуу"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
#, fuzzy
msgid "Failed to install $1 to $2"
msgstr "Дүйнөнү инициалдаштыруу катаÑÑ‹"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -188,49 +204,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Ылдый"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Дүйнө аты"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -238,15 +245,19 @@ msgstr ""
msgid "Credits"
msgstr "Ðлкыштар"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -288,12 +299,13 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Дареги/порту"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "ÐÑ‚Ñ‹/ÑÑ‹Ñ€Ñөзү"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -303,7 +315,7 @@ msgid "Public Serverlist"
msgstr "Жалпылык Ñерверлердин тизмеÑи:"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Өчүрүү"
@@ -312,15 +324,33 @@ msgstr "Өчүрүү"
msgid "Connect"
msgstr "Туташуу"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Жаратуу режими"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "күйгүзүлгөн"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "күйгүзүлгөн"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Жаңы"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "ЫраÑтоо"
@@ -329,17 +359,18 @@ msgstr "ЫраÑтоо"
msgid "Start Game"
msgstr "Оюнду баштоо/туташуу"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Дүйнөнү тандаңыз:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Жаратуу режими"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Убалды күйгүзүү"
@@ -347,6 +378,10 @@ msgstr "Убалды күйгүзүү"
msgid "Public"
msgstr "Жалпылык"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "ÐÑ‚Ñ‹/ÑÑ‹Ñ€Ñөзү"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -359,151 +394,183 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Күңүрт Ñуу"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Кооз бактар"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "ЭкиÑызык чыпкалооÑу"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "ҮчÑызык чыпкалооÑу"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Тегиз жарык"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Бөлүкчөлөрдү күйгүзүү"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D-булуттар"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Кооз бактар"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
#, fuzzy
msgid "Opaque Water"
msgstr "Күңүрт Ñуу"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Туташуу"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-текÑтуралоо"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "ÐÐ½Ð¸Ð·Ð°Ñ‚Ñ€Ð¾Ð¿Ð¸Ñ Ñ‡Ñ‹Ð¿ÐºÐ°Ð»Ð¾Ð¾Ñу"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "ЭкиÑызык чыпкалооÑу"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "ҮчÑызык чыпкалооÑу"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Көлөкөлөгүчтөр"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "БаÑкычтарды өзгөртүү"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Бир кишилик"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip-текÑтуралоо"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "ЫраÑтоолор"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Бир кишилик"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "ЫраÑтоо"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Башкы меню"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Ойноо"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Бир кишилик"
@@ -519,40 +586,187 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Жүктөлүүдө..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "Дареги чечилүүдө..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Буюм текÑтуралары..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Башкы меню"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Туташтыруу катаÑÑ‹ (убактыңыз өтүп кеттиби?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Дүйнө тандалган жок жана дареги киргизилген жок. Кылууга Ñч нерÑе жок."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Оюнду табуу же жүктөө мүмкүн ÑÐ¼ÐµÑ \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr ""
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Улантуу"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Сиз өлдүңүз."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Кайтадан жаралуу"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"ЖарыÑÐ»Ð°Ð½Ð±Ð°Ñ Ð±Ð°ÑˆÐºÐ°Ñ€ÑƒÑƒ:\n"
+"- WASD: баÑуу\n"
+"- Боштугу: Ñекирүү/өйдө чыгуу\n"
+"- Shift: уурданып баÑуу/ылдый түшүү\n"
+"- Q: буюмду таштоо\n"
+"- I: мүлк-шайман\n"
+"- Чычканы: бурулуу/кароо\n"
+"- Сол чычкан баÑкычы: казуу/Ñогуу\n"
+"- Оң чычкан баÑкычы: коюу/колдонуу\n"
+"- Чычкан дөңгөлөгү: буюмду тандоо\n"
+"- T: маек\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Улантуу"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "СырÑөздү өзгөртүү"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Үн көлөмү"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "БаÑкычтарды өзгөртүү"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Менюга чыгуу"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Оюндан чыгуу"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "Оюн өчүрүлүүдө..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Сервер жаратылууда...."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Клиент жаратылууда..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Дареги чечилүүдө..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Серверге туташтырылууда..."
+
+#: src/game.cpp:2317
#, fuzzy
msgid "Item definitions..."
msgstr "Буюм текÑтуралары..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -560,14 +774,14 @@ msgstr ""
"\n"
"Толугураак маалымат үчүн, debug.txt'ти текшериңиз."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Улантуу"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -580,473 +794,421 @@ msgstr ""
msgid "Double tap \"jump\" to toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "баÑкычты баÑыңыз"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Ðлга"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Ðртка"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Солго"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Оңго"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Колдонуу"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Секирүү"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Уурданып баÑуу"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Ыргытуу"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Мүлк-шайман"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Маек"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Команда"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "КонÑоль"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Учууга которуу"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Тез баÑууга которуу"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Тез баÑууга которуу"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr ""
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "ЭÑки ÑÑ‹Ñ€Ñөз"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Жаңы ÑÑ‹Ñ€Ñөз"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "СырÑөздү аныктоо"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Өзгөртүү"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "СырÑөздөр дал келген жок!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Үн көлөмү: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Чыгуу"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Сол баÑкыч"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Ортоңку баÑкыч"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Оң баÑкыч"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Ðртка"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Тазалоо"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Ctrl"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Кана"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Меню"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Пауза"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Кандзи"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Режимди өзгөртүү"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Кийинки"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Боштук"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Ылдый"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Ðткаруу"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "БаÑма"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Тандоо"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Өйдө"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Жардам"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Тез Ñүрөт"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Сол Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Тиркемелер"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Кош. клав. 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Кош. клав. 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Оң Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Уйку"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Кош. клав. 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Кош. клав. 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Кош. клав. 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Кош. клав. 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Кош. клав. 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Кош. клав. 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Кош. клав. *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Кош. клав. +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Кош. клав. -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Кош. клав. /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Кош. клав. 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Кош. клав. 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Сол Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Оң Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Сол Ctrl"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Сол меню"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Оң Ctrl"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Оң меню"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Үтүр"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Кемитүү белгиÑи"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Ðйланма Ñан"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Кошуу белгиÑи"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "МаÑштаб"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Башкы меню"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Туташтыруу катаÑÑ‹ (убактыңыз өтүп кеттиби?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Дүйнө тандалган жок жана дареги киргизилген жок. Кылууга Ñч нерÑе жок."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Оюнду табуу же жүктөө мүмкүн ÑÐ¼ÐµÑ \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr ""
-
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "Сол баÑкычы: Бардык буюмдарды ташуу, Оң баÑкычы: Бир буюмду ташуу"
-
-#~ msgid "is required by:"
-#~ msgstr "талап кылынганы:"
-
-#~ msgid "Configuration saved. "
-#~ msgstr "ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ñакталды. "
-
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "ЭÑкертүү: Туура ÑÐ¼ÐµÑ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ. "
-
-#~ msgid "Multiplayer"
-#~ msgstr "Көп кишилик"
-
-#~ msgid "Advanced"
-#~ msgstr "Кошумча"
-
-#~ msgid "Show Public"
-#~ msgstr "Жалпылыкты көрÑÓ©Ñ‚Ò¯Ò¯"
-
-#~ msgid "Show Favorites"
-#~ msgstr "Тандалмаларды көрÑÓ©Ñ‚Ò¯Ò¯"
-
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Жергиликтүү Ñерверди жүргүзүү үчүн даректи бош калтырыңыз."
-
-#~ msgid "Create world"
-#~ msgstr "Дүйнөнү жаратуу"
-
-#~ msgid "Address required."
-#~ msgstr "Дареги талап кылынат."
-
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Дүнөнү жаратуу мүмкүн ÑмеÑ: Эч нерÑе тандалган жок"
+#, fuzzy
+#~ msgid "Game Name"
+#~ msgstr "Оюн"
-#~ msgid "Files to be deleted"
-#~ msgstr "Өчүрүлө турган файлдар"
+#, fuzzy
+#~ msgid "Games"
+#~ msgstr "Оюн"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Дүйнөнү жаратуу мүмкүн ÑмеÑ: Оюндар табылган жок"
+#~ msgid "Favorites:"
+#~ msgstr "Тандалмалар:"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Дүйнөнү ыраÑтоо мүмкүн ÑмеÑ: Эч нерÑе тандалган жок"
+#, fuzzy
+#~ msgid "Password"
+#~ msgstr "ЭÑки ÑÑ‹Ñ€Ñөз"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Бардык дүйнө файлдарын өчүрүү оңунан чыккан жок"
+#, fuzzy
+#~ msgid "Finite Liquid"
+#~ msgstr "Чектүү Ñуюктук"
#~ msgid ""
#~ "Default Controls:\n"
@@ -1073,82 +1235,60 @@ msgstr ""
#~ "- ESC: бул меню\n"
#~ "- T: маек\n"
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "ЖарыÑÐ»Ð°Ð½Ð±Ð°Ñ Ð±Ð°ÑˆÐºÐ°Ñ€ÑƒÑƒ:\n"
-#~ "- WASD: баÑуу\n"
-#~ "- Боштугу: Ñекирүү/өйдө чыгуу\n"
-#~ "- Shift: уурданып баÑуу/ылдый түшүү\n"
-#~ "- Q: буюмду таштоо\n"
-#~ "- I: мүлк-шайман\n"
-#~ "- Чычканы: бурулуу/кароо\n"
-#~ "- Сол чычкан баÑкычы: казуу/Ñогуу\n"
-#~ "- Оң чычкан баÑкычы: коюу/колдонуу\n"
-#~ "- Чычкан дөңгөлөгү: буюмду тандоо\n"
-#~ "- T: маек\n"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Бардык дүйнө файлдарын өчүрүү оңунан чыккан жок"
-#~ msgid "Exit to OS"
-#~ msgstr "Оюндан чыгуу"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Дүйнөнү ыраÑтоо мүмкүн ÑмеÑ: Эч нерÑе тандалган жок"
-#~ msgid "Exit to Menu"
-#~ msgstr "Менюга чыгуу"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Дүйнөнү жаратуу мүмкүн ÑмеÑ: Оюндар табылган жок"
-#~ msgid "Sound Volume"
-#~ msgstr "Үн көлөмү"
+#~ msgid "Files to be deleted"
+#~ msgstr "Өчүрүлө турган файлдар"
-#~ msgid "Change Password"
-#~ msgstr "СырÑөздү өзгөртүү"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Дүнөнү жаратуу мүмкүн ÑмеÑ: Эч нерÑе тандалган жок"
-#~ msgid "Continue"
-#~ msgstr "Улантуу"
+#~ msgid "Address required."
+#~ msgstr "Дареги талап кылынат."
-#~ msgid "You died."
-#~ msgstr "Сиз өлдүңүз."
+#~ msgid "Create world"
+#~ msgstr "Дүйнөнү жаратуу"
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Оюн өчүрүлүүдө..."
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Жергиликтүү Ñерверди жүргүзүү үчүн даректи бош калтырыңыз."
-#~ msgid "Connecting to server..."
-#~ msgstr "Серверге туташтырылууда..."
+#~ msgid "Show Favorites"
+#~ msgstr "Тандалмаларды көрÑÓ©Ñ‚Ò¯Ò¯"
-#~ msgid "Resolving address..."
-#~ msgstr "Дареги чечилүүдө..."
+#~ msgid "Show Public"
+#~ msgstr "Жалпылыкты көрÑÓ©Ñ‚Ò¯Ò¯"
-#~ msgid "Creating client..."
-#~ msgstr "Клиент жаратылууда..."
+#~ msgid "Advanced"
+#~ msgstr "Кошумча"
-#~ msgid "Creating server...."
-#~ msgstr "Сервер жаратылууда...."
+#~ msgid "Multiplayer"
+#~ msgstr "Көп кишилик"
-#~ msgid "Loading..."
-#~ msgstr "Жүктөлүүдө..."
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "ЭÑкертүү: Туура ÑÐ¼ÐµÑ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ. "
-#, fuzzy
-#~ msgid "Finite Liquid"
-#~ msgstr "Чектүү Ñуюктук"
+#~ msgid "Configuration saved. "
+#~ msgstr "ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ñакталды. "
-#, fuzzy
-#~ msgid "Password"
-#~ msgstr "ЭÑки ÑÑ‹Ñ€Ñөз"
+#~ msgid "is required by:"
+#~ msgstr "талап кылынганы:"
-#~ msgid "Favorites:"
-#~ msgstr "Тандалмалар:"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "Сол баÑкычы: Бардык буюмдарды ташуу, Оң баÑкычы: Бир буюмду ташуу"
-#, fuzzy
-#~ msgid "Games"
-#~ msgstr "Оюн"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "ÐÐ½Ð¸Ð·Ð°Ñ‚Ñ€Ð¾Ð¿Ð¸Ñ Ñ‡Ñ‹Ð¿ÐºÐ°Ð»Ð¾Ð¾Ñу"
+
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-текÑтуралоо"
#, fuzzy
-#~ msgid "Game Name"
-#~ msgstr "Оюн"
+#~ msgid "Downloading"
+#~ msgstr "Ылдый"
diff --git a/po/lt/minetest.po b/po/lt/minetest.po
index 28054b773..bd68c5840 100644
--- a/po/lt/minetest.po
+++ b/po/lt/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-12-11 19:23+0200\n"
"Last-Translator: Jonas KriauÄiÅ«nas <jonukas@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,57 +19,73 @@ msgstr ""
"%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Gerai"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Įkeliama..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Pasaulis:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "SlÄ—pti vidinius"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Slėpti papild. pakų turinį"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Papildinys:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Priklauso:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Įrašyti"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Atsisakyti"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Įjungti papildinį"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Išjungti papildinį"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "įjungtas"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Įjungti visus"
@@ -110,7 +126,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Pasaulis, pavadintas „$1“ jau yra"
@@ -124,7 +140,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Taip"
@@ -152,30 +168,30 @@ msgstr "Ne"
msgid "Rename Modpack:"
msgstr "Pervadinti papildinių paką:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
#, fuzzy
msgid "Accept"
msgstr "Priimti"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Nepavyko įdiegti $1 į $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -183,48 +199,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Pasaulio pavadinimas"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Įdiegti iš naujo"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Įdiegti"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -232,15 +240,20 @@ msgstr ""
msgid "Credits"
msgstr "PadÄ—kos"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Pagrindiniai kūrėjai"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Aktyvūs pagalbininkai"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Pagrindiniai kūrėjai"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -281,12 +294,13 @@ msgid "Mods"
msgstr "Papildiniai"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Adresas/Prievadas"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Vardas/slaptažodis"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -295,7 +309,7 @@ msgid "Public Serverlist"
msgstr "Viešų serverių sąrašas"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "IÅ¡trinti"
@@ -304,15 +318,33 @@ msgstr "IÅ¡trinti"
msgid "Connect"
msgstr "Jungtis"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "KÅ«rybinÄ— veiksena"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "įjungtas"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "įjungtas"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Žaisti tinkle(klientas)"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Naujas"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Konfigūruoti"
@@ -320,18 +352,19 @@ msgstr "Konfigūruoti"
msgid "Start Game"
msgstr "Pradėti žaidimą"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Pasirinkite pasaulį:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
#, fuzzy
msgid "Creative Mode"
msgstr "KÅ«rybinÄ— veiksena"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Leisti sužeidimus"
@@ -339,6 +372,10 @@ msgstr "Leisti sužeidimus"
msgid "Public"
msgstr "Viešas"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Vardas/slaptažodis"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -351,150 +388,180 @@ msgstr ""
msgid "Server Port"
msgstr "Serverio prievadas"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Serveris"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Nepermatomas vanduo"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
#, fuzzy
msgid "Smooth Lighting"
msgstr "Apšvietimo efektai"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "TrimaÄiai debesys"
-#: builtin/mainmenu/tab_settings.lua:140
-msgid "Fancy Trees"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Nepermatomas vanduo"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Jungtis"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
#, fuzzy
msgid "Shaders"
msgstr "Šešėliai"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Nustatyti klavišus"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Žaisti vienam"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Nustatymai"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Žaisti vienam"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Konfigūruoti"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Pagrindinis meniu"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Žaisti"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Žaisti vienam"
@@ -510,53 +577,189 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Įkeliama..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "Ieškoma adreso..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Pagrindinis meniu"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr ""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr ""
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+#, fuzzy
+msgid "Proceed"
+msgstr "Tęsti"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "JÅ«s numirÄ—te."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Prisikelti"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Tęsti"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Keisti slaptažodį"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr ""
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Nustatyti klavišus"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Grįžti į meniu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Išeiti iš žaidimo"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Kuriamas serveris...."
+
+#: src/game.cpp:1984
+#, fuzzy
+msgid "Creating client..."
+msgstr "Kuriamas klientas..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Ieškoma adreso..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Jungiamasi prie serverio..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
-#: src/guiFormSpecMenu.cpp:2055
-#, fuzzy
-msgid "Proceed"
-msgstr "Tęsti"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -569,509 +772,454 @@ msgstr ""
msgid "Double tap \"jump\" to toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Klavišas jau naudojamas"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "paspauskite klavišą"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Pirmyn"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Atgal"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "KairÄ—n"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Dešinėn"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Naudoti"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Pašokti"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Mesti"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventorius"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Susirašinėti"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Komanda"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr ""
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr ""
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Senas slaptažodis"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Naujas slaptažodis"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Patvirtinti slaptažodį"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Pakeisti"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Slaptažodžiai nesutampa!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr ""
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "IÅ¡eiti"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Kairysis mygtukas"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Vidurinis mygtukas"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Dešinysis mygtukas"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Meniu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift (Lyg2)"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Tarpas"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Žemyn"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Vykdyti"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Aukštyn"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Pagalba"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Įterpti"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr ""
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Kairysis Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Dešinysis Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Kairysis Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Dešinysis Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Kablelis"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plius"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
#, fuzzy
msgid "Zoom"
msgstr "Pritraukti"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Pagrindinis meniu"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr ""
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr ""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr ""
-
-#~ msgid "Exit to OS"
-#~ msgstr "Išeiti iš žaidimo"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Grįžti į meniu"
+#~ msgid "Game Name"
+#~ msgstr "Žaidimo pavadinimas"
-#~ msgid "Change Password"
-#~ msgstr "Keisti slaptažodį"
+#~ msgid "GAMES"
+#~ msgstr "ŽAIDIMAI"
-#~ msgid "Continue"
-#~ msgstr "Tęsti"
+#~ msgid "Games"
+#~ msgstr "Žaidimai"
-#~ msgid "You died."
-#~ msgstr "JÅ«s numirÄ—te."
+#~ msgid "Mods:"
+#~ msgstr "Papildiniai:"
-#~ msgid "Connecting to server..."
-#~ msgstr "Jungiamasi prie serverio..."
+#~ msgid "edit game"
+#~ msgstr "keisti žaidimą"
-#~ msgid "Resolving address..."
-#~ msgstr "Ieškoma adreso..."
+#~ msgid "new game"
+#~ msgstr "naujas žaidimas"
#, fuzzy
-#~ msgid "Creating client..."
-#~ msgstr "Kuriamas klientas..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Kuriamas serveris...."
-
-#~ msgid "Loading..."
-#~ msgstr "Įkeliama..."
-
-#~ msgid "Add mod:"
-#~ msgstr "Pridėti papildinį:"
-
-#~ msgid "MODS"
-#~ msgstr "PAPILDINIAI"
-
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "VIENAS ŽAIDĖJAS"
-
-#~ msgid "SETTINGS"
-#~ msgstr "NUSTATYMAI"
-
-#~ msgid "Password"
-#~ msgstr "Slaptažodis"
-
-#~ msgid "Name"
-#~ msgstr "Vardas"
+#~ msgid "EDIT GAME"
+#~ msgstr "KEISTI ŽAIDIMĄ"
-#~ msgid "START SERVER"
-#~ msgstr "PALEISTI SERVERÄ®"
+#~ msgid "Remove selected mod"
+#~ msgstr "Pašalinti pasirinktą papildinį"
-#~ msgid "Favorites:"
-#~ msgstr "MÄ—giami:"
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Pridėti papildinį"
#~ msgid "CLIENT"
#~ msgstr "ŽAISTI TINKLE"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Pridėti papildinį"
-
-#~ msgid "Remove selected mod"
-#~ msgstr "Pašalinti pasirinktą papildinį"
+#~ msgid "Favorites:"
+#~ msgstr "MÄ—giami:"
-#, fuzzy
-#~ msgid "EDIT GAME"
-#~ msgstr "KEISTI ŽAIDIMĄ"
+#~ msgid "START SERVER"
+#~ msgstr "PALEISTI SERVERÄ®"
-#~ msgid "new game"
-#~ msgstr "naujas žaidimas"
+#~ msgid "Name"
+#~ msgstr "Vardas"
-#~ msgid "edit game"
-#~ msgstr "keisti žaidimą"
+#~ msgid "Password"
+#~ msgstr "Slaptažodis"
-#~ msgid "Mods:"
-#~ msgstr "Papildiniai:"
+#~ msgid "SETTINGS"
+#~ msgstr "NUSTATYMAI"
-#~ msgid "Games"
-#~ msgstr "Žaidimai"
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "VIENAS ŽAIDĖJAS"
-#~ msgid "GAMES"
-#~ msgstr "ŽAIDIMAI"
+#~ msgid "MODS"
+#~ msgstr "PAPILDINIAI"
-#~ msgid "Game Name"
-#~ msgstr "Žaidimo pavadinimas"
+#~ msgid "Add mod:"
+#~ msgstr "Pridėti papildinį:"
diff --git a/po/minetest.pot b/po/minetest.pot
index 9a623788f..2fd0ba1be 100644
--- a/po/minetest.pot
+++ b/po/minetest.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,57 +17,73 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr ""
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr ""
@@ -107,7 +123,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr ""
@@ -121,7 +137,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr ""
@@ -149,29 +165,29 @@ msgstr ""
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -179,47 +195,39 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr ""
-
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
msgstr ""
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -227,15 +235,19 @@ msgstr ""
msgid "Credits"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -276,12 +288,11 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+msgid "Address / Port :"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -290,7 +301,7 @@ msgid "Public Serverlist"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr ""
@@ -299,15 +310,30 @@ msgstr ""
msgid "Connect"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr ""
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr ""
@@ -315,17 +341,18 @@ msgstr ""
msgid "Start Game"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr ""
@@ -333,6 +360,10 @@ msgstr ""
msgid "Public"
msgstr ""
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr ""
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -345,143 +376,172 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:140
-msgid "Fancy Trees"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr ""
@@ -497,50 +557,181 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr ""
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr ""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr ""
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr ""
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr ""
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr ""
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr ""
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr ""
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr ""
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr ""
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr ""
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr ""
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
+#: src/guiFormSpecMenu.cpp:2855
+msgid "Enter "
msgstr ""
-#: src/guiFormSpecMenu.cpp:2846
-msgid "Enter "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
msgstr ""
#: src/guiKeyChangeMenu.cpp:125
@@ -555,422 +746,398 @@ msgstr ""
msgid "Double tap \"jump\" to toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr ""
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr ""
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr ""
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr ""
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr ""
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr ""
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr ""
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr ""
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr ""
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr ""
-
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr ""
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr ""
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr ""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr ""
diff --git a/po/nb/minetest.po b/po/nb/minetest.po
index 438e7e4ac..7dc7361c4 100644
--- a/po/nb/minetest.po
+++ b/po/nb/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-06-21 15:48+0200\n"
"Last-Translator: sfan5 <sfan5@live.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,62 +18,78 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Weblate 1.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr ""
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
#, fuzzy
msgid "World:"
msgstr "Navnet på verdenen"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
#, fuzzy
msgid "Hide Game"
msgstr "Spill"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
#, fuzzy
msgid "Depends:"
msgstr "Avhenger av:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Lagre"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Avbryt"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "Aktiver Alle"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Deaktiver Alle"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "aktivert"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
#, fuzzy
msgid "Enable all"
msgstr "Aktiver Alle"
@@ -114,7 +130,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr ""
@@ -128,7 +144,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ja"
@@ -156,29 +172,29 @@ msgstr "Nei"
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -186,48 +202,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-msgid "Downloading"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Navnet på verdenen"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -235,15 +243,19 @@ msgstr ""
msgid "Credits"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -284,12 +296,11 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+msgid "Address / Port :"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -298,7 +309,7 @@ msgid "Public Serverlist"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr ""
@@ -307,15 +318,33 @@ msgstr ""
msgid "Connect"
msgstr ""
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Opprett"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "aktivert"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "aktivert"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr ""
@@ -323,17 +352,18 @@ msgstr ""
msgid "Start Game"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr ""
@@ -341,6 +371,10 @@ msgstr ""
msgid "Public"
msgstr ""
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr ""
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -353,143 +387,172 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:140
-msgid "Fancy Trees"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
-
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
msgstr ""
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr ""
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr ""
@@ -505,50 +568,181 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr ""
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr ""
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr ""
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr ""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr ""
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr ""
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Du døde."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr ""
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr ""
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr ""
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr ""
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr ""
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr ""
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr ""
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr ""
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr ""
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr ""
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr ""
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
msgstr ""
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
+#: src/guiFormSpecMenu.cpp:2855
+msgid "Enter "
msgstr ""
-#: src/guiFormSpecMenu.cpp:2846
-msgid "Enter "
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
msgstr ""
#: src/guiKeyChangeMenu.cpp:125
@@ -563,438 +757,409 @@ msgstr ""
msgid "Double tap \"jump\" to toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr ""
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr ""
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr ""
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr ""
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr ""
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr ""
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr ""
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr ""
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr ""
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr ""
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr ""
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr ""
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr ""
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr ""
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr ""
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr ""
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr ""
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr ""
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr ""
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr ""
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr ""
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr ""
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr ""
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr ""
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr ""
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr ""
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr ""
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr ""
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr ""
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr ""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr ""
-
-#~ msgid "is required by:"
-#~ msgstr "trengs av:"
-
-#~ msgid "Configuration saved. "
-#~ msgstr "Konfigurasjon lagret. "
+#, fuzzy
+#~ msgid "Game Name"
+#~ msgstr "Spill"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Advarsel: Noen konfigurerte modifikasjoner mangler. \n"
-#~ "Instillingene deres vil bli fjernet når du lagrer konfigurasjonen."
+#, fuzzy
+#~ msgid "Games"
+#~ msgstr "Spill"
#~ msgid ""
#~ "Warning: Some mods are not configured yet.\n"
@@ -1003,13 +1168,15 @@ msgstr ""
#~ "Advarsel: Noen modifikasjoner er ikke konfigurert enda. \n"
#~ "De vil bli aktivert som standard når du lagrer konfigurasjonen."
-#~ msgid "You died."
-#~ msgstr "Du døde."
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Advarsel: Noen konfigurerte modifikasjoner mangler. \n"
+#~ "Instillingene deres vil bli fjernet når du lagrer konfigurasjonen."
-#, fuzzy
-#~ msgid "Games"
-#~ msgstr "Spill"
+#~ msgid "Configuration saved. "
+#~ msgstr "Konfigurasjon lagret. "
-#, fuzzy
-#~ msgid "Game Name"
-#~ msgstr "Spill"
+#~ msgid "is required by:"
+#~ msgstr "trengs av:"
diff --git a/po/nl/minetest.po b/po/nl/minetest.po
index c81353d43..9efaf95fc 100644
--- a/po/nl/minetest.po
+++ b/po/nl/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2014-01-05 11:31+0200\n"
"Last-Translator: b p <bp.atlarge@gmail.com>\n"
"Language-Team: \n"
@@ -18,57 +18,73 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Ok"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Bezig met laden..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Wereld:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Geen std"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Verberg mp mods"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Afhankelijkheden:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Bewaar"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Annuleer"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "MP inschakelen"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "MP uitschakelen"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "ingeschakeld"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Alles aan"
@@ -108,7 +124,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Wereld \"$1\" bestaat al"
@@ -122,7 +138,7 @@ msgstr "Weet je zeker dat je \"$1\" wilt verwijderen?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Ja"
@@ -150,15 +166,15 @@ msgstr "Nee"
msgid "Rename Modpack:"
msgstr "Modverzameling hernoemen:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Accepteren"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Mod installeren: bestand: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -167,15 +183,15 @@ msgstr ""
"\n"
"Mod installeren: niet ondersteund bestandstype \"$1\""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Installeren van $1 in $2 is mislukt"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr "Mod installeren: kan geen geschikte map vinden voor modverzameling $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Mod installeren: kan geen echte modnaam vinden voor: $1"
@@ -183,49 +199,40 @@ msgstr "Mod installeren: kan geen echte modnaam vinden voor: $1"
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Downloaden"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Naam wereld"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Rang"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "opnieuw installeren"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Installeren"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Pagina $1 van $2"
@@ -233,15 +240,20 @@ msgstr "Pagina $1 van $2"
msgid "Credits"
msgstr "Credits"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Hoofdontwikkelaars"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Actieve bijdragers"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Hoofdontwikkelaars"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Vroegere bijdragers"
@@ -282,12 +294,13 @@ msgid "Mods"
msgstr "Mods"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "IP-Adres/Poort"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Naam/Wachtwoord"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -296,7 +309,7 @@ msgid "Public Serverlist"
msgstr "Publieke Serverlijst"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Verwijderen"
@@ -305,15 +318,33 @@ msgstr "Verwijderen"
msgid "Connect"
msgstr "Verbinden"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Creatieve Modus"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "ingeschakeld"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "ingeschakeld"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Client"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nieuw"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Instellingen"
@@ -321,17 +352,18 @@ msgstr "Instellingen"
msgid "Start Game"
msgstr "Start Server"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Selecteer Wereld:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Creatieve Modus"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Schade inschakelen"
@@ -339,6 +371,10 @@ msgstr "Schade inschakelen"
msgid "Public"
msgstr "Publiek"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Naam/Wachtwoord"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -351,150 +387,183 @@ msgstr ""
msgid "Server Port"
msgstr "Serverpoort"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Geen wereldnaam opgegeven of geen spel geselecteerd"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Server"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Ondoorzichtig water"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Mooie bomen"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Bi-Lineaire Filtering"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Tri-Lineare Filtering"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Mooie verlichting"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Deeltjes aanzetten"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D wolken"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Mooie bomen"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Ondoorzichtig water"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Verbinden"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Anisotrope Filtering"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Bi-Lineaire Filtering"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Tri-Lineare Filtering"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shaders"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Toetsen"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Singleplayer"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip-Mapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Om schaduwen mogelijk te maken moet OpenGL worden gebruikt."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Instellingen"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Singleplayer"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Instellingen"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Hoofdmenu"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Speel"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Singleplayer"
@@ -511,39 +580,186 @@ msgstr "Geen informatie aanwezig"
msgid "Texturepacks"
msgstr "Texturen"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Bezig met laden..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "IP-adres opzoeken..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Voorwerp texturen..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Hoofdmenu"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Fout bij verbinden (time out?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Geen wereld en adres geselecteerd. Niks te doen."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Kan niet de game laden of vinden \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Onjuiste gamespec."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Doorgaan"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Je bent gestorven."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Herspawnen"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Standaard toetsen:\n"
+"- W,A,S,D: bewegen\n"
+"- Spatie: spring/klim\n"
+"- Shift: kruip/duik\n"
+"- Q: weggooien\n"
+"- I: rugzak\n"
+"- Muis: draaien/kijken\n"
+"- L.muisknop: graaf/sla\n"
+"- R.muisknop: plaats/gebruik\n"
+"- Muiswiel: selecteer\n"
+"- T: chat\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Verder spelen"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Verander wachtwoord"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volume"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Toetsen"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Terug naar menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Afsluiten"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "Stopzetten..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Bezig server te maken..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Bezig client te maken..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "IP-adres opzoeken..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Verbinding met de server wordt gemaakt..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Voorwerpdefinities..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Node definities..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Media..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -551,14 +767,14 @@ msgstr ""
"\n"
"Lees debug.txt voor details."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Doorgaan"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -573,477 +789,486 @@ msgstr "\"Gebruiken\" = Omlaag"
msgid "Double tap \"jump\" to toggle fly"
msgstr "2x \"springen\" om te vliegen"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Toets is al in gebruik"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "druk op"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Vooruit"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Achteruit"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Links"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Rechts"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Gebruiken"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Springen"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Kruipen"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Weggooien"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Rugzak"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Chatten"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Opdracht"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Console"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Vliegen aan/uit"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Snel bewegen aan/uit"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Snel bewegen aan/uit"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Noclip aan/uit"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Range instellen"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Print stacks"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Huidig wachtwoord"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Nieuw wachtwoord"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Herhaal wachtwoord"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Veranderen"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Wachtwoorden zijn niet gelijk!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Volume: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Terug"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Linkermuisknop"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Muiswielknop"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Rechtmuisknop"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X knop 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Terug"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Wissen"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Terug"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X knop 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Kapitaal"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pauze"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Converteren"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Modus veranderen"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Volgende"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Eerste"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Spatie"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Omlaag"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Uitvoeren"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Selecteren"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Omhoog"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Help"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Screenshot"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Linker Windowstoets"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Menu"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Rechter Windowstoets"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Slaapknop"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Linker Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Rechter Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Linker Ctrl"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Linker Menu"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Rechter Ctrl"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Rechter Menu"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Komma"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Min"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punt"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "SAK"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Erase EOF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Hoofdmenu"
+#~ msgid "Game Name"
+#~ msgstr "Spel"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr: Kan mod \"$1\" niet naar spel \"$2\" kopiëren"
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Fout bij verbinden (time out?)"
+#~ msgid "GAMES"
+#~ msgstr "SPELLEN"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Geen wereld en adres geselecteerd. Niks te doen."
+#~ msgid "Games"
+#~ msgstr "Spellen"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#~ msgid "Mods:"
+#~ msgstr "Mods:"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Kan niet de game laden of vinden \""
+#~ msgid "edit game"
+#~ msgstr "spel aanpassen"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Onjuiste gamespec."
+#~ msgid "new game"
+#~ msgstr "nieuw spel"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr ""
-#~ "Linkermuisknop: Verplaats alle items. Rechtermuisknop: Verplaats één item"
+#~ msgid "EDIT GAME"
+#~ msgstr "SPEL AANPASSEN"
-#~ msgid "is required by:"
-#~ msgstr "is benodigd voor:"
+#~ msgid "Remove selected mod"
+#~ msgstr "Geselecteerde mod verwijderen"
-#~ msgid "Configuration saved. "
-#~ msgstr "Instellingen bewaard. "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Mod toevoegen"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Waarschuwing: Instellingen niet consistent. "
+#~ msgid "CLIENT"
+#~ msgstr "CLIENT"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Kan geen nieuwe wereld aanmaken: de naam bevat onjuiste tekens"
+#~ msgid "Favorites:"
+#~ msgstr "Favorieten:"
-#~ msgid "Multiplayer"
-#~ msgstr "Multiplayer"
+#~ msgid "START SERVER"
+#~ msgstr "START SERVER"
-#~ msgid "Advanced"
-#~ msgstr "Geavanceerd"
+#~ msgid "Name"
+#~ msgstr "Naam"
-#~ msgid "Show Public"
-#~ msgstr "Publieke server"
+#~ msgid "Password"
+#~ msgstr "Wachtwoord"
-#~ msgid "Show Favorites"
-#~ msgstr "Favourieten"
+#~ msgid "SETTINGS"
+#~ msgstr "INSTELLINGEN"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Laat het adres leeg om een lokale server te starten."
+#~ msgid "Preload item visuals"
+#~ msgstr "Voorwerpen vooraf laden"
-#~ msgid "Create world"
-#~ msgstr "Maak wereld aan"
+#~ msgid "Finite Liquid"
+#~ msgstr "Eindige vloeistoffen"
-#~ msgid "Address required."
-#~ msgstr "IP-adres nodig."
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "SINGLEPLAYER"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Kan niets verwijderen: Geen wereld geselecteerd"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "TEXTUREN"
-#~ msgid "Files to be deleted"
-#~ msgstr "Deze bestanden worden verwijderd"
+#~ msgid "MODS"
+#~ msgstr "MODS"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Kan geen wereld aanmaken: Geen games gevonden"
+#~ msgid "Add mod:"
+#~ msgstr "Mod toevoegen:"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Kan instellingen niet aanpassen: Niets geselecteerd"
+#~ msgid "Local install"
+#~ msgstr "Plaatselijk installeren"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Niet alle bestanden zijn verwijderd"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Let op: Nog niet alle mods zijn geconfigueerd. \n"
+#~ "De mods zullen automatisch worden ingeschakeld als je de configuratie "
+#~ "bewaard. "
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "LEt op: Sommige ingestelde mods zijn vermist.\n"
+#~ "Hun instellingen worden verwijderd als je de configuratie opslaat. "
#~ msgid ""
#~ "Default Controls:\n"
@@ -1070,147 +1295,64 @@ msgstr "Onjuiste gamespec."
#~ "- ESC: Menu\n"
#~ "- T: Chat\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "LEt op: Sommige ingestelde mods zijn vermist.\n"
-#~ "Hun instellingen worden verwijderd als je de configuratie opslaat. "
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Let op: Nog niet alle mods zijn geconfigueerd. \n"
-#~ "De mods zullen automatisch worden ingeschakeld als je de configuratie "
-#~ "bewaard. "
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Standaard toetsen:\n"
-#~ "- W,A,S,D: bewegen\n"
-#~ "- Spatie: spring/klim\n"
-#~ "- Shift: kruip/duik\n"
-#~ "- Q: weggooien\n"
-#~ "- I: rugzak\n"
-#~ "- Muis: draaien/kijken\n"
-#~ "- L.muisknop: graaf/sla\n"
-#~ "- R.muisknop: plaats/gebruik\n"
-#~ "- Muiswiel: selecteer\n"
-#~ "- T: chat\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Afsluiten"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Terug naar menu"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volume"
-
-#~ msgid "Change Password"
-#~ msgstr "Verander wachtwoord"
-
-#~ msgid "Continue"
-#~ msgstr "Verder spelen"
-
-#~ msgid "You died."
-#~ msgstr "Je bent gestorven."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Stopzetten..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Verbinding met de server wordt gemaakt..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "IP-adres opzoeken..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Bezig client te maken..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Bezig server te maken..."
-
-#~ msgid "Loading..."
-#~ msgstr "Bezig met laden..."
-
-#~ msgid "Local install"
-#~ msgstr "Plaatselijk installeren"
-
-#~ msgid "Add mod:"
-#~ msgstr "Mod toevoegen:"
-
-#~ msgid "MODS"
-#~ msgstr "MODS"
-
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "TEXTUREN"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Niet alle bestanden zijn verwijderd"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "SINGLEPLAYER"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Kan instellingen niet aanpassen: Niets geselecteerd"
-#~ msgid "Finite Liquid"
-#~ msgstr "Eindige vloeistoffen"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Kan geen wereld aanmaken: Geen games gevonden"
-#~ msgid "Preload item visuals"
-#~ msgstr "Voorwerpen vooraf laden"
+#~ msgid "Files to be deleted"
+#~ msgstr "Deze bestanden worden verwijderd"
-#~ msgid "SETTINGS"
-#~ msgstr "INSTELLINGEN"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Kan niets verwijderen: Geen wereld geselecteerd"
-#~ msgid "Password"
-#~ msgstr "Wachtwoord"
+#~ msgid "Address required."
+#~ msgstr "IP-adres nodig."
-#~ msgid "Name"
-#~ msgstr "Naam"
+#~ msgid "Create world"
+#~ msgstr "Maak wereld aan"
-#~ msgid "START SERVER"
-#~ msgstr "START SERVER"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Laat het adres leeg om een lokale server te starten."
-#~ msgid "Favorites:"
-#~ msgstr "Favorieten:"
+#~ msgid "Show Favorites"
+#~ msgstr "Favourieten"
-#~ msgid "CLIENT"
-#~ msgstr "CLIENT"
+#~ msgid "Show Public"
+#~ msgstr "Publieke server"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Mod toevoegen"
+#~ msgid "Advanced"
+#~ msgstr "Geavanceerd"
-#~ msgid "Remove selected mod"
-#~ msgstr "Geselecteerde mod verwijderen"
+#~ msgid "Multiplayer"
+#~ msgstr "Multiplayer"
-#~ msgid "EDIT GAME"
-#~ msgstr "SPEL AANPASSEN"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Kan geen nieuwe wereld aanmaken: de naam bevat onjuiste tekens"
-#~ msgid "new game"
-#~ msgstr "nieuw spel"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Waarschuwing: Instellingen niet consistent. "
-#~ msgid "edit game"
-#~ msgstr "spel aanpassen"
+#~ msgid "Configuration saved. "
+#~ msgstr "Instellingen bewaard. "
-#~ msgid "Mods:"
-#~ msgstr "Mods:"
+#~ msgid "is required by:"
+#~ msgstr "is benodigd voor:"
-#~ msgid "Games"
-#~ msgstr "Spellen"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr ""
+#~ "Linkermuisknop: Verplaats alle items. Rechtermuisknop: Verplaats één item"
-#~ msgid "GAMES"
-#~ msgstr "SPELLEN"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Anisotrope Filtering"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: Kan mod \"$1\" niet naar spel \"$2\" kopiëren"
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapping"
-#~ msgid "Game Name"
-#~ msgstr "Spel"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "Downloaden"
diff --git a/po/pl/minetest.po b/po/pl/minetest.po
index c49c340cd..23d1f83c8 100644
--- a/po/pl/minetest.po
+++ b/po/pl/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-10-08 21:22+0200\n"
"Last-Translator: Maciej Kasatkin <maciej.kasatkin@yahoo.com>\n"
"Language-Team: Polish <>\n"
@@ -19,59 +19,75 @@ msgstr ""
"|| n%100>=20) ? 1 : 2;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "OK"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Åadowanie..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Åšwiat:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Ukryj GrÄ™"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Zależy od:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Zapisz"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Anuluj"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "WÅ‚Ä…cz wszystkie"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Wyłącz wszystkie"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "włączone"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "WÅ‚Ä…cz wszystkie"
@@ -111,7 +127,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Istnieje już świat o nazwie \"$1\""
@@ -125,7 +141,7 @@ msgstr "Na pewno usunąć \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Tak"
@@ -153,15 +169,15 @@ msgstr "Nie"
msgid "Rename Modpack:"
msgstr "Zmień nazwe Paczki Modów:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Accept"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Zainstaluj mod: plik: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -170,16 +186,16 @@ msgstr ""
"\n"
"Instalacja moda: nieznany typ pliku \"$1\""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Instalacja $1 do $2 nie powiodła się"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Instalacja moda: nie można znaleźć odpowiedniego folderu dla paczki modów $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Instalacja moda: nie można znaleźć nazwy moda $1"
@@ -187,49 +203,40 @@ msgstr "Instalacja moda: nie można znaleźć nazwy moda $1"
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "ÅšciÄ…gnij"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Nazwa świata"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Ocena"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Ponowna instalacja"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Instaluj"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Strona $1 z $2"
@@ -237,15 +244,20 @@ msgstr "Strona $1 z $2"
msgid "Credits"
msgstr "Autorzy"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Twórcy"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Aktywni współautorzy"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Twórcy"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Byli współautorzy"
@@ -289,12 +301,13 @@ msgid "Mods"
msgstr "Mody"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Adres/Port"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nazwa gracza/Hasło"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -303,7 +316,7 @@ msgid "Public Serverlist"
msgstr "Lista publicznych serwerów"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Usuń"
@@ -312,15 +325,33 @@ msgstr "Usuń"
msgid "Connect"
msgstr "Połącz"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Tryb kreatywny"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "włączone"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "włączone"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Klient"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nowy"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Ustaw"
@@ -328,17 +359,18 @@ msgstr "Ustaw"
msgid "Start Game"
msgstr "Rozpocznij grę/Połącz"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Wybierz świat:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Tryb kreatywny"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Włącz obrażenia"
@@ -346,6 +378,10 @@ msgstr "Włącz obrażenia"
msgid "Public"
msgstr "Publiczne"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nazwa gracza/Hasło"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -358,150 +394,183 @@ msgstr ""
msgid "Server Port"
msgstr "Port Serwera"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Nie podano nazwy świata lub nie wybrano gry"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Serwer"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Nieprzeźroczysta woda"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Ozdobne drzewa"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Filtrowanie dwuliniowe"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Filtrowanie trójliniowe"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Płynne oświetlenie"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "WÅ‚Ä…cz czÄ…stki"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Chmury 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Ozdobne drzewa"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Nieprzeźroczysta woda"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Połącz"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mappowanie"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtrowanie anizotropowe"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtrowanie dwuliniowe"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtrowanie trójliniowe"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Shadery"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Zmień klawisze"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Pojedynczy gracz"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip-Mappowanie"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Ustawienia"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Pojedynczy gracz"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Ustaw"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Menu główne"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Graj"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Pojedynczy gracz"
@@ -518,39 +587,186 @@ msgstr "Brak informacjii"
msgid "Texturepacks"
msgstr "Paczki tekstur"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Åadowanie..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "Sprawdzanie adresu..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Tekstury przedmiotów..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menu główne"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Błąd połączenia (brak odpowiedzi?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Nie wybrano świata ani adresu."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Nie można znaleźć lub wczytać trybu gry \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Nieprawidłowa specyfikacja trybu gry."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Kontynuuj"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "ZginÄ…Å‚eÅ›."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Wróć do gry"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Domyślne sterowanie:↵\n"
+"- WASD: ruch↵\n"
+"- Spacja: skok/wspinanie się↵\n"
+"- Shift: skradanie się/schodzenie w dół↵\n"
+"- Q: upuszczenie przedmiotu↵\n"
+"- I: ekwipunek↵\n"
+"- Mysz: obracanie się/patrzenie↵\n"
+"- Lewy przycisk myszy: kopanie/uderzanie↵\n"
+"- Prawy przycisk myszy: postawienie/użycie przedmiotu↵\n"
+"- Rolka myszy: wybór przedmiotu↵\n"
+"- T: chat\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Dalej"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Zmień hasło"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Głośność"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Zmień klawisze"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Wyjście do menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Wyjście z gry"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "Wyłączanie..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Tworzenie serwera...."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Tworzenie klienta..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Sprawdzanie adresu..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "ÅÄ…czenie z serwerem..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definicje przedmiotów..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definicje nod..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Media..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -558,14 +774,14 @@ msgstr ""
"\n"
"Sprawdź plik debug.txt by uzyskać więcej informacji."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Kontynuuj"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -580,478 +796,487 @@ msgstr "\"Użyj\" = wspinaj się"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Wciśnij dwukrotnie \"Skok\" by włączyć tryb latania"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Klawisz już zdefiniowany"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "naciśnij klawisz"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Przód"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Tył"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Lewo"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Prawo"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Użyj"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Skok"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Skradanie"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Upuść"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Ekwipunek"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Czat"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Komenda"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Konsola"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Przełącz tryb latania"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Przełącz tryb szybki"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Przełącz tryb szybki"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Przełącz tryb noclip"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Zasięg widzenia"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Drukuj stosy"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Stare hasło"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Nowe hasło"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Potwierdź hasło"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Zmień"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Hasła nie są jednakowe!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Głośność: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Wyjście"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Lewy przycisk myszy"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Åšrodkowy przycisk myszy"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Prawy przycisk myszy"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X Button 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Backspace"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Delete"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X Button 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pause"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Mode Change"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Next"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Prior"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Spacja"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Dół"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Execute"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Select"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Góra"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Pomoc"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Snapshot"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Lewy Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Apps"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Prawy Windows"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sleep"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Lewy Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Prawy Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Lewy Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Lewy Menu"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Prawy Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Prawy Menu"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Przecinek"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Minus"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Kropka"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Erase OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clear"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menu główne"
+#~ msgid "Game Name"
+#~ msgstr "Nazwa Gry"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr: Kopiowanie moda \"$1\" do gry \"$2\" nie powiodło się"
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Błąd połączenia (brak odpowiedzi?)"
+#~ msgid "GAMES"
+#~ msgstr "GRY"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Nie wybrano świata ani adresu."
+#~ msgid "Games"
+#~ msgstr "Gry"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#~ msgid "Mods:"
+#~ msgstr "Mody:"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Nie można znaleźć lub wczytać trybu gry \""
+#~ msgid "edit game"
+#~ msgstr "edytuj grÄ™"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Nieprawidłowa specyfikacja trybu gry."
+#~ msgid "new game"
+#~ msgstr "nowa gra"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr ""
-#~ "Lewy przycisk myszy: przenieÅ› wszystkie przedmioty, Prawy przycisk myszy: "
-#~ "przenieÅ› pojedynczy przedmiot"
+#~ msgid "EDIT GAME"
+#~ msgstr "EDYTUJ GRĘ"
-#~ msgid "is required by:"
-#~ msgstr "wymagane przez:"
+#~ msgid "Remove selected mod"
+#~ msgstr "Usuń zaznaczony mod"
-#~ msgid "Configuration saved. "
-#~ msgstr "Konfiguracja zapisana. "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<--Dodaj mod"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Ostrzeżenie: Plik konfiguracyjny niespójny. "
+#~ msgid "CLIENT"
+#~ msgstr "KLIENT"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Nie można stworzyć świata: Nazwa zawiera niedozwolone znaki"
+#~ msgid "Favorites:"
+#~ msgstr "Ulubione:"
-#~ msgid "Multiplayer"
-#~ msgstr "Gra wieloosobowa"
+#~ msgid "START SERVER"
+#~ msgstr "URUCHOM SERWER"
-#~ msgid "Advanced"
-#~ msgstr "Zaawansowane"
+#~ msgid "Name"
+#~ msgstr "Nazwa"
-#~ msgid "Show Public"
-#~ msgstr "Pokaż publiczne"
+#~ msgid "Password"
+#~ msgstr "Hasło"
-#~ msgid "Show Favorites"
-#~ msgstr "Pokaż ulubione"
+#~ msgid "SETTINGS"
+#~ msgstr "USTAWIENIA"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Pozostaw pole adresu puste, by uruchomić serwer lokalny."
+#~ msgid "Preload item visuals"
+#~ msgstr "Åaduj obrazy przedmiotów"
-#~ msgid "Create world"
-#~ msgstr "Stwórz świat"
+#~ msgid "Finite Liquid"
+#~ msgstr "Realistyczne ciecze"
-#~ msgid "Address required."
-#~ msgstr "Wymagany adres."
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "TRYB JEDNOOSOBOWY"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Nie można skasować świata: nic nie zaznaczono"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "PACZKI TEKSTUR"
-#~ msgid "Files to be deleted"
-#~ msgstr "Pliki do skasowania"
+#~ msgid "MODS"
+#~ msgstr "MODY"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Nie można utworzyć świata: Nie znaleziono żadnego trybu gry"
+#, fuzzy
+#~ msgid "Add mod:"
+#~ msgstr "<<--Dodaj mod"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Nie można skonfigurować świata: Nic nie zaznaczono"
+#, fuzzy
+#~ msgid "Local install"
+#~ msgstr "Instaluj"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Nie udało się skasować wszystkich plików świata"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Uwaga: Niektóre z modyfikacji nie zostały jeszcze skonfigurowane.\n"
+#~ "Zostaną domyślnie włączone gdy zapiszesz konfigurację. "
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Ostrzeżenie: Niektóre z modyfikacji nie zostały znalezione.\n"
+#~ "Ich ustawienia zostaną usunięte gdy zapiszesz konfigurację. "
#~ msgid ""
#~ "Default Controls:\n"
@@ -1078,148 +1303,65 @@ msgstr "Nieprawidłowa specyfikacja trybu gry."
#~ "- ESC: to menu\n"
#~ "- T: czat\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Ostrzeżenie: Niektóre z modyfikacji nie zostały znalezione.\n"
-#~ "Ich ustawienia zostaną usunięte gdy zapiszesz konfigurację. "
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Uwaga: Niektóre z modyfikacji nie zostały jeszcze skonfigurowane.\n"
-#~ "Zostaną domyślnie włączone gdy zapiszesz konfigurację. "
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Domyślne sterowanie:↵\n"
-#~ "- WASD: ruch↵\n"
-#~ "- Spacja: skok/wspinanie się↵\n"
-#~ "- Shift: skradanie się/schodzenie w dół↵\n"
-#~ "- Q: upuszczenie przedmiotu↵\n"
-#~ "- I: ekwipunek↵\n"
-#~ "- Mysz: obracanie się/patrzenie↵\n"
-#~ "- Lewy przycisk myszy: kopanie/uderzanie↵\n"
-#~ "- Prawy przycisk myszy: postawienie/użycie przedmiotu↵\n"
-#~ "- Rolka myszy: wybór przedmiotu↵\n"
-#~ "- T: chat\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Wyjście z gry"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Wyjście do menu"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Głośność"
-
-#~ msgid "Change Password"
-#~ msgstr "Zmień hasło"
-
-#~ msgid "Continue"
-#~ msgstr "Dalej"
-
-#~ msgid "You died."
-#~ msgstr "ZginÄ…Å‚eÅ›."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Wyłączanie..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "ÅÄ…czenie z serwerem..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Sprawdzanie adresu..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Tworzenie klienta..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Tworzenie serwera...."
-
-#~ msgid "Loading..."
-#~ msgstr "Åadowanie..."
-
-#, fuzzy
-#~ msgid "Local install"
-#~ msgstr "Instaluj"
-
-#, fuzzy
-#~ msgid "Add mod:"
-#~ msgstr "<<--Dodaj mod"
-
-#~ msgid "MODS"
-#~ msgstr "MODY"
-
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "PACZKI TEKSTUR"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Nie udało się skasować wszystkich plików świata"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "TRYB JEDNOOSOBOWY"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Nie można skonfigurować świata: Nic nie zaznaczono"
-#~ msgid "Finite Liquid"
-#~ msgstr "Realistyczne ciecze"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Nie można utworzyć świata: Nie znaleziono żadnego trybu gry"
-#~ msgid "Preload item visuals"
-#~ msgstr "Åaduj obrazy przedmiotów"
+#~ msgid "Files to be deleted"
+#~ msgstr "Pliki do skasowania"
-#~ msgid "SETTINGS"
-#~ msgstr "USTAWIENIA"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Nie można skasować świata: nic nie zaznaczono"
-#~ msgid "Password"
-#~ msgstr "Hasło"
+#~ msgid "Address required."
+#~ msgstr "Wymagany adres."
-#~ msgid "Name"
-#~ msgstr "Nazwa"
+#~ msgid "Create world"
+#~ msgstr "Stwórz świat"
-#~ msgid "START SERVER"
-#~ msgstr "URUCHOM SERWER"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Pozostaw pole adresu puste, by uruchomić serwer lokalny."
-#~ msgid "Favorites:"
-#~ msgstr "Ulubione:"
+#~ msgid "Show Favorites"
+#~ msgstr "Pokaż ulubione"
-#~ msgid "CLIENT"
-#~ msgstr "KLIENT"
+#~ msgid "Show Public"
+#~ msgstr "Pokaż publiczne"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<--Dodaj mod"
+#~ msgid "Advanced"
+#~ msgstr "Zaawansowane"
-#~ msgid "Remove selected mod"
-#~ msgstr "Usuń zaznaczony mod"
+#~ msgid "Multiplayer"
+#~ msgstr "Gra wieloosobowa"
-#~ msgid "EDIT GAME"
-#~ msgstr "EDYTUJ GRĘ"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Nie można stworzyć świata: Nazwa zawiera niedozwolone znaki"
-#~ msgid "new game"
-#~ msgstr "nowa gra"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Ostrzeżenie: Plik konfiguracyjny niespójny. "
-#~ msgid "edit game"
-#~ msgstr "edytuj grÄ™"
+#~ msgid "Configuration saved. "
+#~ msgstr "Konfiguracja zapisana. "
-#~ msgid "Mods:"
-#~ msgstr "Mody:"
+#~ msgid "is required by:"
+#~ msgstr "wymagane przez:"
-#~ msgid "Games"
-#~ msgstr "Gry"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr ""
+#~ "Lewy przycisk myszy: przenieÅ› wszystkie przedmioty, Prawy przycisk myszy: "
+#~ "przenieÅ› pojedynczy przedmiot"
-#~ msgid "GAMES"
-#~ msgstr "GRY"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Filtrowanie anizotropowe"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: Kopiowanie moda \"$1\" do gry \"$2\" nie powiodło się"
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mappowanie"
-#~ msgid "Game Name"
-#~ msgstr "Nazwa Gry"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "ÅšciÄ…gnij"
diff --git a/po/pt/minetest.po b/po/pt/minetest.po
index 24680e2d4..b433ef245 100644
--- a/po/pt/minetest.po
+++ b/po/pt/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2014-01-06 01:45+0200\n"
"Last-Translator: João Farias <jgfd@cin.ufpe.br>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,59 +18,75 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Ok"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "A carregar..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Mundo:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Esconder Jogo"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Extra:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Depende de:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Guardar"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Cancelar"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "Ativar Tudo"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Desativar Tudo"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "ativo"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
#, fuzzy
msgid "Enable all"
msgstr "Ativar Tudo"
@@ -111,7 +127,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "O mundo com o nome \"$1\" já existe"
@@ -125,7 +141,7 @@ msgstr "Tem a certeza que pertende eliminar \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Sim"
@@ -153,15 +169,15 @@ msgstr "Não"
msgid "Rename Modpack:"
msgstr "Renomear Pacote de Extras:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Aceitar"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Instalar Extra: ficheiro: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -170,15 +186,15 @@ msgstr ""
"\n"
"Instalar Extra: tipo de ficheiro desconhecido \"$1\""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Falha ao instalar de $1 ao $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -186,49 +202,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Descarregar"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Nome do Mundo"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Classificação"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "re-Instalar"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Instalar"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Página $1 de $2"
@@ -236,15 +243,20 @@ msgstr "Página $1 de $2"
msgid "Credits"
msgstr "Créditos"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Desenvolvedores Chave"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Contribuintes Ativos"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Desenvolvedores Chave"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Antigos Contribuintes"
@@ -288,12 +300,13 @@ msgid "Mods"
msgstr "Extras"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Endereço/Porta"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nome/Senha"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -302,7 +315,7 @@ msgid "Public Serverlist"
msgstr "Lista de Servidores Públicos"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Eliminar"
@@ -311,15 +324,33 @@ msgstr "Eliminar"
msgid "Connect"
msgstr "Ligar"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Modo Criativo"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "ativo"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "ativo"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Cliente"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Novo"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Configurar"
@@ -327,17 +358,18 @@ msgstr "Configurar"
msgid "Start Game"
msgstr "Jogar"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Seleccionar Mundo:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Modo Criativo"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Ativar Dano"
@@ -345,6 +377,10 @@ msgstr "Ativar Dano"
msgid "Public"
msgstr "Público"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nome/Senha"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -357,150 +393,183 @@ msgstr ""
msgid "Server Port"
msgstr "Porta"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Mundo sem nome ou nenhum jogo selecionado"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Servidor"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Ãgua Opaca"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Ãrvores Melhoradas"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Filtro Bi-Linear"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Filtro Tri-Linear"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Iluminação Suave"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Ativar Partículas"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Nuvens 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Ãrvores Melhoradas"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Ãgua Opaca"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Ligar"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-Mapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtro Anisotrópico"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtro Bi-Linear"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtro Tri-Linear"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Sombras"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Mudar teclas"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Um Jogador"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip-Mapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Definições"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Um Jogador"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Configurar"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Menu Principal"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Jogar"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Um Jogador"
@@ -517,39 +586,187 @@ msgstr "Sem informação"
msgid "Texturepacks"
msgstr "Pacotes de Texturas"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "A carregar..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "A resolver endereço..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Texturas dos items..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menu Principal"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Erro de conexão (excedeu tempo?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+"Nenhum mundo seleccionado e nenhum endereço providenciado. Nada para fazer."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Não foi possível encontrar ou carregar jogo \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "gamespec inválido."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Continuar"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Morreste."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Reaparecer"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Teclas por defeito:\n"
+"- WASD: mover\n"
+"- Barra de espaço: saltar/subir\n"
+"- Shift: andar cuidadosamente/descer\n"
+"- Q: Largar item\n"
+"- I: Inventário\n"
+"- Rato: virar/olhar\n"
+"- Clique esquerdo: cavar/bater\n"
+"- Clique direito: colocar/utilizar\n"
+"- Roda do rato: seleccionar item\n"
+"- T: conversação\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Continuar"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Mudar Senha"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volume do som"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Mudar teclas"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Sair para Menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Sair para o sistema"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "A desligar..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "A criar servidor..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "A criar cliente..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "A resolver endereço..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "A conectar ao servidor..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definições dos Itens..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Dados..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -557,14 +774,14 @@ msgstr ""
"\n"
"Consulte debug.txt para mais detalhes."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Continuar"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr "Teclas. (Se este menu se estragar, remova as linhas do minetest.conf)"
@@ -577,477 +794,489 @@ msgstr "\"Use\" = descer"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Carregue duas vezes em \"saltar\" para ativar o vôo"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Tecla já em uso"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "pressione a tecla"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Avançar"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Recuar"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Esquerda"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Direita"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Usar"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Saltar"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Agachar"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Largar"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventário"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Conversa"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Comando"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Consola"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Ativar/Desativar vôo"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Ativar/Desativar correr"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Ativar/Desativar correr"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Ativar/Desativar noclip"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Seleccionar Distância"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Imprimir stacks"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Senha antiga"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Senha Nova"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Confirmar Senha"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Mudar"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Senhas não correspondem!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Volume do som: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Sair"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Botão Esquerdo"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Roda do Rato"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Botão Direito"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Botão X 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Voltar"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Limpar"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabulação"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Botão X 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Capital"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pausa"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Converter"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "ESC"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nãoconverter"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Mode Change"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Próximo"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Prévio"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Espaço"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Baixo"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Executar"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Seleccionar"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Cima"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Ajuda"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Screenshot"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "WINDOWS Esq."
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "App"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "WINDOWS Dir."
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Suspender"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Shift Esquerdo"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Shift Direito"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Control Esq"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Menu Esquerdo"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Control Direito"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Menu Direito"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Virgula"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Menos"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Período"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Mais"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attm"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Apagar OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "Limpar OEM"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PAL"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menu Principal"
+#~ msgid "Game Name"
+#~ msgstr "Nome do Jogo"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr ""
+#~ "Mensagem de Jogo: Impossível fazer cópia do extra \"$1\" para o jogo "
+#~ "\"$2\""
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Erro de conexão (excedeu tempo?)"
+#~ msgid "GAMES"
+#~ msgstr "JOGOS"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-"Nenhum mundo seleccionado e nenhum endereço providenciado. Nada para fazer."
+#~ msgid "Games"
+#~ msgstr "Jogos"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#~ msgid "Mods:"
+#~ msgstr "Extras:"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Não foi possível encontrar ou carregar jogo \""
+#~ msgid "edit game"
+#~ msgstr "editar jogo"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "gamespec inválido."
+#~ msgid "new game"
+#~ msgstr "novo jogo"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "Botão esq: Mover todos os itens Botão dir: Mover um item"
+#~ msgid "EDIT GAME"
+#~ msgstr "EDITAR JOGO"
-#~ msgid "is required by:"
-#~ msgstr "é necessário pelo:"
+#~ msgid "Remove selected mod"
+#~ msgstr "Remover extra selecionado"
-#~ msgid "Configuration saved. "
-#~ msgstr "Configuração gravada. "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Adicionar extra"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Atenção: Configuração não compatível. "
+#~ msgid "CLIENT"
+#~ msgstr "CLIENTE"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Não foi possível criar mundo: Nome com caracteres inválidos"
+#~ msgid "Favorites:"
+#~ msgstr "Favoritos:"
-#~ msgid "Multiplayer"
-#~ msgstr "Vários jogadores"
+#~ msgid "START SERVER"
+#~ msgstr "INICIAR SERVIDOR"
-#~ msgid "Advanced"
-#~ msgstr "Avançado"
+#~ msgid "Name"
+#~ msgstr "Nome"
-#~ msgid "Show Public"
-#~ msgstr "Mostrar Públicos"
+#~ msgid "Password"
+#~ msgstr "Senha"
-#~ msgid "Show Favorites"
-#~ msgstr "Mostrar Favoritos"
+#~ msgid "SETTINGS"
+#~ msgstr "DEFINIÇÕES"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Deixe endereço em branco para iniciar servidor local."
+#~ msgid "Preload item visuals"
+#~ msgstr "Pré-carregamento dos itens"
-#~ msgid "Create world"
-#~ msgstr "Criar mundo"
+#~ msgid "Finite Liquid"
+#~ msgstr "Líquido Finito"
-#~ msgid "Address required."
-#~ msgstr "Endereço necessário."
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "Um Jogador"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Não foi possível eliminar mundo: Nada seleccionado"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "PACOTES DE TEXTURAS"
-#~ msgid "Files to be deleted"
-#~ msgstr "Ficheiros para eliminar"
+#~ msgid "MODS"
+#~ msgstr "EXTRAS"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Não foi possível criar mundo: Não foram detectados jogos"
+#, fuzzy
+#~ msgid "Add mod:"
+#~ msgstr "<<-- Adicionar extra"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Não foi possível configurar mundo: Nada seleccionado"
+#, fuzzy
+#~ msgid "Local install"
+#~ msgstr "Instalar"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Falhou a remoção de todos os ficheiros dos mundos"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Atenção: Alguns mods ainda não estão configurados.\n"
+#~ "Eles vão ser ativados por predefinição quando guardar a configuração. "
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Atenção: Alguns mods configurados estão em falta.\n"
+#~ "As suas definições vão ser removidas quando gravar a configuração. "
#~ msgid ""
#~ "Default Controls:\n"
@@ -1074,150 +1303,63 @@ msgstr "gamespec inválido."
#~ "- ESC: Este menu\n"
#~ "- T: Chat\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Atenção: Alguns mods configurados estão em falta.\n"
-#~ "As suas definições vão ser removidas quando gravar a configuração. "
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Atenção: Alguns mods ainda não estão configurados.\n"
-#~ "Eles vão ser ativados por predefinição quando guardar a configuração. "
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Teclas por defeito:\n"
-#~ "- WASD: mover\n"
-#~ "- Barra de espaço: saltar/subir\n"
-#~ "- Shift: andar cuidadosamente/descer\n"
-#~ "- Q: Largar item\n"
-#~ "- I: Inventário\n"
-#~ "- Rato: virar/olhar\n"
-#~ "- Clique esquerdo: cavar/bater\n"
-#~ "- Clique direito: colocar/utilizar\n"
-#~ "- Roda do rato: seleccionar item\n"
-#~ "- T: conversação\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Sair para o sistema"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Sair para Menu"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volume do som"
-
-#~ msgid "Change Password"
-#~ msgstr "Mudar Senha"
-
-#~ msgid "Continue"
-#~ msgstr "Continuar"
-
-#~ msgid "You died."
-#~ msgstr "Morreste."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "A desligar..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "A conectar ao servidor..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "A resolver endereço..."
-
-#~ msgid "Creating client..."
-#~ msgstr "A criar cliente..."
-
-#~ msgid "Creating server...."
-#~ msgstr "A criar servidor..."
-
-#~ msgid "Loading..."
-#~ msgstr "A carregar..."
-
-#, fuzzy
-#~ msgid "Local install"
-#~ msgstr "Instalar"
-
-#, fuzzy
-#~ msgid "Add mod:"
-#~ msgstr "<<-- Adicionar extra"
-
-#~ msgid "MODS"
-#~ msgstr "EXTRAS"
-
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "PACOTES DE TEXTURAS"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Falhou a remoção de todos os ficheiros dos mundos"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "Um Jogador"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Não foi possível configurar mundo: Nada seleccionado"
-#~ msgid "Finite Liquid"
-#~ msgstr "Líquido Finito"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Não foi possível criar mundo: Não foram detectados jogos"
-#~ msgid "Preload item visuals"
-#~ msgstr "Pré-carregamento dos itens"
+#~ msgid "Files to be deleted"
+#~ msgstr "Ficheiros para eliminar"
-#~ msgid "SETTINGS"
-#~ msgstr "DEFINIÇÕES"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Não foi possível eliminar mundo: Nada seleccionado"
-#~ msgid "Password"
-#~ msgstr "Senha"
+#~ msgid "Address required."
+#~ msgstr "Endereço necessário."
-#~ msgid "Name"
-#~ msgstr "Nome"
+#~ msgid "Create world"
+#~ msgstr "Criar mundo"
-#~ msgid "START SERVER"
-#~ msgstr "INICIAR SERVIDOR"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Deixe endereço em branco para iniciar servidor local."
-#~ msgid "Favorites:"
-#~ msgstr "Favoritos:"
+#~ msgid "Show Favorites"
+#~ msgstr "Mostrar Favoritos"
-#~ msgid "CLIENT"
-#~ msgstr "CLIENTE"
+#~ msgid "Show Public"
+#~ msgstr "Mostrar Públicos"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Adicionar extra"
+#~ msgid "Advanced"
+#~ msgstr "Avançado"
-#~ msgid "Remove selected mod"
-#~ msgstr "Remover extra selecionado"
+#~ msgid "Multiplayer"
+#~ msgstr "Vários jogadores"
-#~ msgid "EDIT GAME"
-#~ msgstr "EDITAR JOGO"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Não foi possível criar mundo: Nome com caracteres inválidos"
-#~ msgid "new game"
-#~ msgstr "novo jogo"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Atenção: Configuração não compatível. "
-#~ msgid "edit game"
-#~ msgstr "editar jogo"
+#~ msgid "Configuration saved. "
+#~ msgstr "Configuração gravada. "
-#~ msgid "Mods:"
-#~ msgstr "Extras:"
+#~ msgid "is required by:"
+#~ msgstr "é necessário pelo:"
-#~ msgid "Games"
-#~ msgstr "Jogos"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "Botão esq: Mover todos os itens Botão dir: Mover um item"
-#~ msgid "GAMES"
-#~ msgstr "JOGOS"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Filtro Anisotrópico"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr ""
-#~ "Mensagem de Jogo: Impossível fazer cópia do extra \"$1\" para o jogo "
-#~ "\"$2\""
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapping"
-#~ msgid "Game Name"
-#~ msgstr "Nome do Jogo"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "Descarregar"
diff --git a/po/pt_BR/minetest.po b/po/pt_BR/minetest.po
index b8b68a661..830bd5e82 100644
--- a/po/pt_BR/minetest.po
+++ b/po/pt_BR/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-11-25 02:13+0200\n"
"Last-Translator: Frederico Guimarães <frederico@teia.bio.br>\n"
"Language-Team: LANGUAGE <contato@ejweb.com.br>\n"
@@ -18,57 +18,73 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Ok"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Carregando..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Mundo:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Ocultar jogos"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Ocultar conteúdo PMs"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Depende de:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Salvar"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Cancelar"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Habilitar PMs"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Desabilitar PMs"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "habilitado"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Habilitar todos"
@@ -108,7 +124,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Já existe um mundo com o nome \"$1\""
@@ -123,7 +139,7 @@ msgstr "Tem certeza que deseja excluir \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Sim"
@@ -151,15 +167,15 @@ msgstr "Não"
msgid "Rename Modpack:"
msgstr "Renomear pacote de módulos:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Aceitar"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Instalação de módulo: arquivo: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -168,17 +184,17 @@ msgstr ""
"\n"
"Instalação de módulo: o tipo de arquivo \"$1\" não é suportado"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Não foi possível instalar $1 em $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Instalação de módulo: não foi possível encontrar o nome adequado da pasta "
"para o pacote de módulos $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
"Instalação de módulo: não foi possível encontrar o nome real do módulo: $1"
@@ -187,49 +203,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Baixar"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Nome do mundo"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Classificação"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "reinstalar"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Instalar"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Página $1 de $2"
@@ -237,15 +244,20 @@ msgstr "Página $1 de $2"
msgid "Credits"
msgstr "Créditos"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Desenvolvedores principais"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Colaboradores ativos"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Desenvolvedores principais"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Colaboradores anteriores"
@@ -286,12 +298,13 @@ msgid "Mods"
msgstr "Módulos"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Endereço/Porta"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nome/Senha"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -300,7 +313,7 @@ msgid "Public Serverlist"
msgstr "Servidores públicos"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Excluir"
@@ -309,15 +322,33 @@ msgstr "Excluir"
msgid "Connect"
msgstr "Conectar"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Modo criativo"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "habilitado"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "habilitado"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Cliente"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Novo"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Configurar"
@@ -325,17 +356,18 @@ msgstr "Configurar"
msgid "Start Game"
msgstr "Iniciar o jogo"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Selecione um mundo:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Modo criativo"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Habilitar dano"
@@ -343,6 +375,10 @@ msgstr "Habilitar dano"
msgid "Public"
msgstr "Público"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nome/Senha"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -355,150 +391,184 @@ msgstr ""
msgid "Server Port"
msgstr "Porta do servidor"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr ""
+"Não foi fornecido nenhum nome para o mundo ou não foi selecionado nenhum jogo"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Servidor"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Ãgua opaca"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Ãrvores melhores"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Filtragem bi-linear"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Filtragem tri-linear"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Iluminação suave"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Habilitar partículas"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Nuvens 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Ãrvores melhores"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Ãgua opaca"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Conectar"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mipmapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtragem anisotrópica"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtragem bi-linear"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtragem tri-linear"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Sombreadores"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Mudar teclas"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Um jogador"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mipmapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Para habilitar os sombreadores é necessário usar o driver OpenGL."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Configurações"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Um jogador"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Configurar"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Menu principal"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Jogar"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Um jogador"
@@ -515,39 +585,188 @@ msgstr "Nenhuma informação disponível"
msgid "Texturepacks"
msgstr "Pacotes de texturas"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Carregando..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "Resolvendo os endereços..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Texturas dos itens..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Menu principal"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Erro de conexão (tempo excedido?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr ""
+"Nenhum mundo foi selecionado e nenhum endereço fornecido. Não existe nada a "
+"ser feito."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Não foi possível localizar ou carregar jogo \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Especificação do jogo inválida."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "needs_fallback_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Continuar"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Você morreu."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Reviver"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Controles padrão:\n"
+"- WASD: mover\n"
+"- Espaço: pular/escalar\n"
+"- Shift: esgueirar/descer\n"
+"- Q: descartar o item\n"
+"- I: inventário\n"
+"- Mouse: virar/olhar\n"
+"- Botão esquerdo: cavar/atingir\n"
+"- Botão direito: colocar/usar\n"
+"- Roda: selecionar item\n"
+"- T: bate-papo\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Continuar"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Alterar a senha"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volume do som"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Mudar teclas"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Sair para o menu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Sair do Minetest"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "Desligando tudo..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Criando o servidor..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Criando o cliente..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Resolvendo os endereços..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Conectando ao servidor..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definições dos itens..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definições dos nós..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Mídia..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -555,14 +774,14 @@ msgstr ""
"\n"
"Verifique o debug.txt para mais detalhes."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Continuar"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -577,478 +796,485 @@ msgstr "\"Usar\" = descer"
msgid "Double tap \"jump\" to toggle fly"
msgstr "\"Pular\" duas vezes ativa o voo"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Essa tecla já está em uso"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "press. uma tecla"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Avançar"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Voltar"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Esquerda"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Direita"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Usar"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Pular"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Esgueirar"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Soltar"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventário"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Bate-papo"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Comando"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Console"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Alternar voo"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Alternar corrida"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Alternar corrida"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Alternar noclip"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Sel. distância"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Impr. pilha (log)"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Senha antiga"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Nova senha"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Confirmar a senha"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Alterar"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "As senhas não correspondem!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Volume do som: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Sair"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Botão esquerdo"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Roda do mouse"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Botão direito"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Botão X 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Backspace"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Limpar"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Botão X 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Ctrl"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Menu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pause"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Mode Change"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Page Down"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Page Up"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Espaço"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Abaixo"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Executar"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print Screen"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Select"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Acima"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Ajuda"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Captura de tela"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Windows esq."
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Apps"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Tecl.num.0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Tecl.num.1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Windows dir."
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sleep"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Tecl.num.2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Tecl.num.3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Tecl.num.4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Tecl.num.5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Tecl.num.6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Tecl.num.7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Tecl.num.*"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Tecl.num.+"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Tecl.num.-"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Tecl.num./"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Tecl.num.8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Tecl.num.9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Shift esq."
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Shift dir."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Ctrl esq."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Menu esq."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Ctrl dir."
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Menu dir."
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Vírgula"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Menos"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Ponto"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Mais"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Apagar OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "Limpar OEM"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Zoom"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Menu principal"
+#~ msgid "Game Name"
+#~ msgstr "Nome do jogo"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr: Não foi possível copiar o mod \"$1\" para o jogo \"$2\""
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Erro de conexão (tempo excedido?)"
+#~ msgid "GAMES"
+#~ msgstr "JOGOS"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr ""
-"Nenhum mundo foi selecionado e nenhum endereço fornecido. Não existe nada a "
-"ser feito."
+#~ msgid "Games"
+#~ msgstr "Jogos"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#~ msgid "Mods:"
+#~ msgstr "Módulos:"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Não foi possível localizar ou carregar jogo \""
+#~ msgid "edit game"
+#~ msgstr "editar o jogo"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Especificação do jogo inválida."
+#~ msgid "new game"
+#~ msgstr "novo jogo"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "Botão esquerdo: Move todos os itens. Botão direito: Move um item"
+#~ msgid "EDIT GAME"
+#~ msgstr "EDITAR JOGO"
-#~ msgid "is required by:"
-#~ msgstr "é necessário para:"
+#~ msgid "Remove selected mod"
+#~ msgstr "Remover o módulo selecionado"
-#~ msgid "Configuration saved. "
-#~ msgstr "A configuração foi salva. "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Adicionar módulo"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Atenção: A configuração não está consistente."
+#~ msgid "CLIENT"
+#~ msgstr "CLIENTE"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Não foi possível criar o mundo: O nome contém caracteres inválidos"
+#~ msgid "Favorites:"
+#~ msgstr "Favoritos:"
-#~ msgid "Multiplayer"
-#~ msgstr "Vários jogadores"
+#~ msgid "START SERVER"
+#~ msgstr "SERVIDOR"
-#~ msgid "Advanced"
-#~ msgstr "Avançado"
+#~ msgid "Name"
+#~ msgstr "Nome"
-#~ msgid "Show Public"
-#~ msgstr "Exibir públicos"
+#~ msgid "Password"
+#~ msgstr "Senha"
-#~ msgid "Show Favorites"
-#~ msgstr "Exibir favoritos"
+#~ msgid "SETTINGS"
+#~ msgstr "CONFIGURAÇÕES"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Deixe o endereço em branco para iniciar um servidor local."
+#~ msgid "Preload item visuals"
+#~ msgstr "Precarga de elementos visuais"
-#~ msgid "Create world"
-#~ msgstr "Criar o mundo"
+#~ msgid "Finite Liquid"
+#~ msgstr "Líquido finito"
-#~ msgid "Address required."
-#~ msgstr "É necessário um endereço."
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "UM JOGADOR"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Não foi possível excluir o mundo: Nenhum foi selecionado"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "TEXTURAS"
-#~ msgid "Files to be deleted"
-#~ msgstr "Arquivos a serem excluídos"
+#~ msgid "MODS"
+#~ msgstr "MÓDULOS"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Não foi possivel criar o mundo: Não foi encontrado nenhum jogo"
+#~ msgid "Add mod:"
+#~ msgstr "Adicionar módulo:"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Não foi possível configurar o mundo: Nada foi selecionado"
+#~ msgid "Local install"
+#~ msgstr "Instalação local"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Não foi possível excluir todos os arquivos do mundo"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Atenção: Alguns mods ainda não foram configurados.\n"
+#~ "E eles serão ativados por padrão, quando você salvar a configuração."
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Atenção: Alguns mods configurados não foram encontrados.\n"
+#~ "Suas definições serão removidas quando você salvar a configuração."
#~ msgid ""
#~ "Default Controls:\n"
@@ -1075,146 +1301,63 @@ msgstr "Especificação do jogo inválida."
#~ "- ESC: este menu\n"
#~ "- T: bate-papo\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Atenção: Alguns mods configurados não foram encontrados.\n"
-#~ "Suas definições serão removidas quando você salvar a configuração."
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Atenção: Alguns mods ainda não foram configurados.\n"
-#~ "E eles serão ativados por padrão, quando você salvar a configuração."
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Controles padrão:\n"
-#~ "- WASD: mover\n"
-#~ "- Espaço: pular/escalar\n"
-#~ "- Shift: esgueirar/descer\n"
-#~ "- Q: descartar o item\n"
-#~ "- I: inventário\n"
-#~ "- Mouse: virar/olhar\n"
-#~ "- Botão esquerdo: cavar/atingir\n"
-#~ "- Botão direito: colocar/usar\n"
-#~ "- Roda: selecionar item\n"
-#~ "- T: bate-papo\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Sair do Minetest"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Sair para o menu"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volume do som"
-
-#~ msgid "Change Password"
-#~ msgstr "Alterar a senha"
-
-#~ msgid "Continue"
-#~ msgstr "Continuar"
-
-#~ msgid "You died."
-#~ msgstr "Você morreu."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Desligando tudo..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Conectando ao servidor..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Resolvendo os endereços..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Criando o cliente..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Criando o servidor..."
-
-#~ msgid "Loading..."
-#~ msgstr "Carregando..."
-
-#~ msgid "Local install"
-#~ msgstr "Instalação local"
-
-#~ msgid "Add mod:"
-#~ msgstr "Adicionar módulo:"
-
-#~ msgid "MODS"
-#~ msgstr "MÓDULOS"
-
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "TEXTURAS"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Não foi possível excluir todos os arquivos do mundo"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "UM JOGADOR"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Não foi possível configurar o mundo: Nada foi selecionado"
-#~ msgid "Finite Liquid"
-#~ msgstr "Líquido finito"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Não foi possivel criar o mundo: Não foi encontrado nenhum jogo"
-#~ msgid "Preload item visuals"
-#~ msgstr "Precarga de elementos visuais"
+#~ msgid "Files to be deleted"
+#~ msgstr "Arquivos a serem excluídos"
-#~ msgid "SETTINGS"
-#~ msgstr "CONFIGURAÇÕES"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Não foi possível excluir o mundo: Nenhum foi selecionado"
-#~ msgid "Password"
-#~ msgstr "Senha"
+#~ msgid "Address required."
+#~ msgstr "É necessário um endereço."
-#~ msgid "Name"
-#~ msgstr "Nome"
+#~ msgid "Create world"
+#~ msgstr "Criar o mundo"
-#~ msgid "START SERVER"
-#~ msgstr "SERVIDOR"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Deixe o endereço em branco para iniciar um servidor local."
-#~ msgid "Favorites:"
-#~ msgstr "Favoritos:"
+#~ msgid "Show Favorites"
+#~ msgstr "Exibir favoritos"
-#~ msgid "CLIENT"
-#~ msgstr "CLIENTE"
+#~ msgid "Show Public"
+#~ msgstr "Exibir públicos"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Adicionar módulo"
+#~ msgid "Advanced"
+#~ msgstr "Avançado"
-#~ msgid "Remove selected mod"
-#~ msgstr "Remover o módulo selecionado"
+#~ msgid "Multiplayer"
+#~ msgstr "Vários jogadores"
-#~ msgid "EDIT GAME"
-#~ msgstr "EDITAR JOGO"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Não foi possível criar o mundo: O nome contém caracteres inválidos"
-#~ msgid "new game"
-#~ msgstr "novo jogo"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Atenção: A configuração não está consistente."
-#~ msgid "edit game"
-#~ msgstr "editar o jogo"
+#~ msgid "Configuration saved. "
+#~ msgstr "A configuração foi salva. "
-#~ msgid "Mods:"
-#~ msgstr "Módulos:"
+#~ msgid "is required by:"
+#~ msgstr "é necessário para:"
-#~ msgid "Games"
-#~ msgstr "Jogos"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "Botão esquerdo: Move todos os itens. Botão direito: Move um item"
-#~ msgid "GAMES"
-#~ msgstr "JOGOS"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Filtragem anisotrópica"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: Não foi possível copiar o mod \"$1\" para o jogo \"$2\""
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mipmapping"
-#~ msgid "Game Name"
-#~ msgstr "Nome do jogo"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "Baixar"
diff --git a/po/ro/minetest.po b/po/ro/minetest.po
index eefdd5f22..e58dace34 100644
--- a/po/ro/minetest.po
+++ b/po/ro/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-12-18 21:44+0200\n"
"Last-Translator: King Artur <david1989mail@yahoo.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,57 +19,73 @@ msgstr ""
"20)) ? 1 : 2;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr "Ok"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Se încarcă..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Lume:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Ascunde Joc"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Ascunde conținutul mp"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Mod:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "Dependințe:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Salvează"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Anulează"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Activează MP"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Dezactivează MP"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "activat"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Activează tot"
@@ -109,7 +125,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "O lume cu numele \"$1\" deja există"
@@ -123,7 +139,7 @@ msgstr "Ești sigur că vrei să ștergi \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Da"
@@ -151,15 +167,15 @@ msgstr "Nu"
msgid "Rename Modpack:"
msgstr "Redenumiți Pachetul de moduri:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "Acceptă"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "Instalare Mod: fișier: \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -168,17 +184,17 @@ msgstr ""
"\n"
"Instalare Mod: tip de fișier neacceptat \"$1\""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Eșuare la instalarea $1 în $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"Instalare Mod: nu se poate găsi nume de folder potrivit pentru pachetul de "
"mod $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "Instalare mod: nu se poate găsi numele real pentru: $1"
@@ -186,49 +202,40 @@ msgstr "Instalare mod: nu se poate găsi numele real pentru: $1"
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Descarcă"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Numele lumii"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Notă"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "Reinstalează"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "Instalează"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Pagina $1 din $2"
@@ -236,15 +243,20 @@ msgstr "Pagina $1 din $2"
msgid "Credits"
msgstr "Credits"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "Dezvoltatori de bază"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Contribuitori activi"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Dezvoltatori de bază"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr "Foști contribuitori"
@@ -285,12 +297,13 @@ msgid "Mods"
msgstr "Moduri"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "Adresă/Port"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Nume/Parolă"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -299,7 +312,7 @@ msgid "Public Serverlist"
msgstr "Listă de servere publică"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Șterge"
@@ -308,15 +321,33 @@ msgstr "Șterge"
msgid "Connect"
msgstr "Conectează"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Modul Creativ"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "activat"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "activat"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Client"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Nou"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Configurează"
@@ -324,17 +355,18 @@ msgstr "Configurează"
msgid "Start Game"
msgstr "ÃŽncepe jocul"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Selectează lumea:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Modul Creativ"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Activează Daune"
@@ -342,6 +374,10 @@ msgstr "Activează Daune"
msgid "Public"
msgstr "Public"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Nume/Parolă"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -354,150 +390,183 @@ msgstr ""
msgid "Server Port"
msgstr "Port server"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Jocul nu are nume, sau nu ai selectat un joc"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Server"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Apă opacă"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Copaci fantezici"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Filtrare Biliniară"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Filtrare Triliniară"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Lumină mai bună"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Activează particulele"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "Nori 3D"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Copaci fantezici"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "Apă opacă"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "Conectează"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip Mapping"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Filtru Anizotropic"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Filtrare Biliniară"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Filtrare Triliniară"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Umbră"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Modifică tastele"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Singleplayer"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "Mip Mapping"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Pentru a permite shadere OpenGL trebuie să fie folosite."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "Setări"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Singleplayer"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Configurează"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Meniul Principal"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Joacă"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Singleplayer"
@@ -514,39 +583,186 @@ msgstr "Nici o informație disponibilă"
msgid "Texturepacks"
msgstr "Pachete de tetură"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "Se încarcă..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "Se rezolvă adresa..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "Texturi..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Meniul Principal"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Eroare de conexiune (timeout?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Nici un cuvânt selectat și nici o adresă scrisă. Nimic de făcut."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Nu se poate găsi sau încărca jocul \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Specificare invalidă"
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "lipsă_tip_font"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Continuă"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Ai murit."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "Reînviere"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Controale prestabilite:\n"
+"- WASD: mișcare\n"
+"- Spațiu: sărire/urcare\n"
+"- Shift: furișare/coborâre\n"
+"- Q: aruncă obiect\n"
+"- I: inventar\n"
+"- Mouse: întoarcere/vedere\n"
+"- Click stânga: săpare/lovire\n"
+"- Click dreapta: pune/folosește\n"
+"- Rotiță mouse: selectează obiect\n"
+"- T: chat\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Continuă"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Schimbă Parola"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Volum Sunet"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Modifică tastele"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Ieși în Meniu"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Ieși din joc"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "Se închide..."
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Se crează serverul..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Se creează clientul..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Se rezolvă adresa..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Se conectează la server..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "Definițiile obiectelor..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "Definițiile Blocurilor..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Media..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -554,14 +770,14 @@ msgstr ""
"\n"
"Verifică deug.txt pentru detalii."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Continuă"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -575,556 +791,482 @@ msgstr "\"Aleargă\" = coboară"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Apasă de 2 ori \"sari\" pentru a zbura"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Tastă deja folosită"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "apasă o tastă"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "ÃŽnainte"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "ÃŽnapoi"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Stânga"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Dreapta"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "Aleargă"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Sari"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "Furișează"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Aruncă"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Inventar"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Chat"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Comandă"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "Consloă"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Intră pe zbor"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Intră pe rapid"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Intră pe rapid"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Intră pe noclip"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Selectare distanță"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Salvează logurile"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Vechea parolă"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Noua parolă"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Confirmarea parolei"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Schimbă"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Parolele nu se potrivesc!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "Volum sunet: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Ieșire"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Stânga"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Rotiță"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Drepata"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "X Butonul 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "ÃŽnapoi"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Șterge"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "ÃŽnapoi"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "X Butonul 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Capital"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Control"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Meniu"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Pauză"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Convert"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Final"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Nonconvert"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Schimbă modul"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Următorul"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Anteriorul"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Spațiu"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Jos"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Execută"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Selectează"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Sus"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Ajutor"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "PrintScreen"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Windows Stânga"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Aplicații"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Numpad 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Numpad 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Windows Dreapta"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sleep"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Numpad 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Numpad 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Numpad 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Numpad 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Numpad 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Numpad 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Numpad *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Numpad +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Numpad -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Numpad /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Numpad 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Numpad 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Shift Stânga"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Shift Dreapta"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Ctrl Stânga"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Meniu Stânga"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Ctrl Dreapta"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Meniu Drepata"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Virgulă"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "Minus"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Punct"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "Plus"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attn"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Ștergere OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "Curățare OEM"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "Mărire"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Meniul Principal"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
-
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Eroare de conexiune (timeout?)"
-
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Nici un cuvânt selectat și nici o adresă scrisă. Nimic de făcut."
-
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
-
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Nu se poate găsi sau încărca jocul \""
-
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Specificare invalidă"
-
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr ""
-#~ "Click stânga: Mută toate obiectele, Click dreapta: Mută un singur obiect"
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Controale prestabilite:\n"
-#~ "- WASD: mișcare\n"
-#~ "- Spațiu: sărire/urcare\n"
-#~ "- Shift: furișare/coborâre\n"
-#~ "- Q: aruncă obiect\n"
-#~ "- I: inventar\n"
-#~ "- Mouse: întoarcere/vedere\n"
-#~ "- Click stânga: săpare/lovire\n"
-#~ "- Click dreapta: pune/folosește\n"
-#~ "- Rotiță mouse: selectează obiect\n"
-#~ "- T: chat\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Ieși din joc"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Ieși în Meniu"
-
-#~ msgid "Sound Volume"
-#~ msgstr "Volum Sunet"
-
-#~ msgid "Change Password"
-#~ msgstr "Schimbă Parola"
-
-#~ msgid "Continue"
-#~ msgstr "Continuă"
-
-#~ msgid "You died."
-#~ msgstr "Ai murit."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Se închide..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Se conectează la server..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Se rezolvă adresa..."
+#~ msgid "Game Name"
+#~ msgstr "Numele jocului"
-#~ msgid "Creating client..."
-#~ msgstr "Se creează clientul..."
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr: Nu se poate copia modul \"$1\" în jocul \"$2\""
-#~ msgid "Creating server...."
-#~ msgstr "Se crează serverul..."
+#~ msgid "GAMES"
+#~ msgstr "JOCURI"
-#~ msgid "Loading..."
-#~ msgstr "Se încarcă..."
+#~ msgid "Games"
+#~ msgstr "Jocuri"
-#~ msgid "Local install"
-#~ msgstr "Instalare locală"
+#~ msgid "Mods:"
+#~ msgstr "Moduri:"
-#~ msgid "Add mod:"
-#~ msgstr "Adăugaţi mod:"
+#~ msgid "edit game"
+#~ msgstr "modifică jocul"
-#~ msgid "MODS"
-#~ msgstr "MODURI"
+#~ msgid "new game"
+#~ msgstr "joc nou"
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "PACHETE DE TEXTURÄ‚"
+#~ msgid "EDIT GAME"
+#~ msgstr "MODIFICÄ‚ JOCUL"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "SINGLE PLAYER"
+#~ msgid "Remove selected mod"
+#~ msgstr "Șterge modul selectat"
-#~ msgid "Finite Liquid"
-#~ msgstr "Lichid finit"
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Adaugă modul"
-#~ msgid "Preload item visuals"
-#~ msgstr "Pre-încarcă imaginile obiectelor"
+#~ msgid "CLIENT"
+#~ msgstr "CLIENT"
-#~ msgid "SETTINGS"
-#~ msgstr "SETÄ‚RI"
+#~ msgid "Favorites:"
+#~ msgstr "Preferate:"
-#~ msgid "Password"
-#~ msgstr "Parolă"
+#~ msgid "START SERVER"
+#~ msgstr "DESCHIDE SERVERUL"
#~ msgid "Name"
#~ msgstr "Nume"
-#~ msgid "START SERVER"
-#~ msgstr "DESCHIDE SERVERUL"
+#~ msgid "Password"
+#~ msgstr "Parolă"
-#~ msgid "Favorites:"
-#~ msgstr "Preferate:"
+#~ msgid "SETTINGS"
+#~ msgstr "SETÄ‚RI"
-#~ msgid "CLIENT"
-#~ msgstr "CLIENT"
+#~ msgid "Preload item visuals"
+#~ msgstr "Pre-încarcă imaginile obiectelor"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Adaugă modul"
+#~ msgid "Finite Liquid"
+#~ msgstr "Lichid finit"
-#~ msgid "Remove selected mod"
-#~ msgstr "Șterge modul selectat"
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "SINGLE PLAYER"
-#~ msgid "EDIT GAME"
-#~ msgstr "MODIFICÄ‚ JOCUL"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "PACHETE DE TEXTURÄ‚"
-#~ msgid "new game"
-#~ msgstr "joc nou"
+#~ msgid "MODS"
+#~ msgstr "MODURI"
-#~ msgid "edit game"
-#~ msgstr "modifică jocul"
+#~ msgid "Add mod:"
+#~ msgstr "Adăugaţi mod:"
-#~ msgid "Mods:"
-#~ msgstr "Moduri:"
+#~ msgid "Local install"
+#~ msgstr "Instalare locală"
-#~ msgid "Games"
-#~ msgstr "Jocuri"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr ""
+#~ "Click stânga: Mută toate obiectele, Click dreapta: Mută un singur obiect"
-#~ msgid "GAMES"
-#~ msgstr "JOCURI"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Filtru Anizotropic"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: Nu se poate copia modul \"$1\" în jocul \"$2\""
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip Mapping"
-#~ msgid "Game Name"
-#~ msgstr "Numele jocului"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "Descarcă"
diff --git a/po/ru/minetest.po b/po/ru/minetest.po
index 5d9d16deb..9675095ae 100644
--- a/po/ru/minetest.po
+++ b/po/ru/minetest.po
@@ -7,69 +7,87 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2013-12-29 16:58+0200\n"
-"Last-Translator: Сергей Голубев <seria-2@mail.ru>\n"
-"Language-Team: Russian\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-07-30 15:32+0200\n"
+"Last-Translator: Andrey K. <narutrey@ya.ru>\n"
+"Language-Team: Russian "
+"<https://hosted.weblate.org/projects/minetest/minetest/ru/>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
-"X-Generator: Weblate 1.7-dev\n"
+"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<="
+"4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
+"X-Generator: Weblate 2.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr "Ошибка в Ñкрипте Lua в моде:"
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr "Произошла ошибка:"
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
-msgstr "Ok"
+msgstr "OK"
+
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Загрузка..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+"Попробуйте обновить ÑпиÑок публичных Ñерверов и проверьте ÑвÑзь Ñ Ð˜Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚Ð¾Ð¼."
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "Мир:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "Скрыть игру"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "Скрыть Ñодержимое модпака"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "Мод:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "ЗавиÑит от:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Сохранить"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Отменить"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "Включить мультиплеер"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "Отключить мультиплеер"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "включено"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "Включить вÑÑ‘"
@@ -79,7 +97,7 @@ msgstr "Ðазвание мира"
#: builtin/mainmenu/dlg_create_world.lua:53
msgid "Seed"
-msgstr "Сид"
+msgstr "Зерно"
#: builtin/mainmenu/dlg_create_world.lua:56
msgid "Mapgen"
@@ -95,21 +113,21 @@ msgstr "Создать"
#: builtin/mainmenu/dlg_create_world.lua:68
msgid "You have no subgames installed."
-msgstr ""
+msgstr "У Ð²Ð°Ñ Ð½Ðµ уÑтановлено мини-игр."
#: builtin/mainmenu/dlg_create_world.lua:69
msgid "Download one from minetest.net"
-msgstr ""
+msgstr "Загрузите их Ñ minetest.net"
#: builtin/mainmenu/dlg_create_world.lua:72
msgid "Warning: The minimal development test is meant for developers."
-msgstr ""
+msgstr "Внимание: \"Minimal development test\" Ð´Ð»Ñ Ñ€Ð°Ð·Ñ€Ð°Ð±Ð¾Ñ‚Ñ‡Ð¸ÐºÐ¾Ð²."
#: builtin/mainmenu/dlg_create_world.lua:73
msgid "Download a subgame, such as minetest_game, from minetest.net"
-msgstr ""
+msgstr "Скачайте мини-игры, такие как minetest_game, на minetest.net"
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "Мир под названием \"$1\" уже ÑущеÑтвует"
@@ -123,7 +141,7 @@ msgstr "Уверены, что хотите удалить \"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Да"
@@ -151,83 +169,72 @@ msgstr "Ðет"
msgid "Rename Modpack:"
msgstr "Переименовать модпак:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "ПринÑÑ‚ÑŒ"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "УÑтановка мода: файл \"$1\""
-#: builtin/mainmenu/modmgr.lua:343
-#, fuzzy
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
"\n"
-"УÑтановка мода: неподдерживаемый тип \"$1\""
+"УÑтановка мода: неподдерживаемый тип файла \"$1\" или битый архив"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "Ошибка при уÑтановке $1 в $2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
"УÑтановка мода: невозможно найти подходÑщее Ð¸Ð¼Ñ Ð´Ð¸Ñ€ÐµÐºÑ‚Ð¾Ñ€Ð¸Ð¸ Ð´Ð»Ñ Ð¼Ð¾Ð´Ð¿Ð°ÐºÐ° $1"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "УÑтановка мода: невозможно определить название мода Ð´Ð»Ñ $1"
#: builtin/mainmenu/store.lua:88
msgid "Unsorted"
-msgstr ""
+msgstr "Ðе Ñортировано"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Загрузить"
+msgstr "ПоиÑк"
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr ""
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr "Загрузка $1..."
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
-msgstr ""
+msgstr "УÑпешно уÑтановлено:"
-#: builtin/mainmenu/store.lua:163
-#, fuzzy
+#: builtin/mainmenu/store.lua:162
msgid "Shortname:"
-msgstr "Ðазвание мира"
-
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
+msgstr "Краткое имÑ:"
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "Рейтинг"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "ПереуÑтановить"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "УÑтановить"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
-msgstr ""
+msgstr "Закрыть хранилище"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "Страница $1 из $2"
@@ -235,17 +242,21 @@ msgstr "Страница $1 из $2"
msgid "Credits"
msgstr "БлагодарноÑти"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr "ОÑновные разработчики"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr "Ðктивные контрибьюторы"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr "Разработчики в отÑтавке"
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
-msgstr "Ð’ отÑтавке"
+msgstr "Контрибьюторы в отÑтавке"
#: builtin/mainmenu/tab_mods.lua:30
msgid "Installed Mods:"
@@ -253,7 +264,7 @@ msgstr "УÑтановленные моды:"
#: builtin/mainmenu/tab_mods.lua:39
msgid "Online mod repository"
-msgstr "Онлайн хранилище модов"
+msgstr "Онлайн-хранилище модов"
#: builtin/mainmenu/tab_mods.lua:78
msgid "No mod description available"
@@ -284,13 +295,12 @@ msgid "Mods"
msgstr "Моды"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
-msgstr "ÐдреÑ/Порт"
+msgid "Address / Port :"
+msgstr "ÐÐ´Ñ€ÐµÑ / Порт:"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
-msgstr "ИмÑ/Пароль"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
+msgstr "Ð˜Ð¼Ñ / Пароль:"
#: builtin/mainmenu/tab_multiplayer.lua:29
#: builtin/mainmenu/tab_simple_main.lua:30
@@ -298,7 +308,7 @@ msgid "Public Serverlist"
msgstr "СпиÑок публичных Ñерверов"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Удалить"
@@ -307,15 +317,30 @@ msgstr "Удалить"
msgid "Connect"
msgstr "ПодключитьÑÑ"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "Режим творчеÑтва"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "Разрешить увечьÑ"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "PvP разрешён"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "Клиент"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Ðовый"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "ÐаÑтроить"
@@ -323,180 +348,207 @@ msgstr "ÐаÑтроить"
msgid "Start Game"
msgstr "Ðачать игру"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Выберите мир:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
-msgstr "Режим ÑозданиÑ"
+msgstr "Режим творчеÑтва"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
-msgstr "Включить урон"
+msgstr "Разрешить увечьÑ"
#: builtin/mainmenu/tab_server.lua:35
msgid "Public"
msgstr "Публичные"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Ð˜Ð¼Ñ / Пароль"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
-msgstr ""
+msgstr "ÐдреÑ"
#: builtin/mainmenu/tab_server.lua:47
msgid "Port"
-msgstr ""
+msgstr "Порт"
#: builtin/mainmenu/tab_server.lua:51
msgid "Server Port"
-msgstr "Порт"
+msgstr "Порт Ñервера"
+
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr "Ðе выбран мир!"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "Сервер"
+#: builtin/mainmenu/tab_settings.lua:21
+msgid "Opaque Leaves"
+msgstr "ÐÐµÐ¿Ñ€Ð¾Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð»Ð¸Ñтва"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr "Ð£Ð¿Ñ€Ð¾Ñ‰Ñ‘Ð½Ð½Ð°Ñ Ð»Ð¸Ñтва"
+
#: builtin/mainmenu/tab_settings.lua:23
+msgid "Fancy Leaves"
+msgstr "КраÑÐ¸Ð²Ð°Ñ Ð»Ð¸Ñтва"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "Без фильтраций"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "Ð‘Ð¸Ð»Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ñ"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "Ð¢Ñ€Ð¸Ð»Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ñ"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr "Без Мипмаппинга"
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr "Мипмаппинг"
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr "Мипмаппинг Ñ Ð°Ð½Ð¸Ð·Ð¾Ñ‚Ñ€. фильтром"
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
-msgstr ""
+msgstr "Уверены, что хотите ÑброÑить мир одиночной игры?"
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
-msgstr ""
+msgstr "Ðет!"
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "ÐœÑгкое оÑвещение"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Включить чаÑтицы"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D облака"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "КраÑивые деревьÑ"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "ÐÐµÐ¿Ñ€Ð¾Ð·Ñ€Ð°Ñ‡Ð½Ð°Ñ Ð²Ð¾Ð´Ð°"
-#: builtin/mainmenu/tab_settings.lua:144
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:210
msgid "Connected Glass"
-msgstr "ПодключитьÑÑ"
+msgstr "Стёкла без швов"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
-msgstr ""
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "ПодÑветка нод"
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "Mip-текÑтурирование"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "ТекÑтуирование:"
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "ÐÐ½Ð¸Ð·Ð¾Ñ‚Ñ€Ð¾Ð¿Ð½Ð°Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ñ"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "ОтриÑовка:"
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Ð‘Ð¸Ð»Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ñ"
-
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Ð¢Ñ€Ð¸Ð»Ð¸Ð½ÐµÐ¹Ð½Ð°Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ñ"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "ПерезапуÑтите Minetest Ð´Ð»Ñ Ð¿Ñ€Ð¸Ð½ÑÑ‚Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ð¹"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Шейдеры"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Смена управлениÑ"
-#: builtin/mainmenu/tab_settings.lua:167
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:236
msgid "Reset singleplayer world"
-msgstr "ÐžÐ´Ð¸Ð½Ð¾Ñ‡Ð½Ð°Ñ Ð¸Ð³Ñ€Ð°"
+msgstr "Ð¡Ð±Ñ€Ð¾Ñ Ð¾Ð´Ð¸Ð½Ð¾Ñ‡Ð½Ð¾Ð¹ игры"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
-msgstr ""
+msgstr "МаÑштаб интерфейÑа"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
-msgstr ""
+msgstr "КоÑффициент маÑштаба интерфейÑа: "
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
-msgstr ""
+msgstr "Свободный выбор цели"
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
-msgstr ""
+msgstr "ЧувÑтвительноÑÑ‚ÑŒ (пк)"
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
-#, fuzzy
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
msgid "Bumpmapping"
-msgstr "Mip-текÑтурирование"
+msgstr "Бампмаппинг"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
-msgstr ""
+msgstr "Генерировать карты нормалей"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
-msgstr ""
+msgstr "Parallax Occlusion"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
-msgstr ""
+msgstr "Волны на воде"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
-msgstr ""
+msgstr "Покачивание лиÑтвы"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
-msgstr ""
+msgstr "Покачивание раÑтений"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "Ð”Ð»Ñ Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ ÑˆÐµÐ¹Ð´ÐµÑ€Ð¾Ð² необходим драйвер OpenGL."
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "ÐаÑтройки"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
-#, fuzzy
+#: builtin/mainmenu/tab_simple_main.lua:82
msgid "Start Singleplayer"
-msgstr "ÐžÐ´Ð¸Ð½Ð¾Ñ‡Ð½Ð°Ñ Ð¸Ð³Ñ€Ð°"
+msgstr "Ðачать одиночную игру"
-#: builtin/mainmenu/tab_simple_main.lua:72
-#, fuzzy
+#: builtin/mainmenu/tab_simple_main.lua:83
msgid "Config mods"
-msgstr "ÐаÑтроить"
+msgstr "ÐаÑтройка модов"
-#: builtin/mainmenu/tab_simple_main.lua:191
-#, fuzzy
+#: builtin/mainmenu/tab_simple_main.lua:201
msgid "Main"
msgstr "Главное меню"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Играть"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "ÐžÐ´Ð¸Ð½Ð¾Ñ‡Ð½Ð°Ñ Ð¸Ð³Ñ€Ð°"
@@ -509,43 +561,197 @@ msgid "No information available"
msgstr "ОпиÑание отÑутÑтвует"
#: builtin/mainmenu/tab_texturepacks.lua:114
-#, fuzzy
msgid "Texturepacks"
msgstr "Пакеты текÑтур"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "Загрузка текÑтур..."
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "Сборка шейдеров..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "Инициирование нод..."
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr "Инициирование нод"
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "ТекÑтуры предметов..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "Готово!"
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Главное меню"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Ð˜Ð¼Ñ Ð¸Ð³Ñ€Ð¾ÐºÐ° Ñлишком длинное."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (таймаут?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Ðе выбран мир и не введен адреÑ."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "По Ñтому пути мира нет: "
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Ðевозможно найти или загрузить игру \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð³Ñ€Ñ‹."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "no"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Продолжить"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Вы умерли."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "ВозродитьÑÑ"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+"Управление по умолчанию:\n"
+"Ðе в меню:\n"
+"- одно нажатие: кнопка активаций\n"
+"- двойное нажатие: положить/иÑпользовать\n"
+"- Ñкольжение пальцем: оÑмотретьÑÑ\n"
+"В меню/инвертаре:\n"
+"- двойное нажатие (вне меню)\n"
+"--> закрыть меню\n"
+"- Ðажать на Ñтопку, затем на Ñчейку:\n"
+"--> Двигать Ñтопку\n"
+"- Потащить одним пальцем Ñтопку в нужную Ñчейку, нажать вторым пальцем на "
+"Ñкран:\n"
+"--> Положить один предмет в Ñчейку\n"
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Управление по умолчанию:\n"
+"- WASD: движение\n"
+"- Пробел: прыжок/вверх\n"
+"- Shift: краÑÑ‚ÑŒÑÑ/вниз\n"
+"- Q: броÑить предмет\n"
+"- I: инвентарь\n"
+"- Мышка: поворот\n"
+"- ЛКМ: копать/удар\n"
+"- ПКМ: поÑтавить/иÑпользовать\n"
+"- КолеÑико мыши: выбор предмета\n"
+"- T: чат\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Продолжить"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Изменить пароль"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "ГромкоÑÑ‚ÑŒ звука"
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "Смена управлениÑ"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Выход в меню"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Выход в ОС"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "Завершение..."
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr "Создание Ñервера..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Создание клиента..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Получение адреÑа..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Подключение к Ñерверу..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "ОпиÑÐ°Ð½Ð¸Ñ Ð¿Ñ€ÐµÐ´Ð¼ÐµÑ‚Ð¾Ð²..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "ОпиÑÐ°Ð½Ð¸Ñ Ð½Ð¾Ð´..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "Медиафайлы..."
-#: src/game.cpp:2267
-msgid " KB/s"
-msgstr ""
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr "КиБ/Ñ"
-#: src/game.cpp:2271
-msgid " MB/s"
-msgstr ""
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr "МиБ/Ñ"
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -553,13 +759,13 @@ msgstr ""
"\n"
"ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð² debug.txt."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Продолжить"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
-msgstr ""
+msgstr "Введите "
+
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "OK"
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
@@ -575,476 +781,485 @@ msgstr "\"ИÑпользовать\" = вниз"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Двойной прыжок = летать"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Клавиша уже иÑпользуетÑÑ"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "нажмите клавишу"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Вперед"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Ðазад"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Влево"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Вправо"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "ИÑпользовать"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Прыжок"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "КраÑÑ‚ÑŒÑÑ"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "БроÑить"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Инвентарь"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Чат"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Команда"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "КонÑоль"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Полёт"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "УÑкорение"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+msgid "Toggle Cinematic"
+msgstr "КиношноÑÑ‚ÑŒ"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Включить noclip"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Зона видимоÑти"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Печать Ñтеков"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Старый пароль"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Ðовый пароль"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "Подтверждение паролÑ"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Изменить"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Пароли не Ñовпадают!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "ГромкоÑÑ‚ÑŒ звука: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Выход"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Ð›ÐµÐ²Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "СреднÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ°"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "ÐŸÑ€Ð°Ð²Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Доп. кнопка 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "Ðазад"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "ОчиÑтить"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "ВернутьÑÑ"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Доп. кнопка 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Ctrl"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Кана"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Меню"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Пауза"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "Преобразовать"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escape"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Конец"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Кандзи"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "Ðе преобразовано"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "Mode Change"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Next"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Prior"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "Пробел"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Вниз"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "Выполнить"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Выбор"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Вверх"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Справка"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "Cнимок"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Ð›ÐµÐ²Ð°Ñ ÐºÐ». Win"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "ПриложениÑ"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Доп. клав. 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Доп. клав. 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Прав. кл. Win"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Sleep"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Доп. клав. 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Доп. клав. 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Доп. клав. 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Доп. клав. 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Доп. клав. 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Доп. клав. 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Доп. клав. *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Доп. клав. +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Доп. клав. -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Доп. клав. /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Доп. клав. 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Доп. клав. 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Левый Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Правый Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Левый Ctrl"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Ð›ÐµÐ²Ð°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ° меню"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Правый Ctrl"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "ÐŸÑ€Ð°Ð²Ð°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ° меню"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "ЗапÑтаÑ"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "МинуÑ"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "Период"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "ПлюÑ"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Внимание"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Стереть ОÐС"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "ОчиÑтить OEM"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "МаÑштаб"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Главное меню"
+#~ msgid "Game Name"
+#~ msgstr "Ðазвание"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "Gamemgr: Ðе могу Ñкопировать мод \"$1\" в игру \"$2\""
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Ошибка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (таймаут?)"
+#~ msgid "GAMES"
+#~ msgstr "ИГРЫ"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Ðе выбран мир и не введен адреÑ."
+#~ msgid "Games"
+#~ msgstr "Игры"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#~ msgid "Mods:"
+#~ msgstr "Моды:"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Ðевозможно найти или загрузить игру \""
+#~ msgid "edit game"
+#~ msgstr "Редактировать"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð³Ñ€Ñ‹."
+#~ msgid "new game"
+#~ msgstr "Создать игру"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "ЛКМ: ПеремеÑтить вÑе предметы, ПКМ: ПеремеÑтить один предмет"
+#~ msgid "EDIT GAME"
+#~ msgstr "РЕДÐКТИРОВÐÐИЕ"
-#~ msgid "is required by:"
-#~ msgstr "требуетÑÑ Ð´Ð»Ñ:"
+#~ msgid "Remove selected mod"
+#~ msgstr "Удалить мод"
-#~ msgid "Configuration saved. "
-#~ msgstr "ÐаÑтройки Ñохранены. "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- Добавить мод"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "Предупреждение: ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ. "
+#~ msgid "CLIENT"
+#~ msgstr "КЛИЕÐТ"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Ðевозможно Ñоздать мир: Ð˜Ð¼Ñ Ñодержит недопуÑтимые Ñимволы"
+#~ msgid "Favorites:"
+#~ msgstr "Избранное:"
-#~ msgid "Multiplayer"
-#~ msgstr "Ð¡ÐµÑ‚ÐµÐ²Ð°Ñ Ð¸Ð³Ñ€Ð°"
+#~ msgid "START SERVER"
+#~ msgstr "СЕРВЕР"
-#~ msgid "Advanced"
-#~ msgstr "Дополнительно"
+#~ msgid "Name"
+#~ msgstr "ИмÑ"
-#~ msgid "Show Public"
-#~ msgstr "Публичные"
+#~ msgid "Password"
+#~ msgstr "Пароль"
-#~ msgid "Show Favorites"
-#~ msgstr "Избранные"
+#~ msgid "SETTINGS"
+#~ msgstr "ÐÐСТРОЙКИ"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "ОÑтавьте Ð°Ð´Ñ€ÐµÑ Ð¿ÑƒÑтым Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка локального Ñервера."
+#~ msgid "Preload item visuals"
+#~ msgstr "Предзагрузка изображений"
-#~ msgid "Create world"
-#~ msgstr "Создать мир"
+#~ msgid "Finite Liquid"
+#~ msgstr "Конечные жидкоÑти"
-#~ msgid "Address required."
-#~ msgstr "Ðужно ввеÑти адреÑ."
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "ОДИÐОЧÐÐЯ ИГРÐ"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Ðевозможно удалить мир: Ðичего не выбрано"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "ПÐКЕТЫ ТЕКСТУР"
-#~ msgid "Files to be deleted"
-#~ msgstr "Следующие файлы будут удалены"
+#~ msgid "MODS"
+#~ msgstr "МОДЫ"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Ðевозможно Ñоздать мир: Ðи одной игры не найдено"
+#~ msgid "Add mod:"
+#~ msgstr "Добавить мод:"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Ðевозможно наÑтроить мир: ничего не выбрано"
+#~ msgid "Local install"
+#~ msgstr "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ ÑƒÑтановка"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Ошибка при удалении файлов мира"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "Предупреждение: Ðекоторые моды еще не наÑтроены.\n"
+#~ "Их Ñтандартные наÑтройки будут уÑтановлены, когда вы Ñохраните "
+#~ "конфигурацию. "
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "Предупреждение: Ðекоторые моды не найдены.\n"
+#~ "Их наÑтройки будут удалены, когда вы Ñохраните конфигурацию. "
#~ msgid ""
#~ "Default Controls:\n"
@@ -1071,147 +1286,63 @@ msgstr "ÐÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð¸Ð³Ñ€Ñ‹."
#~ "- ESC: Ñто меню\n"
#~ "- T: чат\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "Предупреждение: Ðекоторые моды не найдены.\n"
-#~ "Их наÑтройки будут удалены, когда вы Ñохраните конфигурацию. "
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "Предупреждение: Ðекоторые моды еще не наÑтроены.\n"
-#~ "Их Ñтандартные наÑтройки будут уÑтановлены, когда вы Ñохраните "
-#~ "конфигурацию. "
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "Управление по умолчанию:\n"
-#~ "- WASD: движение\n"
-#~ "- Пробел: прыжок/вверх\n"
-#~ "- Shift: краÑÑ‚ÑŒÑÑ/вниз\n"
-#~ "- Q: броÑить предмет\n"
-#~ "- I: инвентарь\n"
-#~ "- Мышка: поворот\n"
-#~ "- ЛКМ: копать/удар\n"
-#~ "- ПКМ: поÑтавить/иÑпользовать\n"
-#~ "- КолеÑико мыши: выбор предмета\n"
-#~ "- T: чат\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Выход в ОС"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Выход в меню"
-
-#~ msgid "Sound Volume"
-#~ msgstr "ГромкоÑÑ‚ÑŒ звука"
-
-#~ msgid "Change Password"
-#~ msgstr "Изменить пароль"
-
-#~ msgid "Continue"
-#~ msgstr "Продолжить"
-
-#~ msgid "You died."
-#~ msgstr "Вы умерли."
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "Завершение работы..."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "Подключение к Ñерверу..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "Получение адреÑа..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Создание клиента..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Создание Ñервера..."
-
-#~ msgid "Loading..."
-#~ msgstr "Загрузка..."
-
-#~ msgid "Local install"
-#~ msgstr "Ð›Ð¾ÐºÐ°Ð»ÑŒÐ½Ð°Ñ ÑƒÑтановка"
-
-#~ msgid "Add mod:"
-#~ msgstr "Добавить мод:"
-
-#~ msgid "MODS"
-#~ msgstr "МОДЫ"
-
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "ПÐКЕТЫ ТЕКСТУР"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Ошибка при удалении файлов мира"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "ОДИÐОЧÐÐЯ ИГРÐ"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Ðевозможно наÑтроить мир: ничего не выбрано"
-#~ msgid "Finite Liquid"
-#~ msgstr "Конечные жидкоÑти"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Ðевозможно Ñоздать мир: Ðи одной игры не найдено"
-#~ msgid "Preload item visuals"
-#~ msgstr "Предзагрузка изображений"
+#~ msgid "Files to be deleted"
+#~ msgstr "Следующие файлы будут удалены"
-#~ msgid "SETTINGS"
-#~ msgstr "ÐÐСТРОЙКИ"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Ðевозможно удалить мир: Ðичего не выбрано"
-#~ msgid "Password"
-#~ msgstr "Пароль"
+#~ msgid "Address required."
+#~ msgstr "Ðужно ввеÑти адреÑ."
-#~ msgid "Name"
-#~ msgstr "ИмÑ"
+#~ msgid "Create world"
+#~ msgstr "Создать мир"
-#~ msgid "START SERVER"
-#~ msgstr "СЕРВЕР"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "ОÑтавьте Ð°Ð´Ñ€ÐµÑ Ð¿ÑƒÑтым Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка локального Ñервера."
-#~ msgid "Favorites:"
-#~ msgstr "Избранное:"
+#~ msgid "Show Favorites"
+#~ msgstr "Избранные"
-#~ msgid "CLIENT"
-#~ msgstr "КЛИЕÐТ"
+#~ msgid "Show Public"
+#~ msgstr "Публичные"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- Добавить мод"
+#~ msgid "Advanced"
+#~ msgstr "Дополнительно"
-#~ msgid "Remove selected mod"
-#~ msgstr "Удалить мод"
+#~ msgid "Multiplayer"
+#~ msgstr "Ð¡ÐµÑ‚ÐµÐ²Ð°Ñ Ð¸Ð³Ñ€Ð°"
-#~ msgid "EDIT GAME"
-#~ msgstr "РЕДÐКТИРОВÐÐИЕ"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Ðевозможно Ñоздать мир: Ð˜Ð¼Ñ Ñодержит недопуÑтимые Ñимволы"
-#~ msgid "new game"
-#~ msgstr "Создать игру"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "Предупреждение: ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ. "
-#~ msgid "edit game"
-#~ msgstr "Редактировать"
+#~ msgid "Configuration saved. "
+#~ msgstr "ÐаÑтройки Ñохранены. "
-#~ msgid "Mods:"
-#~ msgstr "Моды:"
+#~ msgid "is required by:"
+#~ msgstr "требуетÑÑ Ð´Ð»Ñ:"
-#~ msgid "Games"
-#~ msgstr "Игры"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "ЛКМ: ПеремеÑтить вÑе предметы, ПКМ: ПеремеÑтить один предмет"
-#~ msgid "GAMES"
-#~ msgstr "ИГРЫ"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "ÐÐ½Ð¸Ð·Ð¾Ñ‚Ñ€Ð¾Ð¿Ð½Ð°Ñ Ñ„Ð¸Ð»ÑŒÑ‚Ñ€Ð°Ñ†Ð¸Ñ"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: Ðе могу Ñкопировать мод \"$1\" в игру \"$2\""
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-текÑтурирование"
-#~ msgid "Game Name"
-#~ msgstr "Ðазвание"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "Загрузить"
diff --git a/po/tr/minetest.po b/po/tr/minetest.po
new file mode 100644
index 000000000..2fdb5b6bb
--- /dev/null
+++ b/po/tr/minetest.po
@@ -0,0 +1,1211 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR mahmutelmas06@gmail.com, 2015.
+msgid ""
+msgstr ""
+"Project-Id-Version: 0.1.2\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-07-14 16:48+0200\n"
+"Last-Translator: Michal ÄŒihaÅ™ <michal@cihar.com>\n"
+"Language-Team: Turkish <https://hosted.weblate.org/projects/minetest/"
+"minetest/tr/>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n > 1;\n"
+"X-Generator: Weblate 2.4-dev\n"
+"X-Poedit-Language: Turkish\n"
+"X-Poedit-Basepath: \n"
+
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
+msgid "Ok"
+msgstr "Tamam"
+
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "Yükleniyor..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
+msgid "World:"
+msgstr "Dünya:"
+
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
+msgid "Hide Game"
+msgstr "Oyunu Gizle"
+
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
+msgid "Hide mp content"
+msgstr "Detayları gizle"
+
+#: builtin/mainmenu/dlg_config_world.lua:49
+msgid "Mod:"
+msgstr "Eklnt:"
+
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
+msgid "Depends:"
+msgstr "Bağımlılıklar :"
+
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
+msgid "Save"
+msgstr "Kaydet"
+
+#: builtin/mainmenu/dlg_config_world.lua:55
+#: builtin/mainmenu/dlg_create_world.lua:64
+#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
+#: src/keycode.cpp:223
+msgid "Cancel"
+msgstr "Vazgeç"
+
+#: builtin/mainmenu/dlg_config_world.lua:71
+msgid "Enable MP"
+msgstr "Paketi Aç"
+
+#: builtin/mainmenu/dlg_config_world.lua:73
+msgid "Disable MP"
+msgstr "Paketi Kapat"
+
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
+msgid "enabled"
+msgstr "EtkinleÅŸtirildi"
+
+#: builtin/mainmenu/dlg_config_world.lua:85
+msgid "Enable all"
+msgstr "Hepsini etkinleÅŸtir"
+
+#: builtin/mainmenu/dlg_create_world.lua:50
+msgid "World name"
+msgstr "Dünya adı"
+
+#: builtin/mainmenu/dlg_create_world.lua:53
+msgid "Seed"
+msgstr "Çekirdek"
+
+#: builtin/mainmenu/dlg_create_world.lua:56
+msgid "Mapgen"
+msgstr "Mapgen"
+
+#: builtin/mainmenu/dlg_create_world.lua:59
+msgid "Game"
+msgstr "Oyun"
+
+#: builtin/mainmenu/dlg_create_world.lua:63
+msgid "Create"
+msgstr "OluÅŸtur"
+
+#: builtin/mainmenu/dlg_create_world.lua:68
+msgid "You have no subgames installed."
+msgstr "Ek bir oyun modu yüklü değil."
+
+#: builtin/mainmenu/dlg_create_world.lua:69
+msgid "Download one from minetest.net"
+msgstr "Minetest.net adresinden indirin"
+
+#: builtin/mainmenu/dlg_create_world.lua:72
+msgid "Warning: The minimal development test is meant for developers."
+msgstr "Uyarı : Minimal Development Test geliştiriciler içindir."
+
+#: builtin/mainmenu/dlg_create_world.lua:73
+msgid "Download a subgame, such as minetest_game, from minetest.net"
+msgstr "Minetest.net adresinden bir oyun modu indirin"
+
+#: builtin/mainmenu/dlg_create_world.lua:99
+msgid "A world named \"$1\" already exists"
+msgstr " \"$1\" isimli dünya zaten var"
+
+#: builtin/mainmenu/dlg_create_world.lua:116
+msgid "No worldname given or no game selected"
+msgstr "Dünya seçilmedi ya da adlandırılmadı"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:26
+msgid "Are you sure you want to delete \"$1\"?"
+msgstr " \"$1\" 'i silmek istediÄŸinizden emin misiniz ?"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:27
+#: builtin/mainmenu/dlg_delete_world.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
+msgid "Yes"
+msgstr "Evet"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:28
+msgid "No of course not!"
+msgstr "Elbette hayır!"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:41
+msgid "Modmgr: failed to delete \"$1\""
+msgstr "Modmgr:\"$1\" dosyası silerken hata"
+
+#: builtin/mainmenu/dlg_delete_mod.lua:45
+msgid "Modmgr: invalid modpath \"$1\""
+msgstr "Modmgr: \"$1\" eklenti konumu yanlış"
+
+#: builtin/mainmenu/dlg_delete_world.lua:24
+msgid "Delete World \"$1\"?"
+msgstr " \"$1\" dünyasını sil ?"
+
+#: builtin/mainmenu/dlg_delete_world.lua:26
+msgid "No"
+msgstr "Hayır"
+
+#: builtin/mainmenu/dlg_rename_modpack.lua:26
+msgid "Rename Modpack:"
+msgstr "Eklenti paketini yeniden adlandır :"
+
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
+msgid "Accept"
+msgstr "Kabul et"
+
+#: builtin/mainmenu/modmgr.lua:344
+msgid "Install Mod: file: \"$1\""
+msgstr "Eklenti yükle: Dosya: \"$1\""
+
+#: builtin/mainmenu/modmgr.lua:345
+msgid ""
+"\n"
+"Install Mod: unsupported filetype \"$1\" or broken archive"
+msgstr ""
+"\n"
+"Eklenti yükle: Desteklenmeyen dosya uzantısı \"$1\" veya bozuk dosya"
+
+#: builtin/mainmenu/modmgr.lua:365
+msgid "Failed to install $1 to $2"
+msgstr " $1 arası $2 yükleme başarısız"
+
+#: builtin/mainmenu/modmgr.lua:368
+msgid "Install Mod: unable to find suitable foldername for modpack $1"
+msgstr "Eklenti yükle:$1 eklenti paketi için uygun bir klasör adı bulunamadı"
+
+#: builtin/mainmenu/modmgr.lua:388
+msgid "Install Mod: unable to find real modname for: $1"
+msgstr "Eklenti yükle: $1 için eklenti adı bulunamadı"
+
+#: builtin/mainmenu/store.lua:88
+msgid "Unsorted"
+msgstr "Sıralanmamış"
+
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
+msgid "Search"
+msgstr "Ara"
+
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
+msgstr " $1, indiriliyor, lütfen bekleyin"
+
+#: builtin/mainmenu/store.lua:160
+msgid "Successfully installed:"
+msgstr "Yükleme başarılı :"
+
+#: builtin/mainmenu/store.lua:162
+msgid "Shortname:"
+msgstr "Takma ad :"
+
+#: builtin/mainmenu/store.lua:472
+msgid "Rating"
+msgstr "Oylama"
+
+#: builtin/mainmenu/store.lua:497
+msgid "re-Install"
+msgstr "yeniden yükle"
+
+#: builtin/mainmenu/store.lua:499
+msgid "Install"
+msgstr "Yükle"
+
+#: builtin/mainmenu/store.lua:518
+msgid "Close store"
+msgstr "Mağazayı kapat"
+
+#: builtin/mainmenu/store.lua:526
+msgid "Page $1 of $2"
+msgstr "$2 sayfadan $1 'cisi"
+
+#: builtin/mainmenu/tab_credits.lua:22
+msgid "Credits"
+msgstr "Hakkında"
+
+#: builtin/mainmenu/tab_credits.lua:31
+msgid "Core Developers"
+msgstr "Ana geliÅŸtiriciler"
+
+#: builtin/mainmenu/tab_credits.lua:47
+msgid "Active Contributors"
+msgstr "Aktif katkı sağlayanlar"
+
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "Ana geliÅŸtiriciler"
+
+#: builtin/mainmenu/tab_credits.lua:59
+msgid "Previous Contributors"
+msgstr "Katkı sağlayanlar"
+
+#: builtin/mainmenu/tab_mods.lua:30
+msgid "Installed Mods:"
+msgstr "Yüklenen eklentiler :"
+
+#: builtin/mainmenu/tab_mods.lua:39
+msgid "Online mod repository"
+msgstr "Çevirimiçi eklenti deposu"
+
+#: builtin/mainmenu/tab_mods.lua:78
+msgid "No mod description available"
+msgstr "Eklenti bilgisi yok"
+
+#: builtin/mainmenu/tab_mods.lua:82
+msgid "Mod information:"
+msgstr "Eklenti bilgileri:"
+
+#: builtin/mainmenu/tab_mods.lua:93
+msgid "Rename"
+msgstr "Adlandır"
+
+#: builtin/mainmenu/tab_mods.lua:95
+msgid "Uninstall selected modpack"
+msgstr "Seçilen eklenti paketini sil"
+
+#: builtin/mainmenu/tab_mods.lua:106
+msgid "Uninstall selected mod"
+msgstr "Seçili eklentiyi sil"
+
+#: builtin/mainmenu/tab_mods.lua:121
+msgid "Select Mod File:"
+msgstr "Eklenti seç :"
+
+#: builtin/mainmenu/tab_mods.lua:165
+msgid "Mods"
+msgstr "Eklentiler"
+
+#: builtin/mainmenu/tab_multiplayer.lua:23
+msgid "Address / Port :"
+msgstr "Adres / Port :"
+
+#: builtin/mainmenu/tab_multiplayer.lua:24
+msgid "Name / Password :"
+msgstr "Kullanıcı Adı / Şifre :"
+
+#: builtin/mainmenu/tab_multiplayer.lua:29
+#: builtin/mainmenu/tab_simple_main.lua:30
+msgid "Public Serverlist"
+msgstr "Çevirimiçi Oyun Listesi"
+
+#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
+msgid "Delete"
+msgstr "Sil"
+
+#: builtin/mainmenu/tab_multiplayer.lua:38
+#: builtin/mainmenu/tab_simple_main.lua:34
+msgid "Connect"
+msgstr "BaÄŸlan"
+
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+msgid "Creative mode"
+msgstr "Yaratıcı mod"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+msgid "Damage enabled"
+msgstr "Hasar alma etkin"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+msgid "PvP enabled"
+msgstr "Mücadele modu"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
+msgid "Client"
+msgstr "Çevirimiçi Oyna"
+
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
+msgid "New"
+msgstr "Yeni"
+
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
+msgid "Configure"
+msgstr "Ayarla"
+
+#: builtin/mainmenu/tab_server.lua:29
+msgid "Start Game"
+msgstr "Oyunu BaÅŸlat"
+
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
+msgid "Select World:"
+msgstr "Dünya seç :"
+
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
+msgid "Creative Mode"
+msgstr "Yaratıcı Mod"
+
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
+msgid "Enable Damage"
+msgstr "Hasarı etkinleştir"
+
+#: builtin/mainmenu/tab_server.lua:35
+msgid "Public"
+msgstr "Herkese Açık"
+
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Kullanıcı adı/Şifre"
+
+#: builtin/mainmenu/tab_server.lua:45
+msgid "Bind Address"
+msgstr "Adresi doÄŸrula"
+
+#: builtin/mainmenu/tab_server.lua:47
+msgid "Port"
+msgstr "Port"
+
+#: builtin/mainmenu/tab_server.lua:51
+msgid "Server Port"
+msgstr "Sunucu portu"
+
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "Dünya seçilmedi ya da adlandırılmadı"
+
+#: builtin/mainmenu/tab_server.lua:191
+msgid "Server"
+msgstr "Sunucu Kur"
+
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Åžeffaf su"
+
+#: builtin/mainmenu/tab_settings.lua:22
+#, fuzzy
+msgid "Simple Leaves"
+msgstr "Dalgalanan Yapraklar"
+
+#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Şık ağaçlar"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr "Süzme yok"
+
+#: builtin/mainmenu/tab_settings.lua:33
+msgid "Bilinear Filter"
+msgstr "İki yönlü süzme"
+
+#: builtin/mainmenu/tab_settings.lua:34
+msgid "Trilinear Filter"
+msgstr "Üç yönlü süzme"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr "Mipmap kapalı"
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr "Mipmap"
+
+#: builtin/mainmenu/tab_settings.lua:45
+#, fuzzy
+msgid "Mipmap + Aniso. Filter"
+msgstr "Mipmap Aniso. Süzgeci"
+
+#: builtin/mainmenu/tab_settings.lua:98
+msgid "Are you sure to reset your singleplayer world?"
+msgstr "Tek kişilik dünyayı sıfırlamak istediğinizden emin misiniz ?"
+
+#: builtin/mainmenu/tab_settings.lua:102
+msgid "No!!!"
+msgstr "Hayır!!!"
+
+#: builtin/mainmenu/tab_settings.lua:202
+msgid "Smooth Lighting"
+msgstr "Pürüzsüz ışıklandırma"
+
+#: builtin/mainmenu/tab_settings.lua:204
+msgid "Enable Particles"
+msgstr "Parçacıkları etkinleştir"
+
+#: builtin/mainmenu/tab_settings.lua:206
+msgid "3D Clouds"
+msgstr "3 boyutlu bulutlar"
+
+#: builtin/mainmenu/tab_settings.lua:208
+msgid "Opaque Water"
+msgstr "Åžeffaf su"
+
+#: builtin/mainmenu/tab_settings.lua:210
+msgid "Connected Glass"
+msgstr "İçiçe geçmiş cam"
+
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
+msgstr "Nesne seçme göstergesi"
+
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr "Doku:"
+
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr "Kaplama:"
+
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "Değişikliklerin etkin olabilmesi için minetesti yeniden başlatın"
+
+#: builtin/mainmenu/tab_settings.lua:228
+msgid "Shaders"
+msgstr "Shaders"
+
+#: builtin/mainmenu/tab_settings.lua:233
+msgid "Change keys"
+msgstr "Tuşları değiştir"
+
+#: builtin/mainmenu/tab_settings.lua:236
+msgid "Reset singleplayer world"
+msgstr "Tek kişilik oyunu sıfırlayın"
+
+#: builtin/mainmenu/tab_settings.lua:240
+msgid "GUI scale factor"
+msgstr "Menü boyutları"
+
+#: builtin/mainmenu/tab_settings.lua:244
+msgid "Scaling factor applied to menu elements: "
+msgstr "Ölçeklendirme menülere işlendi:"
+
+#: builtin/mainmenu/tab_settings.lua:250
+msgid "Touch free target"
+msgstr "Touch free target"
+
+#: builtin/mainmenu/tab_settings.lua:256
+msgid "Touchthreshold (px)"
+msgstr "Touchthreshold (px)"
+
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
+msgid "Bumpmapping"
+msgstr "Engebeler"
+
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
+msgid "Generate Normalmaps"
+msgstr "Normal haritalar oluÅŸtur"
+
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
+msgid "Parallax Occlusion"
+msgstr "Parallax Occlusion"
+
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
+msgid "Waving Water"
+msgstr "Dalgalanan Su"
+
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
+msgid "Waving Leaves"
+msgstr "Dalgalanan Yapraklar"
+
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
+msgid "Waving Plants"
+msgstr "Dalgalanan Bitkiler"
+
+#: builtin/mainmenu/tab_settings.lua:308
+msgid "To enable shaders the OpenGL driver needs to be used."
+msgstr "OpenGL sürücüleri seçilmeden Shader etkinleştirilemez."
+
+#: builtin/mainmenu/tab_settings.lua:430
+msgid "Settings"
+msgstr "Ayarlar"
+
+#: builtin/mainmenu/tab_simple_main.lua:82
+msgid "Start Singleplayer"
+msgstr "Tek kiÅŸilik oyunu baÅŸlat"
+
+#: builtin/mainmenu/tab_simple_main.lua:83
+msgid "Config mods"
+msgstr "Eklentileri ayarla"
+
+#: builtin/mainmenu/tab_simple_main.lua:201
+msgid "Main"
+msgstr "Ana"
+
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
+msgid "Play"
+msgstr "Oyna"
+
+#: builtin/mainmenu/tab_singleplayer.lua:246
+msgid "Singleplayer"
+msgstr "Tek KiÅŸilik"
+
+#: builtin/mainmenu/tab_texturepacks.lua:49
+msgid "Select texture pack:"
+msgstr "Doku paketi seç :"
+
+#: builtin/mainmenu/tab_texturepacks.lua:69
+msgid "No information available"
+msgstr "Bilgi yok"
+
+#: builtin/mainmenu/tab_texturepacks.lua:114
+msgid "Texturepacks"
+msgstr "Doku paketi"
+
+#: src/client.cpp:1721
+msgid "Loading textures..."
+msgstr "Dokular yükleniyor..."
+
+#: src/client.cpp:1736
+msgid "Rebuilding shaders..."
+msgstr "Shader inÅŸa ediliyor..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr "Nesneler yükleniyor..."
+
+#: src/client.cpp:1760
+#, fuzzy
+msgid "Initializing nodes"
+msgstr "Nesneler yükleniyor..."
+
+#: src/client.cpp:1768
+msgid "Item textures..."
+msgstr "Nesne dokuları ..."
+
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr "Tamam!"
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Ana menu"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "Kullanıcı adı çok uzun."
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Bağlantı hatası ( Zaman aşımı ? )"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Dünya veya adres seçilmedi."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "Belirtilen dünya konumu yok:"
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Oyun yüklenemiyor \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Geçersiz oyun özellikleri."
+
+#: src/fontengine.cpp:70 src/fontengine.cpp:226
+msgid "needs_fallback_font"
+msgstr "needs_fallback_font"
+
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Uygula"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Geberdin."
+
+#: src/game.cpp:1073
+msgid "Respawn"
+msgstr "Yeniden Canlan"
+
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+"Varsayılan Kontroller:\n"
+"Tüm menüler gizli:\n"
+"- Tek tık: tuş etkin\n"
+"- Çift tık: yerleştir/kullan\n"
+"- Parmağı kaydır: etrafa bak\n"
+"Menu/Encanter görünür:\n"
+"- çift tık (dışarda):\n"
+" -->kapat\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- tut&bırak, iki parmağı kullan\n"
+" --> slotuna bir item bırak\n"
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Varsayılanlar Kontroller:\n"
+"- WASD: Hareket et\n"
+"- Boşluk: Zıpla/Tırman\n"
+"- Shift: Sessiz yürü/Aşağı in\n"
+"- Q: Elindekini bırak\n"
+"- I: Envanter\n"
+"- Fare: Dön/Bak\n"
+"- Sol fare: Kaz/Vur\n"
+"- SaÄŸ fare: YerleÅŸtir/Kullan\n"
+"- Fare tekerleği: Araç seç\n"
+"- T: Sohbet\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Devam et"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Åžifre deÄŸiÅŸtir"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "Ses yüksekliği :"
+
+#: src/game.cpp:1136
+msgid "Change Keys"
+msgstr "Tuşları değiştir"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Menüye dön"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Oyundan Çık"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr "Kapatılıyor..."
+
+#: src/game.cpp:1948
+msgid "Creating server..."
+msgstr "Sunucu oluÅŸturuluyor..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Ä°stemci oluÅŸturuluyor..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "Adres çözümleniyor..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "Sunucuya bağlanılıyor..."
+
+#: src/game.cpp:2317
+msgid "Item definitions..."
+msgstr "Nesne tanımlamaları..."
+
+#: src/game.cpp:2322
+msgid "Node definitions..."
+msgstr "Blok tanımlamaları..."
+
+#: src/game.cpp:2329
+msgid "Media..."
+msgstr "Media..."
+
+#: src/game.cpp:2334
+msgid "KiB/s"
+msgstr ""
+
+#: src/game.cpp:2338
+msgid "MiB/s"
+msgstr ""
+
+#: src/game.cpp:4363
+msgid ""
+"\n"
+"Check debug.txt for details."
+msgstr ""
+"\n"
+"Hata ayrıntıları için debug.txt dosyasını inceleyin."
+
+#: src/guiFormSpecMenu.cpp:2855
+msgid "Enter "
+msgstr "Entrer "
+
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "tamam"
+
+#: src/guiKeyChangeMenu.cpp:125
+msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
+msgstr "Tuş ayaları. ( Olağandışı durumlarda minetest.conf 'u düzenleyin )"
+
+#: src/guiKeyChangeMenu.cpp:165
+msgid "\"Use\" = climb down"
+msgstr "\"Kullan Tuşu\" = Aşağı in"
+
+#: src/guiKeyChangeMenu.cpp:180
+msgid "Double tap \"jump\" to toggle fly"
+msgstr "Çift zıplayarak uçma modunu aç/kapa"
+
+#: src/guiKeyChangeMenu.cpp:295
+msgid "Key already in use"
+msgstr "Tuş zaten kullanımda"
+
+#: src/guiKeyChangeMenu.cpp:373
+msgid "press key"
+msgstr "tuÅŸa bas"
+
+#: src/guiKeyChangeMenu.cpp:399
+msgid "Forward"
+msgstr "Ä°leri"
+
+#: src/guiKeyChangeMenu.cpp:400
+msgid "Backward"
+msgstr "Geri"
+
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
+msgid "Left"
+msgstr "Sol"
+
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
+msgid "Right"
+msgstr "SaÄŸ"
+
+#: src/guiKeyChangeMenu.cpp:403
+msgid "Use"
+msgstr "Kullan"
+
+#: src/guiKeyChangeMenu.cpp:404
+msgid "Jump"
+msgstr "Zıpla"
+
+#: src/guiKeyChangeMenu.cpp:405
+msgid "Sneak"
+msgstr "Sessiz Yürü"
+
+#: src/guiKeyChangeMenu.cpp:406
+msgid "Drop"
+msgstr "Bırak"
+
+#: src/guiKeyChangeMenu.cpp:407
+msgid "Inventory"
+msgstr "Envanter"
+
+#: src/guiKeyChangeMenu.cpp:408
+msgid "Chat"
+msgstr "KonuÅŸma"
+
+#: src/guiKeyChangeMenu.cpp:409
+msgid "Command"
+msgstr "Komut"
+
+#: src/guiKeyChangeMenu.cpp:410
+msgid "Console"
+msgstr "Konsol"
+
+#: src/guiKeyChangeMenu.cpp:411
+msgid "Toggle fly"
+msgstr "Uçuş modu aç/kapa"
+
+#: src/guiKeyChangeMenu.cpp:412
+msgid "Toggle fast"
+msgstr "Hız modu aç/kapa"
+
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Hız modu aç/kapa"
+
+#: src/guiKeyChangeMenu.cpp:414
+msgid "Toggle noclip"
+msgstr "Noclip aç/kapa"
+
+#: src/guiKeyChangeMenu.cpp:415
+msgid "Range select"
+msgstr "Uzaklık seçimi"
+
+#: src/guiKeyChangeMenu.cpp:416
+msgid "Print stacks"
+msgstr "Yazdırma yığınları"
+
+#: src/guiPasswordChange.cpp:108
+msgid "Old Password"
+msgstr "Eski ÅŸifre"
+
+#: src/guiPasswordChange.cpp:124
+msgid "New Password"
+msgstr "Yeni ÅŸifre"
+
+#: src/guiPasswordChange.cpp:139
+msgid "Confirm Password"
+msgstr "Şifreyi doğrulayın"
+
+#: src/guiPasswordChange.cpp:155
+msgid "Change"
+msgstr "DeÄŸiÅŸtir"
+
+#: src/guiPasswordChange.cpp:164
+msgid "Passwords do not match!"
+msgstr "Åžifreler uyuÅŸmuyor !"
+
+#: src/guiVolumeChange.cpp:105
+msgid "Sound Volume: "
+msgstr "Ses yüksekliği :"
+
+#: src/guiVolumeChange.cpp:119
+msgid "Exit"
+msgstr "Çıkış"
+
+#: src/keycode.cpp:223
+msgid "Left Button"
+msgstr "Sol tuÅŸu"
+
+#: src/keycode.cpp:223
+msgid "Middle Button"
+msgstr "Orta TuÅŸ"
+
+#: src/keycode.cpp:223
+msgid "Right Button"
+msgstr "SaÄŸ tuÅŸ"
+
+#: src/keycode.cpp:223
+msgid "X Button 1"
+msgstr "X Button 1"
+
+#: src/keycode.cpp:224
+msgid "Back"
+msgstr "Geri"
+
+#: src/keycode.cpp:224
+msgid "Clear"
+msgstr "Temizle"
+
+#: src/keycode.cpp:224
+msgid "Return"
+msgstr "Return"
+
+#: src/keycode.cpp:224
+msgid "Tab"
+msgstr "Tab"
+
+#: src/keycode.cpp:224
+msgid "X Button 2"
+msgstr "X Button 2"
+
+#: src/keycode.cpp:225
+msgid "Capital"
+msgstr "Büyük"
+
+#: src/keycode.cpp:225
+msgid "Control"
+msgstr "Kontroller"
+
+#: src/keycode.cpp:225
+msgid "Kana"
+msgstr "Kana"
+
+#: src/keycode.cpp:225
+msgid "Menu"
+msgstr "Menü"
+
+#: src/keycode.cpp:225
+msgid "Pause"
+msgstr "Beklet"
+
+#: src/keycode.cpp:225
+msgid "Shift"
+msgstr "Shift"
+
+#: src/keycode.cpp:226
+msgid "Convert"
+msgstr "Dönüştür"
+
+#: src/keycode.cpp:226
+msgid "Escape"
+msgstr "Çıkış"
+
+#: src/keycode.cpp:226
+msgid "Final"
+msgstr "BitiÅŸ"
+
+#: src/keycode.cpp:226
+msgid "Junja"
+msgstr "Junja"
+
+#: src/keycode.cpp:226
+msgid "Kanji"
+msgstr "Kanji"
+
+#: src/keycode.cpp:226
+msgid "Nonconvert"
+msgstr "Dönüştürme"
+
+#: src/keycode.cpp:227
+msgid "End"
+msgstr "Son"
+
+#: src/keycode.cpp:227
+msgid "Home"
+msgstr "Ev"
+
+#: src/keycode.cpp:227
+msgid "Mode Change"
+msgstr "Mod deÄŸiÅŸtir"
+
+#: src/keycode.cpp:227
+msgid "Next"
+msgstr "Ä°leri"
+
+#: src/keycode.cpp:227
+msgid "Prior"
+msgstr "Öncelikli"
+
+#: src/keycode.cpp:227
+msgid "Space"
+msgstr "BoÅŸluk"
+
+#: src/keycode.cpp:228
+msgid "Down"
+msgstr "Aşağı"
+
+#: src/keycode.cpp:228
+msgid "Execute"
+msgstr "Çalıştır"
+
+#: src/keycode.cpp:228
+msgid "Print"
+msgstr "Yazdır"
+
+#: src/keycode.cpp:228
+msgid "Select"
+msgstr "Seç"
+
+#: src/keycode.cpp:228
+msgid "Up"
+msgstr "Yukarı"
+
+#: src/keycode.cpp:229
+msgid "Help"
+msgstr "Yardım"
+
+#: src/keycode.cpp:229
+msgid "Insert"
+msgstr "Insert"
+
+#: src/keycode.cpp:229
+msgid "Snapshot"
+msgstr "Ekran Resmi"
+
+#: src/keycode.cpp:232
+msgid "Left Windows"
+msgstr "Sol Windows tuÅŸu"
+
+#: src/keycode.cpp:233
+msgid "Apps"
+msgstr "Uygulamalar"
+
+#: src/keycode.cpp:233
+msgid "Numpad 0"
+msgstr "Numpad 0"
+
+#: src/keycode.cpp:233
+msgid "Numpad 1"
+msgstr "Numpad 1"
+
+#: src/keycode.cpp:233
+msgid "Right Windows"
+msgstr "SaÄŸ Windows tuÅŸu"
+
+#: src/keycode.cpp:233
+msgid "Sleep"
+msgstr "Uyu"
+
+#: src/keycode.cpp:234
+msgid "Numpad 2"
+msgstr "Numpad 2"
+
+#: src/keycode.cpp:234
+msgid "Numpad 3"
+msgstr "Numpad 3"
+
+#: src/keycode.cpp:234
+msgid "Numpad 4"
+msgstr "Numpad 4"
+
+#: src/keycode.cpp:234
+msgid "Numpad 5"
+msgstr "Numpad 5"
+
+#: src/keycode.cpp:234
+msgid "Numpad 6"
+msgstr "Numpad 6"
+
+#: src/keycode.cpp:234
+msgid "Numpad 7"
+msgstr "Numpad 7"
+
+#: src/keycode.cpp:235
+msgid "Numpad *"
+msgstr "Numpad *"
+
+#: src/keycode.cpp:235
+msgid "Numpad +"
+msgstr "Numpad +"
+
+#: src/keycode.cpp:235
+msgid "Numpad -"
+msgstr "Numpad -"
+
+#: src/keycode.cpp:235
+msgid "Numpad /"
+msgstr "Numpad /"
+
+#: src/keycode.cpp:235
+msgid "Numpad 8"
+msgstr "Numpad 8"
+
+#: src/keycode.cpp:235
+msgid "Numpad 9"
+msgstr "Numpad 9"
+
+#: src/keycode.cpp:239
+msgid "Num Lock"
+msgstr "Num Lock"
+
+#: src/keycode.cpp:239
+msgid "Scroll Lock"
+msgstr "Scroll Lock"
+
+#: src/keycode.cpp:240
+msgid "Left Shift"
+msgstr "Sol Shift"
+
+#: src/keycode.cpp:240
+msgid "Right Shift"
+msgstr "SaÄŸ Shift"
+
+#: src/keycode.cpp:241
+msgid "Left Control"
+msgstr "Sol CTRL"
+
+#: src/keycode.cpp:241
+msgid "Left Menu"
+msgstr "Sol Menu"
+
+#: src/keycode.cpp:241
+msgid "Right Control"
+msgstr "SaÄŸ CTRL"
+
+#: src/keycode.cpp:241
+msgid "Right Menu"
+msgstr "SaÄŸ Menu"
+
+#: src/keycode.cpp:243
+msgid "Comma"
+msgstr "Virgul"
+
+#: src/keycode.cpp:243
+msgid "Minus"
+msgstr "Eksi"
+
+#: src/keycode.cpp:243
+msgid "Period"
+msgstr "Dönem"
+
+#: src/keycode.cpp:243
+msgid "Plus"
+msgstr "Artı"
+
+#: src/keycode.cpp:247
+msgid "Attn"
+msgstr "Dikkat"
+
+#: src/keycode.cpp:247
+msgid "CrSel"
+msgstr "CrSel"
+
+#: src/keycode.cpp:248
+msgid "Erase OEF"
+msgstr "l'OEF 'i sil"
+
+#: src/keycode.cpp:248
+msgid "ExSel"
+msgstr "ExSel"
+
+#: src/keycode.cpp:248
+msgid "OEM Clear"
+msgstr "OEM Temizle"
+
+#: src/keycode.cpp:248
+msgid "PA1"
+msgstr "PA1"
+
+#: src/keycode.cpp:248
+msgid "Zoom"
+msgstr "Yakınlaştır"
+
+#~ msgid "Numpad "
+#~ msgstr "Numpad "
+
+#~ msgid " MB/s"
+#~ msgstr " MB/s"
+
+#~ msgid " KB/s"
+#~ msgstr " KB/s"
+
+#~ msgid "Fly mode"
+#~ msgstr "Uçuş modu"
+
+#~ msgid "Tri-Linear Filtering"
+#~ msgstr "Üç yönlü süzme"
+
+#~ msgid "Bi-Linear Filtering"
+#~ msgstr "Çift yönlü süzme"
+
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Eşyönsüz süzme"
+
+#~ msgid "Mip-Mapping"
+#~ msgstr "Mip-Mapping"
+
+#~ msgid "please wait..."
+#~ msgstr "lütfen bekleyin..."
+
+#~ msgid "Downloading"
+#~ msgstr "Ä°ndiriliyor"
diff --git a/po/uk/minetest.po b/po/uk/minetest.po
index f503948e7..697214e08 100644
--- a/po/uk/minetest.po
+++ b/po/uk/minetest.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
"PO-Revision-Date: 2013-06-27 01:22+0200\n"
"Last-Translator: Vladimir a <c-vld@ya.ru>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -19,62 +19,78 @@ msgstr ""
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Weblate 1.4-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "ЗавантаженнÑ..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
#, fuzzy
msgid "World:"
msgstr "Виберіть Ñвіт:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
#, fuzzy
msgid "Hide Game"
msgstr "Гра"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr ""
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
#, fuzzy
msgid "Depends:"
msgstr "залежить від:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "Зберегти"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "Відміна"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
#, fuzzy
msgid "Enable MP"
msgstr "Увімкнути Ð’Ñе"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
#, fuzzy
msgid "Disable MP"
msgstr "Вимкнути УÑе"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "Увімкнено"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
#, fuzzy
msgid "Enable all"
msgstr "Увімкнути Ð’Ñе"
@@ -115,7 +131,7 @@ msgstr ""
msgid "Download a subgame, such as minetest_game, from minetest.net"
msgstr ""
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
#, fuzzy
msgid "A world named \"$1\" already exists"
msgstr "Ðеможливо Ñтворити Ñвіт: Світ з таким ім'Ñм вже Ñ–Ñнує"
@@ -130,7 +146,7 @@ msgstr ""
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "Так"
@@ -159,31 +175,31 @@ msgstr "ÐÑ–"
msgid "Rename Modpack:"
msgstr ""
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
#, fuzzy
msgid "Accept"
msgstr "Підтвердити"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr ""
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
msgid ""
"\n"
"Install Mod: unsupported filetype \"$1\" or broken archive"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
#, fuzzy
msgid "Failed to install $1 to $2"
msgstr "Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ Ñвіт"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr ""
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr ""
@@ -191,49 +207,40 @@ msgstr ""
msgid "Unsorted"
msgstr ""
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
msgstr ""
-#: builtin/mainmenu/store.lua:125
-#, fuzzy
-msgid "Downloading"
-msgstr "Вниз"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
+#: builtin/mainmenu/store.lua:126
+msgid "Downloading $1, please wait..."
msgstr ""
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
msgstr ""
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
msgstr "Ðазва Світу"
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
-
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr ""
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr ""
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr ""
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
msgstr ""
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr ""
@@ -241,15 +248,19 @@ msgstr ""
msgid "Credits"
msgstr "ПодÑка"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
msgstr ""
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:54
+msgid "Previous Core Developers"
+msgstr ""
+
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
msgstr ""
@@ -291,12 +302,13 @@ msgid "Mods"
msgstr ""
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "ÐдреÑа/Порт"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "Ім'Ñ/Пароль"
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -306,7 +318,7 @@ msgid "Public Serverlist"
msgstr "СпиÑок публічних Ñерверів:"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "Видалити"
@@ -315,15 +327,33 @@ msgstr "Видалити"
msgid "Connect"
msgstr "ПідключитиÑÑ"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "Режим СтвореннÑ"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "Увімкнено"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "Увімкнено"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "Ðовий"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "Ðалаштувати"
@@ -332,17 +362,18 @@ msgstr "Ðалаштувати"
msgid "Start Game"
msgstr "Почати гру / ПідключитиÑÑ"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "Виберіть Ñвіт:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "Режим СтвореннÑ"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
msgstr "Ввімкнути урон"
@@ -350,6 +381,10 @@ msgstr "Ввімкнути урон"
msgid "Public"
msgstr "Публичний"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "Ім'Ñ/Пароль"
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
msgstr ""
@@ -362,151 +397,183 @@ msgstr ""
msgid "Server Port"
msgstr ""
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+msgid "No world created or selected!"
+msgstr ""
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr ""
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "Ðепрозора вода"
+
+#: builtin/mainmenu/tab_settings.lua:22
+msgid "Simple Leaves"
+msgstr ""
+
#: builtin/mainmenu/tab_settings.lua:23
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "Гарні дерева"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "Білінійна фільтраціÑ"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "Трилінійна фільтраціÑ"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:98
msgid "Are you sure to reset your singleplayer world?"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
+#: builtin/mainmenu/tab_settings.lua:102
msgid "No!!!"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "Рівне оÑвітленнÑ"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "Ввімкнути чаÑтки"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
msgstr "3D Хмари"
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "Гарні дерева"
-
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
#, fuzzy
msgid "Opaque Water"
msgstr "Ðепрозора вода"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
msgstr "ПідключитиÑÑ"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "MIP-текÑтуруваннÑ"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "Ðнізотропна фільтраціÑ"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "Білінійна фільтраціÑ"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "Трилінійна фільтраціÑ"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "Шейдери"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "Змінити клавіши"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
msgstr "Одиночна гра"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "MIP-текÑтуруваннÑ"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "ÐалаштуваннÑ"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "Одиночна гра"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
msgstr "Ðалаштувати"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "Головне Меню"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "Грати"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "Одиночна гра"
@@ -522,39 +589,186 @@ msgstr ""
msgid "Texturepacks"
msgstr ""
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "ЗавантаженнÑ..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑи..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "ТекÑтура предметів..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "Головне Меню"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr ""
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "Помилка з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (Ñ‡Ð°Ñ Ð²Ð¸Ð¹ÑˆÐ¾Ð²?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "Жоден Ñвіт не вибрано та не надано адреÑи. Ðічого не робити."
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr ""
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "Ðеможливо знайти, або завантажити гру \""
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "Помилкова ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð³Ñ€Ð¸."
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr ""
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "Далі"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "Ви загинули."
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "ÐародитиÑÑ"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+#, fuzzy
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð·Ð° замовчанню:\n"
+"- WASD: рух\n"
+"- Space: Ñтрибок/лізти в гору\n"
+"- Shift: краÑтиÑÑ/лізти в низ\n"
+"- Q: кинути предмет\n"
+"- I: інвентар\n"
+"- Мишка: поворот/дивитиÑÑ\n"
+"- Ліва клавіша миші: копати/удар\n"
+"- Права клавіша миші: поÑтавити/викориÑтовувати\n"
+"- КолеÑо миші: вибір предмета\n"
+"- T: чат\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "Продовжити"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "Змінити Пароль"
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "ГучніÑÑ‚ÑŒ звуку"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "Змінити клавіши"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "Вихід в меню"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "Вихід з гри"
+
+#: src/game.cpp:1841
+msgid "Shutting down..."
+msgstr ""
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñервера..."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ñ”Ð½Ñ‚Ð°..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑи..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ Ñервера..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr ""
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr ""
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr ""
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -562,14 +776,14 @@ msgstr ""
"\n"
"Деталі у файлі debug.txt."
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "Далі"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
msgstr ""
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr ""
+
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
msgstr ""
@@ -585,573 +799,501 @@ msgstr "\"ВикориÑтовувати\" = підніматиÑÑ Ð² гору"
msgid "Double tap \"jump\" to toggle fly"
msgstr "Подвійний \"Стрибок\" щоб полетіти"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "Клавіша вже викориÑтовуєтьÑÑ"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "ÐатиÑніть клавішу"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "Уперед"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "Ðазад"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "Ліворуч"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "Праворуч"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "ВикориÑтовувати"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "Стрибок"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "КраÑтиÑÑ"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "Викинути"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "Інвентар"
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "Чат"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "Комманда"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "КонÑоль"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
msgstr "Переключити режим польоту"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
msgstr "Переключити швидкий режим"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "Переключити швидкий режим"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "Переключити режим Ð¿Ñ€Ð¾Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ñкрізь Ñтін"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "Вибір діапазону"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "Ðадрукувати Ñтек"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "Старий Пароль"
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "Ðовий Пароль"
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "ÐŸÑ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ паролÑ"
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "Змінити"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "Паролі не збігаютьÑÑ!"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "ГучніÑÑ‚ÑŒ Звуку: "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "Вихід"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "Ліва кнопка"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "Ð¡ÐµÑ€ÐµÐ´Ð½Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "Права кнопка"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Додаткова кнопка 1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
#, fuzzy
msgid "Back"
msgstr "Ðазад"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
#, fuzzy
msgid "Clear"
msgstr "Clear"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
#, fuzzy
msgid "Return"
msgstr "Enter"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tab"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Додаткова кнопка 2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "Caps Lock"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
msgstr "Ctrl"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "Kana"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "Меню"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "Пауза"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shift"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
#, fuzzy
msgid "Convert"
msgstr "Конвертувати"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Esc"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
#, fuzzy
msgid "Final"
msgstr "Кинець"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junja"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanji"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
#, fuzzy
msgid "Nonconvert"
msgstr "Ðе конвертуванно"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "End"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
#, fuzzy
msgid "Home"
msgstr "Home"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
#, fuzzy
msgid "Mode Change"
msgstr "Mode"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "Page Up"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Page Down"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
#, fuzzy
msgid "Space"
msgstr "Space"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "Вниз"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
#, fuzzy
msgid "Execute"
msgstr "Виконати"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "Print Screen"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "Select"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "Вгору"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "Допомога"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "Insert"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
#, fuzzy
msgid "Snapshot"
msgstr "Знімок"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "Ліва клавіша Win (Command)"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "Додатки"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "Num 0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "Num 1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "Права клавіша Win (Command)"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "Сон"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "Num 2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "Num 3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "Num 4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "Num 5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "Num 6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "Num 7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "Num *"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "Num +"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "Num -"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "Num /"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "Num 8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "Num 9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "Num Lock"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Lock"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "Ліва клавіша Shift"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "Права клавіша Shift"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "Ліва клавіша Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "Ліва клавіша Menu"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "Права клавіша Control"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "Права клавіша Menu"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "Кома"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "МінуÑ"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
#, fuzzy
msgid "Period"
msgstr "Період"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "ПлюÑ"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
#, fuzzy
msgid "Attn"
msgstr "Увага"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
#, fuzzy
msgid "Erase OEF"
msgstr "Erase OEF"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
#, fuzzy
msgid "ExSel"
msgstr "ExSel"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
#, fuzzy
msgid "OEM Clear"
msgstr "OEM ОчиÑтити"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
#, fuzzy
msgid "Zoom"
msgstr "Збільшити"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "Головне Меню"
-
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#, fuzzy
+#~ msgid "Game Name"
+#~ msgstr "Гра"
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "Помилка з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (Ñ‡Ð°Ñ Ð²Ð¸Ð¹ÑˆÐ¾Ð²?)"
+#, fuzzy
+#~ msgid "Games"
+#~ msgstr "Гра"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "Жоден Ñвіт не вибрано та не надано адреÑи. Ðічого не робити."
+#~ msgid "Favorites:"
+#~ msgstr "Улюблені:"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#, fuzzy
+#~ msgid "Password"
+#~ msgstr "Старий Пароль"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "Ðеможливо знайти, або завантажити гру \""
+#~ msgid "Preload item visuals"
+#~ msgstr "Попереднє Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½ÑŒ"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "Помилкова ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ð³Ñ€Ð¸."
+#, fuzzy
+#~ msgid "Finite Liquid"
+#~ msgstr "Кінцеві рідини"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr ""
-#~ "Ліва кнопка миші: ПереміÑтити уÑÑ– предмети, Права кнопка миші: "
-#~ "ПереміÑтити один предмет"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "Помилка при видаленні файлів Ñвіту"
-#~ msgid "is required by:"
-#~ msgstr "необхідний длÑ:"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "Ðеможливо налаштувати Ñвіт: Ðічого не вибрано"
-#~ msgid "Configuration saved. "
-#~ msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð¾. "
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "Ðеможливо Ñтворити Ñвіт: Ðе знайдено жодної гри"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "ПопередженнÑ: Помилкова конфігураціÑ. "
+#~ msgid "Files to be deleted"
+#~ msgstr "Файлів, що підлÑгають видаленню"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "Ðеможливо Ñтворити Ñвіт: Ім'Ñ Ð¼Ñ–Ñтить недопуÑтимі Ñимволи"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "Ðеможливо видалити Ñвіт: Ðічого не вибрано"
-#~ msgid "Multiplayer"
-#~ msgstr "Мережева гра"
+#~ msgid "Address required."
+#~ msgstr "ÐдреÑа необхідна."
-#~ msgid "Advanced"
-#~ msgstr "Додатково"
+#~ msgid "Create world"
+#~ msgstr "Створити Ñвіт"
-#~ msgid "Show Public"
-#~ msgstr "Показати Публічні"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "Залишіть адреÑу незаповненою Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñерверу."
#~ msgid "Show Favorites"
#~ msgstr "Показати Улюблені"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "Залишіть адреÑу незаповненою Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð»Ð¾ÐºÐ°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñерверу."
-
-#~ msgid "Create world"
-#~ msgstr "Створити Ñвіт"
+#~ msgid "Show Public"
+#~ msgstr "Показати Публічні"
-#~ msgid "Address required."
-#~ msgstr "ÐдреÑа необхідна."
+#~ msgid "Advanced"
+#~ msgstr "Додатково"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "Ðеможливо видалити Ñвіт: Ðічого не вибрано"
+#~ msgid "Multiplayer"
+#~ msgstr "Мережева гра"
-#~ msgid "Files to be deleted"
-#~ msgstr "Файлів, що підлÑгають видаленню"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "Ðеможливо Ñтворити Ñвіт: Ім'Ñ Ð¼Ñ–Ñтить недопуÑтимі Ñимволи"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "Ðеможливо Ñтворити Ñвіт: Ðе знайдено жодної гри"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "ПопередженнÑ: Помилкова конфігураціÑ. "
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "Ðеможливо налаштувати Ñвіт: Ðічого не вибрано"
+#~ msgid "Configuration saved. "
+#~ msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð¾. "
-#~ msgid "Failed to delete all world files"
-#~ msgstr "Помилка при видаленні файлів Ñвіту"
+#~ msgid "is required by:"
+#~ msgstr "необхідний длÑ:"
-#, fuzzy
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
+#~ msgid "Left click: Move all items, Right click: Move single item"
#~ msgstr ""
-#~ "Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð·Ð° замовчанню:\n"
-#~ "- WASD: рух\n"
-#~ "- Space: Ñтрибок/лізти в гору\n"
-#~ "- Shift: краÑтиÑÑ/лізти в низ\n"
-#~ "- Q: кинути предмет\n"
-#~ "- I: інвентар\n"
-#~ "- Мишка: поворот/дивитиÑÑ\n"
-#~ "- Ліва клавіша миші: копати/удар\n"
-#~ "- Права клавіша миші: поÑтавити/викориÑтовувати\n"
-#~ "- КолеÑо миші: вибір предмета\n"
-#~ "- T: чат\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "Вихід з гри"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "Вихід в меню"
-
-#~ msgid "Sound Volume"
-#~ msgstr "ГучніÑÑ‚ÑŒ звуку"
-
-#~ msgid "Change Password"
-#~ msgstr "Змінити Пароль"
-
-#~ msgid "Continue"
-#~ msgstr "Продовжити"
-
-#~ msgid "You died."
-#~ msgstr "Ви загинули."
-
-#~ msgid "Connecting to server..."
-#~ msgstr "ÐŸÑ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð´Ð¾ Ñервера..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "ÐžÑ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð°Ð´Ñ€ÐµÑи..."
-
-#~ msgid "Creating client..."
-#~ msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ñ”Ð½Ñ‚Ð°..."
-
-#~ msgid "Creating server...."
-#~ msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñервера..."
-
-#~ msgid "Loading..."
-#~ msgstr "ЗавантаженнÑ..."
-
-#, fuzzy
-#~ msgid "Finite Liquid"
-#~ msgstr "Кінцеві рідини"
-
-#~ msgid "Preload item visuals"
-#~ msgstr "Попереднє Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½ÑŒ"
-
-#, fuzzy
-#~ msgid "Password"
-#~ msgstr "Старий Пароль"
+#~ "Ліва кнопка миші: ПереміÑтити уÑÑ– предмети, Права кнопка миші: "
+#~ "ПереміÑтити один предмет"
-#~ msgid "Favorites:"
-#~ msgstr "Улюблені:"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "Ðнізотропна фільтраціÑ"
-#, fuzzy
-#~ msgid "Games"
-#~ msgstr "Гра"
+#~ msgid "Mip-Mapping"
+#~ msgstr "MIP-текÑтуруваннÑ"
#, fuzzy
-#~ msgid "Game Name"
-#~ msgstr "Гра"
+#~ msgid "Downloading"
+#~ msgstr "Вниз"
diff --git a/po/zh_CN/minetest.po b/po/zh_CN/minetest.po
index e0b96ce15..bda9dc6e7 100644
--- a/po/zh_CN/minetest.po
+++ b/po/zh_CN/minetest.po
@@ -7,9 +7,9 @@ msgid ""
msgstr ""
"Project-Id-Version: minetest\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-12-13 15:24+0100\n"
-"PO-Revision-Date: 2013-11-25 10:04+0200\n"
-"Last-Translator: Shen Zheyu <arsdragonfly@gmail.com>\n"
+"POT-Creation-Date: 2015-07-17 07:23+0200\n"
+"PO-Revision-Date: 2015-04-19 02:00+0800\n"
+"Last-Translator: Ang Weijie <fishyWET@hotmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
@@ -18,57 +18,73 @@ msgstr ""
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 1.7-dev\n"
-#: builtin/fstk/ui.lua:67
+#: builtin/fstk/ui.lua:82
+msgid "An error occured in a Lua script, such as a mod:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:84
+msgid "An error occured:"
+msgstr ""
+
+#: builtin/fstk/ui.lua:89 builtin/mainmenu/store.lua:165
msgid "Ok"
-msgstr "OK"
+msgstr "确定"
-#: builtin/mainmenu/dlg_config_world.lua:26
+#: builtin/mainmenu/common.lua:239 src/game.cpp:1891
+msgid "Loading..."
+msgstr "载入中..."
+
+#: builtin/mainmenu/common.lua:240
+msgid "Try reenabling public serverlist and check your internet connection."
+msgstr ""
+
+#: builtin/mainmenu/dlg_config_world.lua:29
msgid "World:"
msgstr "世界:"
-#: builtin/mainmenu/dlg_config_world.lua:30
-#: builtin/mainmenu/dlg_config_world.lua:32
+#: builtin/mainmenu/dlg_config_world.lua:33
+#: builtin/mainmenu/dlg_config_world.lua:35
msgid "Hide Game"
msgstr "éšè—游æˆ"
-#: builtin/mainmenu/dlg_config_world.lua:36
-#: builtin/mainmenu/dlg_config_world.lua:38
+#: builtin/mainmenu/dlg_config_world.lua:39
+#: builtin/mainmenu/dlg_config_world.lua:41
msgid "Hide mp content"
msgstr "éšè—MOD包内容"
-#: builtin/mainmenu/dlg_config_world.lua:46
+#: builtin/mainmenu/dlg_config_world.lua:49
msgid "Mod:"
msgstr "MOD:"
-#: builtin/mainmenu/dlg_config_world.lua:48
+#: builtin/mainmenu/dlg_config_world.lua:51 builtin/mainmenu/tab_mods.lua:99
msgid "Depends:"
msgstr "ä¾èµ–于:"
-#: builtin/mainmenu/dlg_config_world.lua:51 src/guiKeyChangeMenu.cpp:191
+#: builtin/mainmenu/dlg_config_world.lua:54 src/guiKeyChangeMenu.cpp:191
msgid "Save"
msgstr "ä¿å­˜"
-#: builtin/mainmenu/dlg_config_world.lua:52
+#: builtin/mainmenu/dlg_config_world.lua:55
#: builtin/mainmenu/dlg_create_world.lua:64
#: builtin/mainmenu/dlg_rename_modpack.lua:33 src/guiKeyChangeMenu.cpp:199
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Cancel"
msgstr "å–消"
-#: builtin/mainmenu/dlg_config_world.lua:68
+#: builtin/mainmenu/dlg_config_world.lua:71
msgid "Enable MP"
msgstr "å¯ç”¨MOD包"
-#: builtin/mainmenu/dlg_config_world.lua:70
+#: builtin/mainmenu/dlg_config_world.lua:73
msgid "Disable MP"
msgstr "ç¦ç”¨MOD包"
-#: builtin/mainmenu/dlg_config_world.lua:74
-#: builtin/mainmenu/dlg_config_world.lua:76
+#: builtin/mainmenu/dlg_config_world.lua:77
+#: builtin/mainmenu/dlg_config_world.lua:79
msgid "enabled"
msgstr "å¯ç”¨"
-#: builtin/mainmenu/dlg_config_world.lua:82
+#: builtin/mainmenu/dlg_config_world.lua:85
msgid "Enable all"
msgstr "全部å¯ç”¨"
@@ -94,21 +110,21 @@ msgstr "创建"
#: builtin/mainmenu/dlg_create_world.lua:68
msgid "You have no subgames installed."
-msgstr ""
+msgstr "你没有安装任何游æˆ"
#: builtin/mainmenu/dlg_create_world.lua:69
msgid "Download one from minetest.net"
-msgstr ""
+msgstr "从minetest.net下载一个"
#: builtin/mainmenu/dlg_create_world.lua:72
msgid "Warning: The minimal development test is meant for developers."
-msgstr ""
+msgstr "警告: 最å°åŒ–å¼€å‘测试为开å‘人员所使用。"
#: builtin/mainmenu/dlg_create_world.lua:73
msgid "Download a subgame, such as minetest_game, from minetest.net"
-msgstr ""
+msgstr "从minetest.net下载一个游æˆï¼Œä¾‹å¦‚:minetest_game"
-#: builtin/mainmenu/dlg_create_world.lua:97
+#: builtin/mainmenu/dlg_create_world.lua:99
msgid "A world named \"$1\" already exists"
msgstr "å为 \"$1\" 的世界已ç»å­˜åœ¨"
@@ -122,7 +138,7 @@ msgstr "你确认è¦åˆ é™¤\"$1\"?"
#: builtin/mainmenu/dlg_delete_mod.lua:27
#: builtin/mainmenu/dlg_delete_world.lua:25
-#: builtin/mainmenu/tab_settings.lua:25
+#: builtin/mainmenu/tab_settings.lua:100
msgid "Yes"
msgstr "是"
@@ -150,15 +166,15 @@ msgstr "å¦"
msgid "Rename Modpack:"
msgstr "é‡å‘½åMOD包:"
-#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:228
+#: builtin/mainmenu/dlg_rename_modpack.lua:31 src/keycode.cpp:227
msgid "Accept"
msgstr "接å—"
-#: builtin/mainmenu/modmgr.lua:342
+#: builtin/mainmenu/modmgr.lua:344
msgid "Install Mod: file: \"$1\""
msgstr "安装MOD:文件:â€$1“"
-#: builtin/mainmenu/modmgr.lua:343
+#: builtin/mainmenu/modmgr.lua:345
#, fuzzy
msgid ""
"\n"
@@ -167,83 +183,80 @@ msgstr ""
"\n"
"安装MOD:ä¸æ”¯æŒçš„文件类型“$1“"
-#: builtin/mainmenu/modmgr.lua:363
+#: builtin/mainmenu/modmgr.lua:365
msgid "Failed to install $1 to $2"
msgstr "无法安装$1到$2"
-#: builtin/mainmenu/modmgr.lua:366
+#: builtin/mainmenu/modmgr.lua:368
msgid "Install Mod: unable to find suitable foldername for modpack $1"
msgstr "安装MOD:找ä¸åˆ°MOD包$1çš„åˆé€‚文件夹å"
-#: builtin/mainmenu/modmgr.lua:386
+#: builtin/mainmenu/modmgr.lua:388
msgid "Install Mod: unable to find real modname for: $1"
msgstr "安装MOD:找ä¸åˆ°$1的真正MODå"
#: builtin/mainmenu/store.lua:88
msgid "Unsorted"
-msgstr ""
+msgstr "未分类"
-#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:584
+#: builtin/mainmenu/store.lua:99 builtin/mainmenu/store.lua:580
msgid "Search"
-msgstr ""
+msgstr "æœç´¢"
-#: builtin/mainmenu/store.lua:125
+#: builtin/mainmenu/store.lua:126
#, fuzzy
-msgid "Downloading"
-msgstr "下载"
-
-#: builtin/mainmenu/store.lua:127
-msgid "please wait..."
-msgstr ""
+msgid "Downloading $1, please wait..."
+msgstr "请ç¨å€™..."
-#: builtin/mainmenu/store.lua:159
+#: builtin/mainmenu/store.lua:160
msgid "Successfully installed:"
-msgstr ""
+msgstr "æˆåŠŸçš„安装:"
-#: builtin/mainmenu/store.lua:163
+#: builtin/mainmenu/store.lua:162
#, fuzzy
msgid "Shortname:"
-msgstr "世界å称"
-
-#: builtin/mainmenu/store.lua:167 src/guiFormSpecMenu.cpp:2866
-msgid "ok"
-msgstr ""
+msgstr "短å称"
-#: builtin/mainmenu/store.lua:476
+#: builtin/mainmenu/store.lua:472
msgid "Rating"
msgstr "评级"
-#: builtin/mainmenu/store.lua:501
+#: builtin/mainmenu/store.lua:497
msgid "re-Install"
msgstr "é‡æ–°å®‰è£…"
-#: builtin/mainmenu/store.lua:503
+#: builtin/mainmenu/store.lua:499
msgid "Install"
msgstr "安装"
-#: builtin/mainmenu/store.lua:522
+#: builtin/mainmenu/store.lua:518
msgid "Close store"
-msgstr ""
+msgstr "关闭商店"
-#: builtin/mainmenu/store.lua:530
+#: builtin/mainmenu/store.lua:526
msgid "Page $1 of $2"
msgstr "第$1页,共$2页"
#: builtin/mainmenu/tab_credits.lua:22
msgid "Credits"
-msgstr "关于"
+msgstr "归功"
-#: builtin/mainmenu/tab_credits.lua:29
+#: builtin/mainmenu/tab_credits.lua:31
msgid "Core Developers"
-msgstr "核心开å‘人员"
+msgstr "内部开å‘人员"
-#: builtin/mainmenu/tab_credits.lua:43
+#: builtin/mainmenu/tab_credits.lua:47
msgid "Active Contributors"
-msgstr "活跃的贡献者"
+msgstr "积æžè´¡çŒ®è€…"
+
+#: builtin/mainmenu/tab_credits.lua:54
+#, fuzzy
+msgid "Previous Core Developers"
+msgstr "内部开å‘人员"
-#: builtin/mainmenu/tab_credits.lua:48
+#: builtin/mainmenu/tab_credits.lua:59
msgid "Previous Contributors"
-msgstr "以往的贡献者"
+msgstr "å‰è´¡çŒ®è€…"
#: builtin/mainmenu/tab_mods.lua:30
msgid "Installed Mods:"
@@ -251,27 +264,27 @@ msgstr "已安装的MOD:"
#: builtin/mainmenu/tab_mods.lua:39
msgid "Online mod repository"
-msgstr "在线mod库"
+msgstr "网上MOD库"
#: builtin/mainmenu/tab_mods.lua:78
msgid "No mod description available"
-msgstr "æ— å¯ç”¨modä¿¡æ¯"
+msgstr "æ— MOD资料å¯å¾—"
#: builtin/mainmenu/tab_mods.lua:82
msgid "Mod information:"
-msgstr "modä¿¡æ¯ï¼š"
+msgstr "MOD资料:"
#: builtin/mainmenu/tab_mods.lua:93
msgid "Rename"
-msgstr "é‡å‘½å"
+msgstr "改å"
#: builtin/mainmenu/tab_mods.lua:95
msgid "Uninstall selected modpack"
-msgstr "删除选定mod包"
+msgstr "删除选中的MOD包"
#: builtin/mainmenu/tab_mods.lua:106
msgid "Uninstall selected mod"
-msgstr "删除选中MOD"
+msgstr "删除选中的MOD"
#: builtin/mainmenu/tab_mods.lua:121
msgid "Select Mod File:"
@@ -282,12 +295,13 @@ msgid "Mods"
msgstr "MODS"
#: builtin/mainmenu/tab_multiplayer.lua:23
-msgid "Address/Port"
+#, fuzzy
+msgid "Address / Port :"
msgstr "地å€/端å£"
-#: builtin/mainmenu/tab_multiplayer.lua:24 builtin/mainmenu/tab_server.lua:37
-#: builtin/mainmenu/tab_simple_main.lua:25
-msgid "Name/Password"
+#: builtin/mainmenu/tab_multiplayer.lua:24
+#, fuzzy
+msgid "Name / Password :"
msgstr "åå­—/密ç "
#: builtin/mainmenu/tab_multiplayer.lua:29
@@ -296,7 +310,7 @@ msgid "Public Serverlist"
msgstr "公共æœåŠ¡å™¨åˆ—表"
#: builtin/mainmenu/tab_multiplayer.lua:34 builtin/mainmenu/tab_server.lua:26
-#: builtin/mainmenu/tab_singleplayer.lua:85 src/keycode.cpp:230
+#: builtin/mainmenu/tab_singleplayer.lua:96 src/keycode.cpp:229
msgid "Delete"
msgstr "删除"
@@ -305,15 +319,33 @@ msgstr "删除"
msgid "Connect"
msgstr "连接"
-#: builtin/mainmenu/tab_multiplayer.lua:252
+#: builtin/mainmenu/tab_multiplayer.lua:62
+#: builtin/mainmenu/tab_simple_main.lua:45
+#, fuzzy
+msgid "Creative mode"
+msgstr "创造模å¼"
+
+#: builtin/mainmenu/tab_multiplayer.lua:63
+#: builtin/mainmenu/tab_simple_main.lua:46
+#, fuzzy
+msgid "Damage enabled"
+msgstr "å¯ç”¨"
+
+#: builtin/mainmenu/tab_multiplayer.lua:64
+#: builtin/mainmenu/tab_simple_main.lua:47
+#, fuzzy
+msgid "PvP enabled"
+msgstr "å¯ç”¨"
+
+#: builtin/mainmenu/tab_multiplayer.lua:257
msgid "Client"
msgstr "客户端"
-#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:86
+#: builtin/mainmenu/tab_server.lua:27 builtin/mainmenu/tab_singleplayer.lua:97
msgid "New"
msgstr "新建"
-#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:87
+#: builtin/mainmenu/tab_server.lua:28 builtin/mainmenu/tab_singleplayer.lua:98
msgid "Configure"
msgstr "é…ç½®"
@@ -321,180 +353,219 @@ msgstr "é…ç½®"
msgid "Start Game"
msgstr "å¯åŠ¨æ¸¸æˆ"
-#: builtin/mainmenu/tab_server.lua:30 builtin/mainmenu/tab_singleplayer.lua:89
+#: builtin/mainmenu/tab_server.lua:30
+#: builtin/mainmenu/tab_singleplayer.lua:100
msgid "Select World:"
msgstr "选择世界:"
-#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:63
-#: builtin/mainmenu/tab_singleplayer.lua:90
+#: builtin/mainmenu/tab_server.lua:31 builtin/mainmenu/tab_simple_main.lua:76
+#: builtin/mainmenu/tab_singleplayer.lua:101
msgid "Creative Mode"
msgstr "创造模å¼"
-#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:65
-#: builtin/mainmenu/tab_singleplayer.lua:92
+#: builtin/mainmenu/tab_server.lua:33 builtin/mainmenu/tab_simple_main.lua:78
+#: builtin/mainmenu/tab_singleplayer.lua:103
msgid "Enable Damage"
-msgstr "å¼€å¯ä¼¤å®³"
+msgstr "å¼€å¯ä¼¤å®³é£Žé™©"
#: builtin/mainmenu/tab_server.lua:35
msgid "Public"
msgstr "公共æœåŠ¡å™¨"
+#: builtin/mainmenu/tab_server.lua:37 builtin/mainmenu/tab_simple_main.lua:25
+msgid "Name/Password"
+msgstr "åå­—/密ç "
+
#: builtin/mainmenu/tab_server.lua:45
msgid "Bind Address"
-msgstr ""
+msgstr "绑定地å€"
#: builtin/mainmenu/tab_server.lua:47
msgid "Port"
-msgstr ""
+msgstr "端å£"
#: builtin/mainmenu/tab_server.lua:51
msgid "Server Port"
msgstr "æœåŠ¡å™¨ç«¯å£"
-#: builtin/mainmenu/tab_server.lua:174
+#: builtin/mainmenu/tab_server.lua:138
+#: builtin/mainmenu/tab_singleplayer.lua:165
+#, fuzzy
+msgid "No world created or selected!"
+msgstr "未给定世界å或未选择游æˆ"
+
+#: builtin/mainmenu/tab_server.lua:191
msgid "Server"
msgstr "æœåŠ¡å™¨"
+#: builtin/mainmenu/tab_settings.lua:21
+#, fuzzy
+msgid "Opaque Leaves"
+msgstr "ä¸é€æ˜Žçš„æ°´"
+
+#: builtin/mainmenu/tab_settings.lua:22
+#, fuzzy
+msgid "Simple Leaves"
+msgstr "摇动的å¶å­"
+
#: builtin/mainmenu/tab_settings.lua:23
-msgid "Are you sure to reset your singleplayer world?"
+#, fuzzy
+msgid "Fancy Leaves"
+msgstr "花å¼æ ‘"
+
+#: builtin/mainmenu/tab_settings.lua:32
+msgid "No Filter"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:27
-msgid "No!!!"
+#: builtin/mainmenu/tab_settings.lua:33
+#, fuzzy
+msgid "Bilinear Filter"
+msgstr "åŒçº¿æ€§è¿‡æ»¤"
+
+#: builtin/mainmenu/tab_settings.lua:34
+#, fuzzy
+msgid "Trilinear Filter"
+msgstr "三线性过滤"
+
+#: builtin/mainmenu/tab_settings.lua:43
+msgid "No Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:44
+msgid "Mipmap"
+msgstr ""
+
+#: builtin/mainmenu/tab_settings.lua:45
+msgid "Mipmap + Aniso. Filter"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:134
+#: builtin/mainmenu/tab_settings.lua:98
+msgid "Are you sure to reset your singleplayer world?"
+msgstr "你确定è¦é‡ç½®æ‚¨çš„å•äººä¸–ç•Œå—?"
+
+#: builtin/mainmenu/tab_settings.lua:102
+msgid "No!!!"
+msgstr "ä¸!!!"
+
+#: builtin/mainmenu/tab_settings.lua:202
msgid "Smooth Lighting"
msgstr "平滑光照"
-#: builtin/mainmenu/tab_settings.lua:136
+#: builtin/mainmenu/tab_settings.lua:204
msgid "Enable Particles"
msgstr "å¯ç”¨ç²’å­æ•ˆæžœ"
-#: builtin/mainmenu/tab_settings.lua:138
+#: builtin/mainmenu/tab_settings.lua:206
msgid "3D Clouds"
-msgstr "3D云彩"
-
-#: builtin/mainmenu/tab_settings.lua:140
-#, fuzzy
-msgid "Fancy Trees"
-msgstr "更漂亮的树"
+msgstr "三维云彩"
-#: builtin/mainmenu/tab_settings.lua:142
+#: builtin/mainmenu/tab_settings.lua:208
msgid "Opaque Water"
msgstr "ä¸é€æ˜Žçš„æ°´"
-#: builtin/mainmenu/tab_settings.lua:144
+#: builtin/mainmenu/tab_settings.lua:210
#, fuzzy
msgid "Connected Glass"
-msgstr "连接"
+msgstr "连接的玻璃"
-#: builtin/mainmenu/tab_settings.lua:149
-msgid "Restart minetest for driver change to take effect"
+#: builtin/mainmenu/tab_settings.lua:212
+msgid "Node Highlighting"
msgstr ""
-#: builtin/mainmenu/tab_settings.lua:151
-msgid "Mip-Mapping"
-msgstr "贴图处ç†"
-
-#: builtin/mainmenu/tab_settings.lua:153
-msgid "Anisotropic Filtering"
-msgstr "å„å‘异性过滤"
+#: builtin/mainmenu/tab_settings.lua:217
+msgid "Texturing:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:155
-msgid "Bi-Linear Filtering"
-msgstr "åŒçº¿æ€§è¿‡æ»¤"
+#: builtin/mainmenu/tab_settings.lua:222
+msgid "Rendering:"
+msgstr ""
-#: builtin/mainmenu/tab_settings.lua:157
-msgid "Tri-Linear Filtering"
-msgstr "三线性过滤"
+#: builtin/mainmenu/tab_settings.lua:226
+msgid "Restart minetest for driver change to take effect"
+msgstr "é‡å¯minetest让驱动å˜åŒ–生效"
-#: builtin/mainmenu/tab_settings.lua:160
+#: builtin/mainmenu/tab_settings.lua:228
msgid "Shaders"
msgstr "ç€è‰²å™¨"
-#: builtin/mainmenu/tab_settings.lua:164
+#: builtin/mainmenu/tab_settings.lua:233
msgid "Change keys"
msgstr "改å˜é”®ä½è®¾ç½®"
-#: builtin/mainmenu/tab_settings.lua:167
+#: builtin/mainmenu/tab_settings.lua:236
#, fuzzy
msgid "Reset singleplayer world"
-msgstr "å•äººæ¸¸æˆ"
+msgstr "é‡ç½®å•äººæ¸¸æˆ"
-#: builtin/mainmenu/tab_settings.lua:171
+#: builtin/mainmenu/tab_settings.lua:240
msgid "GUI scale factor"
-msgstr ""
+msgstr "GUI缩放因å­"
-#: builtin/mainmenu/tab_settings.lua:175
+#: builtin/mainmenu/tab_settings.lua:244
msgid "Scaling factor applied to menu elements: "
-msgstr ""
+msgstr "èœå•å…ƒç´ åº”用缩放因å­"
-#: builtin/mainmenu/tab_settings.lua:181
+#: builtin/mainmenu/tab_settings.lua:250
msgid "Touch free target"
-msgstr ""
+msgstr "自由触摸目标"
-#: builtin/mainmenu/tab_settings.lua:187
+#: builtin/mainmenu/tab_settings.lua:256
msgid "Touchthreshold (px)"
-msgstr ""
+msgstr "触控阈值(åƒç´ ï¼‰"
-#: builtin/mainmenu/tab_settings.lua:194 builtin/mainmenu/tab_settings.lua:208
+#: builtin/mainmenu/tab_settings.lua:263 builtin/mainmenu/tab_settings.lua:277
#, fuzzy
msgid "Bumpmapping"
msgstr "贴图处ç†"
-#: builtin/mainmenu/tab_settings.lua:196 builtin/mainmenu/tab_settings.lua:209
+#: builtin/mainmenu/tab_settings.lua:265 builtin/mainmenu/tab_settings.lua:278
msgid "Generate Normalmaps"
-msgstr ""
+msgstr "产生法线贴图"
-#: builtin/mainmenu/tab_settings.lua:198 builtin/mainmenu/tab_settings.lua:210
+#: builtin/mainmenu/tab_settings.lua:267 builtin/mainmenu/tab_settings.lua:279
msgid "Parallax Occlusion"
-msgstr ""
+msgstr "视差贴图"
-#: builtin/mainmenu/tab_settings.lua:200 builtin/mainmenu/tab_settings.lua:211
+#: builtin/mainmenu/tab_settings.lua:269 builtin/mainmenu/tab_settings.lua:280
msgid "Waving Water"
-msgstr ""
+msgstr "摇动的水"
-#: builtin/mainmenu/tab_settings.lua:202 builtin/mainmenu/tab_settings.lua:212
+#: builtin/mainmenu/tab_settings.lua:271 builtin/mainmenu/tab_settings.lua:281
msgid "Waving Leaves"
-msgstr ""
+msgstr "摇动的å¶å­"
-#: builtin/mainmenu/tab_settings.lua:204 builtin/mainmenu/tab_settings.lua:213
+#: builtin/mainmenu/tab_settings.lua:273 builtin/mainmenu/tab_settings.lua:282
msgid "Waving Plants"
-msgstr ""
+msgstr "摇动的æ¤ç‰©"
-#: builtin/mainmenu/tab_settings.lua:255
+#: builtin/mainmenu/tab_settings.lua:308
msgid "To enable shaders the OpenGL driver needs to be used."
msgstr "å¯ç”¨ç€è‰²å™¨éœ€è¦ä½¿ç”¨OpenGL驱动。"
-#: builtin/mainmenu/tab_settings.lua:330
+#: builtin/mainmenu/tab_settings.lua:430
msgid "Settings"
msgstr "设置"
-#: builtin/mainmenu/tab_simple_main.lua:67
-msgid "Fly mode"
-msgstr ""
-
-#: builtin/mainmenu/tab_simple_main.lua:71
+#: builtin/mainmenu/tab_simple_main.lua:82
#, fuzzy
msgid "Start Singleplayer"
msgstr "å•äººæ¸¸æˆ"
-#: builtin/mainmenu/tab_simple_main.lua:72
+#: builtin/mainmenu/tab_simple_main.lua:83
#, fuzzy
msgid "Config mods"
-msgstr "é…ç½®"
+msgstr "é…ç½®MOD"
-#: builtin/mainmenu/tab_simple_main.lua:191
+#: builtin/mainmenu/tab_simple_main.lua:201
#, fuzzy
msgid "Main"
msgstr "主èœå•"
-#: builtin/mainmenu/tab_singleplayer.lua:88 src/keycode.cpp:249
+#: builtin/mainmenu/tab_singleplayer.lua:99 src/keycode.cpp:248
msgid "Play"
msgstr "开始游æˆ"
-#: builtin/mainmenu/tab_singleplayer.lua:224
+#: builtin/mainmenu/tab_singleplayer.lua:246
msgid "Singleplayer"
msgstr "å•äººæ¸¸æˆ"
@@ -504,46 +575,193 @@ msgstr "选择æ质包:"
#: builtin/mainmenu/tab_texturepacks.lua:69
msgid "No information available"
-msgstr "æ— å¯ç”¨ä¿¡æ¯"
+msgstr "无资料å¯å¾—"
#: builtin/mainmenu/tab_texturepacks.lua:114
#, fuzzy
msgid "Texturepacks"
msgstr "æ质包"
-#: src/client.cpp:2726
+#: src/client.cpp:1721
+#, fuzzy
+msgid "Loading textures..."
+msgstr "载入中..."
+
+#: src/client.cpp:1736
+#, fuzzy
+msgid "Rebuilding shaders..."
+msgstr "正在解æžåœ°å€..."
+
+#: src/client.cpp:1743
+msgid "Initializing nodes..."
+msgstr ""
+
+#: src/client.cpp:1760
+msgid "Initializing nodes"
+msgstr ""
+
+#: src/client.cpp:1768
msgid "Item textures..."
msgstr "物å“æè´¨..."
+#: src/client.cpp:1793
+msgid "Done!"
+msgstr ""
+
+#: src/client/clientlauncher.cpp:185
+msgid "Main Menu"
+msgstr "主èœå•"
+
+#: src/client/clientlauncher.cpp:223
+msgid "Player name too long."
+msgstr "玩家的å字太长了"
+
+#: src/client/clientlauncher.cpp:261
+msgid "Connection error (timed out?)"
+msgstr "连接出错(超时?)"
+
+#: src/client/clientlauncher.cpp:425
+msgid "No world selected and no address provided. Nothing to do."
+msgstr "没有选择世界或æ供地å€ã€‚未执行æ“作。"
+
+#: src/client/clientlauncher.cpp:432
+msgid "Provided world path doesn't exist: "
+msgstr "æ供世界地å€ä¸å­˜åœ¨"
+
+#: src/client/clientlauncher.cpp:441
+msgid "Could not find or load game \""
+msgstr "无法找到或载入游æˆæ¨¡å¼"
+
+#: src/client/clientlauncher.cpp:459
+msgid "Invalid gamespec."
+msgstr "éžæ³•æ¸¸æˆæ¨¡å¼è§„格。"
+
#: src/fontengine.cpp:70 src/fontengine.cpp:226
msgid "needs_fallback_font"
msgstr "yes"
-#: src/game.cpp:1063
+#: src/game.cpp:1052 src/guiFormSpecMenu.cpp:2065
+msgid "Proceed"
+msgstr "继续"
+
+#: src/game.cpp:1072
+msgid "You died."
+msgstr "你死了。"
+
+#: src/game.cpp:1073
msgid "Respawn"
msgstr "é‡ç”Ÿ"
-#: src/game.cpp:2250
+#: src/game.cpp:1092
+msgid ""
+"Default Controls:\n"
+"No menu visible:\n"
+"- single tap: button activate\n"
+"- double tap: place/use\n"
+"- slide finger: look around\n"
+"Menu/Inventory visible:\n"
+"- double tap (outside):\n"
+" -->close\n"
+"- touch stack, touch slot:\n"
+" --> move stack\n"
+"- touch&drag, tap 2nd finger\n"
+" --> place single item to slot\n"
+msgstr ""
+
+#: src/game.cpp:1106
+msgid ""
+"Default Controls:\n"
+"- WASD: move\n"
+"- Space: jump/climb\n"
+"- Shift: sneak/go down\n"
+"- Q: drop item\n"
+"- I: inventory\n"
+"- Mouse: turn/look\n"
+"- Mouse left: dig/punch\n"
+"- Mouse right: place/use\n"
+"- Mouse wheel: select item\n"
+"- T: chat\n"
+msgstr ""
+"默认控制:\n"
+"W/A/S/D: 移动\n"
+"空格: 跳/爬\n"
+"Shift: 潜行/å‘下\n"
+"Q: 丢物å“\n"
+"I: 物å“æ \n"
+"鼠标:转身/环顾\n"
+"鼠标左键: 挖\n"
+"é¼ æ ‡å³é”®: 放/使用\n"
+"鼠标滚轮: 选择物å“\n"
+"T: èŠå¤©\n"
+
+#: src/game.cpp:1125
+msgid "Continue"
+msgstr "继续"
+
+#: src/game.cpp:1129
+msgid "Change Password"
+msgstr "更改密ç "
+
+#: src/game.cpp:1134
+msgid "Sound Volume"
+msgstr "音é‡"
+
+#: src/game.cpp:1136
+#, fuzzy
+msgid "Change Keys"
+msgstr "改å˜é”®ä½è®¾ç½®"
+
+#: src/game.cpp:1139
+msgid "Exit to Menu"
+msgstr "退出至èœå•"
+
+#: src/game.cpp:1141
+msgid "Exit to OS"
+msgstr "退出至æ“作系统"
+
+#: src/game.cpp:1841
+#, fuzzy
+msgid "Shutting down..."
+msgstr "关闭中......"
+
+#: src/game.cpp:1948
+#, fuzzy
+msgid "Creating server..."
+msgstr "正在建立æœåŠ¡å™¨...."
+
+#: src/game.cpp:1984
+msgid "Creating client..."
+msgstr "正在建立客户端..."
+
+#: src/game.cpp:2159
+msgid "Resolving address..."
+msgstr "正在解æžåœ°å€..."
+
+#: src/game.cpp:2261
+msgid "Connecting to server..."
+msgstr "正在连接æœåŠ¡å™¨..."
+
+#: src/game.cpp:2317
msgid "Item definitions..."
msgstr "物å“定义..."
-#: src/game.cpp:2255
+#: src/game.cpp:2322
msgid "Node definitions..."
msgstr "æ–¹å—定义..."
-#: src/game.cpp:2262
+#: src/game.cpp:2329
msgid "Media..."
msgstr "媒体..."
-#: src/game.cpp:2267
-msgid " KB/s"
+#: src/game.cpp:2334
+msgid "KiB/s"
msgstr ""
-#: src/game.cpp:2271
-msgid " MB/s"
+#: src/game.cpp:2338
+msgid "MiB/s"
msgstr ""
-#: src/game.cpp:4220
+#: src/game.cpp:4363
msgid ""
"\n"
"Check debug.txt for details."
@@ -551,13 +769,13 @@ msgstr ""
"\n"
"查看 debug.txt 以获得详细信æ¯ã€‚"
-#: src/guiFormSpecMenu.cpp:2055
-msgid "Proceed"
-msgstr "继续游æˆ"
-
-#: src/guiFormSpecMenu.cpp:2846
+#: src/guiFormSpecMenu.cpp:2855
msgid "Enter "
-msgstr ""
+msgstr "输入"
+
+#: src/guiFormSpecMenu.cpp:2875
+msgid "ok"
+msgstr "确定"
#: src/guiKeyChangeMenu.cpp:125
msgid "Keybindings. (If this menu screws up, remove stuff from minetest.conf)"
@@ -569,478 +787,487 @@ msgstr "“使用†= å‘下爬"
#: src/guiKeyChangeMenu.cpp:180
msgid "Double tap \"jump\" to toggle fly"
-msgstr "连按两次“跳â€åˆ‡æ¢é£žè¡ŒçŠ¶æ€"
+msgstr "连按两次“跳â€åˆ‡æ¢é£žè¡Œæ¨¡å¼"
-#: src/guiKeyChangeMenu.cpp:296
+#: src/guiKeyChangeMenu.cpp:295
msgid "Key already in use"
msgstr "按键已被å ç”¨"
-#: src/guiKeyChangeMenu.cpp:371
+#: src/guiKeyChangeMenu.cpp:373
msgid "press key"
msgstr "按键"
-#: src/guiKeyChangeMenu.cpp:397
+#: src/guiKeyChangeMenu.cpp:399
msgid "Forward"
msgstr "å‘å‰"
-#: src/guiKeyChangeMenu.cpp:398
+#: src/guiKeyChangeMenu.cpp:400
msgid "Backward"
msgstr "å‘åŽ"
-#: src/guiKeyChangeMenu.cpp:399 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:401 src/keycode.cpp:228
msgid "Left"
msgstr "å‘å·¦"
-#: src/guiKeyChangeMenu.cpp:400 src/keycode.cpp:229
+#: src/guiKeyChangeMenu.cpp:402 src/keycode.cpp:228
msgid "Right"
msgstr "å‘å³"
-#: src/guiKeyChangeMenu.cpp:401
+#: src/guiKeyChangeMenu.cpp:403
msgid "Use"
msgstr "使用"
-#: src/guiKeyChangeMenu.cpp:402
+#: src/guiKeyChangeMenu.cpp:404
msgid "Jump"
msgstr "è·³"
-#: src/guiKeyChangeMenu.cpp:403
+#: src/guiKeyChangeMenu.cpp:405
msgid "Sneak"
msgstr "潜行"
-#: src/guiKeyChangeMenu.cpp:404
+#: src/guiKeyChangeMenu.cpp:406
msgid "Drop"
msgstr "丢出"
-#: src/guiKeyChangeMenu.cpp:405
+#: src/guiKeyChangeMenu.cpp:407
msgid "Inventory"
msgstr "物å“æ "
-#: src/guiKeyChangeMenu.cpp:406
+#: src/guiKeyChangeMenu.cpp:408
msgid "Chat"
msgstr "èŠå¤©"
-#: src/guiKeyChangeMenu.cpp:407
+#: src/guiKeyChangeMenu.cpp:409
msgid "Command"
msgstr "命令"
-#: src/guiKeyChangeMenu.cpp:408
+#: src/guiKeyChangeMenu.cpp:410
msgid "Console"
msgstr "控制å°"
-#: src/guiKeyChangeMenu.cpp:409
+#: src/guiKeyChangeMenu.cpp:411
msgid "Toggle fly"
-msgstr "切æ¢é£žè¡ŒçŠ¶æ€"
+msgstr "切æ¢é£žè¡Œæ¨¡å¼"
-#: src/guiKeyChangeMenu.cpp:410
+#: src/guiKeyChangeMenu.cpp:412
msgid "Toggle fast"
-msgstr "切æ¢å¿«é€Ÿç§»åŠ¨çŠ¶æ€"
+msgstr "切æ¢å¿«é€Ÿç§»åŠ¨æ¨¡å¼"
-#: src/guiKeyChangeMenu.cpp:411
+#: src/guiKeyChangeMenu.cpp:413
+#, fuzzy
+msgid "Toggle Cinematic"
+msgstr "切æ¢å¿«é€Ÿç§»åŠ¨æ¨¡å¼"
+
+#: src/guiKeyChangeMenu.cpp:414
msgid "Toggle noclip"
msgstr "切æ¢ç©¿å¢™æ¨¡å¼"
-#: src/guiKeyChangeMenu.cpp:412
+#: src/guiKeyChangeMenu.cpp:415
msgid "Range select"
msgstr "选择范围"
-#: src/guiKeyChangeMenu.cpp:413
+#: src/guiKeyChangeMenu.cpp:416
msgid "Print stacks"
msgstr "打å°æ ˆ"
-#: src/guiPasswordChange.cpp:106
+#: src/guiPasswordChange.cpp:108
msgid "Old Password"
msgstr "旧密ç "
-#: src/guiPasswordChange.cpp:122
+#: src/guiPasswordChange.cpp:124
msgid "New Password"
msgstr "新密ç "
-#: src/guiPasswordChange.cpp:137
+#: src/guiPasswordChange.cpp:139
msgid "Confirm Password"
msgstr "确认密ç "
-#: src/guiPasswordChange.cpp:153
+#: src/guiPasswordChange.cpp:155
msgid "Change"
msgstr "更改"
-#: src/guiPasswordChange.cpp:162
+#: src/guiPasswordChange.cpp:164
msgid "Passwords do not match!"
msgstr "密ç ä¸åŒ¹é…ï¼"
-#: src/guiVolumeChange.cpp:106
+#: src/guiVolumeChange.cpp:105
msgid "Sound Volume: "
msgstr "音é‡ï¼š "
-#: src/guiVolumeChange.cpp:120
+#: src/guiVolumeChange.cpp:119
msgid "Exit"
msgstr "退出"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Left Button"
msgstr "左键"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Middle Button"
msgstr "中键"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "Right Button"
msgstr "å³é”®"
-#: src/keycode.cpp:224
+#: src/keycode.cpp:223
msgid "X Button 1"
msgstr "Xé”®1"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Back"
msgstr "退格"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Clear"
msgstr "Clearé”®"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Return"
msgstr "回车"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "Tab"
msgstr "Tabé”®"
-#: src/keycode.cpp:225
+#: src/keycode.cpp:224
msgid "X Button 2"
msgstr "Xé”®2"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Capital"
msgstr "大写"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Control"
-msgstr "Ctrl"
+msgstr "Ctrlé”®"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Kana"
msgstr "å‡å"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Menu"
msgstr "èœå•"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Pause"
msgstr "æš‚åœ"
-#: src/keycode.cpp:226
+#: src/keycode.cpp:225
msgid "Shift"
msgstr "Shifté”®"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Convert"
msgstr "转æ¢"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Escape"
msgstr "Escapeé”®"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Final"
msgstr "Finalé”®"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Junja"
msgstr "Junjaé”®"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Kanji"
msgstr "Kanjié”®"
-#: src/keycode.cpp:227
+#: src/keycode.cpp:226
msgid "Nonconvert"
msgstr "æ— å˜æ¢"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "End"
msgstr "Endé”®"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Home"
msgstr "Homeé”®"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Mode Change"
msgstr "改å˜æ¨¡å¼"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Next"
msgstr "下一个"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Prior"
msgstr "Prioré”®"
-#: src/keycode.cpp:228
+#: src/keycode.cpp:227
msgid "Space"
msgstr "空格"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Down"
msgstr "å‘下"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Execute"
msgstr "执行"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Print"
msgstr "打å°"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Select"
msgstr "选择"
-#: src/keycode.cpp:229
+#: src/keycode.cpp:228
msgid "Up"
msgstr "å‘上"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Help"
msgstr "帮助"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Insert"
msgstr "æ’å…¥"
-#: src/keycode.cpp:230
+#: src/keycode.cpp:229
msgid "Snapshot"
msgstr "å¿«ç…§"
-#: src/keycode.cpp:233
+#: src/keycode.cpp:232
msgid "Left Windows"
msgstr "左窗å£"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Apps"
msgstr "应用"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 0"
msgstr "å°é”®ç›˜0"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Numpad 1"
msgstr "å°é”®ç›˜1"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Right Windows"
msgstr "å³çª—å£"
-#: src/keycode.cpp:234
+#: src/keycode.cpp:233
msgid "Sleep"
msgstr "ç¡çœ "
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 2"
msgstr "å°é”®ç›˜2"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 3"
msgstr "å°é”®ç›˜3"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 4"
msgstr "å°é”®ç›˜4"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 5"
msgstr "å°é”®ç›˜5"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 6"
msgstr "å°é”®ç›˜6"
-#: src/keycode.cpp:235
+#: src/keycode.cpp:234
msgid "Numpad 7"
msgstr "å°é”®ç›˜7"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad *"
msgstr "å°é”®ç›˜*"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad +"
msgstr "å°é”®ç›˜+"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad -"
msgstr "å°é”®ç›˜-"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad /"
msgstr "å°é”®ç›˜/"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 8"
msgstr "å°é”®ç›˜8"
-#: src/keycode.cpp:236
+#: src/keycode.cpp:235
msgid "Numpad 9"
msgstr "å°é”®ç›˜9"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Num Lock"
msgstr "å°é”®ç›˜é”"
-#: src/keycode.cpp:240
+#: src/keycode.cpp:239
msgid "Scroll Lock"
msgstr "Scroll Locké”®"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Left Shift"
msgstr "å·¦Shifté”®"
-#: src/keycode.cpp:241
+#: src/keycode.cpp:240
msgid "Right Shift"
msgstr "å³Shifté”®"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Control"
msgstr "å·¦Controlé”®"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Left Menu"
msgstr "å·¦èœå•"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Control"
msgstr "å³Controlé”®"
-#: src/keycode.cpp:242
+#: src/keycode.cpp:241
msgid "Right Menu"
msgstr "å³èœå•"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Comma"
msgstr "逗å·"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Minus"
msgstr "å‡å·"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Period"
msgstr "å¥å·"
-#: src/keycode.cpp:244
+#: src/keycode.cpp:243
msgid "Plus"
msgstr "加å·"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "Attn"
msgstr "Attné”®"
-#: src/keycode.cpp:248
+#: src/keycode.cpp:247
msgid "CrSel"
msgstr "CrSelé”®"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Erase OEF"
msgstr "Erase OEFé”®"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "ExSel"
msgstr "ExSelé”®"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "OEM Clear"
msgstr "OEM Clearé”®"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "PA1"
msgstr "PA1é”®"
-#: src/keycode.cpp:249
+#: src/keycode.cpp:248
msgid "Zoom"
msgstr "缩放"
-#: src/main.cpp:1681
-msgid "Main Menu"
-msgstr "主èœå•"
+#~ msgid "Game Name"
+#~ msgstr "游æˆå"
-#: src/main.cpp:1719
-msgid "Player name too long."
-msgstr ""
+#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
+#~ msgstr "游æˆç®¡ç†: 无法å¤åˆ¶MOD“$1â€åˆ°æ¸¸æˆâ€œ$2â€"
-#: src/main.cpp:1757
-msgid "Connection error (timed out?)"
-msgstr "连接出错(超时?)"
+#~ msgid "GAMES"
+#~ msgstr "游æˆ"
-#: src/main.cpp:1919
-msgid "No world selected and no address provided. Nothing to do."
-msgstr "没有选择世界或æ供地å€ã€‚未执行æ“作。"
+#~ msgid "Games"
+#~ msgstr "游æˆ"
-#: src/main.cpp:1926
-msgid "Provided world path doesn't exist: "
-msgstr ""
+#~ msgid "Mods:"
+#~ msgstr "MODS:"
-#: src/main.cpp:1935
-msgid "Could not find or load game \""
-msgstr "无法找到或载入游æˆæ¨¡å¼â€œ"
+#~ msgid "edit game"
+#~ msgstr "编辑游æˆ"
-#: src/main.cpp:1953
-msgid "Invalid gamespec."
-msgstr "éžæ³•æ¸¸æˆæ¨¡å¼è§„格。"
+#~ msgid "new game"
+#~ msgstr "新建游æˆ"
-#~ msgid "Left click: Move all items, Right click: Move single item"
-#~ msgstr "左键:移动所有物å“,å³é”®ï¼šç§»åŠ¨å•ä¸ªç‰©å“"
+#~ msgid "EDIT GAME"
+#~ msgstr "编辑游æˆ"
-#~ msgid "is required by:"
-#~ msgstr "被需è¦ï¼š"
+#~ msgid "Remove selected mod"
+#~ msgstr "删除选中MOD"
-#~ msgid "Configuration saved. "
-#~ msgstr "é…置已ä¿å­˜ã€‚ "
+#~ msgid "<<-- Add mod"
+#~ msgstr "<<-- 添加MOD"
-#~ msgid "Warning: Configuration not consistent. "
-#~ msgstr "警告:é…ç½®ä¸ä¸€è‡´ã€‚ "
+#~ msgid "CLIENT"
+#~ msgstr "客户端"
-#~ msgid "Cannot create world: Name contains invalid characters"
-#~ msgstr "无法创建世界:å字包å«éžæ³•å­—符"
+#~ msgid "Favorites:"
+#~ msgstr "最爱的æœåŠ¡å™¨ï¼š"
-#~ msgid "Multiplayer"
-#~ msgstr "多人游æˆ"
+#~ msgid "START SERVER"
+#~ msgstr "å¯åŠ¨æœåŠ¡å™¨"
-#~ msgid "Advanced"
-#~ msgstr "高级è”机设置"
+#~ msgid "Name"
+#~ msgstr "åå­—"
-#~ msgid "Show Public"
-#~ msgstr "显示公共"
+#~ msgid "Password"
+#~ msgstr "密ç "
-#~ msgid "Show Favorites"
-#~ msgstr "显示最爱"
+#~ msgid "SETTINGS"
+#~ msgstr "设置"
-#~ msgid "Leave address blank to start a local server."
-#~ msgstr "地å€æ ç•™ç©ºå¯å¯åŠ¨æœ¬åœ°æœåŠ¡å™¨ã€‚"
+#~ msgid "Preload item visuals"
+#~ msgstr "预先加载物å“图åƒ"
-#~ msgid "Create world"
-#~ msgstr "创造世界"
+#~ msgid "Finite Liquid"
+#~ msgstr "液体有é™å»¶ä¼¸"
-#~ msgid "Address required."
-#~ msgstr "需è¦åœ°å€ã€‚"
+#~ msgid "SINGLE PLAYER"
+#~ msgstr "å•äººæ¸¸æˆ"
-#~ msgid "Cannot delete world: Nothing selected"
-#~ msgstr "无法删除世界:没有选择世界"
+#~ msgid "TEXTURE PACKS"
+#~ msgstr "æ质包"
-#~ msgid "Files to be deleted"
-#~ msgstr "将被删除的文件"
+#~ msgid "MODS"
+#~ msgstr "MODS"
-#~ msgid "Cannot create world: No games found"
-#~ msgstr "无法创造世界:未找到游æˆæ¨¡å¼"
+#~ msgid "Add mod:"
+#~ msgstr "添加MOD:"
-#~ msgid "Cannot configure world: Nothing selected"
-#~ msgstr "无法é…置世界:没有选择世界"
+#~ msgid "Local install"
+#~ msgstr "本地安装"
-#~ msgid "Failed to delete all world files"
-#~ msgstr "无法删除所有该世界的文件"
+#~ msgid ""
+#~ "Warning: Some mods are not configured yet.\n"
+#~ "They will be enabled by default when you save the configuration. "
+#~ msgstr ""
+#~ "警告:一些MODä»æœªè®¾å®šã€‚\n"
+#~ "它们会在你ä¿å­˜é…置的时候自动å¯ç”¨ã€‚ "
+
+#~ msgid ""
+#~ "Warning: Some configured mods are missing.\n"
+#~ "Their setting will be removed when you save the configuration. "
+#~ msgstr ""
+#~ "警告:缺少一些设定了的MOD。\n"
+#~ "它们的设置会在你ä¿å­˜é…置的时候被移除。 "
#~ msgid ""
#~ "Default Controls:\n"
@@ -1068,146 +1295,72 @@ msgstr "éžæ³•æ¸¸æˆæ¨¡å¼è§„格。"
#~ "ESC:èœå•\n"
#~ "T:èŠå¤©\n"
-#~ msgid ""
-#~ "Warning: Some configured mods are missing.\n"
-#~ "Their setting will be removed when you save the configuration. "
-#~ msgstr ""
-#~ "警告:缺少一些设定了的MOD。\n"
-#~ "它们的设置会在你ä¿å­˜é…置的时候被移除。 "
-
-#~ msgid ""
-#~ "Warning: Some mods are not configured yet.\n"
-#~ "They will be enabled by default when you save the configuration. "
-#~ msgstr ""
-#~ "警告:一些MODä»æœªè®¾å®šã€‚\n"
-#~ "它们会在你ä¿å­˜é…置的时候自动å¯ç”¨ã€‚ "
-
-#~ msgid ""
-#~ "Default Controls:\n"
-#~ "- WASD: move\n"
-#~ "- Space: jump/climb\n"
-#~ "- Shift: sneak/go down\n"
-#~ "- Q: drop item\n"
-#~ "- I: inventory\n"
-#~ "- Mouse: turn/look\n"
-#~ "- Mouse left: dig/punch\n"
-#~ "- Mouse right: place/use\n"
-#~ "- Mouse wheel: select item\n"
-#~ "- T: chat\n"
-#~ msgstr ""
-#~ "默认控制:\n"
-#~ "W/A/S/D: 移动\n"
-#~ "空格: 跳/爬\n"
-#~ "Shift: 潜行/å‘下\n"
-#~ "Q: 丢物å“\n"
-#~ "I: 物å“æ \n"
-#~ "鼠标:转身/环顾\n"
-#~ "鼠标左键: 挖\n"
-#~ "é¼ æ ‡å³é”®: 放/使用\n"
-#~ "鼠标滚轮: 选择物å“\n"
-#~ "T: èŠå¤©\n"
-
-#~ msgid "Exit to OS"
-#~ msgstr "退出至æ“作系统"
-
-#~ msgid "Exit to Menu"
-#~ msgstr "退出至èœå•"
-
-#~ msgid "Sound Volume"
-#~ msgstr "音é‡"
-
-#~ msgid "Change Password"
-#~ msgstr "更改密ç "
-
-#~ msgid "Continue"
-#~ msgstr "继续"
-
-#~ msgid "You died."
-#~ msgstr "你死了。"
-
-#~ msgid "Shutting down stuff..."
-#~ msgstr "关闭中......"
-
-#~ msgid "Connecting to server..."
-#~ msgstr "正在连接æœåŠ¡å™¨..."
-
-#~ msgid "Resolving address..."
-#~ msgstr "正在解æžåœ°å€..."
-
-#~ msgid "Creating client..."
-#~ msgstr "正在建立客户端..."
-
-#~ msgid "Creating server...."
-#~ msgstr "正在建立æœåŠ¡å™¨...."
-
-#~ msgid "Loading..."
-#~ msgstr "载入中..."
-
-#~ msgid "Local install"
-#~ msgstr "本地安装"
+#~ msgid "Failed to delete all world files"
+#~ msgstr "无法删除所有该世界的文件"
-#~ msgid "Add mod:"
-#~ msgstr "添加MOD:"
+#~ msgid "Cannot configure world: Nothing selected"
+#~ msgstr "无法é…置世界:没有选择世界"
-#~ msgid "MODS"
-#~ msgstr "MODS"
+#~ msgid "Cannot create world: No games found"
+#~ msgstr "无法创造世界:未找到游æˆæ¨¡å¼"
-#~ msgid "TEXTURE PACKS"
-#~ msgstr "æ质包"
+#~ msgid "Files to be deleted"
+#~ msgstr "将被删除的文件"
-#~ msgid "SINGLE PLAYER"
-#~ msgstr "å•äººæ¸¸æˆ"
+#~ msgid "Cannot delete world: Nothing selected"
+#~ msgstr "无法删除世界:没有选择世界"
-#~ msgid "Finite Liquid"
-#~ msgstr "液体有é™å»¶ä¼¸"
+#~ msgid "Address required."
+#~ msgstr "需è¦åœ°å€ã€‚"
-#~ msgid "Preload item visuals"
-#~ msgstr "预先加载物å“图åƒ"
+#~ msgid "Create world"
+#~ msgstr "创造世界"
-#~ msgid "SETTINGS"
-#~ msgstr "设置"
+#~ msgid "Leave address blank to start a local server."
+#~ msgstr "地å€æ ç•™ç©ºå¯å¯åŠ¨æœ¬åœ°æœåŠ¡å™¨ã€‚"
-#~ msgid "Password"
-#~ msgstr "密ç "
+#~ msgid "Show Favorites"
+#~ msgstr "显示最爱"
-#~ msgid "Name"
-#~ msgstr "åå­—"
+#~ msgid "Show Public"
+#~ msgstr "显示公共"
-#~ msgid "START SERVER"
-#~ msgstr "å¯åŠ¨æœåŠ¡å™¨"
+#~ msgid "Advanced"
+#~ msgstr "高级è”机设置"
-#~ msgid "Favorites:"
-#~ msgstr "最爱的æœåŠ¡å™¨ï¼š"
+#~ msgid "Multiplayer"
+#~ msgstr "多人游æˆ"
-#~ msgid "CLIENT"
-#~ msgstr "客户端"
+#~ msgid "Cannot create world: Name contains invalid characters"
+#~ msgstr "无法创建世界:å字包å«éžæ³•å­—符"
-#~ msgid "<<-- Add mod"
-#~ msgstr "<<-- 添加MOD"
+#~ msgid "Warning: Configuration not consistent. "
+#~ msgstr "警告:é…ç½®ä¸ä¸€è‡´ã€‚ "
-#~ msgid "Remove selected mod"
-#~ msgstr "删除选中MOD"
+#~ msgid "Configuration saved. "
+#~ msgstr "é…置已ä¿å­˜ã€‚ "
-#~ msgid "EDIT GAME"
-#~ msgstr "编辑游æˆ"
+#~ msgid "is required by:"
+#~ msgstr "被需è¦ï¼š"
-#~ msgid "new game"
-#~ msgstr "新建游æˆ"
+#~ msgid "Left click: Move all items, Right click: Move single item"
+#~ msgstr "左键:移动所有物å“,å³é”®ï¼šç§»åŠ¨å•ä¸ªç‰©å“"
-#~ msgid "edit game"
-#~ msgstr "编辑游æˆ"
+#~ msgid " MB/s"
+#~ msgstr "兆字节/秒"
-#~ msgid "Mods:"
-#~ msgstr "MODS:"
+#~ msgid " KB/s"
+#~ msgstr "åƒå­—节/秒"
-#~ msgid "Games"
-#~ msgstr "游æˆ"
+#~ msgid "Fly mode"
+#~ msgstr "飞行模å¼"
-#~ msgid "GAMES"
-#~ msgstr "游æˆ"
+#~ msgid "Anisotropic Filtering"
+#~ msgstr "å„å‘异性过滤"
-#~ msgid "Gamemgr: Unable to copy mod \"$1\" to game \"$2\""
-#~ msgstr "Gamemgr: 无法å¤åˆ¶MOD“$1â€åˆ°æ¸¸æˆâ€œ$2â€"
+#~ msgid "Mip-Mapping"
+#~ msgstr "贴图处ç†"
-#~ msgid "Game Name"
-#~ msgstr "游æˆå"
+#, fuzzy
+#~ msgid "Downloading"
+#~ msgstr "下载中"
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 93083f369..614e81908 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,7 +1,7 @@
+cmake_minimum_required(VERSION 2.6)
+
project(minetest)
-cmake_minimum_required( VERSION 2.6 )
-INCLUDE(CheckCSourceRuns)
INCLUDE(CheckIncludeFiles)
# Add custom SemiDebug build mode
@@ -22,83 +22,69 @@ set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING
FORCE
)
+
# Set some random things default to not being visible in the GUI
mark_as_advanced(EXECUTABLE_OUTPUT_PATH LIBRARY_OUTPUT_PATH)
-option(ENABLE_CURL "Enable cURL support for fetching media" 1)
-if (NOT ENABLE_CURL)
- mark_as_advanced(CLEAR CURL_LIBRARY CURL_INCLUDE_DIR)
-endif(NOT ENABLE_CURL)
+option(ENABLE_CURL "Enable cURL support for fetching media" TRUE)
+set(USE_CURL FALSE)
-if( ENABLE_CURL )
+if(ENABLE_CURL)
find_package(CURL)
-endif( ENABLE_CURL )
-set(USE_CURL 0)
-if (CURL_FOUND AND ENABLE_CURL)
- message(STATUS "cURL support enabled")
- set(USE_CURL 1)
-endif(CURL_FOUND AND ENABLE_CURL)
+ if (CURL_FOUND)
+ message(STATUS "cURL support enabled.")
+ set(USE_CURL TRUE)
+ endif()
+else()
+ mark_as_advanced(CLEAR CURL_LIBRARY CURL_INCLUDE_DIR)
+endif()
-# user-visible option to enable/disable gettext usage
-OPTION(ENABLE_GETTEXT "Use GetText for internationalization" 0)
-# this is only set to 1 if gettext is enabled _and_ available
-set(USE_GETTEXT 0)
+option(ENABLE_GETTEXT "Use GetText for internationalization" FALSE)
+set(USE_GETTEXT FALSE)
if(ENABLE_GETTEXT)
find_package(GettextLib)
+ if(GETTEXT_FOUND)
+ if(WIN32)
+ message(STATUS "GetText library: ${GETTEXT_LIBRARY}")
+ message(STATUS "GetText DLL: ${GETTEXT_DLL}")
+ message(STATUS "GetText iconv DLL: ${GETTEXT_ICONV_DLL}")
+ endif()
+ set(USE_GETTEXT TRUE)
+ message(STATUS "GetText enabled; locales found: ${GETTEXT_AVAILABLE_LOCALES}")
+ endif(GETTEXT_FOUND)
else()
- MARK_AS_ADVANCED(GETTEXT_ICONV_DLL GETTEXT_INCLUDE_DIR GETTEXT_LIBRARY GETTEXT_MSGFMT)
+ mark_as_advanced(GETTEXT_ICONV_DLL GETTEXT_INCLUDE_DIR GETTEXT_LIBRARY GETTEXT_MSGFMT)
+ message(STATUS "GetText disabled.")
endif()
-if(GETTEXT_FOUND AND ENABLE_GETTEXT)
- message(STATUS "gettext include path: ${GETTEXT_INCLUDE_DIR}")
- message(STATUS "gettext msgfmt path: ${GETTEXT_MSGFMT}")
- if(WIN32)
- message(STATUS "gettext library: ${GETTEXT_LIBRARY}")
- message(STATUS "gettext dll: ${GETTEXT_DLL}")
- message(STATUS "gettext iconv dll: ${GETTEXT_ICONV_DLL}")
- endif()
- set(USE_GETTEXT 1)
- message(STATUS "GetText enabled; locales found: ${GETTEXT_AVAILABLE_LOCALES}")
-elseif(GETTEXT_FOUND AND NOT ENABLE_GETTEXT)
- MESSAGE(STATUS "GetText found but disabled;")
-else(GETTEXT_FOUND AND ENABLE_GETTEXT)
- message(STATUS "GetText disabled")
-endif(GETTEXT_FOUND AND ENABLE_GETTEXT)
-
-# user visible option to enable/disable sound
-OPTION(ENABLE_SOUND "Enable sound" ON)
-
-# this is only set to 1 if sound is enabled _and_ available
-set(USE_SOUND 0)
-set(SOUND_PROBLEM 0)
-
-if(ENABLE_SOUND AND BUILD_CLIENT)
+
+option(ENABLE_SOUND "Enable sound" TRUE)
+set(USE_SOUND FALSE)
+
+if(BUILD_CLIENT AND ENABLE_SOUND)
# Sound libraries
find_package(OpenAL)
find_package(Vorbis)
if(NOT OPENAL_FOUND)
message(STATUS "Sound enabled, but OpenAL not found!")
- set(SOUND_PROBLEM 1)
- MARK_AS_ADVANCED(CLEAR OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
+ mark_as_advanced(CLEAR OPENAL_LIBRARY OPENAL_INCLUDE_DIR)
endif()
if(NOT VORBIS_FOUND)
message(STATUS "Sound enabled, but Vorbis libraries not found!")
- set(SOUND_PROBLEM 1)
- MARK_AS_ADVANCED(CLEAR OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
+ mark_as_advanced(CLEAR OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY)
endif()
if(OPENAL_FOUND AND VORBIS_FOUND)
- set(USE_SOUND 1)
- message(STATUS "Sound enabled")
+ set(USE_SOUND TRUE)
+ message(STATUS "Sound enabled.")
+ else()
+ message(FATAL_ERROR "Sound enabled, but cannot be used.\n"
+ "To continue, either fill in the required paths or disable sound. (-DENABLE_SOUND=0)")
endif()
-endif(ENABLE_SOUND AND BUILD_CLIENT)
-
-if(SOUND_PROBLEM)
- message(FATAL_ERROR "Sound enabled, but cannot be used.\n"
- "To continue, either fill in the required paths or disable sound. (-DENABLE_SOUND=0)")
endif()
+
if(USE_SOUND)
set(sound_SRCS sound_openal.cpp)
set(SOUND_INCLUDE_DIRS
@@ -112,18 +98,110 @@ if(USE_SOUND)
)
endif()
-option(ENABLE_FREETYPE "Enable freetype2 (truetype fonts and basic unicode support)" OFF)
-set(USE_FREETYPE 0)
+
+option(ENABLE_GLES "Enable OpenGL ES support" FALSE)
+mark_as_advanced(ENABLE_GLES)
+if(ENABLE_GLES)
+ find_package(OpenGLES2)
+endif()
+
+
+option(ENABLE_FREETYPE "Enable FreeType2 (TrueType fonts and basic unicode support)" TRUE)
+set(USE_FREETYPE FALSE)
+
if(ENABLE_FREETYPE)
- set(USE_FREETYPE 1)
+##
+## Note: FindFreetype.cmake seems to have been fixed in recent versions of
+## CMake. If issues persist, re-enable this workaround specificially for the
+## failing platforms.
+##
+# if(UNIX)
+# include(FindPkgConfig)
+# if(PKG_CONFIG_FOUND)
+# pkg_check_modules(FREETYPE QUIET freetype2)
+# if(FREETYPE_FOUND)
+# SET(FREETYPE_PKGCONFIG_FOUND TRUE)
+# SET(FREETYPE_LIBRARY ${FREETYPE_LIBRARIES})
+# # Because CMake is idiotic
+# string(REPLACE ";" " " FREETYPE_CFLAGS_STR ${FREETYPE_CFLAGS})
+# string(REPLACE ";" " " FREETYPE_LDFLAGS_STR ${FREETYPE_LDFLAGS})
+# endif(FREETYPE_FOUND)
+# endif(PKG_CONFIG_FOUND)
+# endif(UNIX)
+# if(NOT FREETYPE_FOUND)
+# find_package(Freetype)
+# endif()
+ find_package(Freetype)
+ if(FREETYPE_FOUND)
+ message(STATUS "Freetype enabled.")
+ set(USE_FREETYPE TRUE)
+ set(CGUITTFONT_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cguittfont")
+ set(CGUITTFONT_LIBRARY cguittfont)
+ endif()
endif(ENABLE_FREETYPE)
+
+find_package(Lua REQUIRED)
+
+find_package(GMP REQUIRED)
+
+option(ENABLE_LEVELDB "Enable LevelDB backend" TRUE)
+set(USE_LEVELDB FALSE)
+
+if(ENABLE_LEVELDB)
+ find_library(LEVELDB_LIBRARY leveldb)
+ find_path(LEVELDB_INCLUDE_DIR db.h PATH_SUFFIXES leveldb)
+ if(LEVELDB_LIBRARY AND LEVELDB_INCLUDE_DIR)
+ set(USE_LEVELDB TRUE)
+ message(STATUS "LevelDB backend enabled.")
+ include_directories(${LEVELDB_INCLUDE_DIR})
+ else()
+ message(STATUS "LevelDB not found!")
+ endif()
+endif(ENABLE_LEVELDB)
+
+
+OPTION(ENABLE_REDIS "Enable Redis backend" TRUE)
+set(USE_REDIS FALSE)
+
+if(ENABLE_REDIS)
+ find_library(REDIS_LIBRARY hiredis)
+ find_path(REDIS_INCLUDE_DIR hiredis.h PATH_SUFFIXES hiredis)
+ if(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
+ set(USE_REDIS TRUE)
+ message(STATUS "Redis backend enabled.")
+ include_directories(${REDIS_INCLUDE_DIR})
+ else(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
+ message(STATUS "Redis not found!")
+ endif(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
+endif(ENABLE_REDIS)
+
+
+find_package(SQLite3 REQUIRED)
+find_package(Json REQUIRED)
+
+OPTION(ENABLE_SPATIAL "Enable SpatialIndex AreaStore backend" TRUE)
+set(USE_SPATIAL FALSE)
+
+if(ENABLE_SPATIAL)
+ find_library(SPATIAL_LIBRARY spatialindex)
+ find_path(SPATIAL_INCLUDE_DIR spatialindex/SpatialIndex.h)
+ if(SPATIAL_LIBRARY AND SPATIAL_INCLUDE_DIR)
+ set(USE_SPATIAL TRUE)
+ message(STATUS "SpatialIndex AreaStore backend enabled.")
+ include_directories(${SPATIAL_INCLUDE_DIR})
+ else(SPATIAL_LIBRARY AND SPATIAL_INCLUDE_DIR)
+ message(STATUS "SpatialIndex not found!")
+ endif(SPATIAL_LIBRARY AND SPATIAL_INCLUDE_DIR)
+endif(ENABLE_SPATIAL)
+
+
if(NOT MSVC)
- set(USE_GPROF 0 CACHE BOOL "Use -pg flag for g++")
+ set(USE_GPROF FALSE CACHE BOOL "Use -pg flag for g++")
endif()
# Use cmake_config.h
-add_definitions ( -DUSE_CMAKE_CONFIG_H )
+add_definitions(-DUSE_CMAKE_CONFIG_H)
if(WIN32)
# Windows
@@ -132,8 +210,10 @@ if(WIN32)
# Surpress some useless warnings
add_definitions ( /D "_CRT_SECURE_NO_DEPRECATE" /W1 )
else() # Probably MinGW = GCC
- set(PLATFORM_LIBS ws2_32.lib)
+ set(PLATFORM_LIBS "")
endif()
+ set(PLATFORM_LIBS ws2_32.lib shlwapi.lib ${PLATFORM_LIBS})
+
# Zlib stuff
set(ZLIB_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../../zlib/zlib-1.2.5"
CACHE PATH "Zlib include directory")
@@ -150,7 +230,7 @@ if(WIN32)
CACHE PATH "freetype include dir")
set(FREETYPE_LIBRARY "${PROJECT_SOURCE_DIR}/../../freetype2/objs/win32/vc2005/freetype247.lib"
CACHE FILEPATH "Path to freetype247.lib")
- endif(USE_FREETYPE)
+ endif()
if(ENABLE_SOUND)
set(OPENAL_DLL "" CACHE FILEPATH "Path to OpenAL32.dll for installation (optional)")
set(OGG_DLL "" CACHE FILEPATH "Path to libogg.dll for installation (optional)")
@@ -166,9 +246,9 @@ else()
find_package(BZip2 REQUIRED)
find_package(PNG REQUIRED)
if(APPLE)
- FIND_LIBRARY(CARBON_LIB Carbon)
- FIND_LIBRARY(COCOA_LIB Cocoa)
- FIND_LIBRARY(IOKIT_LIB IOKit)
+ find_library(CARBON_LIB Carbon)
+ find_library(COCOA_LIB Cocoa)
+ find_library(IOKIT_LIB IOKit)
mark_as_advanced(
CARBON_LIB
COCOA_LIB
@@ -184,184 +264,51 @@ else()
else()
set(PLATFORM_LIBS -lrt ${PLATFORM_LIBS})
endif(APPLE)
- #set(CLIENT_PLATFORM_LIBS -lXxf86vm)
+
# This way Xxf86vm is found on OpenBSD too
find_library(XXF86VM_LIBRARY Xxf86vm)
mark_as_advanced(XXF86VM_LIBRARY)
set(CLIENT_PLATFORM_LIBS ${CLIENT_PLATFORM_LIBS} ${XXF86VM_LIBRARY})
-endif()
-
-find_package(SQLite3 REQUIRED)
-find_package(Json REQUIRED)
-
-option(ENABLE_GLES "Enable OpenGL ES support" 0)
-mark_as_advanced(ENABLE_GLES)
-if(ENABLE_GLES)
- find_package(OpenGLES2)
-endif(ENABLE_GLES)
-
-if(USE_FREETYPE)
- if(UNIX)
- include(FindPkgConfig)
- if(PKG_CONFIG_FOUND)
- pkg_check_modules(FREETYPE QUIET freetype2)
- if(FREETYPE_FOUND)
- SET(FREETYPE_PKGCONFIG_FOUND TRUE)
- SET(FREETYPE_LIBRARY ${FREETYPE_LIBRARIES})
- # because cmake is idiotic
- string(REPLACE ";" " " FREETYPE_CFLAGS_STR ${FREETYPE_CFLAGS})
- string(REPLACE ";" " " FREETYPE_LDFLAGS_STR ${FREETYPE_LDFLAGS})
- endif(FREETYPE_FOUND)
- endif(PKG_CONFIG_FOUND)
- endif(UNIX)
- if(NOT FREETYPE_FOUND)
- find_package(Freetype REQUIRED)
- endif(NOT FREETYPE_FOUND)
- set(CGUITTFONT_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/cguittfont")
- set(CGUITTFONT_LIBRARY cguittfont)
-endif(USE_FREETYPE)
-
-if (NOT DISABLE_LUAJIT)
- find_library(LUA_LIBRARY luajit
- NAMES luajit-5.1)
- find_path(LUA_INCLUDE_DIR luajit.h
- NAMES luajit.h
- PATH_SUFFIXES luajit-2.0)
- message (STATUS "LuaJIT library: ${LUA_LIBRARY}")
- message (STATUS "LuaJIT headers: ${LUA_INCLUDE_DIR}")
-else (NOT ${DISABLE_LUAJIT} MATCHES "1")
- message (STATUS "LuaJIT detection disabled! (DISABLE_LUAJIT=1)")
- set(LUA_LIBRARY "")
- set(LUA_INCLUDE_DIR "")
-endif (NOT DISABLE_LUAJIT)
-
-set(USE_LUAJIT 0)
-if(LUA_LIBRARY AND LUA_INCLUDE_DIR)
- message (STATUS "LuaJIT found, checking for broken versions...")
- if(CMAKE_CROSSCOMPILING)
- message(WARNING "Cross-compiling enabled, assuming LuaJIT is not broken")
- set(VALID_LUAJIT_VERSION 1)
- else(CMAKE_CROSSCOMPILING)
- set(BACKUP_REQUIRED_INCS CMAKE_REQUIRED_INCLUDES)
- set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES} ${LUA_INCLUDE_DIR}")
- CHECK_C_SOURCE_RUNS("
- #include <luajit.h>
- #include <stdio.h>
- #include <string.h>
-
- #define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
-
- static char *broken_luajit_versions[] = {
- \"LuaJIT 2.0.0-beta7\",
- \"LuaJIT 2.0.0-beta6\",
- \"LuaJIT 2.0.0-beta5\",
- \"LuaJIT 2.0.0-beta4\",
- \"LuaJIT 2.0.0-beta3\",
- \"LuaJIT 2.0.0-beta2\",
- \"LuaJIT 2.0.0-beta1\"
- };
-
- int main(int argc, char *argv[]) {
- unsigned int i;
- for (i = 0; i < ARRAYSIZE(broken_luajit_versions); i++) {
- if (strcmp(LUAJIT_VERSION, broken_luajit_versions[i]) == 0) {
- return 1;
- }
- }
- return 0;
- }
- "
- VALID_LUAJIT_VERSION)
- set(CMAKE_REQUIRED_INCLUDES BACKUP_REQUIRED_INCS)
- endif(CMAKE_CROSSCOMPILING)
- if (VALID_LUAJIT_VERSION)
- message (STATUS "LuaJIT version ok")
- set(USE_LUAJIT 1)
- else (VALID_LUAJIT_VERSION)
- message (STATUS "LuaJIT versions till 2.0.0beta7 known to be broken, update to at least beta8")
- set(USE_LUAJIT 0)
- endif (VALID_LUAJIT_VERSION)
-endif (LUA_LIBRARY AND LUA_INCLUDE_DIR)
-
-if(NOT USE_LUAJIT)
- message (STATUS "LuaJIT not found, using bundled Lua.")
- set(LUA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/lua/src")
- set(LUA_LIBRARY "lua")
- add_subdirectory(lua)
-endif(NOT USE_LUAJIT)
-
-mark_as_advanced(LUA_LIBRARY)
-mark_as_advanced(LUA_INCLUDE_DIR)
-
-set(USE_LEVELDB 0)
-
-OPTION(ENABLE_LEVELDB "Enable LevelDB backend")
-
-if(ENABLE_LEVELDB)
- find_library(LEVELDB_LIBRARY leveldb)
- find_path(LEVELDB_INCLUDE_DIR db.h PATH_SUFFIXES leveldb)
- message (STATUS "LevelDB library: ${LEVELDB_LIBRARY}")
- message (STATUS "LevelDB headers: ${LEVELDB_INCLUDE_DIR}")
- if(LEVELDB_LIBRARY AND LEVELDB_INCLUDE_DIR)
- set(USE_LEVELDB 1)
- message(STATUS "LevelDB backend enabled")
- include_directories(${LEVELDB_INCLUDE_DIR})
- else(LEVELDB_LIBRARY AND LEVELDB_INCLUDE_DIR)
- set(USE_LEVELDB 0)
- message(STATUS "LevelDB not found!")
- endif(LEVELDB_LIBRARY AND LEVELDB_INCLUDE_DIR)
-endif(ENABLE_LEVELDB)
-set(USE_REDIS 0)
-
-OPTION(ENABLE_REDIS "Enable redis backend" 0)
-
-if(ENABLE_REDIS)
- find_library(REDIS_LIBRARY hiredis)
- find_path(REDIS_INCLUDE_DIR hiredis.h PATH_SUFFIXES hiredis)
- message(STATUS "redis library: ${REDIS_LIBRARY}")
- message(STATUS "redis headers: ${REDIS_INCLUDE_DIR}")
- if(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
- set(USE_REDIS 1)
- message(STATUS "redis backend enabled")
- include_directories(${REDIS_INCLUDE_DIR})
- else(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
- set(USE_REDIS 0)
- message(STATUS "redis not found!")
- endif(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
-endif(ENABLE_REDIS)
+ # Prefer local iconv if installed
+ find_library(ICONV_LIBRARY iconv)
+ mark_as_advanced(ICONV_LIBRARY)
+ if (ICONV_LIBRARY)
+ set(PLATFORM_LIBS ${PLATFORM_LIBS} ${ICONV_LIBRARY})
+ endif()
+endif()
-CHECK_INCLUDE_FILES(endian.h HAVE_ENDIAN_H)
-if(NOT HAVE_ENDIAN_H)
- set(HAVE_ENDIAN_H 0)
-endif(NOT HAVE_ENDIAN_H)
+check_include_files(endian.h HAVE_ENDIAN_H)
configure_file(
"${PROJECT_SOURCE_DIR}/cmake_config.h.in"
"${PROJECT_BINARY_DIR}/cmake_config.h"
)
+
# Add a target that always rebuilds cmake_config_githash.h
add_custom_target(GenerateVersion
COMMAND ${CMAKE_COMMAND}
-D "GENERATE_VERSION_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}"
-D "GENERATE_VERSION_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}"
-D "VERSION_STRING=${VERSION_STRING}"
- -D "VERSION_EXTRA=${VERSION_EXTRA}"
+ -D "DEVELOPMENT_BUILD=${DEVELOPMENT_BUILD}"
-P "${CMAKE_SOURCE_DIR}/cmake/Modules/GenerateVersion.cmake"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
+
add_subdirectory(jthread)
+add_subdirectory(network)
add_subdirectory(script)
+add_subdirectory(unittest)
add_subdirectory(util)
set(common_SRCS
+ areastore.cpp
ban.cpp
- base64.cpp
cavegen.cpp
clientiface.cpp
collision.cpp
- connection.cpp
content_abm.cpp
content_mapnode.cpp
content_nodemeta.cpp
@@ -406,10 +353,12 @@ set(common_SRCS
nodemetadata.cpp
nodetimer.cpp
noise.cpp
+ objdef.cpp
object_properties.cpp
pathfinder.cpp
player.cpp
porting.cpp
+ profiler.cpp
quicktune.cpp
rollback.cpp
rollback_interface.cpp
@@ -418,25 +367,26 @@ set(common_SRCS
serverlist.cpp
serverobject.cpp
settings.cpp
- sha1.cpp
socket.cpp
sound.cpp
staticobject.cpp
subgame.cpp
- test.cpp
tool.cpp
treegen.cpp
version.cpp
voxel.cpp
voxelalgorithms.cpp
+ ${common_network_SRCS}
${JTHREAD_SRCS}
${common_SCRIPT_SRCS}
${UTIL_SRCS}
+ ${UNITTEST_SRCS}
)
+
# This gives us the icon and file version information
if(WIN32)
- set(WINRESOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../misc/winresource.rc)
+ set(WINRESOURCE_FILE "${CMAKE_CURRENT_SOURCE_DIR}/../misc/winresource.rc")
if(MINGW)
if(NOT CMAKE_RC_COMPILER)
set(CMAKE_RC_COMPILER "windres.exe")
@@ -453,10 +403,17 @@ if(WIN32)
endif(MINGW)
endif()
+
# Client sources
-set(minetest_SRCS
+if (BUILD_CLIENT)
+ add_subdirectory(client)
+endif(BUILD_CLIENT)
+
+set(client_SRCS
+ ${client_SRCS}
${common_SRCS}
${sound_SRCS}
+ ${client_network_SRCS}
camera.cpp
chat.cpp
client.cpp
@@ -478,29 +435,32 @@ set(minetest_SRCS
guiFormSpecMenu.cpp
guiKeyChangeMenu.cpp
guiPasswordChange.cpp
+ guiscalingfilter.cpp
guiTable.cpp
guiVolumeChange.cpp
hud.cpp
+ imagefilters.cpp
+ intlGUIEditBox.cpp
keycode.cpp
localplayer.cpp
main.cpp
mapblock_mesh.cpp
mesh.cpp
+ minimap.cpp
particles.cpp
shader.cpp
sky.cpp
- tile.cpp
wieldmesh.cpp
- ${minetest_SCRIPT_SRCS}
+ ${client_SCRIPT_SRCS}
)
-list(SORT minetest_SRCS)
+list(SORT client_SRCS)
# Server sources
-set(minetestserver_SRCS
+set(server_SRCS
${common_SRCS}
main.cpp
)
-list(SORT minetestserver_SRCS)
+list(SORT server_SRCS)
include_directories(
${PROJECT_BINARY_DIR}
@@ -513,29 +473,28 @@ include_directories(
${SOUND_INCLUDE_DIRS}
${SQLITE3_INCLUDE_DIR}
${LUA_INCLUDE_DIR}
+ ${GMP_INCLUDE_DIR}
${JSON_INCLUDE_DIR}
${PROJECT_SOURCE_DIR}/script
)
+
if(USE_FREETYPE)
- include_directories(
- ${FREETYPE_INCLUDE_DIRS}
- ${CGUITTFONT_INCLUDE_DIR}
- )
-endif(USE_FREETYPE)
+ include_directories(${FREETYPE_INCLUDE_DIRS} ${CGUITTFONT_INCLUDE_DIR})
+endif()
if(USE_CURL)
- include_directories(
- ${CURL_INCLUDE_DIR}
- )
-endif(USE_CURL)
+ include_directories(${CURL_INCLUDE_DIR})
+endif()
+
set(EXECUTABLE_OUTPUT_PATH "${CMAKE_SOURCE_DIR}/bin")
+
if(BUILD_CLIENT)
- add_executable(${PROJECT_NAME} ${minetest_SRCS})
+ add_executable(${PROJECT_NAME} ${client_SRCS})
add_dependencies(${PROJECT_NAME} GenerateVersion)
- set(minetest_LIBS
+ set(client_LIBS
${PROJECT_NAME}
${ZLIB_LIBRARIES}
${IRRLICHT_LIBRARY}
@@ -548,6 +507,7 @@ if(BUILD_CLIENT)
${SOUND_LIBRARIES}
${SQLITE3_LIBRARY}
${LUA_LIBRARY}
+ ${GMP_LIBRARY}
${JSON_LIBRARY}
${OPENGLES2_LIBRARIES}
${PLATFORM_LIBS}
@@ -555,12 +515,12 @@ if(BUILD_CLIENT)
)
if(APPLE)
target_link_libraries(
- ${minetest_LIBS}
+ ${client_LIBS}
${ICONV_LIBRARY}
)
else()
target_link_libraries(
- ${minetest_LIBS}
+ ${client_LIBS}
)
endif()
if(USE_CURL)
@@ -568,30 +528,34 @@ if(BUILD_CLIENT)
${PROJECT_NAME}
${CURL_LIBRARY}
)
- endif(USE_CURL)
+ endif()
if(USE_FREETYPE)
if(FREETYPE_PKGCONFIG_FOUND)
set_target_properties(${PROJECT_NAME}
PROPERTIES
COMPILE_FLAGS "${FREETYPE_CFLAGS_STR}"
)
- endif(FREETYPE_PKGCONFIG_FOUND)
+ endif()
target_link_libraries(
${PROJECT_NAME}
${FREETYPE_LIBRARY}
${CGUITTFONT_LIBRARY}
)
- endif(USE_FREETYPE)
+ endif()
if (USE_LEVELDB)
target_link_libraries(${PROJECT_NAME} ${LEVELDB_LIBRARY})
- endif(USE_LEVELDB)
+ endif()
if (USE_REDIS)
target_link_libraries(${PROJECT_NAME} ${REDIS_LIBRARY})
- endif(USE_REDIS)
+ endif()
+ if (USE_SPATIAL)
+ target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY})
+ endif()
endif(BUILD_CLIENT)
+
if(BUILD_SERVER)
- add_executable(${PROJECT_NAME}server ${minetestserver_SRCS})
+ add_executable(${PROJECT_NAME}server ${server_SRCS})
add_dependencies(${PROJECT_NAME}server GenerateVersion)
target_link_libraries(
${PROJECT_NAME}server
@@ -600,26 +564,30 @@ if(BUILD_SERVER)
${JSON_LIBRARY}
${GETTEXT_LIBRARY}
${LUA_LIBRARY}
+ ${GMP_LIBRARY}
${PLATFORM_LIBS}
)
+ set_target_properties(${PROJECT_NAME}server PROPERTIES
+ COMPILE_DEFINITIONS "SERVER")
if (USE_LEVELDB)
target_link_libraries(${PROJECT_NAME}server ${LEVELDB_LIBRARY})
- endif(USE_LEVELDB)
+ endif()
if (USE_REDIS)
target_link_libraries(${PROJECT_NAME}server ${REDIS_LIBRARY})
- endif(USE_REDIS)
+ endif()
+ if (USE_SPATIAL)
+ target_link_libraries(${PROJECT_NAME}server ${SPATIAL_LIBRARY})
+ endif()
if(USE_CURL)
target_link_libraries(
${PROJECT_NAME}server
${CURL_LIBRARY}
)
- endif(USE_CURL)
+ endif()
endif(BUILD_SERVER)
-#
# Set some optimizations and tweaks
-#
include(CheckCXXCompilerFlag)
@@ -640,12 +608,6 @@ if(MSVC)
# Flags for C files (sqlite)
# /MT = Link statically with standard library stuff
set(CMAKE_C_FLAGS_RELEASE "/O2 /Ob2 /MT")
-
- if(BUILD_SERVER)
- set_target_properties(${PROJECT_NAME}server PROPERTIES
- COMPILE_DEFINITIONS "SERVER")
- endif(BUILD_SERVER)
-
else()
# Probably GCC
if(APPLE)
@@ -660,6 +622,7 @@ else()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# clang does not understand __extern_always_inline but libc headers use it
set(OTHER_FLAGS "${OTHER_FLAGS} \"-D__extern_always_inline=extern __always_inline\"")
+ set(OTHER_FLAGS "${OTHER_FLAGS} -Wsign-compare")
endif()
if(MINGW)
@@ -678,20 +641,11 @@ else()
if(USE_GPROF)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pg")
endif()
-
- if(BUILD_SERVER)
- set_target_properties(${PROJECT_NAME}server PROPERTIES
- COMPILE_DEFINITIONS "SERVER")
- endif(BUILD_SERVER)
-
endif()
-#MESSAGE(STATUS "CMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE}")
-#MESSAGE(STATUS "CMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG}")
-#
# Installation
-#
+
if(WIN32)
if(USE_SOUND)
if(OPENAL_DLL)
@@ -728,14 +682,27 @@ if(WIN32)
endif()
if(BUILD_CLIENT)
- install(TARGETS ${PROJECT_NAME} DESTINATION ${BINDIR})
+ install(TARGETS ${PROJECT_NAME}
+ RUNTIME DESTINATION ${BINDIR}
+ LIBRARY DESTINATION ${BINDIR}
+ ARCHIVE DESTINATION ${BINDIR}
+ BUNDLE DESTINATION .
+ )
+
+ if(APPLE)
+ install(CODE "
+ set(BU_CHMOD_BUNDLE_ITEMS ON)
+ include(BundleUtilities)
+ fixup_bundle(\"\${CMAKE_INSTALL_PREFIX}/${BUNDLE_PATH}\" \"\" \"\${CMAKE_INSTALL_PREFIX}/${BINDIR}\")
+ " COMPONENT Runtime)
+ endif()
if(USE_GETTEXT)
foreach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
set_mo_paths(MO_BUILD_PATH MO_DEST_PATH ${LOCALE})
set(MO_BUILD_PATH "${MO_BUILD_PATH}/${PROJECT_NAME}.mo")
install(FILES ${MO_BUILD_PATH} DESTINATION ${MO_DEST_PATH})
- endforeach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
+ endforeach()
endif()
if(WIN32)
@@ -749,19 +716,19 @@ if(BUILD_CLIENT)
if(DEFINED GETTEXT_ICONV_DLL)
install(FILES ${GETTEXT_ICONV_DLL} DESTINATION ${BINDIR})
endif()
- endif(USE_GETTEXT)
+ endif()
endif()
endif(BUILD_CLIENT)
if(BUILD_SERVER)
install(TARGETS ${PROJECT_NAME}server DESTINATION ${BINDIR})
-endif(BUILD_SERVER)
+endif()
if (USE_GETTEXT)
set(MO_FILES)
foreach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
- set(PO_FILE_PATH "${GETTEXT_PO_PATH}/${LOCALE}/minetest.po")
+ set(PO_FILE_PATH "${GETTEXT_PO_PATH}/${LOCALE}/${PROJECT_NAME}.po")
set_mo_paths(MO_BUILD_PATH MO_DEST_PATH ${LOCALE})
set(MO_FILE_PATH "${MO_BUILD_PATH}/${PROJECT_NAME}.mo")
@@ -778,20 +745,15 @@ if (USE_GETTEXT)
)
set(MO_FILES ${MO_FILES} ${MO_FILE_PATH})
- endforeach(LOCALE ${GETTEXT_AVAILABLE_LOCALES})
+ endforeach()
add_custom_target(translations ALL COMMENT "mo update" DEPENDS ${MO_FILES})
-endif(USE_GETTEXT)
+endif()
+
# Subdirectories
if (BUILD_CLIENT AND USE_FREETYPE)
add_subdirectory(cguittfont)
-endif (BUILD_CLIENT AND USE_FREETYPE)
-
-if (JSON_FOUND)
-else (JSON_FOUND)
- add_subdirectory(json)
-endif (JSON_FOUND)
+endif()
-#end
diff --git a/src/activeobject.h b/src/activeobject.h
index 46880fc7f..48f078d3f 100644
--- a/src/activeobject.h
+++ b/src/activeobject.h
@@ -23,7 +23,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_aabb3d.h"
#include <string>
-#define ACTIVEOBJECT_TYPE_INVALID 0
+enum ActiveObjectType {
+ ACTIVEOBJECT_TYPE_INVALID = 0,
+ ACTIVEOBJECT_TYPE_TEST = 1,
+// Deprecated stuff
+ ACTIVEOBJECT_TYPE_ITEM = 2,
+ ACTIVEOBJECT_TYPE_RAT = 3,
+ ACTIVEOBJECT_TYPE_OERKKI1 = 4,
+ ACTIVEOBJECT_TYPE_FIREFLY = 5,
+ ACTIVEOBJECT_TYPE_MOBV2 = 6,
+// End deprecated stuff
+ ACTIVEOBJECT_TYPE_LUAENTITY = 7,
+// Special type, not stored as a static object
+ ACTIVEOBJECT_TYPE_PLAYER = 100,
+// Special type, only exists as CAO
+ ACTIVEOBJECT_TYPE_GENERIC = 101,
+};
// Other types are defined in content_object.h
struct ActiveObjectMessage
@@ -60,7 +75,7 @@ public:
m_id = id;
}
- virtual u8 getType() const = 0;
+ virtual ActiveObjectType getType() const = 0;
virtual bool getCollisionBox(aabb3f *toset) = 0;
virtual bool collideWithObjects() = 0;
protected:
diff --git a/src/areastore.cpp b/src/areastore.cpp
new file mode 100644
index 000000000..f9362c4a6
--- /dev/null
+++ b/src/areastore.cpp
@@ -0,0 +1,343 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <mtest31@outlook.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.
+*/
+
+#include "areastore.h"
+#include "util/serialize.h"
+#include "util/container.h"
+
+#if USE_SPATIAL
+ #include <spatialindex/SpatialIndex.h>
+ #include <spatialindex/RTree.h>
+ #include <spatialindex/Point.h>
+#endif
+
+#define AST_SMALLER_EQ_AS(p, q) (((p).X <= (q).X) && ((p).Y <= (q).Y) && ((p).Z <= (q).Z))
+
+#define AST_OVERLAPS_IN_DIMENSION(amine, amaxe, b, d) \
+ (!(((amine).d > (b)->maxedge.d) || ((amaxe).d < (b)->minedge.d)))
+
+#define AST_CONTAINS_PT(a, p) (AST_SMALLER_EQ_AS((a)->minedge, (p)) && \
+ AST_SMALLER_EQ_AS((p), (a)->maxedge))
+
+#define AST_CONTAINS_AREA(amine, amaxe, b) \
+ (AST_SMALLER_EQ_AS((amine), (b)->minedge) \
+ && AST_SMALLER_EQ_AS((b)->maxedge, (amaxe)))
+
+#define AST_AREAS_OVERLAP(amine, amaxe, b) \
+ (AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), X) && \
+ AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Y) && \
+ AST_OVERLAPS_IN_DIMENSION((amine), (amaxe), (b), Z))
+
+u16 AreaStore::size() const
+{
+ return areas_map.size();
+}
+
+u32 AreaStore::getFreeId(v3s16 minedge, v3s16 maxedge)
+{
+ int keep_on = 100;
+ while (keep_on--) {
+ m_highest_id++;
+ // Handle overflows, we dont want to return 0
+ if (m_highest_id == AREA_ID_INVALID)
+ m_highest_id++;
+ if (areas_map.find(m_highest_id) == areas_map.end())
+ return m_highest_id;
+ }
+ // search failed
+ return AREA_ID_INVALID;
+}
+
+const Area *AreaStore::getArea(u32 id) const
+{
+ const Area *res = NULL;
+ std::map<u32, Area>::const_iterator itr = areas_map.find(id);
+ if (itr != areas_map.end()) {
+ res = &itr->second;
+ }
+ return res;
+}
+
+#if 0
+Currently, serialisation is commented out. This is because of multiple reasons:
+1. Why do we store the areastore into a file, why not into the database?
+2. We don't use libspatial's serialisation, but we should, or perhaps not, because
+ it would remove the ability to switch. Perhaps write migration routines?
+3. Various things need fixing, e.g. the size is serialized as
+ c++ implementation defined size_t
+bool AreaStore::deserialize(std::istream &is)
+{
+ u8 ver = readU8(is);
+ if (ver != 1)
+ return false;
+ u16 count_areas = readU16(is);
+ for (u16 i = 0; i < count_areas; i++) {
+ // deserialize an area
+ Area a;
+ a.id = readU32(is);
+ a.minedge = readV3S16(is);
+ a.maxedge = readV3S16(is);
+ a.datalen = readU16(is);
+ a.data = new char[a.datalen];
+ is.read((char *) a.data, a.datalen);
+ insertArea(a);
+ }
+ return true;
+}
+
+
+static bool serialize_area(void *ostr, Area *a)
+{
+ std::ostream &os = *((std::ostream *) ostr);
+ writeU32(os, a->id);
+ writeV3S16(os, a->minedge);
+ writeV3S16(os, a->maxedge);
+ writeU16(os, a->datalen);
+ os.write(a->data, a->datalen);
+
+ return false;
+}
+
+
+void AreaStore::serialize(std::ostream &os) const
+{
+ // write initial data
+ writeU8(os, 1); // serialisation version
+ writeU16(os, areas_map.size()); //DANGER: not platform independent
+ forEach(&serialize_area, &os);
+}
+
+#endif
+
+void AreaStore::invalidateCache()
+{
+ if (cache_enabled) {
+ m_res_cache.invalidate();
+ }
+}
+
+void AreaStore::setCacheParams(bool enabled, u8 block_radius, size_t limit)
+{
+ cache_enabled = enabled;
+ m_cacheblock_radius = MYMAX(block_radius, 16);
+ m_res_cache.setLimit(MYMAX(limit, 20));
+ invalidateCache();
+}
+
+void AreaStore::cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest)
+{
+ AreaStore *as = (AreaStore *)data;
+ u8 r = as->m_cacheblock_radius;
+
+ // get the points at the edges of the mapblock
+ v3s16 minedge(mpos.X * r, mpos.Y * r, mpos.Z * r);
+ v3s16 maxedge(
+ minedge.X + r - 1,
+ minedge.Y + r - 1,
+ minedge.Z + r - 1);
+
+ as->getAreasInArea(dest, minedge, maxedge, true);
+
+ /* infostream << "Cache miss with " << dest->size() << " areas, between ("
+ << minedge.X << ", " << minedge.Y << ", " << minedge.Z
+ << ") and ("
+ << maxedge.X << ", " << maxedge.Y << ", " << maxedge.Z
+ << ")" << std::endl; // */
+}
+
+void AreaStore::getAreasForPos(std::vector<Area *> *result, v3s16 pos)
+{
+ if (cache_enabled) {
+ v3s16 mblock = getContainerPos(pos, m_cacheblock_radius);
+ const std::vector<Area *> *pre_list = m_res_cache.lookupCache(mblock);
+
+ size_t s_p_l = pre_list->size();
+ for (size_t i = 0; i < s_p_l; i++) {
+ Area *b = (*pre_list)[i];
+ if (AST_CONTAINS_PT(b, pos)) {
+ result->push_back(b);
+ }
+ }
+ } else {
+ return getAreasForPosImpl(result, pos);
+ }
+}
+
+
+////
+// VectorAreaStore
+////
+
+
+void VectorAreaStore::insertArea(const Area &a)
+{
+ areas_map[a.id] = a;
+ m_areas.push_back(&(areas_map[a.id]));
+ invalidateCache();
+}
+
+void VectorAreaStore::reserve(size_t count)
+{
+ m_areas.reserve(count);
+}
+
+bool VectorAreaStore::removeArea(u32 id)
+{
+ std::map<u32, Area>::iterator itr = areas_map.find(id);
+ if (itr != areas_map.end()) {
+ size_t msiz = m_areas.size();
+ for (size_t i = 0; i < msiz; i++) {
+ Area * b = m_areas[i];
+ if (b->id == id) {
+ areas_map.erase(itr);
+ m_areas.erase(m_areas.begin() + i);
+ invalidateCache();
+ return true;
+ }
+ }
+ // we should never get here, it means we did find it in map,
+ // but not in the vector
+ }
+ return false;
+}
+
+void VectorAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
+{
+ size_t msiz = m_areas.size();
+ for (size_t i = 0; i < msiz; i++) {
+ Area *b = m_areas[i];
+ if (AST_CONTAINS_PT(b, pos)) {
+ result->push_back(b);
+ }
+ }
+}
+
+void VectorAreaStore::getAreasInArea(std::vector<Area *> *result,
+ v3s16 minedge, v3s16 maxedge, bool accept_overlap)
+{
+ size_t msiz = m_areas.size();
+ for (size_t i = 0; i < msiz; i++) {
+ Area * b = m_areas[i];
+ if (accept_overlap ? AST_AREAS_OVERLAP(minedge, maxedge, b) :
+ AST_CONTAINS_AREA(minedge, maxedge, b)) {
+ result->push_back(b);
+ }
+ }
+}
+
+#if 0
+bool VectorAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
+{
+ size_t msiz = m_areas.size();
+ for (size_t i = 0; i < msiz; i++) {
+ if (callback(args, m_areas[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+#endif
+
+#if USE_SPATIAL
+
+static inline SpatialIndex::Region get_spatial_region(const v3s16 minedge,
+ const v3s16 maxedge)
+{
+ const double p_low[] = {(double)minedge.X,
+ (double)minedge.Y, (double)minedge.Z};
+ const double p_high[] = {(double)maxedge.X, (double)maxedge.Y,
+ (double)maxedge.Z};
+ return SpatialIndex::Region(p_low, p_high, 3);
+}
+
+static inline SpatialIndex::Point get_spatial_point(const v3s16 pos)
+{
+ const double p[] = {(double)pos.X, (double)pos.Y, (double)pos.Z};
+ return SpatialIndex::Point(p, 3);
+}
+
+
+void SpatialAreaStore::insertArea(const Area &a)
+{
+ areas_map[a.id] = a;
+ m_tree->insertData(0, NULL, get_spatial_region(a.minedge, a.maxedge), a.id);
+ invalidateCache();
+}
+
+bool SpatialAreaStore::removeArea(u32 id)
+{
+ std::map<u32, Area>::iterator itr = areas_map.find(id);
+ if (itr != areas_map.end()) {
+ Area *a = &itr->second;
+ bool result = m_tree->deleteData(get_spatial_region(a->minedge,
+ a->maxedge), id);
+ invalidateCache();
+ return result;
+ } else {
+ return false;
+ }
+}
+
+void SpatialAreaStore::getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos)
+{
+ VectorResultVisitor visitor(result, this);
+ m_tree->pointLocationQuery(get_spatial_point(pos), visitor);
+}
+
+void SpatialAreaStore::getAreasInArea(std::vector<Area *> *result,
+ v3s16 minedge, v3s16 maxedge, bool accept_overlap)
+{
+ VectorResultVisitor visitor(result, this);
+ if (accept_overlap) {
+ m_tree->intersectsWithQuery(get_spatial_region(minedge, maxedge),
+ visitor);
+ } else {
+ m_tree->containsWhatQuery(get_spatial_region(minedge, maxedge), visitor);
+ }
+}
+
+#if 0
+bool SpatialAreaStore::forEach(bool (*callback)(void *args, Area *a), void *args) const
+{
+ // TODO ?? (this is only needed for serialisation, but libspatial has its own serialisation)
+ return false;
+}
+#endif
+
+SpatialAreaStore::~SpatialAreaStore()
+{
+ delete m_tree;
+}
+
+SpatialAreaStore::SpatialAreaStore()
+{
+ m_storagemanager =
+ SpatialIndex::StorageManager::createNewMemoryStorageManager();
+ SpatialIndex::id_type id;
+ m_tree = SpatialIndex::RTree::createNewRTree(
+ *m_storagemanager,
+ .7, // Fill factor
+ 100, // Index capacity
+ 100, // Leaf capacity
+ 3, // dimension :)
+ SpatialIndex::RTree::RV_RSTAR,
+ id);
+}
+
+#endif
diff --git a/src/areastore.h b/src/areastore.h
new file mode 100644
index 000000000..57d96450b
--- /dev/null
+++ b/src/areastore.h
@@ -0,0 +1,196 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <mtest31@outlook.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.
+*/
+
+#ifndef AREASTORE_H_
+#define AREASTORE_H_
+
+#include "irr_v3d.h"
+#include "noise.h" // for PcgRandom
+#include <map>
+#include <list>
+#include <vector>
+#include <istream>
+#include "util/container.h"
+#include "util/numeric.h"
+#ifndef ANDROID
+ #include "cmake_config.h"
+#endif
+#if USE_SPATIAL
+ #include <spatialindex/SpatialIndex.h>
+ #include "util/serialize.h"
+#endif
+
+#define AST_EXTREMIFY(min, max, pa, pb) \
+ (min).X = MYMIN((pa).X, (pb).X); \
+ (min).Y = MYMIN((pa).Y, (pb).Y); \
+ (min).Z = MYMIN((pa).Z, (pb).Z); \
+ (max).X = MYMAX((pa).X, (pb).X); \
+ (max).Y = MYMAX((pa).Y, (pb).Y); \
+ (max).Z = MYMAX((pa).Z, (pb).Z);
+
+#define AREA_ID_INVALID 0
+
+struct Area {
+ Area(const v3s16 &minedge, const v3s16 &maxedge)
+ {
+ this->minedge = minedge;
+ this->maxedge = maxedge;
+ }
+
+ Area() {}
+
+ void extremifyEdges()
+ {
+ v3s16 nminedge;
+ v3s16 nmaxedge;
+
+ AST_EXTREMIFY(nminedge, nmaxedge, minedge, maxedge)
+
+ maxedge = nmaxedge;
+ minedge = nminedge;
+ }
+
+ u32 id;
+ v3s16 minedge;
+ v3s16 maxedge;
+ std::string data;
+};
+
+std::vector<std::string> get_areastore_typenames();
+
+class AreaStore {
+protected:
+ // TODO change to unordered_map when we can
+ std::map<u32, Area> areas_map;
+ void invalidateCache();
+ virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos) = 0;
+ bool cache_enabled; // don't write to this from subclasses, only read.
+public:
+ virtual void insertArea(const Area &a) = 0;
+ virtual void reserve(size_t count) {};
+ virtual bool removeArea(u32 id) = 0;
+ void getAreasForPos(std::vector<Area *> *result, v3s16 pos);
+ virtual void getAreasInArea(std::vector<Area *> *result,
+ v3s16 minedge, v3s16 maxedge, bool accept_overlap) = 0;
+
+#if 0
+ // calls a passed function for every stored area, until the
+ // callback returns true. If that happens, it returns true,
+ // if the search is exhausted, it returns false
+ virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const = 0;
+#endif
+
+ virtual ~AreaStore()
+ {}
+
+ AreaStore() :
+ cache_enabled(true),
+ m_cacheblock_radius(64),
+ m_res_cache(1000, &cacheMiss, this),
+ m_highest_id(0)
+ {
+ }
+
+ void setCacheParams(bool enabled, u8 block_radius, size_t limit);
+
+ u32 getFreeId(v3s16 minedge, v3s16 maxedge);
+ const Area *getArea(u32 id) const;
+ u16 size() const;
+#if 0
+ bool deserialize(std::istream &is);
+ void serialize(std::ostream &is) const;
+#endif
+private:
+ static void cacheMiss(void *data, const v3s16 &mpos, std::vector<Area *> *dest);
+ u8 m_cacheblock_radius; // if you modify this, call invalidateCache()
+ LRUCache<v3s16, std::vector<Area *> > m_res_cache;
+ u32 m_highest_id;
+
+};
+
+
+class VectorAreaStore : public AreaStore {
+protected:
+ virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
+public:
+ virtual void insertArea(const Area &a);
+ virtual void reserve(size_t count);
+ virtual bool removeArea(u32 id);
+ virtual void getAreasInArea(std::vector<Area *> *result,
+ v3s16 minedge, v3s16 maxedge, bool accept_overlap);
+ // virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const;
+private:
+ std::vector<Area *> m_areas;
+};
+
+#if USE_SPATIAL
+
+class SpatialAreaStore : public AreaStore {
+protected:
+ virtual void getAreasForPosImpl(std::vector<Area *> *result, v3s16 pos);
+public:
+ SpatialAreaStore();
+ virtual void insertArea(const Area &a);
+ virtual bool removeArea(u32 id);
+ virtual void getAreasInArea(std::vector<Area *> *result,
+ v3s16 minedge, v3s16 maxedge, bool accept_overlap);
+ // virtual bool forEach(bool (*callback)(void *args, Area *a), void *args) const;
+
+ virtual ~SpatialAreaStore();
+private:
+ SpatialIndex::ISpatialIndex *m_tree;
+ SpatialIndex::IStorageManager *m_storagemanager;
+
+ class VectorResultVisitor : public SpatialIndex::IVisitor {
+ private:
+ SpatialAreaStore *m_store;
+ std::vector<Area *> *m_result;
+ public:
+ VectorResultVisitor(std::vector<Area *> *result, SpatialAreaStore *store)
+ {
+ m_store = store;
+ m_result = result;
+ }
+
+ virtual void visitNode(const SpatialIndex::INode &in)
+ {
+ }
+
+ virtual void visitData(const SpatialIndex::IData &in)
+ {
+ u32 id = in.getIdentifier();
+
+ std::map<u32, Area>::iterator itr = m_store->areas_map.find(id);
+ assert(itr != m_store->areas_map.end());
+ m_result->push_back(&itr->second);
+ }
+
+ virtual void visitData(std::vector<const SpatialIndex::IData *> &v)
+ {
+ for (size_t i = 0; i < v.size(); i++)
+ visitData(*(v[i]));
+ }
+
+ ~VectorResultVisitor() {}
+ };
+};
+
+#endif
+
+#endif /* AREASTORE_H_ */
diff --git a/src/ban.cpp b/src/ban.cpp
index 55d9b22fe..7c1a68d45 100644
--- a/src/ban.cpp
+++ b/src/ban.cpp
@@ -56,7 +56,7 @@ void BanManager::load()
infostream<<"BanManager: failed loading from "<<m_banfilepath<<std::endl;
throw SerializationError("BanManager::load(): Couldn't open file");
}
-
+
while(!is.eof() && is.good())
{
std::string line;
@@ -74,18 +74,14 @@ void BanManager::load()
void BanManager::save()
{
JMutexAutoLock lock(m_mutex);
- infostream<<"BanManager: saving to "<<m_banfilepath<<std::endl;
+ infostream << "BanManager: saving to " << m_banfilepath << std::endl;
std::ostringstream ss(std::ios_base::binary);
- for(std::map<std::string, std::string>::iterator
- i = m_ips.begin();
- i != m_ips.end(); i++)
- {
- ss << i->first << "|" << i->second << "\n";
- }
+ for (StringMap::iterator it = m_ips.begin(); it != m_ips.end(); ++it)
+ ss << it->first << "|" << it->second << "\n";
- if(!fs::safeWriteToFile(m_banfilepath, ss.str())) {
- infostream<<"BanManager: failed saving to "<<m_banfilepath<<std::endl;
+ if (!fs::safeWriteToFile(m_banfilepath, ss.str())) {
+ infostream << "BanManager: failed saving to " << m_banfilepath << std::endl;
throw SerializationError("BanManager::save(): Couldn't write file");
}
@@ -102,25 +98,23 @@ std::string BanManager::getBanDescription(const std::string &ip_or_name)
{
JMutexAutoLock lock(m_mutex);
std::string s = "";
- for(std::map<std::string, std::string>::iterator
- i = m_ips.begin();
- i != m_ips.end(); i++)
- {
- if(i->first == ip_or_name || i->second == ip_or_name
- || ip_or_name == "")
- s += i->first + "|" + i->second + ", ";
+ for (StringMap::iterator it = m_ips.begin(); it != m_ips.end(); ++it) {
+ if (it->first == ip_or_name || it->second == ip_or_name
+ || ip_or_name == "") {
+ s += it->first + "|" + it->second + ", ";
+ }
}
- s = s.substr(0, s.size()-2);
+ s = s.substr(0, s.size() - 2);
return s;
}
std::string BanManager::getBanName(const std::string &ip)
{
JMutexAutoLock lock(m_mutex);
- std::map<std::string, std::string>::iterator i = m_ips.find(ip);
- if(i == m_ips.end())
+ StringMap::iterator it = m_ips.find(ip);
+ if (it == m_ips.end())
return "";
- return i->second;
+ return it->second;
}
void BanManager::add(const std::string &ip, const std::string &name)
@@ -133,19 +127,16 @@ void BanManager::add(const std::string &ip, const std::string &name)
void BanManager::remove(const std::string &ip_or_name)
{
JMutexAutoLock lock(m_mutex);
- for(std::map<std::string, std::string>::iterator
- i = m_ips.begin();
- i != m_ips.end();)
- {
- if((i->first == ip_or_name) || (i->second == ip_or_name)) {
- m_ips.erase(i++);
+ for (StringMap::iterator it = m_ips.begin(); it != m_ips.end();) {
+ if ((it->first == ip_or_name) || (it->second == ip_or_name)) {
+ m_ips.erase(it++);
} else {
- ++i;
+ ++it;
}
}
m_modified = true;
}
-
+
bool BanManager::isModified()
{
diff --git a/src/ban.h b/src/ban.h
index 02a472f51..5db7179de 100644
--- a/src/ban.h
+++ b/src/ban.h
@@ -20,8 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef BAN_HEADER
#define BAN_HEADER
-#include <map>
-#include <string>
+#include "util/string.h"
#include "jthread/jthread.h"
#include "jthread/jmutex.h"
#include "exceptions.h"
@@ -43,7 +42,7 @@ public:
private:
JMutex m_mutex;
std::string m_banfilepath;
- std::map<std::string, std::string> m_ips;
+ StringMap m_ips;
bool m_modified;
};
diff --git a/src/camera.cpp b/src/camera.cpp
index 5200f71ba..0c6d03e4e 100644
--- a/src/camera.cpp
+++ b/src/camera.cpp
@@ -20,14 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "camera.h"
#include "debug.h"
#include "client.h"
-#include "main.h" // for g_settings
#include "map.h"
-#include "clientmap.h" // MapDrawControl
+#include "clientmap.h" // MapDrawControl
#include "player.h"
#include <cmath>
#include "settings.h"
#include "wieldmesh.h"
-#include "noise.h" // easeCurve
+#include "noise.h" // easeCurve
#include "gamedef.h"
#include "sound.h"
#include "event.h"
@@ -93,10 +92,9 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control,
// all other 3D scene nodes and before the GUI.
m_wieldmgr = smgr->createNewSceneManager();
m_wieldmgr->addCameraSceneNode();
- m_wieldnode = new WieldMeshSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr, -1, true);
+ m_wieldnode = new WieldMeshSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr, -1, false);
m_wieldnode->setItem(ItemStack(), m_gamedef);
m_wieldnode->drop(); // m_wieldmgr grabbed it
- m_wieldlightnode = m_wieldmgr->addLightSceneNode(NULL, v3f(0.0, 50.0, 0.0));
/* TODO: Add a callback function so these can be updated when a setting
* changes. At this point in time it doesn't matter (e.g. /set
@@ -119,34 +117,22 @@ Camera::~Camera()
m_wieldmgr->drop();
}
-bool Camera::successfullyCreated(std::wstring& error_message)
+bool Camera::successfullyCreated(std::string &error_message)
{
- if (m_playernode == NULL)
- {
- error_message = L"Failed to create the player scene node";
- return false;
- }
- if (m_headnode == NULL)
- {
- error_message = L"Failed to create the head scene node";
- return false;
- }
- if (m_cameranode == NULL)
- {
- error_message = L"Failed to create the camera scene node";
- return false;
- }
- if (m_wieldmgr == NULL)
- {
- error_message = L"Failed to create the wielded item scene manager";
- return false;
- }
- if (m_wieldnode == NULL)
- {
- error_message = L"Failed to create the wielded item scene node";
- return false;
+ if (!m_playernode) {
+ error_message = "Failed to create the player scene node";
+ } else if (!m_headnode) {
+ error_message = "Failed to create the head scene node";
+ } else if (!m_cameranode) {
+ error_message = "Failed to create the camera scene node";
+ } else if (!m_wieldmgr) {
+ error_message = "Failed to create the wielded item scene manager";
+ } else if (!m_wieldnode) {
+ error_message = "Failed to create the wielded item scene node";
+ } else {
+ error_message.clear();
}
- return true;
+ return error_message.empty();
}
// Returns the fractional part of x
@@ -175,55 +161,37 @@ void Camera::step(f32 dtime)
{
//f32 offset = dtime * m_view_bobbing_speed * 0.035;
f32 offset = dtime * m_view_bobbing_speed * 0.030;
- if (m_view_bobbing_state == 2)
- {
-#if 0
+ if (m_view_bobbing_state == 2) {
// Animation is getting turned off
- if (m_view_bobbing_anim < 0.5)
+ if (m_view_bobbing_anim < 0.25) {
m_view_bobbing_anim -= offset;
- else
- m_view_bobbing_anim += offset;
- if (m_view_bobbing_anim <= 0 || m_view_bobbing_anim >= 1)
- {
- m_view_bobbing_anim = 0;
- m_view_bobbing_state = 0;
- }
-#endif
-#if 1
- // Animation is getting turned off
- if(m_view_bobbing_anim < 0.25)
- {
- m_view_bobbing_anim -= offset;
- } else if(m_view_bobbing_anim > 0.75) {
+ } else if (m_view_bobbing_anim > 0.75) {
m_view_bobbing_anim += offset;
}
- if(m_view_bobbing_anim < 0.5)
- {
+
+ if (m_view_bobbing_anim < 0.5) {
m_view_bobbing_anim += offset;
- if(m_view_bobbing_anim > 0.5)
+ if (m_view_bobbing_anim > 0.5)
m_view_bobbing_anim = 0.5;
} else {
m_view_bobbing_anim -= offset;
- if(m_view_bobbing_anim < 0.5)
+ if (m_view_bobbing_anim < 0.5)
m_view_bobbing_anim = 0.5;
}
- if(m_view_bobbing_anim <= 0 || m_view_bobbing_anim >= 1 ||
- fabs(m_view_bobbing_anim - 0.5) < 0.01)
- {
+
+ if (m_view_bobbing_anim <= 0 || m_view_bobbing_anim >= 1 ||
+ fabs(m_view_bobbing_anim - 0.5) < 0.01) {
m_view_bobbing_anim = 0;
m_view_bobbing_state = 0;
}
-#endif
}
- else
- {
+ else {
float was = m_view_bobbing_anim;
m_view_bobbing_anim = my_modf(m_view_bobbing_anim + offset);
bool step = (was == 0 ||
(was < 0.5f && m_view_bobbing_anim >= 0.5f) ||
(was > 0.5f && m_view_bobbing_anim <= 0.5f));
- if(step)
- {
+ if(step) {
MtEvent *e = new SimpleTriggerEvent("ViewBobbingStep");
m_gamedef->event()->put(e);
}
@@ -482,11 +450,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime,
m_wieldnode->setPosition(wield_position);
m_wieldnode->setRotation(wield_rotation);
- // Shine light upon the wield mesh
- video::SColor black(255,0,0,0);
- m_wieldmgr->setAmbientLight(player->light_color.getInterpolated(black, 0.7));
- m_wieldlightnode->getLightData().DiffuseColor = player->light_color.getInterpolated(black, 0.3);
- m_wieldlightnode->setPosition(v3f(30+5*sin(2*player->getYaw()*M_PI/180), -50, 0));
+ m_wieldnode->setColor(player->light_color);
// Render distance feedback loop
updateViewingRange(frametime, busytime);
diff --git a/src/camera.h b/src/camera.h
index 3f10b87d7..006f4b3ce 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "inventory.h"
#include "mesh.h"
-#include "tile.h"
+#include "client/tile.h"
#include "util/numeric.h"
#include <ICameraSceneNode.h>
@@ -110,7 +110,7 @@ public:
}
// Checks if the constructor was able to create the scene nodes
- bool successfullyCreated(std::wstring& error_message);
+ bool successfullyCreated(std::string &error_message);
// Step the camera: updates the viewing range and view bobbing.
void step(f32 dtime);
@@ -159,7 +159,6 @@ private:
scene::ISceneManager* m_wieldmgr;
WieldMeshSceneNode* m_wieldnode;
- scene::ILightSceneNode* m_wieldlightnode;
// draw control
MapDrawControl& m_draw_control;
diff --git a/src/cavegen.cpp b/src/cavegen.cpp
index 22bf03d17..8372f70b5 100644
--- a/src/cavegen.cpp
+++ b/src/cavegen.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "map.h"
#include "mapgen.h"
+#include "mapgen_v5.h"
#include "mapgen_v6.h"
#include "mapgen_v7.h"
#include "cavegen.h"
@@ -27,28 +28,272 @@ with this program; if not, write to the Free Software Foundation, Inc.,
NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
-///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////// Caves V5
-CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave) {
- this->mg = mg;
- this->vm = mg->vm;
- this->ndef = mg->ndef;
- this->water_level = mg->water_level;
- this->large_cave = is_large_cave;
- this->ps = ps;
- this->ps2 = ps2;
+CaveV5::CaveV5(MapgenV5 *mg, PseudoRandom *ps)
+{
+ this->mg = mg;
+ this->vm = mg->vm;
+ this->ndef = mg->ndef;
+ this->water_level = mg->water_level;
+ this->ps = ps;
+ this->c_water_source = mg->c_water_source;
+ this->c_lava_source = mg->c_lava_source;
+ this->c_ice = mg->c_ice;
+ this->np_caveliquids = &nparams_caveliquids;
+
+ dswitchint = ps->range(1, 14);
+ flooded = ps->range(1, 2) == 2;
+
+ part_max_length_rs = ps->range(2, 4);
+ tunnel_routepoints = ps->range(5, ps->range(15, 30));
+ min_tunnel_diameter = 5;
+ max_tunnel_diameter = ps->range(7, ps->range(8, 24));
+
+ large_cave_is_flat = (ps->range(0, 1) == 0);
+}
+
+
+void CaveV5::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
+{
+ node_min = nmin;
+ node_max = nmax;
+ main_direction = v3f(0, 0, 0);
+
+ // Allowed route area size in nodes
+ ar = node_max - node_min + v3s16(1, 1, 1);
+ // Area starting point in nodes
+ of = node_min;
+
+ // Allow a bit more
+ //(this should be more than the maximum radius of the tunnel)
+ s16 insure = 10;
+ s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
+ ar += v3s16(1,0,1) * more * 2;
+ of -= v3s16(1,0,1) * more;
+
+ route_y_min = 0;
+ // Allow half a diameter + 7 over stone surface
+ route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
+
+ // Limit maximum to area
+ route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
+
+ s16 min = 0;
+ if (node_min.Y < water_level && node_max.Y > water_level) {
+ min = water_level - max_tunnel_diameter/3 - of.Y;
+ route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
+ }
+ route_y_min = ps->range(min, min + max_tunnel_diameter);
+ route_y_min = rangelim(route_y_min, 0, route_y_max);
+
+ s16 route_start_y_min = route_y_min;
+ s16 route_start_y_max = route_y_max;
+
+ route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
+ route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
+
+ // Randomize starting position
+ orp = v3f(
+ (float)(ps->next() % ar.X) + 0.5,
+ (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
+ (float)(ps->next() % ar.Z) + 0.5
+ );
+
+ // Add generation notify begin event
+ v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
+ GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN;
+ mg->gennotify.addEvent(notifytype, abs_pos);
+
+ // Generate some tunnel starting from orp
+ for (u16 j = 0; j < tunnel_routepoints; j++)
+ makeTunnel(j % dswitchint == 0);
+
+ // Add generation notify end event
+ abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
+ notifytype = GENNOTIFY_LARGECAVE_END;
+ mg->gennotify.addEvent(notifytype, abs_pos);
+}
+
+
+void CaveV5::makeTunnel(bool dirswitch)
+{
+ // Randomize size
+ s16 min_d = min_tunnel_diameter;
+ s16 max_d = max_tunnel_diameter;
+ rs = ps->range(min_d, max_d);
+ s16 rs_part_max_length_rs = rs * part_max_length_rs;
+
+ v3s16 maxlen;
+ maxlen = v3s16(
+ rs_part_max_length_rs,
+ rs_part_max_length_rs / 2,
+ rs_part_max_length_rs
+ );
+
+ v3f vec;
+ // Jump downward sometimes
+ vec = v3f(
+ (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
+ (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
+ (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
+ );
+
+ // Do not make caves that are above ground.
+ // It is only necessary to check the startpoint and endpoint.
+ v3s16 orpi(orp.X, orp.Y, orp.Z);
+ v3s16 veci(vec.X, vec.Y, vec.Z);
+ v3s16 p;
+
+ p = orpi + veci + of + rs / 2;
+ if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
+ p.X >= node_min.X && p.X <= node_max.X) {
+ u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
+ s16 h = mg->heightmap[index];
+ if (h < p.Y)
+ return;
+ } else if (p.Y > water_level) {
+ return; // If it's not in our heightmap, use a simple heuristic
+ }
+
+ p = orpi + of + rs / 2;
+ if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
+ p.X >= node_min.X && p.X <= node_max.X) {
+ u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
+ s16 h = mg->heightmap[index];
+ if (h < p.Y)
+ return;
+ } else if (p.Y > water_level) {
+ return;
+ }
+
+ vec += main_direction;
+
+ v3f rp = orp + vec;
+ if (rp.X < 0)
+ rp.X = 0;
+ else if (rp.X >= ar.X)
+ rp.X = ar.X - 1;
+
+ if (rp.Y < route_y_min)
+ rp.Y = route_y_min;
+ else if (rp.Y >= route_y_max)
+ rp.Y = route_y_max - 1;
+
+ if (rp.Z < 0)
+ rp.Z = 0;
+ else if (rp.Z >= ar.Z)
+ rp.Z = ar.Z - 1;
+
+ vec = rp - orp;
+
+ float veclen = vec.getLength();
+ if (veclen < 0.05)
+ veclen = 1.0;
+
+ // Every second section is rough
+ bool randomize_xz = (ps->range(1, 2) == 1);
+
+ // Carve routes
+ for (float f = 0; f < 1.0; f += 1.0 / veclen)
+ carveRoute(vec, f, randomize_xz);
+
+ orp = rp;
+}
+
+
+void CaveV5::carveRoute(v3f vec, float f, bool randomize_xz)
+{
+ MapNode airnode(CONTENT_AIR);
+ MapNode waternode(c_water_source);
+ MapNode lavanode(c_lava_source);
+
+ v3s16 startp(orp.X, orp.Y, orp.Z);
+ startp += of;
+
+ float nval = NoisePerlin3D(np_caveliquids, startp.X,
+ startp.Y, startp.Z, mg->seed);
+ MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
+
+ v3f fp = orp + vec * f;
+ fp.X += 0.1 * ps->range(-10, 10);
+ fp.Z += 0.1 * ps->range(-10, 10);
+ v3s16 cp(fp.X, fp.Y, fp.Z);
+
+ s16 d0 = -rs/2;
+ s16 d1 = d0 + rs;
+ if (randomize_xz) {
+ d0 += ps->range(-1, 1);
+ d1 += ps->range(-1, 1);
+ }
+
+ for (s16 z0 = d0; z0 <= d1; z0++) {
+ s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
+ for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
+ s16 maxabsxz = MYMAX(abs(x0), abs(z0));
+
+ s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
+
+ for (s16 y0 = -si2; y0 <= si2; y0++) {
+ if (large_cave_is_flat) {
+ // Make large caves not so tall
+ if (rs > 7 && abs(y0) >= rs / 3)
+ continue;
+ }
+
+ v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
+ p += of;
+
+ if (vm->m_area.contains(p) == false)
+ continue;
+
+ u32 i = vm->m_area.index(p);
+ content_t c = vm->m_data[i].getContent();
+ if (!ndef->get(c).is_ground_content)
+ continue;
+
+ int full_ymin = node_min.Y - MAP_BLOCKSIZE;
+ int full_ymax = node_max.Y + MAP_BLOCKSIZE;
+
+ if (flooded && full_ymin < water_level &&
+ full_ymax > water_level)
+ vm->m_data[i] = (p.Y <= water_level) ?
+ waternode : airnode;
+ else if (flooded && full_ymax < water_level)
+ vm->m_data[i] = (p.Y < startp.Y - 4) ?
+ liquidnode : airnode;
+ else
+ vm->m_data[i] = airnode;
+ }
+ }
+ }
+}
+
+
+///////////////////////////////////////// Caves V6
+
+
+CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave)
+{
+ this->mg = mg;
+ this->vm = mg->vm;
+ this->ndef = mg->ndef;
+ this->water_level = mg->water_level;
+ this->large_cave = is_large_cave;
+ this->ps = ps;
+ this->ps2 = ps2;
this->c_water_source = mg->c_water_source;
this->c_lava_source = mg->c_lava_source;
min_tunnel_diameter = 2;
max_tunnel_diameter = ps->range(2, 6);
- dswitchint = ps->range(1, 14);
- flooded = true;
+ dswitchint = ps->range(1, 14);
+ flooded = true;
if (large_cave) {
- part_max_length_rs = ps->range(2,4);
- tunnel_routepoints = ps->range(5, ps->range(15,30));
+ part_max_length_rs = ps->range(2,4);
+ tunnel_routepoints = ps->range(5, ps->range(15,30));
min_tunnel_diameter = 5;
max_tunnel_diameter = ps->range(7, ps->range(8,24));
} else {
@@ -60,7 +305,8 @@ CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_
}
-void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
+void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
+{
node_min = nmin;
node_max = nmax;
max_stone_y = max_stone_height;
@@ -127,7 +373,8 @@ void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
}
-void CaveV6::makeTunnel(bool dirswitch) {
+void CaveV6::makeTunnel(bool dirswitch)
+{
if (dirswitch && !large_cave) {
main_direction = v3f(
((float)(ps->next() % 20) - (float)10) / 10,
@@ -141,37 +388,72 @@ void CaveV6::makeTunnel(bool dirswitch) {
s16 min_d = min_tunnel_diameter;
s16 max_d = max_tunnel_diameter;
rs = ps->range(min_d, max_d);
+ s16 rs_part_max_length_rs = rs * part_max_length_rs;
v3s16 maxlen;
if (large_cave) {
maxlen = v3s16(
- rs * part_max_length_rs,
- rs * part_max_length_rs / 2,
- rs * part_max_length_rs
+ rs_part_max_length_rs,
+ rs_part_max_length_rs / 2,
+ rs_part_max_length_rs
);
} else {
maxlen = v3s16(
- rs * part_max_length_rs,
- ps->range(1, rs * part_max_length_rs),
- rs * part_max_length_rs
+ rs_part_max_length_rs,
+ ps->range(1, rs_part_max_length_rs),
+ rs_part_max_length_rs
);
}
v3f vec(
- (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
- (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
- (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
+ (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
+ (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
+ (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
);
// Jump downward sometimes
if (!large_cave && ps->range(0, 12) == 0) {
vec = v3f(
- (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
+ (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
(float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
- (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
+ (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
);
}
+ // Do not make caves that are entirely above ground, to fix
+ // shadow bugs caused by overgenerated large caves.
+ // It is only necessary to check the startpoint and endpoint.
+ v3s16 orpi(orp.X, orp.Y, orp.Z);
+ v3s16 veci(vec.X, vec.Y, vec.Z);
+ s16 h1;
+ s16 h2;
+
+ v3s16 p1 = orpi + veci + of + rs / 2;
+ if (p1.Z >= node_min.Z && p1.Z <= node_max.Z &&
+ p1.X >= node_min.X && p1.X <= node_max.X) {
+ u32 index1 = (p1.Z - node_min.Z) * mg->ystride +
+ (p1.X - node_min.X);
+ h1 = mg->heightmap[index1];
+ } else {
+ h1 = water_level; // If not in heightmap
+ }
+
+ v3s16 p2 = orpi + of + rs / 2;
+ if (p2.Z >= node_min.Z && p2.Z <= node_max.Z &&
+ p2.X >= node_min.X && p2.X <= node_max.X) {
+ u32 index2 = (p2.Z - node_min.Z) * mg->ystride +
+ (p2.X - node_min.X);
+ h2 = mg->heightmap[index2];
+ } else {
+ h2 = water_level;
+ }
+
+ // If startpoint and endpoint are above ground,
+ // disable placing of nodes in carveRoute while
+ // still running all pseudorandom calls to ensure
+ // caves consistent with existing worlds.
+ bool tunnel_above_ground = p1.Y > h1 && p2.Y > h2;
+
vec += main_direction;
v3f rp = orp + vec;
@@ -202,13 +484,14 @@ void CaveV6::makeTunnel(bool dirswitch) {
// Carve routes
for (float f = 0; f < 1.0; f += 1.0 / veclen)
- carveRoute(vec, f, randomize_xz);
+ carveRoute(vec, f, randomize_xz, tunnel_above_ground);
orp = rp;
}
-void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
+void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground)
+{
MapNode airnode(CONTENT_AIR);
MapNode waternode(c_water_source);
MapNode lavanode(c_lava_source);
@@ -231,6 +514,9 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
for (s16 z0 = d0; z0 <= d1; z0++) {
s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
+ if (tunnel_above_ground)
+ continue;
+
s16 maxabsxz = MYMAX(abs(x0), abs(z0));
s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
for (s16 y0 = -si2; y0 <= si2; y0++) {
@@ -255,17 +541,18 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
int full_ymin = node_min.Y - MAP_BLOCKSIZE;
int full_ymax = node_max.Y + MAP_BLOCKSIZE;
- if (flooded && full_ymin < water_level && full_ymax > water_level) {
- vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
+ if (flooded && full_ymin < water_level &&
+ full_ymax > water_level) {
+ vm->m_data[i] = (p.Y <= water_level) ?
+ waternode : airnode;
} else if (flooded && full_ymax < water_level) {
- vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
+ vm->m_data[i] = (p.Y < startp.Y - 2) ?
+ lavanode : airnode;
} else {
vm->m_data[i] = airnode;
}
} else {
- // Don't replace air or water or lava or ignore
- if (c == CONTENT_IGNORE || c == CONTENT_AIR ||
- c == c_water_source || c == c_lava_source)
+ if (c == CONTENT_IGNORE || c == CONTENT_AIR)
continue;
vm->m_data[i] = airnode;
@@ -279,13 +566,14 @@ void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
///////////////////////////////////////// Caves V7
-CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps, bool is_large_cave) {
- this->mg = mg;
- this->vm = mg->vm;
- this->ndef = mg->ndef;
- this->water_level = mg->water_level;
- this->large_cave = is_large_cave;
- this->ps = ps;
+
+CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps)
+{
+ this->mg = mg;
+ this->vm = mg->vm;
+ this->ndef = mg->ndef;
+ this->water_level = mg->water_level;
+ this->ps = ps;
this->c_water_source = mg->c_water_source;
this->c_lava_source = mg->c_lava_source;
this->c_ice = mg->c_ice;
@@ -294,23 +582,17 @@ CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps, bool is_large_cave) {
dswitchint = ps->range(1, 14);
flooded = ps->range(1, 2) == 2;
- if (large_cave) {
- part_max_length_rs = ps->range(2, 4);
- tunnel_routepoints = ps->range(5, ps->range(15, 30));
- min_tunnel_diameter = 5;
- max_tunnel_diameter = ps->range(7, ps->range(8, 24));
- } else {
- part_max_length_rs = ps->range(2, 9);
- tunnel_routepoints = ps->range(10, ps->range(15, 30));
- min_tunnel_diameter = 2;
- max_tunnel_diameter = ps->range(2, 6);
- }
+ part_max_length_rs = ps->range(2, 4);
+ tunnel_routepoints = ps->range(5, ps->range(15, 30));
+ min_tunnel_diameter = 5;
+ max_tunnel_diameter = ps->range(7, ps->range(8, 24));
large_cave_is_flat = (ps->range(0, 1) == 0);
}
-void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
+void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
+{
node_min = nmin;
node_max = nmax;
max_stone_y = max_stone_height;
@@ -335,15 +617,13 @@ void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
// Limit maximum to area
route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
- if (large_cave) {
- s16 min = 0;
- if (node_min.Y < water_level && node_max.Y > water_level) {
- min = water_level - max_tunnel_diameter/3 - of.Y;
- route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
- }
- route_y_min = ps->range(min, min + max_tunnel_diameter);
- route_y_min = rangelim(route_y_min, 0, route_y_max);
+ s16 min = 0;
+ if (node_min.Y < water_level && node_max.Y > water_level) {
+ min = water_level - max_tunnel_diameter/3 - of.Y;
+ route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
}
+ route_y_min = ps->range(min, min + max_tunnel_diameter);
+ route_y_min = rangelim(route_y_min, 0, route_y_max);
s16 route_start_y_min = route_y_min;
s16 route_start_y_max = route_y_max;
@@ -360,8 +640,7 @@ void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
// Add generation notify begin event
v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
- GenNotifyType notifytype = large_cave ?
- GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
+ GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN;
mg->gennotify.addEvent(notifytype, abs_pos);
// Generate some tunnel starting from orp
@@ -370,86 +649,60 @@ void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
// Add generation notify end event
abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
- notifytype = large_cave ?
- GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
+ notifytype = GENNOTIFY_LARGECAVE_END;
mg->gennotify.addEvent(notifytype, abs_pos);
}
-void CaveV7::makeTunnel(bool dirswitch) {
- if (dirswitch && !large_cave) {
- main_direction = v3f(
- ((float)(ps->next() % 20) - (float)10) / 10,
- ((float)(ps->next() % 20) - (float)10) / 30,
- ((float)(ps->next() % 20) - (float)10) / 10
- );
- main_direction *= (float)ps->range(0, 10) / 10;
- }
-
+void CaveV7::makeTunnel(bool dirswitch)
+{
// Randomize size
s16 min_d = min_tunnel_diameter;
s16 max_d = max_tunnel_diameter;
rs = ps->range(min_d, max_d);
+ s16 rs_part_max_length_rs = rs * part_max_length_rs;
v3s16 maxlen;
- if (large_cave) {
- maxlen = v3s16(
- rs * part_max_length_rs,
- rs * part_max_length_rs / 2,
- rs * part_max_length_rs
- );
- } else {
- maxlen = v3s16(
- rs * part_max_length_rs,
- ps->range(1, rs * part_max_length_rs),
- rs * part_max_length_rs
- );
- }
+ maxlen = v3s16(
+ rs_part_max_length_rs,
+ rs_part_max_length_rs / 2,
+ rs_part_max_length_rs
+ );
v3f vec;
// Jump downward sometimes
- if (!large_cave && ps->range(0, 12) == 0) {
- vec = v3f(
- (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
- (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
- (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
- );
- } else {
- vec = v3f(
- (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
- (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
- (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
- );
- }
+ vec = v3f(
+ (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
+ (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
+ (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
+ );
- // Do not make large caves that are above ground.
+ // Do not make caves that are above ground.
// It is only necessary to check the startpoint and endpoint.
- if (large_cave) {
- v3s16 orpi(orp.X, orp.Y, orp.Z);
- v3s16 veci(vec.X, vec.Y, vec.Z);
- v3s16 p;
+ v3s16 orpi(orp.X, orp.Y, orp.Z);
+ v3s16 veci(vec.X, vec.Y, vec.Z);
+ v3s16 p;
- p = orpi + veci + of + rs / 2;
- if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
+ p = orpi + veci + of + rs / 2;
+ if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
p.X >= node_min.X && p.X <= node_max.X) {
- u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
- s16 h = mg->ridge_heightmap[index];
- if (h < p.Y)
- return;
- } else if (p.Y > water_level) {
- return; // If it's not in our heightmap, use a simple heuristic
- }
+ u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
+ s16 h = mg->ridge_heightmap[index];
+ if (h < p.Y)
+ return;
+ } else if (p.Y > water_level) {
+ return; // If it's not in our heightmap, use a simple heuristic
+ }
- p = orpi + of + rs / 2;
- if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
+ p = orpi + of + rs / 2;
+ if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
p.X >= node_min.X && p.X <= node_max.X) {
- u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
- s16 h = mg->ridge_heightmap[index];
- if (h < p.Y)
- return;
- } else if (p.Y > water_level) {
+ u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
+ s16 h = mg->ridge_heightmap[index];
+ if (h < p.Y)
return;
- }
+ } else if (p.Y > water_level) {
+ return;
}
vec += main_direction;
@@ -479,20 +732,16 @@ void CaveV7::makeTunnel(bool dirswitch) {
// Every second section is rough
bool randomize_xz = (ps->range(1, 2) == 1);
- // Make a ravine every once in a while if it's long enough
- //float xylen = vec.X * vec.X + vec.Z * vec.Z;
- //disable ravines for now
- bool is_ravine = false; //(xylen > 500.0) && !large_cave && (ps->range(1, 8) == 1);
-
// Carve routes
for (float f = 0; f < 1.0; f += 1.0 / veclen)
- carveRoute(vec, f, randomize_xz, is_ravine);
+ carveRoute(vec, f, randomize_xz);
orp = rp;
}
-void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
+void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz)
+{
MapNode airnode(CONTENT_AIR);
MapNode waternode(c_water_source);
MapNode lavanode(c_lava_source);
@@ -501,8 +750,9 @@ void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
startp += of;
float nval = NoisePerlin3D(np_caveliquids, startp.X,
- startp.Y, startp.Z, mg->seed);
- MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
+ startp.Y, startp.Z, mg->seed);
+ MapNode liquidnode = (nval < 0.40 && node_max.Y < MGV7_LAVA_DEPTH) ?
+ lavanode : waternode;
v3f fp = orp + vec * f;
fp.X += 0.1 * ps->range(-10, 10);
@@ -516,22 +766,14 @@ void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
d1 += ps->range(-1, 1);
}
- bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
- bool should_make_cave_hole = ps->range(1, 10) == 1;
-
for (s16 z0 = d0; z0 <= d1; z0++) {
s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
s16 maxabsxz = MYMAX(abs(x0), abs(z0));
- s16 si2 = is_ravine ? MYMIN(ps->range(25, 26), ar.Y) :
- rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
+ s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
for (s16 y0 = -si2; y0 <= si2; y0++) {
- // Make better floors in small caves
- if(flat_cave_floor && y0 <= -rs/2 && rs<=7)
- continue;
-
if (large_cave_is_flat) {
// Make large caves not so tall
if (rs > 7 && abs(y0) >= rs / 3)
@@ -541,42 +783,26 @@ void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
p += of;
- if (!is_ravine && mg->heightmap && should_make_cave_hole &&
- p.X <= node_max.X && p.Z <= node_max.Z) {
- int maplen = node_max.X - node_min.X + 1;
- int idx = (p.Z - node_min.Z) * maplen + (p.X - node_min.X);
- if (p.Y >= mg->heightmap[idx] - 2)
- continue;
- }
-
if (vm->m_area.contains(p) == false)
continue;
u32 i = vm->m_area.index(p);
-
- // Don't replace air, water, lava, or ice
content_t c = vm->m_data[i].getContent();
- if (!ndef->get(c).is_ground_content || c == CONTENT_AIR ||
- c == c_water_source || c == c_lava_source || c == c_ice)
+ if (!ndef->get(c).is_ground_content)
continue;
- if (large_cave) {
- int full_ymin = node_min.Y - MAP_BLOCKSIZE;
- int full_ymax = node_max.Y + MAP_BLOCKSIZE;
-
- if (flooded && full_ymin < water_level && full_ymax > water_level)
- vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
- else if (flooded && full_ymax < water_level)
- vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
- else
- vm->m_data[i] = airnode;
- } else {
- if (c == CONTENT_IGNORE)
- continue;
-
+ int full_ymin = node_min.Y - MAP_BLOCKSIZE;
+ int full_ymax = node_max.Y + MAP_BLOCKSIZE;
+
+ if (flooded && full_ymin < water_level &&
+ full_ymax > water_level)
+ vm->m_data[i] = (p.Y <= water_level) ?
+ waternode : airnode;
+ else if (flooded && full_ymax < water_level)
+ vm->m_data[i] = (p.Y < startp.Y - 4) ?
+ liquidnode : airnode;
+ else
vm->m_data[i] = airnode;
- vm->m_flags[i] |= VMANIP_FLAG_CAVE;
- }
}
}
}
diff --git a/src/cavegen.h b/src/cavegen.h
index 7371df6fa..b9662587b 100644
--- a/src/cavegen.h
+++ b/src/cavegen.h
@@ -21,10 +21,57 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CAVEGEN_HEADER
#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
+#define MGV7_LAVA_DEPTH -256
+class MapgenV5;
class MapgenV6;
class MapgenV7;
+class CaveV5 {
+public:
+ MapgenV5 *mg;
+ MMVManip *vm;
+ INodeDefManager *ndef;
+
+ NoiseParams *np_caveliquids;
+
+ s16 min_tunnel_diameter;
+ s16 max_tunnel_diameter;
+ u16 tunnel_routepoints;
+ int dswitchint;
+ int part_max_length_rs;
+
+ bool large_cave_is_flat;
+ bool flooded;
+
+ s16 max_stone_y;
+ v3s16 node_min;
+ v3s16 node_max;
+
+ v3f orp; // starting point, relative to caved space
+ v3s16 of; // absolute coordinates of caved space
+ v3s16 ar; // allowed route area
+ s16 rs; // tunnel radius size
+ v3f main_direction;
+
+ s16 route_y_min;
+ s16 route_y_max;
+
+ PseudoRandom *ps;
+
+ content_t c_water_source;
+ content_t c_lava_source;
+ content_t c_ice;
+
+ int water_level;
+
+ CaveV5() {}
+ CaveV5(MapgenV5 *mg, PseudoRandom *ps);
+ void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height);
+ void makeTunnel(bool dirswitch);
+ void carveRoute(v3f vec, float f, bool randomize_xz);
+};
+
class CaveV6 {
public:
MapgenV6 *mg;
@@ -66,7 +113,7 @@ public:
CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool large_cave);
void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height);
void makeTunnel(bool dirswitch);
- void carveRoute(v3f vec, float f, bool randomize_xz);
+ void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground);
};
class CaveV7 {
@@ -83,7 +130,6 @@ public:
int dswitchint;
int part_max_length_rs;
- bool large_cave;
bool large_cave_is_flat;
bool flooded;
@@ -109,10 +155,10 @@ public:
int water_level;
CaveV7() {}
- CaveV7(MapgenV7 *mg, PseudoRandom *ps, bool large_cave);
+ CaveV7(MapgenV7 *mg, PseudoRandom *ps);
void makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height);
void makeTunnel(bool dirswitch);
- void carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine);
+ void carveRoute(v3f vec, float f, bool randomize_xz);
};
#endif
diff --git a/src/cguittfont/CGUITTFont.cpp b/src/cguittfont/CGUITTFont.cpp
index f6606996e..2342eb748 100644
--- a/src/cguittfont/CGUITTFont.cpp
+++ b/src/cguittfont/CGUITTFont.cpp
@@ -29,6 +29,7 @@
*/
#include <irrlicht.h>
+#include <iostream>
#include "CGUITTFont.h"
namespace irr
@@ -64,8 +65,24 @@ scene::SMesh CGUITTFont::shared_plane_;
//
+/** Checks that no dimension of the FT_BitMap object is negative. If either is
+ * negative, abort execution.
+ */
+inline void checkFontBitmapSize(const FT_Bitmap &bits)
+{
+ if ((s32)bits.rows < 0 || (s32)bits.width < 0) {
+ std::cout << "Insane font glyph size. File: "
+ << __FILE__ << " Line " << __LINE__
+ << std::endl;
+ abort();
+ }
+}
+
video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVideoDriver* driver) const
{
+ // Make sure our casts to s32 in the loops below will not cause problems
+ checkFontBitmapSize(bits);
+
// Determine what our texture size should be.
// Add 1 because textures are inclusive-exclusive.
core::dimension2du d(bits.width + 1, bits.rows + 1);
@@ -87,10 +104,11 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
const u32 image_pitch = image->getPitch() / sizeof(u16);
u16* image_data = (u16*)image->lock();
u8* glyph_data = bits.buffer;
- for (int y = 0; y < bits.rows; ++y)
+
+ for (s32 y = 0; y < (s32)bits.rows; ++y)
{
u16* row = image_data;
- for (int x = 0; x < bits.width; ++x)
+ for (s32 x = 0; x < (s32)bits.width; ++x)
{
// Monochrome bitmaps store 8 pixels per byte. The left-most pixel is the bit 0x80.
// So, we go through the data each bit at a time.
@@ -116,10 +134,10 @@ video::IImage* SGUITTGlyph::createGlyphImage(const FT_Bitmap& bits, video::IVide
const u32 image_pitch = image->getPitch() / sizeof(u32);
u32* image_data = (u32*)image->lock();
u8* glyph_data = bits.buffer;
- for (int y = 0; y < bits.rows; ++y)
+ for (s32 y = 0; y < (s32)bits.rows; ++y)
{
u8* row = glyph_data;
- for (int x = 0; x < bits.width; ++x)
+ for (s32 x = 0; x < (s32)bits.width; ++x)
{
image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
//data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
diff --git a/src/cguittfont/CMakeLists.txt b/src/cguittfont/CMakeLists.txt
index 21448ecb8..7717a2f91 100644
--- a/src/cguittfont/CMakeLists.txt
+++ b/src/cguittfont/CMakeLists.txt
@@ -27,3 +27,4 @@ target_link_libraries(
${FREETYPE_LIBRARY}
${ZLIB_LIBRARIES} # needed by freetype, repeated here for safety
)
+
diff --git a/src/chat.cpp b/src/chat.cpp
index 1fb872b85..50391d39b 100644
--- a/src/chat.cpp
+++ b/src/chat.cpp
@@ -83,7 +83,7 @@ u32 ChatBuffer::getScrollback() const
const ChatLine& ChatBuffer::getLine(u32 index) const
{
- assert(index < getLineCount());
+ assert(index < getLineCount()); // pre-condition
return m_unformatted[index];
}
@@ -107,7 +107,8 @@ void ChatBuffer::deleteOldest(u32 count)
// keep m_formatted in sync
if (del_formatted < m_formatted.size())
{
- assert(m_formatted[del_formatted].first);
+
+ sanity_check(m_formatted[del_formatted].first);
++del_formatted;
while (del_formatted < m_formatted.size() &&
!m_formatted[del_formatted].first)
@@ -510,7 +511,7 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
{
std::wstring completion = narrow_to_wide(*i);
if (prefix_start == 0)
- completion += L":";
+ completion += L": ";
completions.push_back(completion);
}
}
@@ -540,7 +541,7 @@ void ChatPrompt::nickCompletion(const std::list<std::string>& names, bool backwa
}
}
}
- std::wstring replacement = completions[replacement_index] + L" ";
+ std::wstring replacement = completions[replacement_index];
if (word_end < m_line.size() && isspace(word_end))
++word_end;
diff --git a/src/client.cpp b/src/client.cpp
index 2f70d624c..946f4f1c4 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -22,40 +22,36 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
#include <IFileSystem.h>
#include "jthread/jmutexautolock.h"
+#include "util/auth.h"
#include "util/directiontables.h"
#include "util/pointedthing.h"
#include "util/serialize.h"
#include "util/string.h"
-#include "strfnd.h"
+#include "util/srp.h"
#include "client.h"
-#include "clientserver.h"
-#include "main.h"
+#include "network/clientopcodes.h"
#include "filesys.h"
#include "porting.h"
-#include "mapsector.h"
#include "mapblock_mesh.h"
#include "mapblock.h"
+#include "minimap.h"
#include "settings.h"
#include "profiler.h"
#include "gettext.h"
#include "log.h"
#include "nodemetadata.h"
-#include "nodedef.h"
#include "itemdef.h"
#include "shader.h"
-#include "base64.h"
#include "clientmap.h"
#include "clientmedia.h"
#include "sound.h"
#include "IMeshCache.h"
-#include "serialization.h"
#include "config.h"
#include "version.h"
#include "drawscene.h"
-#include "subgame.h"
-#include "server.h"
-#include "database.h"
#include "database-sqlite3.h"
+#include "serialization.h"
+#include "guiscalingfilter.h"
extern gui::IGUIEnvironment* guienv;
@@ -104,7 +100,7 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se
{
DSTACK(__FUNCTION_NAME);
- assert(data);
+ assert(data); // pre-condition
JMutexAutoLock lock(m_mutex);
@@ -143,7 +139,7 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se
// Returned pointer must be deleted
// Returns NULL if queue is empty
-QueuedMeshUpdate * MeshUpdateQueue::pop()
+QueuedMeshUpdate *MeshUpdateQueue::pop()
{
JMutexAutoLock lock(m_mutex);
@@ -166,35 +162,21 @@ QueuedMeshUpdate * MeshUpdateQueue::pop()
MeshUpdateThread
*/
-void * MeshUpdateThread::Thread()
+void MeshUpdateThread::enqueueUpdate(v3s16 p, MeshMakeData *data,
+ bool ack_block_to_server, bool urgent)
{
- ThreadStarted();
-
- log_register_thread("MeshUpdateThread");
-
- DSTACK(__FUNCTION_NAME);
-
- BEGIN_DEBUG_EXCEPTION_HANDLER
-
- porting::setThreadName("MeshUpdateThread");
+ m_queue_in.addBlock(p, data, ack_block_to_server, urgent);
+ deferUpdate();
+}
- while(!StopRequested())
- {
- QueuedMeshUpdate *q = m_queue_in.pop();
- if(q == NULL)
- {
- sleep_ms(3);
- continue;
- }
+void MeshUpdateThread::doUpdate()
+{
+ QueuedMeshUpdate *q;
+ while ((q = m_queue_in.pop())) {
ScopeProfiler sp(g_profiler, "Client: Mesh making");
MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset);
- if(mesh_new->getMesh()->getMeshBufferCount() == 0)
- {
- delete mesh_new;
- mesh_new = NULL;
- }
MeshUpdateResult r;
r.p = q->p;
@@ -205,10 +187,6 @@ void * MeshUpdateThread::Thread()
delete q;
}
-
- END_DEBUG_EXCEPTION_HANDLER(errorstream)
-
- return NULL;
}
/*
@@ -239,7 +217,7 @@ Client::Client(
m_nodedef(nodedef),
m_sound(sound),
m_event(event),
- m_mesh_update_thread(this),
+ m_mesh_update_thread(),
m_env(
new ClientMap(this, this, control,
device->getSceneManager()->getRootSceneNode(),
@@ -251,6 +229,7 @@ Client::Client(
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, ipv6, this),
m_device(device),
m_server_ser_ver(SER_FMT_VER_INVALID),
+ m_proto_ver(0),
m_playeritem(0),
m_inventory_updated(false),
m_inventory_from_server(NULL),
@@ -262,7 +241,10 @@ Client::Client(
m_highlighted_pos(0,0,0),
m_map_seed(0),
m_password(password),
+ m_chosen_auth_mech(AUTH_MECHANISM_NONE),
+ m_auth_data(NULL),
m_access_denied(false),
+ m_access_denied_reconnect(false),
m_itemdef_received(false),
m_nodedef_received(false),
m_media_downloader(new ClientMediaDownloader()),
@@ -271,28 +253,27 @@ Client::Client(
m_time_of_day_update_timer(0),
m_recommended_send_interval(0.1),
m_removed_sounds_check_timer(0),
- m_state(LC_Created)
+ m_state(LC_Created),
+ m_localdb(NULL)
{
- /*
- Add local player
- */
- {
- Player *player = new LocalPlayer(this, playername);
+ // Add local player
+ m_env.addPlayer(new LocalPlayer(this, playername));
- m_env.addPlayer(player);
- }
+ m_mapper = new Mapper(device, this);
+ m_cache_save_interval = g_settings->getU16("server_map_save_interval");
m_cache_smooth_lighting = g_settings->getBool("smooth_lighting");
+ m_cache_enable_shaders = g_settings->getBool("enable_shaders");
}
void Client::Stop()
{
//request all client managed threads to stop
m_mesh_update_thread.Stop();
- if (localdb != NULL) {
- actionstream << "Local map saving ended" << std::endl;
- localdb->endSave();
- delete localserver;
+ // Save local server map
+ if (m_localdb) {
+ infostream << "Local map saving ended." << std::endl;
+ m_localdb->endSave();
}
}
@@ -310,7 +291,7 @@ Client::~Client()
m_mesh_update_thread.Stop();
m_mesh_update_thread.Wait();
- while(!m_mesh_update_thread.m_queue_out.empty()) {
+ while (!m_mesh_update_thread.m_queue_out.empty()) {
MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
delete r.mesh;
}
@@ -319,20 +300,22 @@ Client::~Client()
delete m_inventory_from_server;
// Delete detached inventories
- for(std::map<std::string, Inventory*>::iterator
+ for (std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
- i != m_detached_inventories.end(); i++){
+ i != m_detached_inventories.end(); ++i) {
delete i->second;
}
// cleanup 3d model meshes on client shutdown
while (m_device->getSceneManager()->getMeshCache()->getMeshCount() != 0) {
- scene::IAnimatedMesh * mesh =
+ scene::IAnimatedMesh *mesh =
m_device->getSceneManager()->getMeshCache()->getMeshByIndex(0);
if (mesh != NULL)
m_device->getSceneManager()->getMeshCache()->removeMesh(mesh);
}
+
+ delete m_mapper;
}
void Client::connect(Address address,
@@ -385,139 +368,41 @@ void Client::step(float dtime)
}
}
-#if 0
- {
- /*
- Delete unused sectors
-
- NOTE: This jams the game for a while because deleting sectors
- clear caches
- */
-
- float &counter = m_delete_unused_sectors_timer;
- counter -= dtime;
- if(counter <= 0.0)
- {
- // 3 minute interval
- //counter = 180.0;
- counter = 60.0;
-
- //JMutexAutoLock lock(m_env_mutex); //bulk comment-out
-
- core::list<v3s16> deleted_blocks;
-
- float delete_unused_sectors_timeout =
- g_settings->getFloat("client_delete_unused_sectors_timeout");
-
- // Delete sector blocks
- /*u32 num = m_env.getMap().unloadUnusedData
- (delete_unused_sectors_timeout,
- true, &deleted_blocks);*/
-
- // Delete whole sectors
- m_env.getMap().unloadUnusedData
- (delete_unused_sectors_timeout,
- &deleted_blocks);
-
- if(deleted_blocks.size() > 0)
- {
- /*infostream<<"Client: Deleted blocks of "<<num
- <<" unused sectors"<<std::endl;*/
- /*infostream<<"Client: Deleted "<<num
- <<" unused sectors"<<std::endl;*/
-
- /*
- Send info to server
- */
-
- // Env is locked so con can be locked.
- //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
-
- core::list<v3s16>::Iterator i = deleted_blocks.begin();
- core::list<v3s16> sendlist;
- for(;;)
- {
- if(sendlist.size() == 255 || i == deleted_blocks.end())
- {
- if(sendlist.size() == 0)
- break;
- /*
- [0] u16 command
- [2] u8 count
- [3] v3s16 pos_0
- [3+6] v3s16 pos_1
- ...
- */
- u32 replysize = 2+1+6*sendlist.size();
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
- reply[2] = sendlist.size();
- u32 k = 0;
- for(core::list<v3s16>::Iterator
- j = sendlist.begin();
- j != sendlist.end(); j++)
- {
- writeV3S16(&reply[2+1+6*k], *j);
- k++;
- }
- m_con.Send(PEER_ID_SERVER, 1, reply, true);
-
- if(i == deleted_blocks.end())
- break;
-
- sendlist.clear();
- }
-
- sendlist.push_back(*i);
- i++;
- }
- }
- }
- }
-#endif
// UGLY hack to fix 2 second startup delay caused by non existent
// server client startup synchronization in local server or singleplayer mode
static bool initial_step = true;
if (initial_step) {
initial_step = false;
}
- else if(m_state == LC_Created)
- {
+ else if(m_state == LC_Created) {
float &counter = m_connection_reinit_timer;
counter -= dtime;
- if(counter <= 0.0)
- {
+ if(counter <= 0.0) {
counter = 2.0;
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
-
Player *myplayer = m_env.getLocalPlayer();
- assert(myplayer != NULL);
- // Send TOSERVER_INIT
- // [0] u16 TOSERVER_INIT
+ FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment.");
+
+ // Send TOSERVER_INIT_LEGACY
+ // [0] u16 TOSERVER_INIT_LEGACY
// [2] u8 SER_FMT_VER_HIGHEST_READ
// [3] u8[20] player_name
// [23] u8[28] password (new in some version)
// [51] u16 minimum supported network protocol version (added sometime)
// [53] u16 maximum supported network protocol version (added later than the previous one)
- SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2);
- writeU16(&data[0], TOSERVER_INIT);
- writeU8(&data[2], SER_FMT_VER_HIGHEST_READ);
-
- memset((char*)&data[3], 0, PLAYERNAME_SIZE);
- snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
- /*infostream<<"Client: sending initial password hash: \""<<m_password<<"\""
- <<std::endl;*/
+ char pName[PLAYERNAME_SIZE];
+ char pPassword[PASSWORD_SIZE];
+ memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
+ memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
- memset((char*)&data[23], 0, PASSWORD_SIZE);
- snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
+ std::string hashed_password = translatePassword(myplayer->getName(), m_password);
+ snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
+ snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());
- writeU16(&data[51], CLIENT_PROTOCOL_VERSION_MIN);
- writeU16(&data[53], CLIENT_PROTOCOL_VERSION_MAX);
-
- // Send as unreliable
- Send(1, data, false);
+ sendLegacyInit(pName, pPassword);
+ if (LATEST_PROTOCOL_VERSION >= 25)
+ sendInit(myplayer->getName());
}
// Not connected, return
@@ -532,29 +417,23 @@ void Client::step(float dtime)
Run Map's timers and unload unused data
*/
const float map_timer_and_unload_dtime = 5.25;
- if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
- {
+ if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) {
ScopeProfiler sp(g_profiler, "Client: map timer and unload");
- std::list<v3s16> deleted_blocks;
+ std::vector<v3s16> deleted_blocks;
m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
- g_settings->getFloat("client_unload_unused_data_timeout"),
- &deleted_blocks);
-
- /*if(deleted_blocks.size() > 0)
- infostream<<"Client: Unloaded "<<deleted_blocks.size()
- <<" unused blocks"<<std::endl;*/
+ g_settings->getFloat("client_unload_unused_data_timeout"),
+ g_settings->getS32("client_mapblock_limit"),
+ &deleted_blocks);
/*
Send info to server
NOTE: This loop is intentionally iterated the way it is.
*/
- std::list<v3s16>::iterator i = deleted_blocks.begin();
- std::list<v3s16> sendlist;
- for(;;)
- {
- if(sendlist.size() == 255 || i == deleted_blocks.end())
- {
+ std::vector<v3s16>::iterator i = deleted_blocks.begin();
+ std::vector<v3s16> sendlist;
+ for(;;) {
+ if(sendlist.size() == 255 || i == deleted_blocks.end()) {
if(sendlist.empty())
break;
/*
@@ -564,19 +443,8 @@ void Client::step(float dtime)
[3+6] v3s16 pos_1
...
*/
- u32 replysize = 2+1+6*sendlist.size();
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOSERVER_DELETEDBLOCKS);
- reply[2] = sendlist.size();
- u32 k = 0;
- for(std::list<v3s16>::iterator
- j = sendlist.begin();
- j != sendlist.end(); ++j)
- {
- writeV3S16(&reply[2+1+6*k], *j);
- k++;
- }
- m_con.Send(PEER_ID_SERVER, 2, reply, true);
+
+ sendDeletedBlocks(sendlist);
if(i == deleted_blocks.end())
break;
@@ -592,62 +460,52 @@ void Client::step(float dtime)
/*
Handle environment
*/
- {
- // Control local player (0ms)
- LocalPlayer *player = m_env.getLocalPlayer();
- assert(player != NULL);
- player->applyControl(dtime);
+ // Control local player (0ms)
+ LocalPlayer *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ player->applyControl(dtime);
- // Step environment
- m_env.step(dtime);
+ // Step environment
+ m_env.step(dtime);
- /*
- Get events
- */
- for(;;)
- {
- ClientEnvEvent event = m_env.getClientEvent();
- if(event.type == CEE_NONE)
- {
- break;
- }
- else if(event.type == CEE_PLAYER_DAMAGE)
- {
- if(m_ignore_damage_timer <= 0)
- {
- u8 damage = event.player_damage.amount;
-
- if(event.player_damage.send_to_server)
- sendDamage(damage);
-
- // Add to ClientEvent queue
- ClientEvent event;
- event.type = CE_PLAYER_DAMAGE;
- event.player_damage.amount = damage;
- m_client_event_queue.push_back(event);
- }
- }
- else if(event.type == CEE_PLAYER_BREATH)
- {
- u16 breath = event.player_breath.amount;
- sendBreath(breath);
+ /*
+ Get events
+ */
+ for(;;) {
+ ClientEnvEvent event = m_env.getClientEvent();
+ if(event.type == CEE_NONE) {
+ break;
+ }
+ else if(event.type == CEE_PLAYER_DAMAGE) {
+ if(m_ignore_damage_timer <= 0) {
+ u8 damage = event.player_damage.amount;
+
+ if(event.player_damage.send_to_server)
+ sendDamage(damage);
+
+ // Add to ClientEvent queue
+ ClientEvent event;
+ event.type = CE_PLAYER_DAMAGE;
+ event.player_damage.amount = damage;
+ m_client_event_queue.push(event);
}
}
+ else if(event.type == CEE_PLAYER_BREATH) {
+ u16 breath = event.player_breath.amount;
+ sendBreath(breath);
+ }
}
/*
Print some info
*/
- {
- float &counter = m_avg_rtt_timer;
- counter += dtime;
- if(counter >= 10)
- {
- counter = 0.0;
- // connectedAndInitialized() is true, peer exists.
- float avg_rtt = getRTT();
- infostream<<"Client: avg_rtt="<<avg_rtt<<std::endl;
- }
+ float &counter = m_avg_rtt_timer;
+ counter += dtime;
+ if(counter >= 10) {
+ counter = 0.0;
+ // connectedAndInitialized() is true, peer exists.
+ float avg_rtt = getRTT();
+ infostream << "Client: avg_rtt=" << avg_rtt << std::endl;
}
/*
@@ -668,48 +526,52 @@ void Client::step(float dtime)
*/
{
int num_processed_meshes = 0;
- while(!m_mesh_update_thread.m_queue_out.empty())
+ while (!m_mesh_update_thread.m_queue_out.empty())
{
num_processed_meshes++;
+
+ MinimapMapblock *minimap_mapblock = NULL;
+ bool do_mapper_update = true;
+
MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_frontNoEx();
MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
- if(block)
- {
+ if (block) {
// Delete the old mesh
- if(block->mesh != NULL)
- {
- // TODO: Remove hardware buffers of meshbuffers of block->mesh
+ if (block->mesh != NULL) {
delete block->mesh;
block->mesh = NULL;
}
- // Replace with the new mesh
- block->mesh = r.mesh;
+ if (r.mesh) {
+ minimap_mapblock = r.mesh->moveMinimapMapblock();
+ if (minimap_mapblock == NULL)
+ do_mapper_update = false;
+ }
+
+ if (r.mesh && r.mesh->getMesh()->getMeshBufferCount() == 0) {
+ delete r.mesh;
+ } else {
+ // Replace with the new mesh
+ block->mesh = r.mesh;
+ }
} else {
delete r.mesh;
}
- if(r.ack_block_to_server)
- {
+
+ if (do_mapper_update)
+ m_mapper->addBlock(r.p, minimap_mapblock);
+
+ if (r.ack_block_to_server) {
/*
Acknowledge block
+ [0] u8 count
+ [1] v3s16 pos_0
*/
- /*
- [0] u16 command
- [2] u8 count
- [3] v3s16 pos_0
- [3+6] v3s16 pos_1
- ...
- */
- u32 replysize = 2+1+6;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOSERVER_GOTBLOCKS);
- reply[2] = 1;
- writeV3S16(&reply[3], r.p);
- // Send as reliable
- m_con.Send(PEER_ID_SERVER, 2, reply, true);
+ sendGotBlocks(r.p);
}
}
- if(num_processed_meshes > 0)
+
+ if (num_processed_meshes > 0)
g_profiler->graphAdd("num_processed_meshes", num_processed_meshes);
}
@@ -771,42 +633,36 @@ void Client::step(float dtime)
Handle removed remotely initiated sounds
*/
m_removed_sounds_check_timer += dtime;
- if(m_removed_sounds_check_timer >= 2.32)
- {
+ if(m_removed_sounds_check_timer >= 2.32) {
m_removed_sounds_check_timer = 0;
// Find removed sounds and clear references to them
- std::set<s32> removed_server_ids;
+ std::vector<s32> removed_server_ids;
for(std::map<s32, int>::iterator
i = m_sounds_server_to_client.begin();
- i != m_sounds_server_to_client.end();)
- {
+ i != m_sounds_server_to_client.end();) {
s32 server_id = i->first;
int client_id = i->second;
i++;
- if(!m_sound->soundExists(client_id)){
+ if(!m_sound->soundExists(client_id)) {
m_sounds_server_to_client.erase(server_id);
m_sounds_client_to_server.erase(client_id);
m_sounds_to_objects.erase(client_id);
- removed_server_ids.insert(server_id);
+ removed_server_ids.push_back(server_id);
}
}
+
// Sync to server
- if(!removed_server_ids.empty())
- {
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOSERVER_REMOVED_SOUNDS);
- size_t server_ids = removed_server_ids.size();
- assert(server_ids <= 0xFFFF);
- writeU16(os, (u16) (server_ids & 0xFFFF));
- for(std::set<s32>::iterator i = removed_server_ids.begin();
- i != removed_server_ids.end(); i++)
- writeS32(os, *i);
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(1, data, true);
+ if(!removed_server_ids.empty()) {
+ sendRemovedSounds(removed_server_ids);
}
}
+
+ // Write server map
+ if (m_localdb && m_localdb_save_interval.step(dtime,
+ m_cache_save_interval)) {
+ m_localdb->endSave();
+ m_localdb->beginSave();
+ }
}
bool Client::loadMedia(const std::string &data, const std::string &filename)
@@ -833,7 +689,9 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
// Create an irrlicht memory file
io::IReadFile *rfile = irrfs->createMemoryReadFile(
*data_rw, data_rw.getSize(), "_tempreadfile");
- assert(rfile);
+
+ FATAL_ERROR_IF(!rfile, "Could not create irrlicht memory file.");
+
// Read image
video::IImage *img = vdrv->createImageFromFile(rfile);
if(!img){
@@ -888,14 +746,19 @@ bool Client::loadMedia(const std::string &data, const std::string &filename)
// Virtual methods from con::PeerHandler
void Client::peerAdded(con::Peer *peer)
{
- infostream<<"Client::peerAdded(): peer->id="
- <<peer->id<<std::endl;
+ infostream << "Client::peerAdded(): peer->id="
+ << peer->id << std::endl;
}
void Client::deletingPeer(con::Peer *peer, bool timeout)
{
- infostream<<"Client::deletingPeer(): "
+ infostream << "Client::deletingPeer(): "
"Server Peer is getting deleted "
- <<"(timeout="<<timeout<<")"<<std::endl;
+ << "(timeout=" << timeout << ")" << std::endl;
+
+ if (timeout) {
+ m_access_denied = true;
+ m_access_denied_reason = gettext("Connection timed out.");
+ }
}
/*
@@ -906,73 +769,55 @@ void Client::deletingPeer(con::Peer *peer, bool timeout)
string name
}
*/
-void Client::request_media(const std::list<std::string> &file_requests)
+void Client::request_media(const std::vector<std::string> &file_requests)
{
std::ostringstream os(std::ios_base::binary);
writeU16(os, TOSERVER_REQUEST_MEDIA);
size_t file_requests_size = file_requests.size();
- assert(file_requests_size <= 0xFFFF);
- writeU16(os, (u16) (file_requests_size & 0xFFFF));
- for(std::list<std::string>::const_iterator i = file_requests.begin();
+ FATAL_ERROR_IF(file_requests_size > 0xFFFF, "Unsupported number of file requests");
+
+ // Packet dynamicly resized
+ NetworkPacket pkt(TOSERVER_REQUEST_MEDIA, 2 + 0);
+
+ pkt << (u16) (file_requests_size & 0xFFFF);
+
+ for(std::vector<std::string>::const_iterator i = file_requests.begin();
i != file_requests.end(); ++i) {
- os<<serializeString(*i);
+ pkt << (*i);
}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(1, data, true);
- infostream<<"Client: Sending media request list to server ("
- <<file_requests.size()<<" files)"<<std::endl;
+ Send(&pkt);
+
+ infostream << "Client: Sending media request list to server ("
+ << file_requests.size() << " files. packet size)" << std::endl;
}
void Client::received_media()
{
- // notify server we received everything
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOSERVER_RECEIVED_MEDIA);
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(1, data, true);
- infostream<<"Client: Notifying server that we received all media"
- <<std::endl;
+ NetworkPacket pkt(TOSERVER_RECEIVED_MEDIA, 0);
+ Send(&pkt);
+ infostream << "Client: Notifying server that we received all media"
+ << std::endl;
}
void Client::initLocalMapSaving(const Address &address,
const std::string &hostname,
bool is_local_server)
{
- localdb = NULL;
-
- if (!g_settings->getBool("enable_local_map_saving") || is_local_server)
+ if (!g_settings->getBool("enable_local_map_saving") || is_local_server) {
return;
+ }
const std::string world_path = porting::path_user
+ DIR_DELIM + "worlds"
+ DIR_DELIM + "server_"
+ hostname + "_" + to_string(address.getPort());
- SubgameSpec gamespec;
-
- if (!getWorldExists(world_path)) {
- gamespec = findSubgame(g_settings->get("default_game"));
- if (!gamespec.isValid())
- gamespec = findSubgame("minimal");
- } else {
- gamespec = findWorldSubgame(world_path);
- }
+ fs::CreateAllDirs(world_path);
- if (!gamespec.isValid()) {
- errorstream << "Couldn't find subgame for local map saving." << std::endl;
- return;
- }
-
- localserver = new Server(world_path, gamespec, false, false);
- localdb = new Database_SQLite3(&(ServerMap&)localserver->getMap(), world_path);
- localdb->beginSave();
+ m_localdb = new Database_SQLite3(world_path);
+ m_localdb->beginSave();
actionstream << "Local map saving started, map will be saved at '" << world_path << "'" << std::endl;
}
@@ -987,16 +832,14 @@ void Client::ReceiveAll()
if(porting::getTimeMs() > start_ms + 100)
break;
- try{
+ try {
Receive();
g_profiler->graphAdd("client_received_packets", 1);
}
- catch(con::NoIncomingDataException &e)
- {
+ catch(con::NoIncomingDataException &e) {
break;
}
- catch(con::InvalidIncomingDataException &e)
- {
+ catch(con::InvalidIncomingDataException &e) {
infostream<<"Client::ReceiveAll(): "
"InvalidIncomingDataException: what()="
<<e.what()<<std::endl;
@@ -1007,27 +850,26 @@ void Client::ReceiveAll()
void Client::Receive()
{
DSTACK(__FUNCTION_NAME);
- SharedBuffer<u8> data;
- u16 sender_peer_id;
- u32 datasize = m_con.Receive(sender_peer_id, data);
- ProcessData(*data, datasize, sender_peer_id);
+ NetworkPacket pkt;
+ m_con.Receive(&pkt);
+ ProcessData(&pkt);
+}
+
+inline void Client::handleCommand(NetworkPacket* pkt)
+{
+ const ToClientCommandHandler& opHandle = toClientCommandTable[pkt->getCommand()];
+ (this->*opHandle.handler)(pkt);
}
/*
sender_peer_id given to this shall be quaranteed to be a valid peer
*/
-void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
+void Client::ProcessData(NetworkPacket *pkt)
{
DSTACK(__FUNCTION_NAME);
- // Ignore packets that don't even fit a command
- if(datasize < 2)
- {
- m_packetcounter.add(60000);
- return;
- }
-
- ToClientCommand command = (ToClientCommand)readU16(&data[0]);
+ ToClientCommand command = (ToClientCommand) pkt->getCommand();
+ u32 sender_peer_id = pkt->getPeerId();
//infostream<<"Client: received command="<<command<<std::endl;
m_packetcounter.add((u16)command);
@@ -1036,1231 +878,374 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
If this check is removed, be sure to change the queue
system to know the ids
*/
- if(sender_peer_id != PEER_ID_SERVER)
- {
- infostream<<"Client::ProcessData(): Discarding data not "
- "coming from server: peer_id="<<sender_peer_id
- <<std::endl;
+ if(sender_peer_id != PEER_ID_SERVER) {
+ infostream << "Client::ProcessData(): Discarding data not "
+ "coming from server: peer_id=" << sender_peer_id
+ << std::endl;
return;
}
- u8 ser_version = m_server_ser_ver;
-
- if(command == TOCLIENT_INIT)
- {
- if(datasize < 3)
- return;
-
- u8 deployed = data[2];
-
- infostream<<"Client: TOCLIENT_INIT received with "
- "deployed="<<((int)deployed&0xff)<<std::endl;
-
- if(!ser_ver_supported(deployed))
- {
- infostream<<"Client: TOCLIENT_INIT: Server sent "
- <<"unsupported ser_fmt_ver"<<std::endl;
- return;
- }
-
- m_server_ser_ver = deployed;
-
- // Get player position
- v3s16 playerpos_s16(0, BS*2+BS*20, 0);
- if(datasize >= 2+1+6)
- playerpos_s16 = readV3S16(&data[2+1]);
- v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS/2, 0);
-
-
- // Set player position
- Player *player = m_env.getLocalPlayer();
- assert(player != NULL);
- player->setPosition(playerpos_f);
-
- if(datasize >= 2+1+6+8)
- {
- // Get map seed
- m_map_seed = readU64(&data[2+1+6]);
- infostream<<"Client: received map seed: "<<m_map_seed<<std::endl;
- }
-
- if(datasize >= 2+1+6+8+4)
- {
- // Get map seed
- m_recommended_send_interval = readF1000(&data[2+1+6+8]);
- infostream<<"Client: received recommended send interval "
- <<m_recommended_send_interval<<std::endl;
- }
-
- // Reply to server
- u32 replysize = 2;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOSERVER_INIT2);
- // Send as reliable
- m_con.Send(PEER_ID_SERVER, 1, reply, true);
-
- m_state = LC_Init;
-
+ // Command must be handled into ToClientCommandHandler
+ if (command >= TOCLIENT_NUM_MSG_TYPES) {
+ infostream << "Client: Ignoring unknown command "
+ << command << std::endl;
return;
}
- if(command == TOCLIENT_ACCESS_DENIED)
- {
- // The server didn't like our password. Note, this needs
- // to be processed even if the serialisation format has
- // not been agreed yet, the same as TOCLIENT_INIT.
- m_access_denied = true;
- m_access_denied_reason = L"Unknown";
- if(datasize >= 4)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- m_access_denied_reason = deSerializeWideString(is);
- }
+ /*
+ * Those packets are handled before m_server_ser_ver is set, it's normal
+ * But we must use the new ToClientConnectionState in the future,
+ * as a byte mask
+ */
+ if(toClientCommandTable[command].state == TOCLIENT_STATE_NOT_CONNECTED) {
+ handleCommand(pkt);
return;
}
- if(ser_version == SER_FMT_VER_INVALID)
- {
- infostream<<"Client: Server serialization"
+ if(m_server_ser_ver == SER_FMT_VER_INVALID) {
+ infostream << "Client: Server serialization"
" format invalid or not initialized."
- " Skipping incoming command="<<command<<std::endl;
+ " Skipping incoming command=" << command << std::endl;
return;
}
/*
Handle runtime commands
*/
- // there's no sane reason why we shouldn't have a player and
- // almost everyone needs a player reference
- Player *player = m_env.getLocalPlayer();
- assert(player != NULL);
-
- if(command == TOCLIENT_REMOVENODE)
- {
- if(datasize < 8)
- return;
- v3s16 p;
- p.X = readS16(&data[2]);
- p.Y = readS16(&data[4]);
- p.Z = readS16(&data[6]);
- removeNode(p);
- }
- else if(command == TOCLIENT_ADDNODE)
- {
- if(datasize < 8 + MapNode::serializedLength(ser_version))
- return;
-
- v3s16 p;
- p.X = readS16(&data[2]);
- p.Y = readS16(&data[4]);
- p.Z = readS16(&data[6]);
-
- MapNode n;
- n.deSerialize(&data[8], ser_version);
-
- bool remove_metadata = true;
- u32 index = 8 + MapNode::serializedLength(ser_version);
- if ((datasize >= index+1) && data[index]){
- remove_metadata = false;
- }
-
- addNode(p, n, remove_metadata);
- }
- else if(command == TOCLIENT_BLOCKDATA)
- {
- // Ignore too small packet
- if(datasize < 8)
- return;
-
- v3s16 p;
- p.X = readS16(&data[2]);
- p.Y = readS16(&data[4]);
- p.Z = readS16(&data[6]);
-
- std::string datastring((char*)&data[8], datasize-8);
- std::istringstream istr(datastring, std::ios_base::binary);
-
- MapSector *sector;
- MapBlock *block;
-
- v2s16 p2d(p.X, p.Z);
- sector = m_env.getMap().emergeSector(p2d);
-
- assert(sector->getPos() == p2d);
-
- block = sector->getBlockNoCreateNoEx(p.Y);
- if(block)
- {
- /*
- Update an existing block
- */
- block->deSerialize(istr, ser_version, false);
- block->deSerializeNetworkSpecific(istr);
- }
- else
- {
- /*
- Create a new block
- */
- block = new MapBlock(&m_env.getMap(), p, this);
- block->deSerialize(istr, ser_version, false);
- block->deSerializeNetworkSpecific(istr);
- sector->insertBlock(block);
- }
-
- if (localdb != NULL) {
- ((ServerMap&) localserver->getMap()).saveBlock(block, localdb);
- }
-
- /*
- Add it to mesh update queue and set it to be acknowledged after update.
- */
- addUpdateMeshTaskWithEdge(p, true);
- }
- else if(command == TOCLIENT_INVENTORY)
- {
- if(datasize < 3)
- return;
-
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- player->inventory.deSerialize(is);
-
- m_inventory_updated = true;
-
- delete m_inventory_from_server;
- m_inventory_from_server = new Inventory(player->inventory);
- m_inventory_from_server_age = 0.0;
-
- }
- else if(command == TOCLIENT_TIME_OF_DAY)
- {
- if(datasize < 4)
- return;
-
- u16 time_of_day = readU16(&data[2]);
- time_of_day = time_of_day % 24000;
- float time_speed = 0;
-
- if(datasize >= 2 + 2 + 4)
- {
- time_speed = readF1000(&data[4]);
- }
- else {
- // Old message; try to approximate speed of time by ourselves
- float time_of_day_f = (float)time_of_day / 24000.0;
- float tod_diff_f = 0;
-
- if(time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
- tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
- else
- tod_diff_f = time_of_day_f - m_last_time_of_day_f;
-
- m_last_time_of_day_f = time_of_day_f;
- float time_diff = m_time_of_day_update_timer;
- m_time_of_day_update_timer = 0;
-
- if(m_time_of_day_set){
- time_speed = (3600.0*24.0) * tod_diff_f / time_diff;
- infostream<<"Client: Measured time_of_day speed (old format): "
- <<time_speed<<" tod_diff_f="<<tod_diff_f
- <<" time_diff="<<time_diff<<std::endl;
- }
- }
-
- // Update environment
- m_env.setTimeOfDay(time_of_day);
- m_env.setTimeOfDaySpeed(time_speed);
- m_time_of_day_set = true;
-
- u32 dr = m_env.getDayNightRatio();
- infostream<<"Client: time_of_day="<<time_of_day
- <<" time_speed="<<time_speed
- <<" dr="<<dr<<std::endl;
- }
- else if(command == TOCLIENT_CHAT_MESSAGE)
- {
- /*
- u16 command
- u16 length
- wstring message
- */
- u8 buf[6];
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- // Read stuff
- is.read((char*) buf, 2);
- u16 len = readU16(buf);
-
- std::wstring message;
- for(unsigned int i=0; i<len; i++)
- {
- is.read((char*)buf, 2);
- message += (wchar_t)readU16(buf);
- }
-
- m_chat_queue.push_back(message);
- }
- else if(command == TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD)
- {
- /*
- u16 command
- u16 count of removed objects
- for all removed objects {
- u16 id
- }
- u16 count of added objects
- for all added objects {
- u16 id
- u8 type
- u32 initialization data length
- string initialization data
- }
- */
-
- char buf[6];
- // Get all data except the command number
- std::string datastring((char*)&data[2], datasize-2);
- // Throw them in an istringstream
- std::istringstream is(datastring, std::ios_base::binary);
-
- // Read removed objects
- is.read(buf, 2);
- u16 removed_count = readU16((u8*)buf);
- for(unsigned int i=0; i<removed_count; i++)
- {
- is.read(buf, 2);
- u16 id = readU16((u8*)buf);
- m_env.removeActiveObject(id);
- }
-
- // Read added objects
- is.read(buf, 2);
- u16 added_count = readU16((u8*)buf);
- for(unsigned int i=0; i<added_count; i++)
- {
- is.read(buf, 2);
- u16 id = readU16((u8*)buf);
- is.read(buf, 1);
- u8 type = readU8((u8*)buf);
- std::string data = deSerializeLongString(is);
- // Add it
- m_env.addActiveObject(id, type, data);
- }
- }
- else if(command == TOCLIENT_ACTIVE_OBJECT_MESSAGES)
- {
- /*
- u16 command
- for all objects
- {
- u16 id
- u16 message length
- string message
- }
- */
- char buf[6];
- // Get all data except the command number
- std::string datastring((char*)&data[2], datasize-2);
- // Throw them in an istringstream
- std::istringstream is(datastring, std::ios_base::binary);
-
- while(is.eof() == false)
- {
- is.read(buf, 2);
- u16 id = readU16((u8*)buf);
- if(is.eof())
- break;
- is.read(buf, 2);
- size_t message_size = readU16((u8*)buf);
- std::string message;
- message.reserve(message_size);
- for(unsigned int i=0; i<message_size; i++)
- {
- is.read(buf, 1);
- message.append(buf, 1);
- }
- // Pass on to the environment
- m_env.processActiveObjectMessage(id, message);
- }
- }
- else if(command == TOCLIENT_MOVEMENT)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- player->movement_acceleration_default = readF1000(is) * BS;
- player->movement_acceleration_air = readF1000(is) * BS;
- player->movement_acceleration_fast = readF1000(is) * BS;
- player->movement_speed_walk = readF1000(is) * BS;
- player->movement_speed_crouch = readF1000(is) * BS;
- player->movement_speed_fast = readF1000(is) * BS;
- player->movement_speed_climb = readF1000(is) * BS;
- player->movement_speed_jump = readF1000(is) * BS;
- player->movement_liquid_fluidity = readF1000(is) * BS;
- player->movement_liquid_fluidity_smooth = readF1000(is) * BS;
- player->movement_liquid_sink = readF1000(is) * BS;
- player->movement_gravity = readF1000(is) * BS;
- }
- else if(command == TOCLIENT_HP)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- u8 oldhp = player->hp;
- u8 hp = readU8(is);
- player->hp = hp;
-
- if(hp < oldhp)
- {
- // Add to ClientEvent queue
- ClientEvent event;
- event.type = CE_PLAYER_DAMAGE;
- event.player_damage.amount = oldhp - hp;
- m_client_event_queue.push_back(event);
- }
- }
- else if(command == TOCLIENT_BREATH)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- player->setBreath(readU16(is));
- }
- else if(command == TOCLIENT_MOVE_PLAYER)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- v3f pos = readV3F1000(is);
- f32 pitch = readF1000(is);
- f32 yaw = readF1000(is);
- player->setPosition(pos);
-
- infostream<<"Client got TOCLIENT_MOVE_PLAYER"
- <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
- <<" pitch="<<pitch
- <<" yaw="<<yaw
- <<std::endl;
-
- /*
- Add to ClientEvent queue.
- This has to be sent to the main program because otherwise
- it would just force the pitch and yaw values to whatever
- the camera points to.
- */
- ClientEvent event;
- event.type = CE_PLAYER_FORCE_MOVE;
- event.player_force_move.pitch = pitch;
- event.player_force_move.yaw = yaw;
- m_client_event_queue.push_back(event);
-
- // Ignore damage for a few seconds, so that the player doesn't
- // get damage from falling on ground
- m_ignore_damage_timer = 3.0;
- }
- else if(command == TOCLIENT_PLAYERITEM)
- {
- infostream<<"Client: WARNING: Ignoring TOCLIENT_PLAYERITEM"<<std::endl;
- }
- else if(command == TOCLIENT_DEATHSCREEN)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
+ handleCommand(pkt);
+}
- bool set_camera_point_target = readU8(is);
- v3f camera_point_target = readV3F1000(is);
+void Client::Send(NetworkPacket* pkt)
+{
+ m_con.Send(PEER_ID_SERVER,
+ serverCommandFactoryTable[pkt->getCommand()].channel,
+ pkt,
+ serverCommandFactoryTable[pkt->getCommand()].reliable);
+}
- ClientEvent event;
- event.type = CE_DEATHSCREEN;
- event.deathscreen.set_camera_point_target = set_camera_point_target;
- event.deathscreen.camera_point_target_x = camera_point_target.X;
- event.deathscreen.camera_point_target_y = camera_point_target.Y;
- event.deathscreen.camera_point_target_z = camera_point_target.Z;
- m_client_event_queue.push_back(event);
+void Client::interact(u8 action, const PointedThing& pointed)
+{
+ if(m_state != LC_Ready) {
+ errorstream << "Client::interact() "
+ "Canceled (not connected)"
+ << std::endl;
+ return;
}
- else if(command == TOCLIENT_ANNOUNCE_MEDIA)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- int num_files = readU16(is);
-
- infostream<<"Client: Received media announcement: packet size: "
- <<datasize<<std::endl;
-
- if (m_media_downloader == NULL ||
- m_media_downloader->isStarted()) {
- const char *problem = m_media_downloader ?
- "we already saw another announcement" :
- "all media has been received already";
- errorstream<<"Client: Received media announcement but "
- <<problem<<"! "
- <<" files="<<num_files
- <<" size="<<datasize<<std::endl;
- return;
- }
- // Mesh update thread must be stopped while
- // updating content definitions
- assert(!m_mesh_update_thread.IsRunning());
+ /*
+ [0] u16 command
+ [2] u8 action
+ [3] u16 item
+ [5] u32 length of the next item
+ [9] serialized PointedThing
+ actions:
+ 0: start digging (from undersurface) or use
+ 1: stop digging (all parameters ignored)
+ 2: digging completed
+ 3: place block or item (to abovesurface)
+ 4: use item
+ */
- for(int i=0; i<num_files; i++)
- {
- std::string name = deSerializeString(is);
- std::string sha1_base64 = deSerializeString(is);
- std::string sha1_raw = base64_decode(sha1_base64);
- m_media_downloader->addFile(name, sha1_raw);
- }
+ NetworkPacket pkt(TOSERVER_INTERACT, 1 + 2 + 0);
- std::vector<std::string> remote_media;
- try {
- Strfnd sf(deSerializeString(is));
- while(!sf.atend()) {
- std::string baseurl = trim(sf.next(","));
- if(baseurl != "")
- m_media_downloader->addRemoteServer(baseurl);
- }
- }
- catch(SerializationError& e) {
- // not supported by server or turned off
- }
+ pkt << action;
+ pkt << (u16)getPlayerItem();
- m_media_downloader->step(this);
- }
- else if(command == TOCLIENT_MEDIA)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
+ std::ostringstream tmp_os(std::ios::binary);
+ pointed.serialize(tmp_os);
- /*
- u16 command
- u16 total number of file bunches
- u16 index of this bunch
- u32 number of files in this bunch
- for each file {
- u16 length of name
- string name
- u32 length of data
- data
- }
- */
- int num_bunches = readU16(is);
- int bunch_i = readU16(is);
- u32 num_files = readU32(is);
- infostream<<"Client: Received files: bunch "<<bunch_i<<"/"
- <<num_bunches<<" files="<<num_files
- <<" size="<<datasize<<std::endl;
-
- if (num_files == 0)
- return;
+ pkt.putLongString(tmp_os.str());
- if (m_media_downloader == NULL ||
- !m_media_downloader->isStarted()) {
- const char *problem = m_media_downloader ?
- "media has not been requested" :
- "all media has been received already";
- errorstream<<"Client: Received media but "
- <<problem<<"! "
- <<" bunch "<<bunch_i<<"/"<<num_bunches
- <<" files="<<num_files
- <<" size="<<datasize<<std::endl;
- return;
- }
+ Send(&pkt);
+}
- // Mesh update thread must be stopped while
- // updating content definitions
- assert(!m_mesh_update_thread.IsRunning());
+void Client::deleteAuthData()
+{
+ if (!m_auth_data)
+ return;
- for(unsigned int i=0; i<num_files; i++){
- std::string name = deSerializeString(is);
- std::string data = deSerializeLongString(is);
- m_media_downloader->conventionalTransferDone(
- name, data, this);
- }
- }
- else if(command == TOCLIENT_TOOLDEF)
- {
- infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<<std::endl;
- }
- else if(command == TOCLIENT_NODEDEF)
- {
- infostream<<"Client: Received node definitions: packet size: "
- <<datasize<<std::endl;
-
- // Mesh update thread must be stopped while
- // updating content definitions
- assert(!m_mesh_update_thread.IsRunning());
-
- // Decompress node definitions
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
- std::ostringstream tmp_os;
- decompressZlib(tmp_is, tmp_os);
-
- // Deserialize node definitions
- std::istringstream tmp_is2(tmp_os.str());
- m_nodedef->deSerialize(tmp_is2);
- m_nodedef_received = true;
- }
- else if(command == TOCLIENT_CRAFTITEMDEF)
- {
- infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<<std::endl;
- }
- else if(command == TOCLIENT_ITEMDEF)
- {
- infostream<<"Client: Received item definitions: packet size: "
- <<datasize<<std::endl;
-
- // Mesh update thread must be stopped while
- // updating content definitions
- assert(!m_mesh_update_thread.IsRunning());
-
- // Decompress item definitions
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
- std::ostringstream tmp_os;
- decompressZlib(tmp_is, tmp_os);
-
- // Deserialize node definitions
- std::istringstream tmp_is2(tmp_os.str());
- m_itemdef->deSerialize(tmp_is2);
- m_itemdef_received = true;
- }
- else if(command == TOCLIENT_PLAY_SOUND)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- s32 server_id = readS32(is);
- std::string name = deSerializeString(is);
- float gain = readF1000(is);
- int type = readU8(is); // 0=local, 1=positional, 2=object
- v3f pos = readV3F1000(is);
- u16 object_id = readU16(is);
- bool loop = readU8(is);
- // Start playing
- int client_id = -1;
- switch(type){
- case 0: // local
- client_id = m_sound->playSound(name, loop, gain);
+ switch (m_chosen_auth_mech) {
+ case AUTH_MECHANISM_FIRST_SRP:
break;
- case 1: // positional
- client_id = m_sound->playSoundAt(name, loop, gain, pos);
+ case AUTH_MECHANISM_SRP:
+ case AUTH_MECHANISM_LEGACY_PASSWORD:
+ srp_user_delete((SRPUser *) m_auth_data);
+ m_auth_data = NULL;
break;
- case 2: { // object
- ClientActiveObject *cao = m_env.getActiveObject(object_id);
- if(cao)
- pos = cao->getPosition();
- client_id = m_sound->playSoundAt(name, loop, gain, pos);
- // TODO: Set up sound to move with object
- break; }
- default:
+ case AUTH_MECHANISM_NONE:
break;
- }
- if(client_id != -1){
- m_sounds_server_to_client[server_id] = client_id;
- m_sounds_client_to_server[client_id] = server_id;
- if(object_id != 0)
- m_sounds_to_objects[client_id] = object_id;
- }
- }
- else if(command == TOCLIENT_STOP_SOUND)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- s32 server_id = readS32(is);
- std::map<s32, int>::iterator i =
- m_sounds_server_to_client.find(server_id);
- if(i != m_sounds_server_to_client.end()){
- int client_id = i->second;
- m_sound->stopSound(client_id);
- }
}
- else if(command == TOCLIENT_PRIVILEGES)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- m_privileges.clear();
- infostream<<"Client: Privileges updated: ";
- u16 num_privileges = readU16(is);
- for(unsigned int i=0; i<num_privileges; i++){
- std::string priv = deSerializeString(is);
- m_privileges.insert(priv);
- infostream<<priv<<" ";
- }
- infostream<<std::endl;
- }
- else if(command == TOCLIENT_INVENTORY_FORMSPEC)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- // Store formspec in LocalPlayer
- player->inventory_formspec = deSerializeLongString(is);
- }
- else if(command == TOCLIENT_DETACHED_INVENTORY)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- std::string name = deSerializeString(is);
+ m_chosen_auth_mech = AUTH_MECHANISM_NONE;
+}
- infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
- Inventory *inv = NULL;
- if(m_detached_inventories.count(name) > 0)
- inv = m_detached_inventories[name];
- else{
- inv = new Inventory(m_itemdef);
- m_detached_inventories[name] = inv;
- }
- inv->deSerialize(is);
- }
- else if(command == TOCLIENT_SHOW_FORMSPEC)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
+AuthMechanism Client::choseAuthMech(const u32 mechs)
+{
+ if (mechs & AUTH_MECHANISM_SRP)
+ return AUTH_MECHANISM_SRP;
- std::string formspec = deSerializeLongString(is);
- std::string formname = deSerializeString(is);
+ if (mechs & AUTH_MECHANISM_FIRST_SRP)
+ return AUTH_MECHANISM_FIRST_SRP;
- ClientEvent event;
- event.type = CE_SHOW_FORMSPEC;
- // pointer is required as event is a struct only!
- // adding a std:string to a struct isn't possible
- event.show_formspec.formspec = new std::string(formspec);
- event.show_formspec.formname = new std::string(formname);
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_SPAWN_PARTICLE)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- v3f pos = readV3F1000(is);
- v3f vel = readV3F1000(is);
- v3f acc = readV3F1000(is);
- float expirationtime = readF1000(is);
- float size = readF1000(is);
- bool collisiondetection = readU8(is);
- std::string texture = deSerializeLongString(is);
- bool vertical = false;
- try {
- vertical = readU8(is);
- } catch (...) {}
-
- ClientEvent event;
- event.type = CE_SPAWN_PARTICLE;
- event.spawn_particle.pos = new v3f (pos);
- event.spawn_particle.vel = new v3f (vel);
- event.spawn_particle.acc = new v3f (acc);
- event.spawn_particle.expirationtime = expirationtime;
- event.spawn_particle.size = size;
- event.spawn_particle.collisiondetection = collisiondetection;
- event.spawn_particle.vertical = vertical;
- event.spawn_particle.texture = new std::string(texture);
-
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_ADD_PARTICLESPAWNER)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- u16 amount = readU16(is);
- float spawntime = readF1000(is);
- v3f minpos = readV3F1000(is);
- v3f maxpos = readV3F1000(is);
- v3f minvel = readV3F1000(is);
- v3f maxvel = readV3F1000(is);
- v3f minacc = readV3F1000(is);
- v3f maxacc = readV3F1000(is);
- float minexptime = readF1000(is);
- float maxexptime = readF1000(is);
- float minsize = readF1000(is);
- float maxsize = readF1000(is);
- bool collisiondetection = readU8(is);
- std::string texture = deSerializeLongString(is);
- u32 id = readU32(is);
- bool vertical = false;
- try {
- vertical = readU8(is);
- } catch (...) {}
-
- ClientEvent event;
- event.type = CE_ADD_PARTICLESPAWNER;
- event.add_particlespawner.amount = amount;
- event.add_particlespawner.spawntime = spawntime;
- event.add_particlespawner.minpos = new v3f (minpos);
- event.add_particlespawner.maxpos = new v3f (maxpos);
- event.add_particlespawner.minvel = new v3f (minvel);
- event.add_particlespawner.maxvel = new v3f (maxvel);
- event.add_particlespawner.minacc = new v3f (minacc);
- event.add_particlespawner.maxacc = new v3f (maxacc);
- event.add_particlespawner.minexptime = minexptime;
- event.add_particlespawner.maxexptime = maxexptime;
- event.add_particlespawner.minsize = minsize;
- event.add_particlespawner.maxsize = maxsize;
- event.add_particlespawner.collisiondetection = collisiondetection;
- event.add_particlespawner.vertical = vertical;
- event.add_particlespawner.texture = new std::string(texture);
- event.add_particlespawner.id = id;
-
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_DELETE_PARTICLESPAWNER)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
+ if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD)
+ return AUTH_MECHANISM_LEGACY_PASSWORD;
- u32 id = readU16(is);
+ return AUTH_MECHANISM_NONE;
+}
- ClientEvent event;
- event.type = CE_DELETE_PARTICLESPAWNER;
- event.delete_particlespawner.id = id;
+void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
+{
+ NetworkPacket pkt(TOSERVER_INIT_LEGACY,
+ 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2);
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_HUDADD)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- u32 id = readU32(is);
- u8 type = readU8(is);
- v2f pos = readV2F1000(is);
- std::string name = deSerializeString(is);
- v2f scale = readV2F1000(is);
- std::string text = deSerializeString(is);
- u32 number = readU32(is);
- u32 item = readU32(is);
- u32 dir = readU32(is);
- v2f align = readV2F1000(is);
- v2f offset = readV2F1000(is);
- v3f world_pos;
- v2s32 size;
- try{
- world_pos = readV3F1000(is);
- }catch(SerializationError &e) {};
- try{
- size = readV2S32(is);
- } catch(SerializationError &e) {};
-
- ClientEvent event;
- event.type = CE_HUDADD;
- event.hudadd.id = id;
- event.hudadd.type = type;
- event.hudadd.pos = new v2f(pos);
- event.hudadd.name = new std::string(name);
- event.hudadd.scale = new v2f(scale);
- event.hudadd.text = new std::string(text);
- event.hudadd.number = number;
- event.hudadd.item = item;
- event.hudadd.dir = dir;
- event.hudadd.align = new v2f(align);
- event.hudadd.offset = new v2f(offset);
- event.hudadd.world_pos = new v3f(world_pos);
- event.hudadd.size = new v2s32(size);
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_HUDRM)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
+ pkt << (u8) SER_FMT_VER_HIGHEST_READ;
+ pkt.putRawString(playerName,PLAYERNAME_SIZE);
+ pkt.putRawString(playerPassword, PASSWORD_SIZE);
+ pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
- u32 id = readU32(is);
+ Send(&pkt);
+}
- ClientEvent event;
- event.type = CE_HUDRM;
- event.hudrm.id = id;
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_HUDCHANGE)
- {
- std::string sdata;
- v2f v2fdata;
- v3f v3fdata;
- u32 intdata = 0;
- v2s32 v2s32data;
-
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- u32 id = readU32(is);
- u8 stat = (HudElementStat)readU8(is);
-
- if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
- stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
- v2fdata = readV2F1000(is);
- else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
- sdata = deSerializeString(is);
- else if (stat == HUD_STAT_WORLD_POS)
- v3fdata = readV3F1000(is);
- else if (stat == HUD_STAT_SIZE )
- v2s32data = readV2S32(is);
- else
- intdata = readU32(is);
-
- ClientEvent event;
- event.type = CE_HUDCHANGE;
- event.hudchange.id = id;
- event.hudchange.stat = (HudElementStat)stat;
- event.hudchange.v2fdata = new v2f(v2fdata);
- event.hudchange.v3fdata = new v3f(v3fdata);
- event.hudchange.sdata = new std::string(sdata);
- event.hudchange.data = intdata;
- event.hudchange.v2s32data = new v2s32(v2s32data);
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_HUD_SET_FLAGS)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
+void Client::sendInit(const std::string &playerName)
+{
+ NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()));
- u32 flags = readU32(is);
- u32 mask = readU32(is);
+ // we don't support network compression yet
+ u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE;
+ pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes;
+ pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
+ pkt << playerName;
- player->hud_flags &= ~mask;
- player->hud_flags |= flags;
- }
- else if(command == TOCLIENT_HUD_SET_PARAM)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
+ Send(&pkt);
+}
- u16 param = readU16(is);
- std::string value = deSerializeString(is);
+void Client::startAuth(AuthMechanism chosen_auth_mechanism)
+{
+ m_chosen_auth_mech = chosen_auth_mechanism;
- if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
- s32 hotbar_itemcount = readS32((u8*) value.c_str());
- if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
- player->hud_hotbar_itemcount = hotbar_itemcount;
- }
- else if (param == HUD_PARAM_HOTBAR_IMAGE) {
- ((LocalPlayer *) player)->hotbar_image = value;
- }
- else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
- ((LocalPlayer *) player)->hotbar_selected_image = value;
+ switch (chosen_auth_mechanism) {
+ case AUTH_MECHANISM_FIRST_SRP: {
+ // send srp verifier to server
+ NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
+ char *salt, *bytes_v;
+ std::size_t len_salt, len_v;
+ salt = NULL;
+ getSRPVerifier(getPlayerName(), m_password,
+ &salt, &len_salt, &bytes_v, &len_v);
+ resp_pkt
+ << std::string((char*)salt, len_salt)
+ << std::string((char*)bytes_v, len_v)
+ << (u8)((m_password == "") ? 1 : 0);
+ free(salt);
+ free(bytes_v);
+ Send(&resp_pkt);
+ break;
}
- }
- else if(command == TOCLIENT_SET_SKY)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- video::SColor *bgcolor = new video::SColor(readARGB8(is));
- std::string *type = new std::string(deSerializeString(is));
- u16 count = readU16(is);
- std::vector<std::string> *params = new std::vector<std::string>;
+ case AUTH_MECHANISM_SRP:
+ case AUTH_MECHANISM_LEGACY_PASSWORD: {
+ u8 based_on = 1;
- for(size_t i=0; i<count; i++)
- params->push_back(deSerializeString(is));
+ if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
+ m_password = translatePassword(getPlayerName(), m_password);
+ based_on = 0;
+ }
- ClientEvent event;
- event.type = CE_SET_SKY;
- event.set_sky.bgcolor = bgcolor;
- event.set_sky.type = type;
- event.set_sky.params = params;
- m_client_event_queue.push_back(event);
+ std::string playername_u = lowercase(getPlayerName());
+ m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
+ getPlayerName().c_str(), playername_u.c_str(),
+ (const unsigned char *) m_password.c_str(),
+ m_password.length(), NULL, NULL);
+ char *bytes_A = 0;
+ size_t len_A = 0;
+ srp_user_start_authentication((struct SRPUser *) m_auth_data,
+ NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A);
+
+ NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
+ resp_pkt << std::string(bytes_A, len_A) << based_on;
+ Send(&resp_pkt);
+ break;
+ }
+ case AUTH_MECHANISM_NONE:
+ break; // not handled in this method
}
- else if(command == TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- bool do_override = readU8(is);
- float day_night_ratio_f = (float)readU16(is) / 65536;
+}
- ClientEvent event;
- event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
- event.override_day_night_ratio.do_override = do_override;
- event.override_day_night_ratio.ratio_f = day_night_ratio_f;
- m_client_event_queue.push_back(event);
- }
- else if(command == TOCLIENT_LOCAL_PLAYER_ANIMATIONS)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
+void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
+{
+ NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
- LocalPlayer *player = m_env.getLocalPlayer();
- assert(player != NULL);
+ pkt << (u8) blocks.size();
- player->local_animations[0] = readV2S32(is);
- player->local_animations[1] = readV2S32(is);
- player->local_animations[2] = readV2S32(is);
- player->local_animations[3] = readV2S32(is);
- player->local_animation_speed = readF1000(is);
+ u32 k = 0;
+ for(std::vector<v3s16>::iterator
+ j = blocks.begin();
+ j != blocks.end(); ++j) {
+ pkt << *j;
+ k++;
}
- else if(command == TOCLIENT_EYE_OFFSET)
- {
- std::string datastring((char *)&data[2], datasize - 2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- LocalPlayer *player = m_env.getLocalPlayer();
- assert(player != NULL);
- player->eye_offset_first = readV3F1000(is);
- player->eye_offset_third = readV3F1000(is);
- }
- else
- {
- infostream<<"Client: Ignoring unknown command "
- <<command<<std::endl;
- }
+ Send(&pkt);
}
-void Client::Send(u16 channelnum, SharedBuffer<u8> data, bool reliable)
+void Client::sendGotBlocks(v3s16 block)
{
- //JMutexAutoLock lock(m_con_mutex); //bulk comment-out
- m_con.Send(PEER_ID_SERVER, channelnum, data, reliable);
+ NetworkPacket pkt(TOSERVER_GOTBLOCKS, 1 + 6);
+ pkt << (u8) 1 << block;
+ Send(&pkt);
}
-void Client::interact(u8 action, const PointedThing& pointed)
+void Client::sendRemovedSounds(std::vector<s32> &soundList)
{
- if(m_state != LC_Ready){
- infostream<<"Client::interact() "
- "cancelled (not connected)"
- <<std::endl;
- return;
- }
+ size_t server_ids = soundList.size();
+ assert(server_ids <= 0xFFFF);
- std::ostringstream os(std::ios_base::binary);
+ NetworkPacket pkt(TOSERVER_REMOVED_SOUNDS, 2 + server_ids * 4);
- /*
- [0] u16 command
- [2] u8 action
- [3] u16 item
- [5] u32 length of the next item
- [9] serialized PointedThing
- actions:
- 0: start digging (from undersurface) or use
- 1: stop digging (all parameters ignored)
- 2: digging completed
- 3: place block or item (to abovesurface)
- 4: use item
- */
- writeU16(os, TOSERVER_INTERACT);
- writeU8(os, action);
- writeU16(os, getPlayerItem());
- std::ostringstream tmp_os(std::ios::binary);
- pointed.serialize(tmp_os);
- os<<serializeLongString(tmp_os.str());
+ pkt << (u16) (server_ids & 0xFFFF);
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ for(std::vector<s32>::iterator i = soundList.begin();
+ i != soundList.end(); i++)
+ pkt << *i;
- // Send as reliable
- Send(0, data, true);
+ Send(&pkt);
}
void Client::sendNodemetaFields(v3s16 p, const std::string &formname,
- const std::map<std::string, std::string> &fields)
+ const StringMap &fields)
{
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOSERVER_NODEMETA_FIELDS);
- writeV3S16(os, p);
- os<<serializeString(formname);
size_t fields_size = fields.size();
- assert(fields_size <= 0xFFFF);
- writeU16(os, (u16) (fields_size & 0xFFFF));
- for(std::map<std::string, std::string>::const_iterator
- i = fields.begin(); i != fields.end(); i++){
- const std::string &name = i->first;
- const std::string &value = i->second;
- os<<serializeString(name);
- os<<serializeLongString(value);
+
+ FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of nodemeta fields");
+
+ NetworkPacket pkt(TOSERVER_NODEMETA_FIELDS, 0);
+
+ pkt << p << formname << (u16) (fields_size & 0xFFFF);
+
+ StringMap::const_iterator it;
+ for (it = fields.begin(); it != fields.end(); ++it) {
+ const std::string &name = it->first;
+ const std::string &value = it->second;
+ pkt << name;
+ pkt.putLongString(value);
}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ Send(&pkt);
}
void Client::sendInventoryFields(const std::string &formname,
- const std::map<std::string, std::string> &fields)
+ const StringMap &fields)
{
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOSERVER_INVENTORY_FIELDS);
- os<<serializeString(formname);
size_t fields_size = fields.size();
- assert(fields_size <= 0xFFFF);
- writeU16(os, (u16) (fields_size & 0xFFFF));
- for(std::map<std::string, std::string>::const_iterator
- i = fields.begin(); i != fields.end(); i++){
- const std::string &name = i->first;
- const std::string &value = i->second;
- os<<serializeString(name);
- os<<serializeLongString(value);
+ FATAL_ERROR_IF(fields_size > 0xFFFF, "Unsupported number of inventory fields");
+
+ NetworkPacket pkt(TOSERVER_INVENTORY_FIELDS, 0);
+ pkt << formname << (u16) (fields_size & 0xFFFF);
+
+ StringMap::const_iterator it;
+ for (it = fields.begin(); it != fields.end(); ++it) {
+ const std::string &name = it->first;
+ const std::string &value = it->second;
+ pkt << name;
+ pkt.putLongString(value);
}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ Send(&pkt);
}
void Client::sendInventoryAction(InventoryAction *a)
{
std::ostringstream os(std::ios_base::binary);
- u8 buf[12];
-
- // Write command
- writeU16(buf, TOSERVER_INVENTORY_ACTION);
- os.write((char*)buf, 2);
a->serialize(os);
// Make data buffer
std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+
+ NetworkPacket pkt(TOSERVER_INVENTORY_ACTION, s.size());
+ pkt.putRawString(s.c_str(),s.size());
+
+ Send(&pkt);
}
void Client::sendChatMessage(const std::wstring &message)
{
- std::ostringstream os(std::ios_base::binary);
- u8 buf[12];
+ NetworkPacket pkt(TOSERVER_CHAT_MESSAGE, 2 + message.size() * sizeof(u16));
- // Write command
- writeU16(buf, TOSERVER_CHAT_MESSAGE);
- os.write((char*)buf, 2);
+ pkt << message;
- // Write length
- size_t messagesize = message.size();
- if (messagesize > 0xFFFF) {
- messagesize = 0xFFFF;
- }
- writeU16(buf, (u16) messagesize);
- os.write((char*)buf, 2);
-
- // Write string
- for(unsigned int i=0; i<message.size(); i++)
- {
- u16 w = message[i];
- writeU16(buf, w);
- os.write((char*)buf, 2);
- }
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ Send(&pkt);
}
-void Client::sendChangePassword(const std::wstring &oldpassword,
- const std::wstring &newpassword)
+void Client::sendChangePassword(const std::string &oldpassword,
+ const std::string &newpassword)
{
Player *player = m_env.getLocalPlayer();
- if(player == NULL)
+ if (player == NULL)
return;
std::string playername = player->getName();
- std::string oldpwd = translatePassword(playername, oldpassword);
- std::string newpwd = translatePassword(playername, newpassword);
+ if (m_proto_ver >= 25) {
+ // get into sudo mode and then send new password to server
+ m_password = oldpassword;
+ m_new_password = newpassword;
+ startAuth(choseAuthMech(m_sudo_auth_methods));
+ } else {
+ std::string oldpwd = translatePassword(playername, oldpassword);
+ std::string newpwd = translatePassword(playername, newpassword);
- std::ostringstream os(std::ios_base::binary);
- u8 buf[2+PASSWORD_SIZE*2];
- /*
- [0] u16 TOSERVER_PASSWORD
- [2] u8[28] old password
- [30] u8[28] new password
- */
+ NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
- writeU16(buf, TOSERVER_PASSWORD);
- for(unsigned int i=0;i<PASSWORD_SIZE-1;i++)
- {
- buf[2+i] = i<oldpwd.length()?oldpwd[i]:0;
- buf[30+i] = i<newpwd.length()?newpwd[i]:0;
- }
- buf[2+PASSWORD_SIZE-1] = 0;
- buf[30+PASSWORD_SIZE-1] = 0;
- os.write((char*)buf, 2+PASSWORD_SIZE*2);
+ for (u8 i = 0; i < PASSWORD_SIZE; i++) {
+ pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
+ }
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ for (u8 i = 0; i < PASSWORD_SIZE; i++) {
+ pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
+ }
+ Send(&pkt);
+ }
}
void Client::sendDamage(u8 damage)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOSERVER_DAMAGE);
- writeU8(os, damage);
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ NetworkPacket pkt(TOSERVER_DAMAGE, sizeof(u8));
+ pkt << damage;
+ Send(&pkt);
}
void Client::sendBreath(u16 breath)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOSERVER_BREATH);
- writeU16(os, breath);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16));
+ pkt << breath;
+ Send(&pkt);
}
void Client::sendRespawn()
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOSERVER_RESPAWN);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ NetworkPacket pkt(TOSERVER_RESPAWN, 0);
+ Send(&pkt);
}
void Client::sendReady()
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOSERVER_CLIENT_READY);
- writeU8(os,VERSION_MAJOR);
- writeU8(os,VERSION_MINOR);
- writeU8(os,VERSION_PATCH_ORIG);
- writeU8(os,0);
+ NetworkPacket pkt(TOSERVER_CLIENT_READY,
+ 1 + 1 + 1 + 1 + 2 + sizeof(char) * strlen(g_version_hash));
- writeU16(os,strlen(minetest_version_hash));
- os.write(minetest_version_hash,strlen(minetest_version_hash));
+ pkt << (u8) VERSION_MAJOR << (u8) VERSION_MINOR << (u8) VERSION_PATCH
+ << (u8) 0 << (u16) strlen(g_version_hash);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- Send(0, data, true);
+ pkt.putRawString(g_version_hash, (u16) strlen(g_version_hash));
+ Send(&pkt);
}
void Client::sendPlayerPos()
@@ -2292,7 +1277,7 @@ void Client::sendPlayerPos()
// Set peer id if not set already
if(myplayer->peer_id == PEER_ID_INEXISTENT)
myplayer->peer_id = our_peer_id;
- // Check that an existing peer_id is the same as the connection's
+
assert(myplayer->peer_id == our_peer_id);
v3f pf = myplayer->getPosition();
@@ -2305,22 +1290,18 @@ void Client::sendPlayerPos()
v3s32 speed(sf.X*100, sf.Y*100, sf.Z*100);
/*
Format:
- [0] u16 command
- [2] v3s32 position*100
- [2+12] v3s32 speed*100
- [2+12+12] s32 pitch*100
- [2+12+12+4] s32 yaw*100
- [2+12+12+4+4] u32 keyPressed
+ [0] v3s32 position*100
+ [12] v3s32 speed*100
+ [12+12] s32 pitch*100
+ [12+12+4] s32 yaw*100
+ [12+12+4+4] u32 keyPressed
*/
- SharedBuffer<u8> data(2+12+12+4+4+4);
- writeU16(&data[0], TOSERVER_PLAYERPOS);
- writeV3S32(&data[2], position);
- writeV3S32(&data[2+12], speed);
- writeS32(&data[2+12+12], pitch);
- writeS32(&data[2+12+12+4], yaw);
- writeU32(&data[2+12+12+4+4], keyPressed);
- // Send as unreliable
- Send(0, data, false);
+
+ NetworkPacket pkt(TOSERVER_PLAYERPOS, 12 + 12 + 4 + 4 + 4);
+
+ pkt << position << speed << pitch << yaw << keyPressed;
+
+ Send(&pkt);
}
void Client::sendPlayerItem(u16 item)
@@ -2334,33 +1315,28 @@ void Client::sendPlayerItem(u16 item)
// Set peer id if not set already
if(myplayer->peer_id == PEER_ID_INEXISTENT)
myplayer->peer_id = our_peer_id;
- // Check that an existing peer_id is the same as the connection's
assert(myplayer->peer_id == our_peer_id);
- SharedBuffer<u8> data(2+2);
- writeU16(&data[0], TOSERVER_PLAYERITEM);
- writeU16(&data[2], item);
+ NetworkPacket pkt(TOSERVER_PLAYERITEM, 2);
- // Send as reliable
- Send(0, data, true);
+ pkt << item;
+
+ Send(&pkt);
}
void Client::removeNode(v3s16 p)
{
std::map<v3s16, MapBlock*> modified_blocks;
- try
- {
+ try {
m_env.getMap().removeNodeAndUpdate(p, modified_blocks);
}
- catch(InvalidPositionException &e)
- {
+ catch(InvalidPositionException &e) {
}
- for(std::map<v3s16, MapBlock * >::iterator
+ for(std::map<v3s16, MapBlock *>::iterator
i = modified_blocks.begin();
- i != modified_blocks.end(); ++i)
- {
+ i != modified_blocks.end(); ++i) {
addUpdateMeshTaskWithEdge(i->first, false, true);
}
}
@@ -2371,18 +1347,16 @@ void Client::addNode(v3s16 p, MapNode n, bool remove_metadata)
std::map<v3s16, MapBlock*> modified_blocks;
- try
- {
+ try {
//TimeTaker timer3("Client::addNode(): addNodeAndUpdate");
m_env.getMap().addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
}
- catch(InvalidPositionException &e)
- {}
+ catch(InvalidPositionException &e) {
+ }
- for(std::map<v3s16, MapBlock * >::iterator
+ for(std::map<v3s16, MapBlock *>::iterator
i = modified_blocks.begin();
- i != modified_blocks.end(); ++i)
- {
+ i != modified_blocks.end(); ++i) {
addUpdateMeshTaskWithEdge(i->first, false, true);
}
}
@@ -2455,7 +1429,8 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
}
break;
default:
- assert(0);
+ FATAL_ERROR("Invalid inventory location type.");
+ break;
}
return NULL;
}
@@ -2576,7 +1551,8 @@ bool Client::getChatMessage(std::wstring &message)
{
if(m_chat_queue.size() == 0)
return false;
- message = m_chat_queue.pop_front();
+ message = m_chat_queue.front();
+ m_chat_queue.pop();
return true;
}
@@ -2592,14 +1568,14 @@ void Client::typeChatMessage(const std::wstring &message)
// Show locally
if (message[0] == L'/')
{
- m_chat_queue.push_back((std::wstring)L"issued command: " + message);
+ m_chat_queue.push((std::wstring)L"issued command: " + message);
}
else
{
LocalPlayer *player = m_env.getLocalPlayer();
assert(player != NULL);
std::wstring name = narrow_to_wide(player->getName());
- m_chat_queue.push_back((std::wstring)L"<" + name + L"> " + message);
+ m_chat_queue.push((std::wstring)L"<" + name + L"> " + message);
}
}
@@ -2613,7 +1589,7 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
Create a task to update the mesh of the block
*/
- MeshMakeData *data = new MeshMakeData(this);
+ MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders);
{
//TimeTaker timer("data fill");
@@ -2626,7 +1602,7 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
}
// Add task to queue
- m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
+ m_mesh_update_thread.enqueueUpdate(p, data, ack_to_server, urgent);
}
void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
@@ -2662,7 +1638,7 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur
try{
addUpdateMeshTask(blockpos, ack_to_server, urgent);
}
- catch(InvalidPositionException &e){}
+ catch(InvalidPositionException &e) {}
// Leading edge
if(nodepos.X == blockpos_relative.X){
@@ -2692,13 +1668,15 @@ void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool ur
ClientEvent Client::getClientEvent()
{
- if(m_client_event_queue.size() == 0)
- {
- ClientEvent event;
+ ClientEvent event;
+ if(m_client_event_queue.size() == 0) {
event.type = CE_NONE;
- return event;
}
- return m_client_event_queue.pop_front();
+ else {
+ event = m_client_event_queue.front();
+ m_client_event_queue.pop();
+ }
+ return event;
}
float Client::mediaReceiveProgress()
@@ -2709,15 +1687,52 @@ float Client::mediaReceiveProgress()
return 1.0; // downloader only exists when not yet done
}
-void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
+typedef struct TextureUpdateArgs {
+ IrrlichtDevice *device;
+ gui::IGUIEnvironment *guienv;
+ u32 last_time_ms;
+ u16 last_percent;
+ const wchar_t* text_base;
+} TextureUpdateArgs;
+
+void texture_update_progress(void *args, u32 progress, u32 max_progress)
+{
+ TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
+ u16 cur_percent = ceil(progress / (double) max_progress * 100.);
+
+ // update the loading menu -- if neccessary
+ bool do_draw = false;
+ u32 time_ms = targs->last_time_ms;
+ if (cur_percent != targs->last_percent) {
+ targs->last_percent = cur_percent;
+ time_ms = getTimeMs();
+ // only draw when the user will notice something:
+ do_draw = (time_ms - targs->last_time_ms > 100);
+ }
+
+ if (do_draw) {
+ targs->last_time_ms = time_ms;
+ std::basic_stringstream<wchar_t> strm;
+ strm << targs->text_base << " " << targs->last_percent << "%...";
+ draw_load_screen(strm.str(), targs->device, targs->guienv, 0,
+ 72 + (u16) ((18. / 100.) * (double) targs->last_percent));
+ }
+}
+
+void Client::afterContentReceived(IrrlichtDevice *device)
{
infostream<<"Client::afterContentReceived() started"<<std::endl;
- assert(m_itemdef_received);
- assert(m_nodedef_received);
- assert(mediaReceived());
+ assert(m_itemdef_received); // pre-condition
+ assert(m_nodedef_received); // pre-condition
+ assert(mediaReceived()); // pre-condition
const wchar_t* text = wgettext("Loading textures...");
+ // Clear cached pre-scaled 2D GUI images, as this cache
+ // might have images with the same name but different
+ // content from previous sessions.
+ guiScalingCacheClear(device->getVideoDriver());
+
// Rebuild inherited images and recreate textures
infostream<<"- Rebuilding images and textures"<<std::endl;
draw_load_screen(text,device, guienv, 0, 70);
@@ -2727,22 +1742,32 @@ void Client::afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font)
// Rebuild shaders
infostream<<"- Rebuilding shaders"<<std::endl;
text = wgettext("Rebuilding shaders...");
- draw_load_screen(text, device, guienv, 0, 75);
+ draw_load_screen(text, device, guienv, 0, 71);
m_shsrc->rebuildShaders();
delete[] text;
// Update node aliases
infostream<<"- Updating node aliases"<<std::endl;
text = wgettext("Initializing nodes...");
- draw_load_screen(text, device, guienv, 0, 80);
+ draw_load_screen(text, device, guienv, 0, 72);
m_nodedef->updateAliases(m_itemdef);
+ std::string texture_path = g_settings->get("texture_path");
+ if (texture_path != "" && fs::IsDir(texture_path))
+ m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
m_nodedef->setNodeRegistrationStatus(true);
- m_nodedef->runNodeResolverCallbacks();
+ m_nodedef->runNodeResolveCallbacks();
delete[] text;
// Update node textures and assign shaders to each tile
infostream<<"- Updating node textures"<<std::endl;
- m_nodedef->updateTextures(this);
+ TextureUpdateArgs tu_args;
+ tu_args.device = device;
+ tu_args.guienv = guienv;
+ tu_args.last_time_ms = getTimeMs();
+ tu_args.last_percent = 0;
+ tu_args.text_base = wgettext("Initializing nodes");
+ m_nodedef->updateTextures(this, texture_update_progress, &tu_args);
+ delete[] tu_args.text_base;
// Preload item textures and meshes if configured to
if(g_settings->getBool("preload_item_visuals"))
@@ -2800,28 +1825,56 @@ void Client::makeScreenshot(IrrlichtDevice *device)
{
irr::video::IVideoDriver *driver = device->getVideoDriver();
irr::video::IImage* const raw_image = driver->createScreenShot();
- if (raw_image) {
- irr::video::IImage* const image = driver->createImage(video::ECF_R8G8B8,
- raw_image->getDimension());
+
+ if (!raw_image)
+ return;
+
+ time_t t = time(NULL);
+ struct tm *tm = localtime(&t);
+
+ char timetstamp_c[64];
+ strftime(timetstamp_c, sizeof(timetstamp_c), "%Y%m%d_%H%M%S", tm);
+
+ std::string filename_base = g_settings->get("screenshot_path")
+ + DIR_DELIM
+ + std::string("screenshot_")
+ + std::string(timetstamp_c);
+ std::string filename_ext = ".png";
+ std::string filename;
+
+ // Try to find a unique filename
+ unsigned serial = 0;
+
+ while (serial < SCREENSHOT_MAX_SERIAL_TRIES) {
+ filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext;
+ std::ifstream tmp(filename.c_str());
+ if (!tmp.good())
+ break; // File did not apparently exist, we'll go with it
+ serial++;
+ }
+
+ if (serial == SCREENSHOT_MAX_SERIAL_TRIES) {
+ infostream << "Could not find suitable filename for screenshot" << std::endl;
+ } else {
+ irr::video::IImage* const image =
+ driver->createImage(video::ECF_R8G8B8, raw_image->getDimension());
if (image) {
raw_image->copyTo(image);
- irr::c8 filename[256];
- snprintf(filename, sizeof(filename), "%s" DIR_DELIM "screenshot_%u.png",
- g_settings->get("screenshot_path").c_str(),
- device->getTimer()->getRealTime());
+
std::ostringstream sstr;
- if (driver->writeImageToFile(image, filename)) {
+ if (driver->writeImageToFile(image, filename.c_str())) {
sstr << "Saved screenshot to '" << filename << "'";
} else {
sstr << "Failed to save screenshot '" << filename << "'";
}
- m_chat_queue.push_back(narrow_to_wide(sstr.str()));
+ m_chat_queue.push(narrow_to_wide(sstr.str()));
infostream << sstr.str() << std::endl;
image->drop();
}
- raw_image->drop();
}
+
+ raw_image->drop();
}
// IGameDef interface
@@ -2853,9 +1906,10 @@ scene::ISceneManager* Client::getSceneManager()
}
u16 Client::allocateUnknownNodeId(const std::string &name)
{
- errorstream<<"Client::allocateUnknownNodeId(): "
- <<"Client cannot allocate node IDs"<<std::endl;
- assert(0);
+ errorstream << "Client::allocateUnknownNodeId(): "
+ << "Client cannot allocate node IDs" << std::endl;
+ FATAL_ERROR("Client allocated unknown node");
+
return CONTENT_IGNORE;
}
ISoundManager* Client::getSoundManager()
@@ -2874,14 +1928,13 @@ ParticleManager* Client::getParticleManager()
scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
{
- std::map<std::string, std::string>::const_iterator i =
- m_mesh_data.find(filename);
- if(i == m_mesh_data.end()){
- errorstream<<"Client::getMesh(): Mesh not found: \""<<filename<<"\""
- <<std::endl;
+ StringMap::const_iterator it = m_mesh_data.find(filename);
+ if (it == m_mesh_data.end()) {
+ errorstream << "Client::getMesh(): Mesh not found: \"" << filename
+ << "\"" << std::endl;
return NULL;
}
- const std::string &data = i->second;
+ const std::string &data = it->second;
scene::ISceneManager *smgr = m_device->getSceneManager();
// Create the mesh, remove it from cache and return it
@@ -2890,7 +1943,7 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
io::IFileSystem *irrfs = m_device->getFileSystem();
io::IReadFile *rfile = irrfs->createMemoryReadFile(
*data_rw, data_rw.getSize(), filename.c_str());
- assert(rfile);
+ FATAL_ERROR_IF(!rfile, "Could not create/open RAM file");
scene::IAnimatedMesh *mesh = smgr->getMesh(rfile);
rfile->drop();
@@ -2900,4 +1953,3 @@ scene::IAnimatedMesh* Client::getMesh(const std::string &filename)
smgr->getMeshCache()->removeMesh(mesh);
return mesh;
}
-
diff --git a/src/client.h b/src/client.h
index fd7e5f08d..547edfeab 100644
--- a/src/client.h
+++ b/src/client.h
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef CLIENT_HEADER
#define CLIENT_HEADER
-#include "connection.h"
+#include "network/connection.h"
#include "environment.h"
#include "irrlichttypes_extrabloated.h"
#include "jthread/jmutex.h"
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "localplayer.h"
#include "hud.h"
#include "particles.h"
+#include "network/networkpacket.h"
struct MeshMakeData;
class MapBlockMesh;
@@ -47,7 +48,8 @@ struct MapDrawControl;
class MtEventManager;
struct PointedThing;
class Database;
-class Server;
+class Mapper;
+struct MinimapMapblock;
struct QueuedMeshUpdate
{
@@ -111,23 +113,27 @@ struct MeshUpdateResult
}
};
-class MeshUpdateThread : public JThread
+class MeshUpdateThread : public UpdateThread
{
+private:
+ MeshUpdateQueue m_queue_in;
+
+protected:
+ const char *getName()
+ { return "MeshUpdateThread"; }
+ virtual void doUpdate();
+
public:
- MeshUpdateThread(IGameDef *gamedef):
- m_gamedef(gamedef)
+ MeshUpdateThread()
{
}
- void * Thread();
-
- MeshUpdateQueue m_queue_in;
+ void enqueueUpdate(v3s16 p, MeshMakeData *data,
+ bool ack_block_to_server, bool urgent);
MutexedQueue<MeshUpdateResult> m_queue_out;
- IGameDef *m_gamedef;
-
v3s16 m_camera_offset;
};
@@ -341,22 +347,77 @@ public:
*/
void step(float dtime);
- void ProcessData(u8 *data, u32 datasize, u16 sender_peer_id);
+ /*
+ * Command Handlers
+ */
+
+ void handleCommand(NetworkPacket* pkt);
+
+ void handleCommand_Null(NetworkPacket* pkt) {};
+ void handleCommand_Deprecated(NetworkPacket* pkt);
+ void handleCommand_Hello(NetworkPacket* pkt);
+ void handleCommand_AuthAccept(NetworkPacket* pkt);
+ void handleCommand_AcceptSudoMode(NetworkPacket* pkt);
+ void handleCommand_DenySudoMode(NetworkPacket* pkt);
+ void handleCommand_InitLegacy(NetworkPacket* pkt);
+ void handleCommand_AccessDenied(NetworkPacket* pkt);
+ void handleCommand_RemoveNode(NetworkPacket* pkt);
+ void handleCommand_AddNode(NetworkPacket* pkt);
+ void handleCommand_BlockData(NetworkPacket* pkt);
+ void handleCommand_Inventory(NetworkPacket* pkt);
+ void handleCommand_TimeOfDay(NetworkPacket* pkt);
+ void handleCommand_ChatMessage(NetworkPacket* pkt);
+ void handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt);
+ void handleCommand_ActiveObjectMessages(NetworkPacket* pkt);
+ void handleCommand_Movement(NetworkPacket* pkt);
+ void handleCommand_HP(NetworkPacket* pkt);
+ void handleCommand_Breath(NetworkPacket* pkt);
+ void handleCommand_MovePlayer(NetworkPacket* pkt);
+ void handleCommand_PlayerItem(NetworkPacket* pkt);
+ void handleCommand_DeathScreen(NetworkPacket* pkt);
+ void handleCommand_AnnounceMedia(NetworkPacket* pkt);
+ void handleCommand_Media(NetworkPacket* pkt);
+ void handleCommand_ToolDef(NetworkPacket* pkt);
+ void handleCommand_NodeDef(NetworkPacket* pkt);
+ void handleCommand_CraftItemDef(NetworkPacket* pkt);
+ void handleCommand_ItemDef(NetworkPacket* pkt);
+ void handleCommand_PlaySound(NetworkPacket* pkt);
+ void handleCommand_StopSound(NetworkPacket* pkt);
+ void handleCommand_Privileges(NetworkPacket* pkt);
+ void handleCommand_InventoryFormSpec(NetworkPacket* pkt);
+ void handleCommand_DetachedInventory(NetworkPacket* pkt);
+ void handleCommand_ShowFormSpec(NetworkPacket* pkt);
+ void handleCommand_SpawnParticle(NetworkPacket* pkt);
+ void handleCommand_AddParticleSpawner(NetworkPacket* pkt);
+ void handleCommand_DeleteParticleSpawner(NetworkPacket* pkt);
+ void handleCommand_HudAdd(NetworkPacket* pkt);
+ void handleCommand_HudRemove(NetworkPacket* pkt);
+ void handleCommand_HudChange(NetworkPacket* pkt);
+ void handleCommand_HudSetFlags(NetworkPacket* pkt);
+ void handleCommand_HudSetParam(NetworkPacket* pkt);
+ void handleCommand_HudSetSky(NetworkPacket* pkt);
+ void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt);
+ void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
+ void handleCommand_EyeOffset(NetworkPacket* pkt);
+ void handleCommand_SrpBytesSandB(NetworkPacket* pkt);
+
+ void ProcessData(NetworkPacket *pkt);
+
// Returns true if something was received
bool AsyncProcessPacket();
bool AsyncProcessData();
- void Send(u16 channelnum, SharedBuffer<u8> data, bool reliable);
+ void Send(NetworkPacket* pkt);
void interact(u8 action, const PointedThing& pointed);
void sendNodemetaFields(v3s16 p, const std::string &formname,
- const std::map<std::string, std::string> &fields);
+ const StringMap &fields);
void sendInventoryFields(const std::string &formname,
- const std::map<std::string, std::string> &fields);
+ const StringMap &fields);
void sendInventoryAction(InventoryAction *a);
void sendChatMessage(const std::wstring &message);
- void sendChangePassword(const std::wstring &oldpassword,
- const std::wstring &newpassword);
+ void sendChangePassword(const std::string &oldpassword,
+ const std::string &newpassword);
void sendDamage(u8 damage);
void sendBreath(u16 breath);
void sendRespawn();
@@ -428,7 +489,9 @@ public:
bool accessDenied()
{ return m_access_denied; }
- std::wstring accessDeniedReason()
+ bool reconnectRequested() { return m_access_denied_reconnect; }
+
+ std::string accessDeniedReason()
{ return m_access_denied_reason; }
bool itemdefReceived()
@@ -438,14 +501,20 @@ public:
bool mediaReceived()
{ return m_media_downloader == NULL; }
+ u8 getProtoVersion()
+ { return m_proto_ver; }
+
float mediaReceiveProgress();
- void afterContentReceived(IrrlichtDevice *device, gui::IGUIFont* font);
+ void afterContentReceived(IrrlichtDevice *device);
float getRTT(void);
float getCurRate(void);
float getAvgRate(void);
+ Mapper* getMapper ()
+ { return m_mapper; }
+
// IGameDef interface
virtual IItemDefManager* getItemDefManager();
virtual INodeDefManager* getNodeDefManager();
@@ -465,7 +534,7 @@ public:
// Insert a media file appropriately into the appropriate manager
bool loadMedia(const std::string &data, const std::string &filename);
// Send a request for conventional media transfer
- void request_media(const std::list<std::string> &file_requests);
+ void request_media(const std::vector<std::string> &file_requests);
// Send a notification that no conventional media transfer is needed
void received_media();
@@ -490,6 +559,21 @@ private:
// Send the item number 'item' as player item to the server
void sendPlayerItem(u16 item);
+ void deleteAuthData();
+ // helper method shared with clientpackethandler
+ static AuthMechanism choseAuthMech(const u32 mechs);
+
+ void sendLegacyInit(const char* playerName, const char* playerPassword);
+ void sendInit(const std::string &playerName);
+ void startAuth(AuthMechanism chosen_auth_mechanism);
+ void sendDeletedBlocks(std::vector<v3s16> &blocks);
+ void sendGotBlocks(v3s16 block);
+ void sendRemovedSounds(std::vector<s32> &soundList);
+
+ // Helper function
+ inline std::string getPlayerName()
+ { return m_env.getLocalPlayer()->getName(); }
+
float m_packetcounter_timer;
float m_connection_reinit_timer;
float m_avg_rtt_timer;
@@ -510,13 +594,21 @@ private:
ParticleManager m_particle_manager;
con::Connection m_con;
IrrlichtDevice *m_device;
+ Mapper *m_mapper;
// Server serialization version
u8 m_server_ser_ver;
+
+ // Used version of the protocol with server
+ // Values smaller than 25 only mean they are smaller than 25,
+ // and aren't accurate. We simply just don't know, because
+ // the server didn't send the version back then.
+ // If 0, server init hasn't been received yet.
+ u8 m_proto_ver;
+
u16 m_playeritem;
bool m_inventory_updated;
Inventory *m_inventory_from_server;
float m_inventory_from_server_age;
- std::set<v3s16> m_active_blocks;
PacketCounter m_packetcounter;
bool m_show_highlighted;
// Block mesh animation parameters
@@ -527,13 +619,28 @@ private:
// 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT
//s32 m_daynight_i;
//u32 m_daynight_ratio;
- Queue<std::wstring> m_chat_queue;
+ std::queue<std::wstring> m_chat_queue;
+
+ // The authentication methods we can use to enter sudo mode (=change password)
+ u32 m_sudo_auth_methods;
+
// The seed returned by the server in TOCLIENT_INIT is stored here
u64 m_map_seed;
+
+ // Auth data
+ std::string m_playername;
std::string m_password;
+ // If set, this will be sent (and cleared) upon a TOCLIENT_ACCEPT_SUDO_MODE
+ std::string m_new_password;
+ // Usable by auth mechanisms.
+ AuthMechanism m_chosen_auth_mech;
+ void * m_auth_data;
+
+
bool m_access_denied;
- std::wstring m_access_denied_reason;
- Queue<ClientEvent> m_client_event_queue;
+ bool m_access_denied_reconnect;
+ std::string m_access_denied_reason;
+ std::queue<ClientEvent> m_client_event_queue;
bool m_itemdef_received;
bool m_nodedef_received;
ClientMediaDownloader *m_media_downloader;
@@ -563,18 +670,19 @@ private:
std::map<std::string, Inventory*> m_detached_inventories;
// Storage for mesh data for creating multiple instances of the same mesh
- std::map<std::string, std::string> m_mesh_data;
+ StringMap m_mesh_data;
// own state
LocalClientState m_state;
// Used for saving server map to disk client-side
- Database *localdb;
- Server *localserver;
+ Database *m_localdb;
+ IntervalLimiter m_localdb_save_interval;
+ u16 m_cache_save_interval;
- // TODO: Add callback to update this when g_settings changes
+ // TODO: Add callback to update these when g_settings changes
bool m_cache_smooth_lighting;
+ bool m_cache_enable_shaders;
};
#endif // !CLIENT_HEADER
-
diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt
new file mode 100644
index 000000000..a1ec37fe3
--- /dev/null
+++ b/src/client/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(client_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/tile.cpp
+ PARENT_SCOPE
+)
+
diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp
new file mode 100644
index 000000000..bad5c384c
--- /dev/null
+++ b/src/client/clientlauncher.cpp
@@ -0,0 +1,726 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "mainmenumanager.h"
+#include "debug.h"
+#include "clouds.h"
+#include "server.h"
+#include "filesys.h"
+#include "guiMainMenu.h"
+#include "game.h"
+#include "chat.h"
+#include "gettext.h"
+#include "profiler.h"
+#include "log.h"
+#include "serverlist.h"
+#include "guiEngine.h"
+#include "player.h"
+#include "fontengine.h"
+#include "clientlauncher.h"
+
+/* mainmenumanager.h
+ */
+gui::IGUIEnvironment *guienv = NULL;
+gui::IGUIStaticText *guiroot = NULL;
+MainMenuManager g_menumgr;
+
+bool noMenuActive()
+{
+ return g_menumgr.menuCount() == 0;
+}
+
+// Passed to menus to allow disconnecting and exiting
+MainGameCallback *g_gamecallback = NULL;
+
+
+// Instance of the time getter
+static TimeGetter *g_timegetter = NULL;
+
+u32 getTimeMs()
+{
+ if (g_timegetter == NULL)
+ return 0;
+ return g_timegetter->getTime(PRECISION_MILLI);
+}
+
+u32 getTime(TimePrecision prec) {
+ if (g_timegetter == NULL)
+ return 0;
+ return g_timegetter->getTime(prec);
+}
+
+ClientLauncher::~ClientLauncher()
+{
+ if (receiver)
+ delete receiver;
+
+ if (input)
+ delete input;
+
+ if (g_fontengine)
+ delete g_fontengine;
+
+ if (device)
+ device->drop();
+}
+
+
+bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
+{
+ init_args(game_params, cmd_args);
+
+ // List video modes if requested
+ if (list_video_modes)
+ return print_video_modes();
+
+ if (!init_engine(game_params.log_level)) {
+ errorstream << "Could not initialize game engine." << std::endl;
+ return false;
+ }
+
+ // Create time getter
+ g_timegetter = new IrrlichtTimeGetter(device);
+
+ // Speed tests (done after irrlicht is loaded to get timer)
+ if (cmd_args.getFlag("speedtests")) {
+ dstream << "Running speed tests" << std::endl;
+ speed_tests();
+ return true;
+ }
+
+ video::IVideoDriver *video_driver = device->getVideoDriver();
+ if (video_driver == NULL) {
+ errorstream << "Could not initialize video driver." << std::endl;
+ return false;
+ }
+
+ porting::setXorgClassHint(video_driver->getExposedVideoData(), PROJECT_NAME_C);
+
+ /*
+ This changes the minimum allowed number of vertices in a VBO.
+ Default is 500.
+ */
+ //driver->setMinHardwareBufferVertexCount(50);
+
+ // Create game callback for menus
+ g_gamecallback = new MainGameCallback(device);
+
+ device->setResizable(true);
+
+ if (random_input)
+ input = new RandomInputHandler();
+ else
+ input = new RealInputHandler(device, receiver);
+
+ smgr = device->getSceneManager();
+ smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true);
+
+ guienv = device->getGUIEnvironment();
+ skin = guienv->getSkin();
+ skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
+ skin->setColor(gui::EGDC_3D_LIGHT, video::SColor(0, 0, 0, 0));
+ skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255, 30, 30, 30));
+ skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255, 0, 0, 0));
+ skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255, 70, 120, 50));
+ skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
+
+ g_fontengine = new FontEngine(g_settings, guienv);
+ FATAL_ERROR_IF(g_fontengine == NULL, "Font engine creation failed.");
+
+#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
+ // Irrlicht 1.8 input colours
+ skin->setColor(gui::EGDC_EDITABLE, video::SColor(255, 128, 128, 128));
+ skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255, 96, 134, 49));
+#endif
+
+ // Create the menu clouds
+ if (!g_menucloudsmgr)
+ g_menucloudsmgr = smgr->createNewSceneManager();
+ if (!g_menuclouds)
+ g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
+ g_menucloudsmgr, -1, rand(), 100);
+ g_menuclouds->update(v2f(0, 0), video::SColor(255, 200, 200, 255));
+ scene::ICameraSceneNode* camera;
+ camera = g_menucloudsmgr->addCameraSceneNode(0,
+ v3f(0, 0, 0), v3f(0, 60, 100));
+ camera->setFarValue(10000);
+
+ /*
+ GUI stuff
+ */
+
+ ChatBackend chat_backend;
+
+ // If an error occurs, this is set to something by menu().
+ // It is then displayed before the menu shows on the next call to menu()
+ std::string error_message;
+ bool reconnect_requested = false;
+
+ bool first_loop = true;
+
+ /*
+ Menu-game loop
+ */
+ bool retval = true;
+ bool *kill = porting::signal_handler_killstatus();
+
+ while (device->run() && !*kill && !g_gamecallback->shutdown_requested)
+ {
+ // Set the window caption
+ const wchar_t *text = wgettext("Main Menu");
+ device->setWindowCaption((utf8_to_wide(PROJECT_NAME_C) + L" [" + text + L"]").c_str());
+ delete[] text;
+
+ try { // This is used for catching disconnects
+
+ guienv->clear();
+
+ /*
+ We need some kind of a root node to be able to add
+ custom gui elements directly on the screen.
+ Otherwise they won't be automatically drawn.
+ */
+ guiroot = guienv->addStaticText(L"", core::rect<s32>(0, 0, 10000, 10000));
+
+ bool game_has_run = launch_game(error_message, reconnect_requested,
+ game_params, cmd_args);
+
+ // If skip_main_menu, we only want to startup once
+ if (skip_main_menu && !first_loop)
+ break;
+
+ first_loop = false;
+
+ if (!game_has_run) {
+ if (skip_main_menu)
+ break;
+ else
+ continue;
+ }
+
+ // Break out of menu-game loop to shut down cleanly
+ if (!device->run() || *kill) {
+ if (g_settings_path != "")
+ g_settings->updateConfigFile(g_settings_path.c_str());
+ break;
+ }
+
+ if (current_playername.length() > PLAYERNAME_SIZE-1) {
+ error_message = gettext("Player name too long.");
+ playername = current_playername.substr(0, PLAYERNAME_SIZE-1);
+ g_settings->set("name", playername);
+ continue;
+ }
+
+ device->getVideoDriver()->setTextureCreationFlag(
+ video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
+
+#ifdef HAVE_TOUCHSCREENGUI
+ receiver->m_touchscreengui = new TouchScreenGUI(device, receiver);
+ g_touchscreengui = receiver->m_touchscreengui;
+#endif
+
+ the_game(
+ kill,
+ random_input,
+ input,
+ device,
+ worldspec.path,
+ current_playername,
+ current_password,
+ current_address,
+ current_port,
+ error_message,
+ chat_backend,
+ &reconnect_requested,
+ gamespec,
+ simple_singleplayer_mode
+ );
+ smgr->clear();
+
+#ifdef HAVE_TOUCHSCREENGUI
+ delete g_touchscreengui;
+ g_touchscreengui = NULL;
+ receiver->m_touchscreengui = NULL;
+#endif
+
+ } //try
+ catch (con::PeerNotFoundException &e) {
+ error_message = gettext("Connection error (timed out?)");
+ errorstream << error_message << std::endl;
+ }
+
+#ifdef NDEBUG
+ catch (std::exception &e) {
+ std::string error_message = "Some exception: \"";
+ error_message += e.what();
+ error_message += "\"";
+ errorstream << error_message << std::endl;
+ }
+#endif
+
+ // If no main menu, show error and exit
+ if (skip_main_menu) {
+ if (!error_message.empty()) {
+ verbosestream << "error_message = "
+ << error_message << std::endl;
+ retval = false;
+ }
+ break;
+ }
+ } // Menu-game loop
+
+ g_menuclouds->drop();
+ g_menucloudsmgr->drop();
+
+ return retval;
+}
+
+void ClientLauncher::init_args(GameParams &game_params, const Settings &cmd_args)
+{
+
+ skip_main_menu = cmd_args.getFlag("go");
+
+ // FIXME: This is confusing (but correct)
+
+ /* If world_path is set then override it unless skipping the main menu using
+ * the --go command line param. Else, give preference to the address
+ * supplied on the command line
+ */
+ address = g_settings->get("address");
+ if (game_params.world_path != "" && !skip_main_menu)
+ address = "";
+ else if (cmd_args.exists("address"))
+ address = cmd_args.get("address");
+
+ playername = g_settings->get("name");
+ if (cmd_args.exists("name"))
+ playername = cmd_args.get("name");
+
+ list_video_modes = cmd_args.getFlag("videomodes");
+
+ use_freetype = g_settings->getBool("freetype");
+
+ random_input = g_settings->getBool("random_input")
+ || cmd_args.getFlag("random-input");
+}
+
+bool ClientLauncher::init_engine(int log_level)
+{
+ receiver = new MyEventReceiver();
+ create_engine_device(log_level);
+ return device != NULL;
+}
+
+bool ClientLauncher::launch_game(std::string &error_message,
+ bool reconnect_requested, GameParams &game_params,
+ const Settings &cmd_args)
+{
+ // Initialize menu data
+ MainMenuData menudata;
+ menudata.address = address;
+ menudata.name = playername;
+ menudata.port = itos(game_params.socket_port);
+ menudata.script_data.errormessage = error_message;
+ menudata.script_data.reconnect_requested = reconnect_requested;
+
+ error_message.clear();
+
+ if (cmd_args.exists("password"))
+ menudata.password = cmd_args.get("password");
+
+ menudata.enable_public = g_settings->getBool("server_announce");
+
+ // If a world was commanded, append and select it
+ if (game_params.world_path != "") {
+ worldspec.gameid = getWorldGameId(game_params.world_path, true);
+ worldspec.name = _("[--world parameter]");
+
+ if (worldspec.gameid == "") { // Create new
+ worldspec.gameid = g_settings->get("default_game");
+ worldspec.name += " [new]";
+ }
+ worldspec.path = game_params.world_path;
+ }
+
+ /* Show the GUI menu
+ */
+ if (!skip_main_menu) {
+ main_menu(&menudata);
+
+ // Skip further loading if there was an exit signal.
+ if (*porting::signal_handler_killstatus())
+ return false;
+
+ address = menudata.address;
+ int newport = stoi(menudata.port);
+ if (newport != 0)
+ game_params.socket_port = newport;
+
+ simple_singleplayer_mode = menudata.simple_singleplayer_mode;
+
+ std::vector<WorldSpec> worldspecs = getAvailableWorlds();
+
+ if (menudata.selected_world >= 0
+ && menudata.selected_world < (int)worldspecs.size()) {
+ g_settings->set("selected_world_path",
+ worldspecs[menudata.selected_world].path);
+ worldspec = worldspecs[menudata.selected_world];
+ }
+ }
+
+ if (!menudata.script_data.errormessage.empty()) {
+ /* The calling function will pass this back into this function upon the
+ * next iteration (if any) causing it to be displayed by the GUI
+ */
+ error_message = menudata.script_data.errormessage;
+ return false;
+ }
+
+ if (menudata.name == "")
+ menudata.name = std::string("Guest") + itos(myrand_range(1000, 9999));
+ else
+ playername = menudata.name;
+
+ password = menudata.password;
+
+ g_settings->set("name", playername);
+
+ current_playername = playername;
+ current_password = password;
+ current_address = address;
+ current_port = game_params.socket_port;
+
+ // If using simple singleplayer mode, override
+ if (simple_singleplayer_mode) {
+ assert(skip_main_menu == false);
+ current_playername = "singleplayer";
+ current_password = "";
+ current_address = "";
+ current_port = myrand_range(49152, 65535);
+ } else if (address != "") {
+ ServerListSpec server;
+ server["name"] = menudata.servername;
+ server["address"] = menudata.address;
+ server["port"] = menudata.port;
+ server["description"] = menudata.serverdescription;
+ ServerList::insert(server);
+ }
+
+ infostream << "Selected world: " << worldspec.name
+ << " [" << worldspec.path << "]" << std::endl;
+
+ if (current_address == "") { // If local game
+ if (worldspec.path == "") {
+ error_message = gettext("No world selected and no address "
+ "provided. Nothing to do.");
+ errorstream << error_message << std::endl;
+ return false;
+ }
+
+ if (!fs::PathExists(worldspec.path)) {
+ error_message = gettext("Provided world path doesn't exist: ")
+ + worldspec.path;
+ errorstream << error_message << std::endl;
+ return false;
+ }
+
+ // Load gamespec for required game
+ gamespec = findWorldSubgame(worldspec.path);
+ if (!gamespec.isValid() && !game_params.game_spec.isValid()) {
+ error_message = gettext("Could not find or load game \"")
+ + worldspec.gameid + "\"";
+ errorstream << error_message << std::endl;
+ return false;
+ }
+
+ if (porting::signal_handler_killstatus())
+ return true;
+
+ if (game_params.game_spec.isValid() &&
+ game_params.game_spec.id != worldspec.gameid) {
+ errorstream << "WARNING: Overriding gamespec from \""
+ << worldspec.gameid << "\" to \""
+ << game_params.game_spec.id << "\"" << std::endl;
+ gamespec = game_params.game_spec;
+ }
+
+ if (!gamespec.isValid()) {
+ error_message = gettext("Invalid gamespec.");
+ error_message += " (world.gameid=" + worldspec.gameid + ")";
+ errorstream << error_message << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ClientLauncher::main_menu(MainMenuData *menudata)
+{
+ bool *kill = porting::signal_handler_killstatus();
+ video::IVideoDriver *driver = device->getVideoDriver();
+
+ infostream << "Waiting for other menus" << std::endl;
+ while (device->run() && *kill == false) {
+ if (noMenuActive())
+ break;
+ driver->beginScene(true, true, video::SColor(255, 128, 128, 128));
+ guienv->drawAll();
+ driver->endScene();
+ // On some computers framerate doesn't seem to be automatically limited
+ sleep_ms(25);
+ }
+ infostream << "Waited for other menus" << std::endl;
+
+ // Cursor can be non-visible when coming from the game
+#ifndef ANDROID
+ device->getCursorControl()->setVisible(true);
+#endif
+
+ /* show main menu */
+ GUIEngine mymenu(device, guiroot, &g_menumgr, smgr, menudata, *kill);
+
+ smgr->clear(); /* leave scene manager in a clean state */
+}
+
+bool ClientLauncher::create_engine_device(int log_level)
+{
+ static const irr::ELOG_LEVEL irr_log_level[5] = {
+ ELL_NONE,
+ ELL_ERROR,
+ ELL_WARNING,
+ ELL_INFORMATION,
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ ELL_INFORMATION
+#else
+ ELL_DEBUG
+#endif
+ };
+
+ // Resolution selection
+ bool fullscreen = g_settings->getBool("fullscreen");
+ u16 screenW = g_settings->getU16("screenW");
+ u16 screenH = g_settings->getU16("screenH");
+
+ // bpp, fsaa, vsync
+ bool vsync = g_settings->getBool("vsync");
+ u16 bits = g_settings->getU16("fullscreen_bpp");
+ u16 fsaa = g_settings->getU16("fsaa");
+
+ // Determine driver
+ video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
+ std::string driverstring = g_settings->get("video_driver");
+ std::vector<video::E_DRIVER_TYPE> drivers
+ = porting::getSupportedVideoDrivers();
+ u32 i;
+ for (i = 0; i != drivers.size(); i++) {
+ if (!strcasecmp(driverstring.c_str(),
+ porting::getVideoDriverName(drivers[i]))) {
+ driverType = drivers[i];
+ break;
+ }
+ }
+ if (i == drivers.size()) {
+ errorstream << "Invalid video_driver specified; "
+ "defaulting to opengl" << std::endl;
+ }
+
+ SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
+ params.DriverType = driverType;
+ params.WindowSize = core::dimension2d<u32>(screenW, screenH);
+ params.Bits = bits;
+ params.AntiAlias = fsaa;
+ params.Fullscreen = fullscreen;
+ params.Stencilbuffer = false;
+ params.Vsync = vsync;
+ params.EventReceiver = receiver;
+ params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
+#ifdef __ANDROID__
+ params.PrivateData = porting::app_global;
+ params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
+ "media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str();
+#endif
+
+ device = createDeviceEx(params);
+
+ if (device) {
+ // Map our log level to irrlicht engine one.
+ ILogger* irr_logger = device->getLogger();
+ irr_logger->setLogLevel(irr_log_level[log_level]);
+
+ porting::initIrrlicht(device);
+ }
+
+ return device != NULL;
+}
+
+void ClientLauncher::speed_tests()
+{
+ // volatile to avoid some potential compiler optimisations
+ volatile static s16 temp16;
+ volatile static f32 tempf;
+ static v3f tempv3f1;
+ static v3f tempv3f2;
+ static std::string tempstring;
+ static std::string tempstring2;
+
+ tempv3f1 = v3f();
+ tempv3f2 = v3f();
+ tempstring = std::string();
+ tempstring2 = std::string();
+
+ {
+ infostream << "The following test should take around 20ms." << std::endl;
+ TimeTaker timer("Testing std::string speed");
+ const u32 jj = 10000;
+ for (u32 j = 0; j < jj; j++) {
+ tempstring = "";
+ tempstring2 = "";
+ const u32 ii = 10;
+ for (u32 i = 0; i < ii; i++) {
+ tempstring2 += "asd";
+ }
+ for (u32 i = 0; i < ii+1; i++) {
+ tempstring += "asd";
+ if (tempstring == tempstring2)
+ break;
+ }
+ }
+ }
+
+ infostream << "All of the following tests should take around 100ms each."
+ << std::endl;
+
+ {
+ TimeTaker timer("Testing floating-point conversion speed");
+ tempf = 0.001;
+ for (u32 i = 0; i < 4000000; i++) {
+ temp16 += tempf;
+ tempf += 0.001;
+ }
+ }
+
+ {
+ TimeTaker timer("Testing floating-point vector speed");
+
+ tempv3f1 = v3f(1, 2, 3);
+ tempv3f2 = v3f(4, 5, 6);
+ for (u32 i = 0; i < 10000000; i++) {
+ tempf += tempv3f1.dotProduct(tempv3f2);
+ tempv3f2 += v3f(7, 8, 9);
+ }
+ }
+
+ {
+ TimeTaker timer("Testing std::map speed");
+
+ std::map<v2s16, f32> map1;
+ tempf = -324;
+ const s16 ii = 300;
+ for (s16 y = 0; y < ii; y++) {
+ for (s16 x = 0; x < ii; x++) {
+ map1[v2s16(x, y)] = tempf;
+ tempf += 1;
+ }
+ }
+ for (s16 y = ii - 1; y >= 0; y--) {
+ for (s16 x = 0; x < ii; x++) {
+ tempf = map1[v2s16(x, y)];
+ }
+ }
+ }
+
+ {
+ infostream << "Around 5000/ms should do well here." << std::endl;
+ TimeTaker timer("Testing mutex speed");
+
+ JMutex m;
+ u32 n = 0;
+ u32 i = 0;
+ do {
+ n += 10000;
+ for (; i < n; i++) {
+ m.Lock();
+ m.Unlock();
+ }
+ }
+ // Do at least 10ms
+ while(timer.getTimerTime() < 10);
+
+ u32 dtime = timer.stop();
+ u32 per_ms = n / dtime;
+ infostream << "Done. " << dtime << "ms, " << per_ms << "/ms" << std::endl;
+ }
+}
+
+bool ClientLauncher::print_video_modes()
+{
+ IrrlichtDevice *nulldevice;
+
+ bool vsync = g_settings->getBool("vsync");
+ u16 fsaa = g_settings->getU16("fsaa");
+ MyEventReceiver* receiver = new MyEventReceiver();
+
+ SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
+ params.DriverType = video::EDT_NULL;
+ params.WindowSize = core::dimension2d<u32>(640, 480);
+ params.Bits = 24;
+ params.AntiAlias = fsaa;
+ params.Fullscreen = false;
+ params.Stencilbuffer = false;
+ params.Vsync = vsync;
+ params.EventReceiver = receiver;
+ params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
+
+ nulldevice = createDeviceEx(params);
+
+ if (nulldevice == NULL) {
+ delete receiver;
+ return false;
+ }
+
+ dstream << _("Available video modes (WxHxD):") << std::endl;
+
+ video::IVideoModeList *videomode_list = nulldevice->getVideoModeList();
+
+ if (videomode_list != NULL) {
+ s32 videomode_count = videomode_list->getVideoModeCount();
+ core::dimension2d<u32> videomode_res;
+ s32 videomode_depth;
+ for (s32 i = 0; i < videomode_count; ++i) {
+ videomode_res = videomode_list->getVideoModeResolution(i);
+ videomode_depth = videomode_list->getVideoModeDepth(i);
+ dstream << videomode_res.Width << "x" << videomode_res.Height
+ << "x" << videomode_depth << std::endl;
+ }
+
+ dstream << _("Active video mode (WxHxD):") << std::endl;
+ videomode_res = videomode_list->getDesktopResolution();
+ videomode_depth = videomode_list->getDesktopDepth();
+ dstream << videomode_res.Width << "x" << videomode_res.Height
+ << "x" << videomode_depth << std::endl;
+
+ }
+
+ nulldevice->drop();
+ delete receiver;
+
+ return videomode_list != NULL;
+}
diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h
new file mode 100644
index 000000000..49ceefc52
--- /dev/null
+++ b/src/client/clientlauncher.h
@@ -0,0 +1,129 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef __CLIENT_LAUNCHER_H__
+#define __CLIENT_LAUNCHER_H__
+
+#include "irrlichttypes_extrabloated.h"
+#include "client/inputhandler.h"
+#include "gameparams.h"
+
+// A small helper class
+class TimeGetter
+{
+public:
+ virtual u32 getTime(TimePrecision prec) = 0;
+};
+
+// A precise irrlicht one
+class IrrlichtTimeGetter: public TimeGetter
+{
+public:
+ IrrlichtTimeGetter(IrrlichtDevice *device):
+ m_device(device)
+ {}
+ u32 getTime(TimePrecision prec)
+ {
+ if (prec == PRECISION_MILLI) {
+ if (m_device == NULL)
+ return 0;
+ return m_device->getTimer()->getRealTime();
+ } else {
+ return porting::getTime(prec);
+ }
+ }
+private:
+ IrrlichtDevice *m_device;
+};
+// Not so precise one which works without irrlicht
+class SimpleTimeGetter: public TimeGetter
+{
+public:
+ u32 getTime(TimePrecision prec)
+ {
+ return porting::getTime(prec);
+ }
+};
+
+class ClientLauncher
+{
+public:
+ ClientLauncher() :
+ list_video_modes(false),
+ skip_main_menu(false),
+ use_freetype(false),
+ random_input(false),
+ address(""),
+ playername(""),
+ password(""),
+ device(NULL),
+ input(NULL),
+ receiver(NULL),
+ skin(NULL),
+ font(NULL),
+ simple_singleplayer_mode(false),
+ current_playername("inv£lid"),
+ current_password(""),
+ current_address("does-not-exist"),
+ current_port(0)
+ {}
+
+ ~ClientLauncher();
+
+ bool run(GameParams &game_params, const Settings &cmd_args);
+
+protected:
+ void init_args(GameParams &game_params, const Settings &cmd_args);
+ bool init_engine(int log_level);
+
+ bool launch_game(std::string &error_message, bool reconnect_requested,
+ GameParams &game_params, const Settings &cmd_args);
+
+ void main_menu(MainMenuData *menudata);
+ bool create_engine_device(int log_level);
+
+ void speed_tests();
+ bool print_video_modes();
+
+ bool list_video_modes;
+ bool skip_main_menu;
+ bool use_freetype;
+ bool random_input;
+ std::string address;
+ std::string playername;
+ std::string password;
+ IrrlichtDevice *device;
+ InputHandler *input;
+ MyEventReceiver *receiver;
+ gui::IGUISkin *skin;
+ gui::IGUIFont *font;
+ scene::ISceneManager *smgr;
+ SubgameSpec gamespec;
+ WorldSpec worldspec;
+ bool simple_singleplayer_mode;
+
+ // These are set up based on the menu and other things
+ // TODO: Are these required since there's already playername, password, etc
+ std::string current_playername;
+ std::string current_password;
+ std::string current_address;
+ int current_port;
+};
+
+#endif
diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h
new file mode 100644
index 000000000..a894e35aa
--- /dev/null
+++ b/src/client/inputhandler.h
@@ -0,0 +1,435 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef __INPUT_HANDLER_H__
+#define __INPUT_HANDLER_H__
+
+#include "irrlichttypes_extrabloated.h"
+
+class MyEventReceiver : public IEventReceiver
+{
+public:
+ // This is the one method that we have to implement
+ virtual bool OnEvent(const SEvent& event)
+ {
+ /*
+ React to nothing here if a menu is active
+ */
+ if (noMenuActive() == false) {
+#ifdef HAVE_TOUCHSCREENGUI
+ if (m_touchscreengui != 0) {
+ m_touchscreengui->Toggle(false);
+ }
+#endif
+ return g_menumgr.preprocessEvent(event);
+ }
+
+ // Remember whether each key is down or up
+ if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
+ if (event.KeyInput.PressedDown) {
+ keyIsDown.set(event.KeyInput);
+ keyWasDown.set(event.KeyInput);
+ } else {
+ keyIsDown.unset(event.KeyInput);
+ }
+ }
+
+#ifdef HAVE_TOUCHSCREENGUI
+ // case of touchscreengui we have to handle different events
+ if ((m_touchscreengui != 0) &&
+ (event.EventType == irr::EET_TOUCH_INPUT_EVENT)) {
+ m_touchscreengui->translateEvent(event);
+ return true;
+ }
+#endif
+ // handle mouse events
+ if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
+ if (noMenuActive() == false) {
+ left_active = false;
+ middle_active = false;
+ right_active = false;
+ } else {
+ left_active = event.MouseInput.isLeftPressed();
+ middle_active = event.MouseInput.isMiddlePressed();
+ right_active = event.MouseInput.isRightPressed();
+
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+ leftclicked = true;
+ }
+ if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
+ rightclicked = true;
+ }
+ if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
+ leftreleased = true;
+ }
+ if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
+ rightreleased = true;
+ }
+ if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+ mouse_wheel += event.MouseInput.Wheel;
+ }
+ }
+ } else if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
+ static const enum LogMessageLevel irr_loglev_conv[] = {
+ LMT_VERBOSE, // ELL_DEBUG
+ LMT_INFO, // ELL_INFORMATION
+ LMT_ACTION, // ELL_WARNING
+ LMT_ERROR, // ELL_ERROR
+ LMT_ERROR, // ELL_NONE
+ };
+ assert(event.LogEvent.Level < ARRLEN(irr_loglev_conv));
+ log_printline(irr_loglev_conv[event.LogEvent.Level],
+ std::string("Irrlicht: ") + (const char *)event.LogEvent.Text);
+ return true;
+ }
+ /* always return false in order to continue processing events */
+ return false;
+ }
+
+ bool IsKeyDown(const KeyPress &keyCode) const
+ {
+ return keyIsDown[keyCode];
+ }
+
+ // Checks whether a key was down and resets the state
+ bool WasKeyDown(const KeyPress &keyCode)
+ {
+ bool b = keyWasDown[keyCode];
+ if (b)
+ keyWasDown.unset(keyCode);
+ return b;
+ }
+
+ s32 getMouseWheel()
+ {
+ s32 a = mouse_wheel;
+ mouse_wheel = 0;
+ return a;
+ }
+
+ void clearInput()
+ {
+ keyIsDown.clear();
+ keyWasDown.clear();
+
+ leftclicked = false;
+ rightclicked = false;
+ leftreleased = false;
+ rightreleased = false;
+
+ left_active = false;
+ middle_active = false;
+ right_active = false;
+
+ mouse_wheel = 0;
+ }
+
+ MyEventReceiver()
+ {
+ clearInput();
+#ifdef HAVE_TOUCHSCREENGUI
+ m_touchscreengui = NULL;
+#endif
+ }
+
+ bool leftclicked;
+ bool rightclicked;
+ bool leftreleased;
+ bool rightreleased;
+
+ bool left_active;
+ bool middle_active;
+ bool right_active;
+
+ s32 mouse_wheel;
+
+#ifdef HAVE_TOUCHSCREENGUI
+ TouchScreenGUI* m_touchscreengui;
+#endif
+
+private:
+ // The current state of keys
+ KeyList keyIsDown;
+ // Whether a key has been pressed or not
+ KeyList keyWasDown;
+};
+
+
+/*
+ Separated input handler
+*/
+
+class RealInputHandler : public InputHandler
+{
+public:
+ RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
+ m_device(device),
+ m_receiver(receiver),
+ m_mousepos(0,0)
+ {
+ }
+ virtual bool isKeyDown(const KeyPress &keyCode)
+ {
+ return m_receiver->IsKeyDown(keyCode);
+ }
+ virtual bool wasKeyDown(const KeyPress &keyCode)
+ {
+ return m_receiver->WasKeyDown(keyCode);
+ }
+ virtual v2s32 getMousePos()
+ {
+ if (m_device->getCursorControl()) {
+ return m_device->getCursorControl()->getPosition();
+ }
+ else {
+ return m_mousepos;
+ }
+ }
+ virtual void setMousePos(s32 x, s32 y)
+ {
+ if (m_device->getCursorControl()) {
+ m_device->getCursorControl()->setPosition(x, y);
+ }
+ else {
+ m_mousepos = v2s32(x,y);
+ }
+ }
+
+ virtual bool getLeftState()
+ {
+ return m_receiver->left_active;
+ }
+ virtual bool getRightState()
+ {
+ return m_receiver->right_active;
+ }
+
+ virtual bool getLeftClicked()
+ {
+ return m_receiver->leftclicked;
+ }
+ virtual bool getRightClicked()
+ {
+ return m_receiver->rightclicked;
+ }
+ virtual void resetLeftClicked()
+ {
+ m_receiver->leftclicked = false;
+ }
+ virtual void resetRightClicked()
+ {
+ m_receiver->rightclicked = false;
+ }
+
+ virtual bool getLeftReleased()
+ {
+ return m_receiver->leftreleased;
+ }
+ virtual bool getRightReleased()
+ {
+ return m_receiver->rightreleased;
+ }
+ virtual void resetLeftReleased()
+ {
+ m_receiver->leftreleased = false;
+ }
+ virtual void resetRightReleased()
+ {
+ m_receiver->rightreleased = false;
+ }
+
+ virtual s32 getMouseWheel()
+ {
+ return m_receiver->getMouseWheel();
+ }
+
+ void clear()
+ {
+ m_receiver->clearInput();
+ }
+private:
+ IrrlichtDevice *m_device;
+ MyEventReceiver *m_receiver;
+ v2s32 m_mousepos;
+};
+
+class RandomInputHandler : public InputHandler
+{
+public:
+ RandomInputHandler()
+ {
+ leftdown = false;
+ rightdown = false;
+ leftclicked = false;
+ rightclicked = false;
+ leftreleased = false;
+ rightreleased = false;
+ keydown.clear();
+ }
+ virtual bool isKeyDown(const KeyPress &keyCode)
+ {
+ return keydown[keyCode];
+ }
+ virtual bool wasKeyDown(const KeyPress &keyCode)
+ {
+ return false;
+ }
+ virtual v2s32 getMousePos()
+ {
+ return mousepos;
+ }
+ virtual void setMousePos(s32 x, s32 y)
+ {
+ mousepos = v2s32(x, y);
+ }
+
+ virtual bool getLeftState()
+ {
+ return leftdown;
+ }
+ virtual bool getRightState()
+ {
+ return rightdown;
+ }
+
+ virtual bool getLeftClicked()
+ {
+ return leftclicked;
+ }
+ virtual bool getRightClicked()
+ {
+ return rightclicked;
+ }
+ virtual void resetLeftClicked()
+ {
+ leftclicked = false;
+ }
+ virtual void resetRightClicked()
+ {
+ rightclicked = false;
+ }
+
+ virtual bool getLeftReleased()
+ {
+ return leftreleased;
+ }
+ virtual bool getRightReleased()
+ {
+ return rightreleased;
+ }
+ virtual void resetLeftReleased()
+ {
+ leftreleased = false;
+ }
+ virtual void resetRightReleased()
+ {
+ rightreleased = false;
+ }
+
+ virtual s32 getMouseWheel()
+ {
+ return 0;
+ }
+
+ virtual void step(float dtime)
+ {
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 40);
+ keydown.toggle(getKeySetting("keymap_jump"));
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 40);
+ keydown.toggle(getKeySetting("keymap_special1"));
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 40);
+ keydown.toggle(getKeySetting("keymap_forward"));
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 40);
+ keydown.toggle(getKeySetting("keymap_left"));
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 20);
+ mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 30);
+ leftdown = !leftdown;
+ if (leftdown)
+ leftclicked = true;
+ if (!leftdown)
+ leftreleased = true;
+ }
+ }
+ {
+ static float counter1 = 0;
+ counter1 -= dtime;
+ if (counter1 < 0.0) {
+ counter1 = 0.1 * Rand(1, 15);
+ rightdown = !rightdown;
+ if (rightdown)
+ rightclicked = true;
+ if (!rightdown)
+ rightreleased = true;
+ }
+ }
+ mousepos += mousespeed;
+ }
+
+ s32 Rand(s32 min, s32 max)
+ {
+ return (myrand()%(max-min+1))+min;
+ }
+private:
+ KeyList keydown;
+ v2s32 mousepos;
+ v2s32 mousespeed;
+ bool leftdown;
+ bool rightdown;
+ bool leftclicked;
+ bool rightclicked;
+ bool leftreleased;
+ bool rightreleased;
+};
+
+#endif
diff --git a/src/tile.cpp b/src/client/tile.cpp
index 81b362d61..a28b40c65 100644
--- a/src/tile.cpp
+++ b/src/client/tile.cpp
@@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h"
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
-#include "main.h" // for g_settings
#include "filesys.h"
#include "settings.h"
#include "mesh.h"
@@ -34,6 +33,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h"
#include "strfnd.h"
#include "util/string.h" // for parseColorString()
+#include "imagefilters.h"
+#include "guiscalingfilter.h"
+#include "nodedef.h"
+
#ifdef __ANDROID__
#include <GLES/gl.h>
@@ -199,7 +202,7 @@ public:
void insert(const std::string &name, video::IImage *img,
bool prefer_local, video::IVideoDriver *driver)
{
- assert(img);
+ assert(img); // Pre-condition
// Remove old image
std::map<std::string, video::IImage*>::iterator n;
n = m_images.find(name);
@@ -329,7 +332,15 @@ public:
*/
video::ITexture* getTexture(u32 id);
- video::ITexture* getTexture(const std::string &name, u32 *id);
+ video::ITexture* getTexture(const std::string &name, u32 *id = NULL);
+
+ /*
+ Get a texture specifically intended for mesh
+ application, i.e. not HUD, compositing, or other 2D
+ use. This texture may be a different size and may
+ have had additional filters applied.
+ */
+ video::ITexture* getTextureForMesh(const std::string &name, u32 *id);
// Returns a pointer to the irrlicht device
virtual IrrlichtDevice* getDevice()
@@ -373,6 +384,9 @@ public:
video::IImage* generateImage(const std::string &name);
video::ITexture* getNormalTexture(const std::string &name);
+ video::SColor getTextureAverageColor(const std::string &name);
+ video::ITexture *getShaderFlagsTexture(bool normamap_present);
+
private:
// The id of the thread that is allowed to use irrlicht directly
@@ -407,7 +421,7 @@ private:
// Textures that have been overwritten with other ones
// but can't be deleted because the ITexture* might still be used
- std::list<video::ITexture*> m_texture_trash;
+ std::vector<video::ITexture*> m_texture_trash;
// Cached settings needed for making textures from meshes
bool m_setting_trilinear_filter;
@@ -423,7 +437,7 @@ IWritableTextureSource* createTextureSource(IrrlichtDevice *device)
TextureSource::TextureSource(IrrlichtDevice *device):
m_device(device)
{
- assert(m_device);
+ assert(m_device); // Pre-condition
m_main_thread = get_current_thread_id();
@@ -455,10 +469,9 @@ TextureSource::~TextureSource()
}
m_textureinfo_cache.clear();
- for (std::list<video::ITexture*>::iterator iter =
+ for (std::vector<video::ITexture*>::iterator iter =
m_texture_trash.begin(); iter != m_texture_trash.end();
- iter++)
- {
+ iter++) {
video::ITexture *t = *iter;
//cleanup trashed texture
@@ -598,7 +611,7 @@ u32 TextureSource::generateTexture(const std::string &name)
}
video::IVideoDriver *driver = m_device->getVideoDriver();
- assert(driver);
+ sanity_check(driver);
video::IImage *img = generateImage(name);
@@ -610,6 +623,7 @@ u32 TextureSource::generateTexture(const std::string &name)
#endif
// Create texture from resulting image
tex = driver->addTexture(name.c_str(), img);
+ guiScalingCache(io::path(name.c_str()), driver, img);
img->drop();
}
@@ -661,6 +675,11 @@ video::ITexture* TextureSource::getTexture(const std::string &name, u32 *id)
return getTexture(actual_id);
}
+video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 *id)
+{
+ return getTexture(name + "^[applyfiltersformesh", id);
+}
+
void TextureSource::processQueue()
{
/*
@@ -685,7 +704,7 @@ void TextureSource::insertSourceImage(const std::string &name, video::IImage *im
{
//infostream<<"TextureSource::insertSourceImage(): name="<<name<<std::endl;
- assert(get_current_thread_id() == m_main_thread);
+ sanity_check(get_current_thread_id() == m_main_thread);
m_sourcecache.insert(name, img, true, m_device->getVideoDriver());
m_source_image_existence.set(name, true);
@@ -696,7 +715,7 @@ void TextureSource::rebuildImagesAndTextures()
JMutexAutoLock lock(m_textureinfo_cache_mutex);
video::IVideoDriver* driver = m_device->getVideoDriver();
- assert(driver != 0);
+ sanity_check(driver);
// Recreate textures
for (u32 i=0; i<m_textureinfo_cache.size(); i++){
@@ -704,13 +723,14 @@ void TextureSource::rebuildImagesAndTextures()
video::IImage *img = generateImage(ti->name);
#ifdef __ANDROID__
img = Align2Npot2(img, driver);
- assert(img->getDimension().Height == npot2(img->getDimension().Height));
- assert(img->getDimension().Width == npot2(img->getDimension().Width));
+ sanity_check(img->getDimension().Height == npot2(img->getDimension().Height));
+ sanity_check(img->getDimension().Width == npot2(img->getDimension().Width));
#endif
// Create texture from resulting image
video::ITexture *t = NULL;
if (img) {
t = driver->addTexture(ti->name.c_str(), img);
+ guiScalingCache(io::path(ti->name.c_str()), driver, img);
img->drop();
}
video::ITexture *t_old = ti->texture;
@@ -726,7 +746,7 @@ video::ITexture* TextureSource::generateTextureFromMesh(
const TextureFromMeshParams &params)
{
video::IVideoDriver *driver = m_device->getVideoDriver();
- assert(driver);
+ sanity_check(driver);
#ifdef __ANDROID__
const GLubyte* renderstr = glGetString(GL_RENDERER);
@@ -742,9 +762,9 @@ video::ITexture* TextureSource::generateTextureFromMesh(
) {
// Get a scene manager
scene::ISceneManager *smgr_main = m_device->getSceneManager();
- assert(smgr_main);
+ sanity_check(smgr_main);
scene::ISceneManager *smgr = smgr_main->createNewSceneManager();
- assert(smgr);
+ sanity_check(smgr);
const float scaling = 0.2;
@@ -831,6 +851,8 @@ video::ITexture* TextureSource::generateTextureFromMesh(
rawImage->copyToScaling(inventory_image);
rawImage->drop();
+ guiScalingCache(io::path(params.rtt_texture_name.c_str()), driver, inventory_image);
+
video::ITexture *rtt = driver->addTexture(params.rtt_texture_name.c_str(), inventory_image);
inventory_image->drop();
@@ -979,7 +1001,7 @@ video::IImage* TextureSource::generateImage(const std::string &name)
video::IVideoDriver* driver = m_device->getVideoDriver();
- assert(driver);
+ sanity_check(driver);
/*
Parse out the last part of the name of the image and act
@@ -988,7 +1010,7 @@ video::IImage* TextureSource::generateImage(const std::string &name)
std::string last_part_of_name = name.substr(last_separator_pos + 1);
- /*
+ /*
If this name is enclosed in parentheses, generate it
and blit it onto the base image
*/
@@ -1079,7 +1101,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
video::IImage *& baseimg)
{
video::IVideoDriver* driver = m_device->getVideoDriver();
- assert(driver);
+ sanity_check(driver);
// Stuff starting with [ are special commands
if (part_of_name.size() == 0 || part_of_name[0] != '[')
@@ -1107,7 +1129,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
//core::dimension2d<u32> dim(2,2);
core::dimension2d<u32> dim(1,1);
image = driver->createImage(video::ECF_A8R8G8B8, dim);
- assert(image);
+ sanity_check(image != NULL);
/*image->setPixel(0,0, video::SColor(255,255,0,0));
image->setPixel(1,0, video::SColor(255,0,255,0));
image->setPixel(0,1, video::SColor(255,0,0,255));
@@ -1170,7 +1192,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
Adds a cracking texture
N = animation frame count, P = crack progression
*/
- if (part_of_name.substr(0,6) == "[crack")
+ if (str_starts_with(part_of_name, "[crack"))
{
if (baseimg == NULL) {
errorstream<<"generateImagePart(): baseimg == NULL "
@@ -1186,28 +1208,29 @@ bool TextureSource::generateImagePart(std::string part_of_name,
s32 frame_count = stoi(sf.next(":"));
s32 progression = stoi(sf.next(":"));
- /*
- Load crack image.
+ if (progression >= 0) {
+ /*
+ Load crack image.
- It is an image with a number of cracking stages
- horizontally tiled.
- */
- video::IImage *img_crack = m_sourcecache.getOrLoad(
+ It is an image with a number of cracking stages
+ horizontally tiled.
+ */
+ video::IImage *img_crack = m_sourcecache.getOrLoad(
"crack_anylength.png", m_device);
- if (img_crack && progression >= 0)
- {
- draw_crack(img_crack, baseimg,
+ if (img_crack) {
+ draw_crack(img_crack, baseimg,
use_overlay, frame_count,
progression, driver);
- img_crack->drop();
+ img_crack->drop();
+ }
}
}
/*
[combine:WxH:X,Y=filename:X,Y=filename2
Creates a bigger texture from an amount of smaller ones
*/
- else if (part_of_name.substr(0,8) == "[combine")
+ else if (str_starts_with(part_of_name, "[combine"))
{
Strfnd sf(part_of_name);
sf.next(":");
@@ -1251,7 +1274,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
/*
"[brighten"
*/
- else if (part_of_name.substr(0,9) == "[brighten")
+ else if (str_starts_with(part_of_name, "[brighten"))
{
if (baseimg == NULL) {
errorstream<<"generateImagePart(): baseimg==NULL "
@@ -1269,7 +1292,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
that the transparent parts don't look completely black
when simple alpha channel is used for rendering.
*/
- else if (part_of_name.substr(0,8) == "[noalpha")
+ else if (str_starts_with(part_of_name, "[noalpha"))
{
if (baseimg == NULL){
errorstream<<"generateImagePart(): baseimg==NULL "
@@ -1293,7 +1316,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
"[makealpha:R,G,B"
Convert one color to transparent.
*/
- else if (part_of_name.substr(0,11) == "[makealpha:")
+ else if (str_starts_with(part_of_name, "[makealpha:"))
{
if (baseimg == NULL) {
errorstream<<"generateImagePart(): baseimg == NULL "
@@ -1349,7 +1372,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
The resulting transform will be equivalent to one of the
eight existing ones, though (see: dihedral group).
*/
- else if (part_of_name.substr(0,10) == "[transform")
+ else if (str_starts_with(part_of_name, "[transform"))
{
if (baseimg == NULL) {
errorstream<<"generateImagePart(): baseimg == NULL "
@@ -1363,7 +1386,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
transform, baseimg->getDimension());
video::IImage *image = driver->createImage(
baseimg->getColorFormat(), dim);
- assert(image);
+ sanity_check(image != NULL);
imageTransform(transform, baseimg, image);
baseimg->drop();
baseimg = image;
@@ -1376,7 +1399,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
Example (a grass block (not actually used in game):
"[inventorycube{grass.png{mud.png&grass_side.png{mud.png&grass_side.png"
*/
- else if (part_of_name.substr(0,14) == "[inventorycube")
+ else if (str_starts_with(part_of_name, "[inventorycube"))
{
if (baseimg != NULL){
errorstream<<"generateImagePart(): baseimg != NULL "
@@ -1423,7 +1446,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
(imagename_left + "__temp__").c_str(), img_left);
video::ITexture *texture_right = driver->addTexture(
(imagename_right + "__temp__").c_str(), img_right);
- assert(texture_top && texture_left && texture_right);
+ FATAL_ERROR_IF(!(texture_top && texture_left && texture_right), "");
// Drop images
img_top->drop();
@@ -1477,7 +1500,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
// Create image of render target
video::IImage *image = driver->createImage(rtt, v2s32(0, 0), params.dim);
- assert(image);
+ FATAL_ERROR_IF(!image, "Could not create image of render target");
// Cleanup texture
driver->removeTexture(rtt);
@@ -1493,7 +1516,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
[lowpart:percent:filename
Adds the lower part of a texture
*/
- else if (part_of_name.substr(0,9) == "[lowpart:")
+ else if (str_starts_with(part_of_name, "[lowpart:"))
{
Strfnd sf(part_of_name);
sf.next(":");
@@ -1529,7 +1552,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
Crops a frame of a vertical animation.
N = frame count, I = frame index
*/
- else if (part_of_name.substr(0,15) == "[verticalframe:")
+ else if (str_starts_with(part_of_name, "[verticalframe:"))
{
Strfnd sf(part_of_name);
sf.next(":");
@@ -1573,7 +1596,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
[mask:filename
Applies a mask to an image
*/
- else if (part_of_name.substr(0,6) == "[mask:")
+ else if (str_starts_with(part_of_name, "[mask:"))
{
if (baseimg == NULL) {
errorstream << "generateImage(): baseimg == NULL "
@@ -1589,6 +1612,7 @@ bool TextureSource::generateImagePart(std::string part_of_name,
if (img) {
apply_mask(img, baseimg, v2s32(0, 0), v2s32(0, 0),
img->getDimension());
+ img->drop();
} else {
errorstream << "generateImage(): Failed to load \""
<< filename << "\".";
@@ -1599,7 +1623,8 @@ bool TextureSource::generateImagePart(std::string part_of_name,
Overlays image with given color
color = color as ColorString
*/
- else if (part_of_name.substr(0,10) == "[colorize:") {
+ else if (str_starts_with(part_of_name, "[colorize:"))
+ {
Strfnd sf(part_of_name);
sf.next(":");
std::string color_str = sf.next(":");
@@ -1636,6 +1661,43 @@ bool TextureSource::generateImagePart(std::string part_of_name,
blit_with_interpolate_overlay(img, baseimg, v2s32(0,0), v2s32(0,0), dim, ratio);
img->drop();
}
+ else if (str_starts_with(part_of_name, "[applyfiltersformesh"))
+ {
+ // Apply the "clean transparent" filter, if configured.
+ if (g_settings->getBool("texture_clean_transparent"))
+ imageCleanTransparent(baseimg, 127);
+
+ /* Upscale textures to user's requested minimum size. This is a trick to make
+ * filters look as good on low-res textures as on high-res ones, by making
+ * low-res textures BECOME high-res ones. This is helpful for worlds that
+ * mix high- and low-res textures, or for mods with least-common-denominator
+ * textures that don't have the resources to offer high-res alternatives.
+ */
+ s32 scaleto = g_settings->getS32("texture_min_size");
+ if (scaleto > 1) {
+ const core::dimension2d<u32> dim = baseimg->getDimension();
+
+ /* Calculate scaling needed to make the shortest texture dimension
+ * equal to the target minimum. If e.g. this is a vertical frames
+ * animation, the short dimension will be the real size.
+ */
+ u32 xscale = scaleto / dim.Width;
+ u32 yscale = scaleto / dim.Height;
+ u32 scale = (xscale > yscale) ? xscale : yscale;
+
+ // Never downscale; only scale up by 2x or more.
+ if (scale > 1) {
+ u32 w = scale * dim.Width;
+ u32 h = scale * dim.Height;
+ const core::dimension2d<u32> newdim = core::dimension2d<u32>(w, h);
+ video::IImage *newimg = driver->createImage(
+ baseimg->getColorFormat(), newdim);
+ baseimg->copyToScaling(newimg);
+ baseimg->drop();
+ baseimg = newimg;
+ }
+ }
+ }
else
{
errorstream << "generateImagePart(): Invalid "
@@ -1893,10 +1955,10 @@ void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
if (src == NULL || dst == NULL)
return;
- core::dimension2d<u32> srcdim = src->getDimension();
core::dimension2d<u32> dstdim = dst->getDimension();
- assert(dstdim == imageTransformDimension(transform, srcdim));
+ // Pre-conditions
+ assert(dstdim == imageTransformDimension(transform, src->getDimension()));
assert(transform <= 7);
/*
@@ -1935,9 +1997,8 @@ void imageTransform(u32 transform, video::IImage *src, video::IImage *dst)
video::ITexture* TextureSource::getNormalTexture(const std::string &name)
{
- u32 id;
if (isKnownSourceImage("override_normal.png"))
- return getTexture("override_normal.png", &id);
+ return getTexture("override_normal.png");
std::string fname_base = name;
std::string normal_ext = "_normal.png";
size_t pos = fname_base.find(".");
@@ -1949,7 +2010,65 @@ video::ITexture* TextureSource::getNormalTexture(const std::string &name)
fname_base.replace(i, 4, normal_ext);
i += normal_ext.length();
}
- return getTexture(fname_base, &id);
+ return getTexture(fname_base);
}
return NULL;
}
+
+video::SColor TextureSource::getTextureAverageColor(const std::string &name)
+{
+ video::IVideoDriver *driver = m_device->getVideoDriver();
+ video::SColor c(0, 0, 0, 0);
+ video::ITexture *texture = getTexture(name);
+ video::IImage *image = driver->createImage(texture,
+ core::position2d<s32>(0, 0),
+ texture->getOriginalSize());
+ u32 total = 0;
+ u32 tR = 0;
+ u32 tG = 0;
+ u32 tB = 0;
+ core::dimension2d<u32> dim = image->getDimension();
+ u16 step = 1;
+ if (dim.Width > 16)
+ step = dim.Width / 16;
+ for (u16 x = 0; x < dim.Width; x += step) {
+ for (u16 y = 0; y < dim.Width; y += step) {
+ c = image->getPixel(x,y);
+ if (c.getAlpha() > 0) {
+ total++;
+ tR += c.getRed();
+ tG += c.getGreen();
+ tB += c.getBlue();
+ }
+ }
+ }
+ image->drop();
+ if (total > 0) {
+ c.setRed(tR / total);
+ c.setGreen(tG / total);
+ c.setBlue(tB / total);
+ }
+ c.setAlpha(255);
+ return c;
+}
+
+
+video::ITexture *TextureSource::getShaderFlagsTexture(bool normalmap_present)
+{
+ std::string tname = "__shaderFlagsTexture";
+ tname += normalmap_present ? "1" : "0";
+
+ if (isKnownSourceImage(tname)) {
+ return getTexture(tname);
+ } else {
+ video::IVideoDriver *driver = m_device->getVideoDriver();
+ video::IImage *flags_image = driver->createImage(
+ video::ECF_A8R8G8B8, core::dimension2d<u32>(1, 1));
+ sanity_check(flags_image != NULL);
+ video::SColor c(255, normalmap_present ? 255 : 0, 0, 0);
+ flags_image->setPixel(0, 0, c);
+ insertSourceImage(tname, flags_image);
+ flags_image->drop();
+ return getTexture(tname);
+ }
+}
diff --git a/src/tile.h b/src/client/tile.h
index ea7a9135a..7796e801d 100644
--- a/src/tile.h
+++ b/src/client/tile.h
@@ -28,8 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "threads.h"
#include <string>
#include <vector>
+#include "util/numeric.h"
class IGameDef;
+struct TileSpec;
+struct TileDef;
/*
tile.{h,cpp}: Texture handling stuff.
@@ -102,11 +105,15 @@ public:
virtual video::ITexture* getTexture(u32 id)=0;
virtual video::ITexture* getTexture(
const std::string &name, u32 *id = NULL)=0;
+ virtual video::ITexture* getTextureForMesh(
+ const std::string &name, u32 *id = NULL) = 0;
virtual IrrlichtDevice* getDevice()=0;
virtual bool isKnownSourceImage(const std::string &name)=0;
virtual video::ITexture* generateTextureFromMesh(
const TextureFromMeshParams &params)=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
+ virtual video::SColor getTextureAverageColor(const std::string &name)=0;
+ virtual video::ITexture *getShaderFlagsTexture(bool normalmap_present)=0;
};
class IWritableTextureSource : public ITextureSource
@@ -128,26 +135,13 @@ public:
virtual void insertSourceImage(const std::string &name, video::IImage *img)=0;
virtual void rebuildImagesAndTextures()=0;
virtual video::ITexture* getNormalTexture(const std::string &name)=0;
+ virtual video::SColor getTextureAverageColor(const std::string &name)=0;
+ virtual video::ITexture *getShaderFlagsTexture(bool normalmap_present)=0;
};
IWritableTextureSource* createTextureSource(IrrlichtDevice *device);
#ifdef __ANDROID__
-/**
- * @param size get next npot2 value
- * @return npot2 value
- */
-inline unsigned int npot2(unsigned int size)
-{
- if (size == 0) return 0;
- unsigned int npot = 1;
-
- while ((size >>= 1) > 0) {
- npot <<= 1;
- }
- return npot;
-}
-
video::IImage * Align2Npot2(video::IImage * image, video::IVideoDriver* driver);
#endif
@@ -172,6 +166,8 @@ enum MaterialType{
// defined by extra parameters
#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08
#define MATERIAL_FLAG_HIGHLIGHTED 0x10
+#define MATERIAL_FLAG_TILEABLE_HORIZONTAL 0x20
+#define MATERIAL_FLAG_TILEABLE_VERTICAL 0x40
/*
This fully defines the looks of a tile.
@@ -182,12 +178,14 @@ struct FrameSpec
FrameSpec():
texture_id(0),
texture(NULL),
- normal_texture(NULL)
+ normal_texture(NULL),
+ flags_texture(NULL)
{
}
u32 texture_id;
video::ITexture *texture;
video::ITexture *normal_texture;
+ video::ITexture *flags_texture;
};
struct TileSpec
@@ -196,6 +194,7 @@ struct TileSpec
texture_id(0),
texture(NULL),
normal_texture(NULL),
+ flags_texture(NULL),
alpha(255),
material_type(TILE_MATERIAL_BASIC),
material_flags(
@@ -251,17 +250,32 @@ struct TileSpec
}
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;
+ if (!(material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)) {
+ material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
+ }
+ if (!(material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)) {
+ material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
+ }
}
void applyMaterialOptionsWithShaders(video::SMaterial &material) const
{
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING)
? true : false;
+ if (!(material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)) {
+ material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
+ material.TextureLayer[1].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
+ }
+ if (!(material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)) {
+ material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
+ material.TextureLayer[1].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
+ }
}
u32 texture_id;
video::ITexture *texture;
video::ITexture *normal_texture;
+ video::ITexture *flags_texture;
// Vertex alpha (when MATERIAL_ALPHA_VERTEX is used)
u8 alpha;
@@ -276,5 +290,4 @@ struct TileSpec
u8 rotation;
};
-
#endif
diff --git a/src/clientiface.cpp b/src/clientiface.cpp
index 40d1ef811..3330e0af9 100644
--- a/src/clientiface.cpp
+++ b/src/clientiface.cpp
@@ -25,13 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
#include "settings.h"
#include "mapblock.h"
-#include "connection.h"
+#include "network/connection.h"
#include "environment.h"
#include "map.h"
#include "emerge.h"
#include "serverobject.h" // TODO this is used for cleanup of only
-#include "main.h" // for g_settings
#include "log.h"
+#include "util/srp.h"
const char *ClientInterface::statenames[] = {
"Invalid",
@@ -59,7 +59,7 @@ void RemoteClient::ResendBlockIfOnWire(v3s16 p)
}
}
-void RemoteClient::GetNextBlocks(
+void RemoteClient::GetNextBlocks (
ServerEnvironment *env,
EmergeManager * emerge,
float dtime,
@@ -182,18 +182,15 @@ void RemoteClient::GetNextBlocks(
//bool queue_is_full = false;
s16 d;
- for(d = d_start; d <= d_max; d++)
- {
+ for(d = d_start; d <= d_max; d++) {
/*
Get the border/face dot coordinates of a "d-radiused"
box
*/
- std::list<v3s16> list;
- getFacePositions(list, d);
+ std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
- std::list<v3s16>::iterator li;
- for(li=list.begin(); li!=list.end(); ++li)
- {
+ std::vector<v3s16>::iterator li;
+ for(li = list.begin(); li != list.end(); ++li) {
v3s16 p = *li + center;
/*
@@ -212,25 +209,19 @@ void RemoteClient::GetNextBlocks(
max_simul_dynamic = max_simul_sends_setting;
// Don't select too many blocks for sending
- if(num_blocks_selected >= max_simul_dynamic)
- {
+ if (num_blocks_selected >= max_simul_dynamic) {
//queue_is_full = true;
goto queue_full_break;
}
// Don't send blocks that are currently being transferred
- if(m_blocks_sending.find(p) != m_blocks_sending.end())
+ if (m_blocks_sending.find(p) != m_blocks_sending.end())
continue;
/*
Do not go over-limit
*/
- if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
+ if (blockpos_over_limit(p))
continue;
// If this is true, inexistent block will be made from scratch
@@ -242,7 +233,7 @@ void RemoteClient::GetNextBlocks(
generate = false;*/
// Limit the send area vertically to 1/2
- if(abs(p.Y - center.Y) > full_d_max / 2)
+ if (abs(p.Y - center.Y) > full_d_max / 2)
continue;
}
@@ -431,10 +422,12 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
//intentionally do nothing
break;
case CS_Created:
- switch(event)
- {
- case CSE_Init:
- m_state = CS_InitSent;
+ switch (event) {
+ case CSE_Hello:
+ m_state = CS_HelloSent;
+ break;
+ case CSE_InitLegacy:
+ m_state = CS_AwaitingInit2;
break;
case CSE_Disconnect:
m_state = CS_Disconnecting;
@@ -451,7 +444,32 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
case CS_Denied:
/* don't do anything if in denied state */
break;
- case CS_InitSent:
+ case CS_HelloSent:
+ switch(event)
+ {
+ case CSE_AuthAccept:
+ m_state = CS_AwaitingInit2;
+ if ((chosen_mech == AUTH_MECHANISM_SRP)
+ || (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
+ srp_verifier_delete((SRPVerifier *) auth_data);
+ chosen_mech = AUTH_MECHANISM_NONE;
+ break;
+ case CSE_Disconnect:
+ m_state = CS_Disconnecting;
+ break;
+ case CSE_SetDenied:
+ m_state = CS_Denied;
+ if ((chosen_mech == AUTH_MECHANISM_SRP)
+ || (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
+ srp_verifier_delete((SRPVerifier *) auth_data);
+ chosen_mech = AUTH_MECHANISM_NONE;
+ break;
+ default:
+ myerror << "HelloSent: Invalid client state transition! " << event;
+ throw ClientStateError(myerror.str());
+ }
+ break;
+ case CS_AwaitingInit2:
switch(event)
{
case CSE_GotInit2:
@@ -518,6 +536,13 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
case CSE_Disconnect:
m_state = CS_Disconnecting;
break;
+ case CSE_SudoSuccess:
+ m_state = CS_SudoMode;
+ if ((chosen_mech == AUTH_MECHANISM_SRP)
+ || (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
+ srp_verifier_delete((SRPVerifier *) auth_data);
+ chosen_mech = AUTH_MECHANISM_NONE;
+ break;
/* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */
default:
myerror << "Active: Invalid client state transition! " << event;
@@ -525,6 +550,24 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
break;
}
break;
+ case CS_SudoMode:
+ switch(event)
+ {
+ case CSE_SetDenied:
+ m_state = CS_Denied;
+ break;
+ case CSE_Disconnect:
+ m_state = CS_Disconnecting;
+ break;
+ case CSE_SudoLeave:
+ m_state = CS_Active;
+ break;
+ default:
+ myerror << "Active: Invalid client state transition! " << event;
+ throw ClientStateError(myerror.str());
+ break;
+ }
+ break;
case CS_Disconnecting:
/* we are already disconnecting */
break;
@@ -563,9 +606,9 @@ ClientInterface::~ClientInterface()
}
}
-std::list<u16> ClientInterface::getClientIDs(ClientState min_state)
+std::vector<u16> ClientInterface::getClientIDs(ClientState min_state)
{
- std::list<u16> reply;
+ std::vector<u16> reply;
JMutexAutoLock clientslock(m_clients_mutex);
for(std::map<u16, RemoteClient*>::iterator
@@ -599,20 +642,22 @@ void ClientInterface::UpdatePlayerList()
{
if (m_env != NULL)
{
- std::list<u16> clients = getClientIDs();
+ std::vector<u16> clients = getClientIDs();
m_clients_names.clear();
if(!clients.empty())
infostream<<"Players:"<<std::endl;
- for(std::list<u16>::iterator
+
+ for(std::vector<u16>::iterator
i = clients.begin();
- i != clients.end(); ++i)
- {
+ i != clients.end(); ++i) {
Player *player = m_env->getPlayer(*i);
- if(player==NULL)
+
+ if (player == NULL)
continue;
- infostream<<"* "<<player->getName()<<"\t";
+
+ infostream << "* " << player->getName() << "\t";
{
JMutexAutoLock clientslock(m_clients_mutex);
@@ -620,30 +665,29 @@ void ClientInterface::UpdatePlayerList()
if(client != NULL)
client->PrintInfo(infostream);
}
+
m_clients_names.push_back(player->getName());
}
}
}
-void ClientInterface::send(u16 peer_id,u8 channelnum,
- SharedBuffer<u8> data, bool reliable)
+void ClientInterface::send(u16 peer_id, u8 channelnum,
+ NetworkPacket* pkt, bool reliable)
{
- m_con->Send(peer_id, channelnum, data, reliable);
+ m_con->Send(peer_id, channelnum, pkt, reliable);
}
void ClientInterface::sendToAll(u16 channelnum,
- SharedBuffer<u8> data, bool reliable)
+ NetworkPacket* pkt, bool reliable)
{
JMutexAutoLock clientslock(m_clients_mutex);
for(std::map<u16, RemoteClient*>::iterator
i = m_clients.begin();
- i != m_clients.end(); ++i)
- {
+ i != m_clients.end(); ++i) {
RemoteClient *client = i->second;
- if (client->net_proto_version != 0)
- {
- m_con->Send(client->peer_id, channelnum, data, reliable);
+ if (client->net_proto_version != 0) {
+ m_con->Send(client->peer_id, channelnum, pkt, reliable);
}
}
}
diff --git a/src/clientiface.h b/src/clientiface.h
index cb3dae04b..f6c4294e2 100644
--- a/src/clientiface.h
+++ b/src/clientiface.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "serialization.h" // for SER_FMT_VER_INVALID
#include "jthread/jmutex.h"
+#include "network/networkpacket.h"
#include <list>
#include <vector>
@@ -46,96 +47,119 @@ class EmergeManager;
| Created |
| |
\-----------------/
- |
- |
-+-----------------------------+ invalid playername, password
-|IN: | or denied by mod
-| TOSERVER_INIT |------------------------------
-+-----------------------------+ |
- | |
- | Auth ok |
- | |
-+-----------------------------+ |
-|OUT: | |
-| TOCLIENT_INIT | |
-+-----------------------------+ |
- | |
- v |
- /-----------------\ |
- | | |
- | InitSent | |
- | | |
- \-----------------/ +------------------
- | | |
-+-----------------------------+ +-----------------------------+ |
-|IN: | |OUT: | |
-| TOSERVER_INIT2 | | TOCLIENT_ACCESS_DENIED | |
-+-----------------------------+ +-----------------------------+ |
- | | |
- v v |
- /-----------------\ /-----------------\ |
- | | | | |
- | InitDone | | Denied | |
- | | | | |
- \-----------------/ \-----------------/ |
- | |
-+-----------------------------+ |
-|OUT: | |
-| TOCLIENT_MOVEMENT | |
-| TOCLIENT_ITEMDEF | |
-| TOCLIENT_NODEDEF | |
-| TOCLIENT_ANNOUNCE_MEDIA | |
-| TOCLIENT_DETACHED_INVENTORY | |
-| TOCLIENT_TIME_OF_DAY | |
-+-----------------------------+ |
- | |
- | |
- | ----------------------------------- |
- v | | |
- /-----------------\ v |
- | | +-----------------------------+ |
- | DefinitionsSent | |IN: | |
- | | | TOSERVER_REQUEST_MEDIA | |
- \-----------------/ | TOSERVER_RECEIVED_MEDIA | |
- | +-----------------------------+ |
- | ^ | |
- | ----------------------------------- |
- | |
-+-----------------------------+ |
-|IN: | |
-| TOSERVER_CLIENT_READY | |
-+-----------------------------+ |
- | async |
- v mod action |
-+-----------------------------+ (ban,kick) |
-|OUT: | |
-| TOCLIENT_MOVE_PLAYER | |
-| TOCLIENT_PRIVILEGES | |
-| TOCLIENT_INVENTORY_FORMSPEC | |
-| UpdateCrafting | |
-| TOCLIENT_INVENTORY | |
-| TOCLIENT_HP (opt) | |
-| TOCLIENT_BREATH | |
-| TOCLIENT_DEATHSCREEN | |
-+-----------------------------+ |
- | |
- v |
- /-----------------\ |
- | |------------------------------------------------------
- | Active |
- | |----------------------------------
- \-----------------/ timeout |
- | +-----------------------------+
- | |OUT: |
- | | TOCLIENT_DISCONNECT |
- | +-----------------------------+
- | |
- | v
-+-----------------------------+ /-----------------\
-|IN: | | |
-| TOSERVER_DISCONNECT |------------------->| Disconnecting |
-+-----------------------------+ | |
- \-----------------/
+ | depending of the incoming packet
+ +---------------------------------------
+ v v
++-----------------------------+ +-----------------------------+
+|IN: | |IN: |
+| TOSERVER_INIT_LEGACY |----- | TOSERVER_INIT | invalid playername,
++-----------------------------+ | +-----------------------------+ password (for _LEGACY),
+ | | | or denied by mod
+ | Auth ok -------------------+---------------------------------
+ v v |
++-----------------------------+ +-----------------------------+ |
+|OUT: | |OUT: | |
+| TOCLIENT_INIT_LEGACY | | TOCLIENT_HELLO | |
++-----------------------------+ +-----------------------------+ |
+ | | |
+ | | |
+ v v |
+ /-----------------\ /-----------------\ |
+ | | | | |
+ | AwaitingInit2 |<--------- | HelloSent | |
+ | | | | | |
+ \-----------------/ | \-----------------/ |
+ | | | |
++-----------------------------+ | *-----------------------------* Auth fails |
+|IN: | | |Authentication, depending on |-----------------+
+| TOSERVER_INIT2 | | | packet sent by client | |
++-----------------------------+ | *-----------------------------* |
+ | | | |
+ | | | Authentication |
+ v | | successful |
+ /-----------------\ | v |
+ | | | +-----------------------------+ |
+ | InitDone | | |OUT: | |
+ | | | | TOCLIENT_AUTH_ACCEPT | |
+ \-----------------/ | +-----------------------------+ |
+ | | | |
++-----------------------------+ --------------------- |
+|OUT: | |
+| TOCLIENT_MOVEMENT | |
+| TOCLIENT_ITEMDEF | |
+| TOCLIENT_NODEDEF | |
+| TOCLIENT_ANNOUNCE_MEDIA | |
+| TOCLIENT_DETACHED_INVENTORY | |
+| TOCLIENT_TIME_OF_DAY | |
++-----------------------------+ |
+ | |
+ | |
+ | ----------------------------- |
+ v | | |
+ /-----------------\ v |
+ | | +-----------------------------+ |
+ | DefinitionsSent | |IN: | |
+ | | | TOSERVER_REQUEST_MEDIA | |
+ \-----------------/ | TOSERVER_RECEIVED_MEDIA | |
+ | +-----------------------------+ |
+ | ^ | |
+ | ----------------------------- |
+ v |
++-----------------------------+ --------------------------------+
+|IN: | | |
+| TOSERVER_CLIENT_READY | v |
++-----------------------------+ +-------------------------------+ |
+ | |OUT: | |
+ v | TOCLIENT_ACCESS_DENIED_LEGAGY | |
++-----------------------------+ +-------------------------------+ |
+|OUT: | | |
+| TOCLIENT_MOVE_PLAYER | v |
+| TOCLIENT_PRIVILEGES | /-----------------\ |
+| TOCLIENT_INVENTORY_FORMSPEC | | | |
+| UpdateCrafting | | Denied | |
+| TOCLIENT_INVENTORY | | | |
+| TOCLIENT_HP (opt) | \-----------------/ |
+| TOCLIENT_BREATH | |
+| TOCLIENT_DEATHSCREEN | |
++-----------------------------+ |
+ | |
+ v |
+ /-----------------\ async mod action (ban, kick) |
+ | |---------------------------------------------------------------
+ ---->| Active |
+ | | |----------------------------------------------
+ | \-----------------/ timeout v
+ | | | +-----------------------------+
+ | | | |OUT: |
+ | | | | TOCLIENT_DISCONNECT |
+ | | | +-----------------------------+
+ | | | |
+ | | v v
+ | | +-----------------------------+ /-----------------\
+ | | |IN: | | |
+ | | | TOSERVER_DISCONNECT |------------------->| Disconnecting |
+ | | +-----------------------------+ | |
+ | | \-----------------/
+ | | any auth packet which was
+ | | allowed in TOCLIENT_AUTH_ACCEPT
+ | v
+ | *-----------------------------* Auth +-------------------------------+
+ | |Authentication, depending on | succeeds |OUT: |
+ | | packet sent by client |---------->| TOCLIENT_ACCEPT_SUDO_MODE |
+ | *-----------------------------* +-------------------------------+
+ | | |
+ | | Auth fails /-----------------\
+ | v | |
+ | +-------------------------------+ | SudoMode |
+ | |OUT: | | |
+ | | TOCLIENT_DENY_SUDO_MODE | \-----------------/
+ | +-------------------------------+ |
+ | | v
+ | | +-----------------------------+
+ | | sets password accordingly |IN: |
+ -------------------+-------------------------------| TOSERVER_FIRST_SRP |
+ +-----------------------------+
+
*/
namespace con {
class Connection;
@@ -149,19 +173,25 @@ enum ClientState
CS_Disconnecting,
CS_Denied,
CS_Created,
- CS_InitSent,
+ CS_AwaitingInit2,
+ CS_HelloSent,
CS_InitDone,
CS_DefinitionsSent,
- CS_Active
+ CS_Active,
+ CS_SudoMode
};
enum ClientStateEvent
{
- CSE_Init,
+ CSE_Hello,
+ CSE_AuthAccept,
+ CSE_InitLegacy,
CSE_GotInit2,
CSE_SetDenied,
CSE_SetDefinitionsSent,
CSE_SetClientReady,
+ CSE_SudoSuccess,
+ CSE_SudoLeave,
CSE_Disconnect
};
@@ -200,10 +230,26 @@ public:
//
u16 net_proto_version;
+ /* Authentication information */
+ std::string enc_pwd;
+ bool create_player_on_auth_success;
+ AuthMechanism chosen_mech;
+ void * auth_data;
+ u32 allowed_auth_mechs;
+ u32 allowed_sudo_mechs;
+
+ bool isSudoMechAllowed(AuthMechanism mech)
+ { return allowed_sudo_mechs & mech; }
+ bool isMechAllowed(AuthMechanism mech)
+ { return allowed_auth_mechs & mech; }
+
RemoteClient():
peer_id(PEER_ID_INEXISTENT),
serialization_version(SER_FMT_VER_INVALID),
net_proto_version(0),
+ create_player_on_auth_success(false),
+ chosen_mech(AUTH_MECHANISM_NONE),
+ auth_data(NULL),
m_time_from_building(9999),
m_pending_serialization_version(SER_FMT_VER_INVALID),
m_state(CS_Created),
@@ -216,6 +262,7 @@ public:
m_version_minor(0),
m_version_patch(0),
m_full_version("unknown"),
+ m_deployed_compression(0),
m_connection_time(getTime(PRECISION_SECONDS))
{
}
@@ -292,13 +339,15 @@ public:
void setPendingSerializationVersion(u8 version)
{ m_pending_serialization_version = version; }
+ void setDeployedCompressionMode(u16 byteFlag)
+ { m_deployed_compression = byteFlag; }
+
void confirmSerializationVersion()
{ serialization_version = m_pending_serialization_version; }
/* get uptime */
u32 uptime();
-
/* set version information */
void setVersionInfo(u8 major, u8 minor, u8 patch, std::string full) {
m_version_major = major;
@@ -369,6 +418,8 @@ private:
std::string m_full_version;
+ u16 m_deployed_compression;
+
/*
time this client was created
*/
@@ -387,16 +438,16 @@ public:
void step(float dtime);
/* get list of active client id's */
- std::list<u16> getClientIDs(ClientState min_state=CS_Active);
+ std::vector<u16> getClientIDs(ClientState min_state=CS_Active);
/* get list of client player names */
std::vector<std::string> getPlayerNames();
/* send message to client */
- void send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable);
+ void send(u16 peer_id, u8 channelnum, NetworkPacket* pkt, bool reliable);
/* send to all clients */
- void sendToAll(u16 channelnum, SharedBuffer<u8> data, bool reliable);
+ void sendToAll(u16 channelnum, NetworkPacket* pkt, bool reliable);
/* delete a client */
void DeleteClient(u16 peer_id);
@@ -425,9 +476,12 @@ public:
/* event to update client state */
void event(u16 peer_id, ClientStateEvent event);
- /* set environment */
- void setEnv(ServerEnvironment* env)
- { assert(m_env == 0); m_env = env; }
+ /* Set environment. Do not call this function if environment is already set */
+ void setEnv(ServerEnvironment *env)
+ {
+ assert(m_env == NULL); // pre-condition
+ m_env = env;
+ }
static std::string state2Name(ClientState state);
@@ -457,7 +511,7 @@ private:
JMutex m_env_mutex;
float m_print_info_timer;
-
+
static const char *statenames[];
};
diff --git a/src/clientmap.cpp b/src/clientmap.cpp
index 2db901f22..288a12135 100644
--- a/src/clientmap.cpp
+++ b/src/clientmap.cpp
@@ -24,12 +24,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <matrix4.h>
#include "log.h"
#include "mapsector.h"
-#include "main.h" // dout_client, g_settings
#include "nodedef.h"
#include "mapblock.h"
#include "profiler.h"
#include "settings.h"
-#include "camera.h" // CameraModes
+#include "camera.h" // CameraModes
#include "util/mathconstants.h"
#include <algorithm>
@@ -102,34 +101,6 @@ MapSector * ClientMap::emergeSector(v2s16 p2d)
return sector;
}
-#if 0
-void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
-{
- DSTACK(__FUNCTION_NAME);
- ClientMapSector *sector = NULL;
-
- //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
-
- core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
-
- if(n != NULL)
- {
- sector = (ClientMapSector*)n->getValue();
- assert(sector->getId() == MAPSECTOR_CLIENT);
- }
- else
- {
- sector = new ClientMapSector(this, p2d);
- {
- //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
- m_sectors.insert(p2d, sector);
- }
- }
-
- sector->deSerialize(is);
-}
-#endif
-
void ClientMap::OnRegisterSceneNode()
{
if(IsVisible)
@@ -161,9 +132,9 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
else
is_transparent = (f.solidness != 2);
if(!is_transparent){
- count++;
- if(count >= needed_count)
+ if(count == needed_count)
return true;
+ count++;
}
step *= stepfac;
}
@@ -247,7 +218,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
continue;
}
- std::list< MapBlock * > sectorblocks;
+ MapBlockVect sectorblocks;
sector->getBlocks(sectorblocks);
/*
@@ -256,8 +227,8 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
u32 sector_blocks_drawn = 0;
- std::list< MapBlock * >::iterator i;
- for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
+ for(MapBlockVect::iterator i = sectorblocks.begin();
+ i != sectorblocks.end(); i++)
{
MapBlock *block = *i;
@@ -391,12 +362,12 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver)
struct MeshBufList
{
video::SMaterial m;
- std::list<scene::IMeshBuffer*> bufs;
+ std::vector<scene::IMeshBuffer*> bufs;
};
struct MeshBufListList
{
- std::list<MeshBufList> lists;
+ std::vector<MeshBufList> lists;
void clear()
{
@@ -405,7 +376,7 @@ struct MeshBufListList
void add(scene::IMeshBuffer *buf)
{
- for(std::list<MeshBufList>::iterator i = lists.begin();
+ for(std::vector<MeshBufList>::iterator i = lists.begin();
i != lists.end(); ++i){
MeshBufList &l = *i;
video::SMaterial &m = buf->getMaterial();
@@ -568,7 +539,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
- scene::SMesh *mesh = mapBlockMesh->getMesh();
+ scene::IMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
u32 c = mesh->getMeshBufferCount();
@@ -595,25 +566,20 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
}
}
- std::list<MeshBufList> &lists = drawbufs.lists;
+ std::vector<MeshBufList> &lists = drawbufs.lists;
int timecheck_counter = 0;
- for(std::list<MeshBufList>::iterator i = lists.begin();
- i != lists.end(); ++i)
- {
- {
- timecheck_counter++;
- if(timecheck_counter > 50)
- {
- timecheck_counter = 0;
- int time2 = time(0);
- if(time2 > time1 + 4)
- {
- infostream<<"ClientMap::renderMap(): "
- "Rendering takes ages, returning."
- <<std::endl;
- return;
- }
+ for(std::vector<MeshBufList>::iterator i = lists.begin();
+ i != lists.end(); ++i) {
+ timecheck_counter++;
+ if(timecheck_counter > 50) {
+ timecheck_counter = 0;
+ int time2 = time(0);
+ if(time2 > time1 + 4) {
+ infostream << "ClientMap::renderMap(): "
+ "Rendering takes ages, returning."
+ << std::endl;
+ return;
}
}
@@ -621,60 +587,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
driver->setMaterial(list.m);
- for(std::list<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
- j != list.bufs.end(); ++j)
- {
+ for(std::vector<scene::IMeshBuffer*>::iterator j = list.bufs.begin();
+ j != list.bufs.end(); ++j) {
scene::IMeshBuffer *buf = *j;
driver->drawMeshBuffer(buf);
vertex_count += buf->getVertexCount();
meshbuffer_count++;
}
-#if 0
- /*
- Draw the faces of the block
- */
- {
- //JMutexAutoLock lock(block->mesh_mutex);
- MapBlockMesh *mapBlockMesh = block->mesh;
- assert(mapBlockMesh);
-
- scene::SMesh *mesh = mapBlockMesh->getMesh();
- assert(mesh);
-
- u32 c = mesh->getMeshBufferCount();
- bool stuff_actually_drawn = false;
- for(u32 i=0; i<c; i++)
- {
- scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
- const video::SMaterial& material = buf->getMaterial();
- video::IMaterialRenderer* rnd =
- driver->getMaterialRenderer(material.MaterialType);
- bool transparent = (rnd && rnd->isTransparent());
- // Render transparent on transparent pass and likewise.
- if(transparent == is_transparent_pass)
- {
- if(buf->getVertexCount() == 0)
- errorstream<<"Block ["<<analyze_block(block)
- <<"] contains an empty meshbuf"<<std::endl;
- /*
- This *shouldn't* hurt too much because Irrlicht
- doesn't change opengl textures if the old
- material has the same texture.
- */
- driver->setMaterial(buf->getMaterial());
- driver->drawMeshBuffer(buf);
- vertex_count += buf->getVertexCount();
- meshbuffer_count++;
- stuff_actually_drawn = true;
- }
- }
- if(stuff_actually_drawn)
- blocks_had_pass_meshbuf++;
- else
- blocks_without_stuff++;
- }
-#endif
}
} // ScopeProfiler
diff --git a/src/clientmedia.cpp b/src/clientmedia.cpp
index 434eeb248..ea11ad239 100644
--- a/src/clientmedia.cpp
+++ b/src/clientmedia.cpp
@@ -18,20 +18,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "clientmedia.h"
-#include "util/serialize.h"
-#include "util/string.h"
#include "httpfetch.h"
#include "client.h"
-#include "clientserver.h"
#include "filecache.h"
#include "filesys.h"
-#include "hex.h"
-#include "sha1.h"
#include "debug.h"
#include "log.h"
#include "porting.h"
#include "settings.h"
-#include "main.h"
+#include "network/networkprotocol.h"
+#include "util/hex.h"
+#include "util/serialize.h"
+#include "util/sha1.h"
+#include "util/string.h"
static std::string getMediaCacheDir()
{
@@ -72,7 +71,7 @@ ClientMediaDownloader::~ClientMediaDownloader()
void ClientMediaDownloader::addFile(std::string name, std::string sha1)
{
- assert(!m_initial_step_done);
+ assert(!m_initial_step_done); // pre-condition
// if name was already announced, ignore the new announcement
if (m_files.count(name) != 0) {
@@ -107,7 +106,7 @@ void ClientMediaDownloader::addFile(std::string name, std::string sha1)
void ClientMediaDownloader::addRemoteServer(std::string baseurl)
{
- assert(!m_initial_step_done);
+ assert(!m_initial_step_done); // pre-condition
#ifdef USE_CURL
@@ -356,11 +355,11 @@ void ClientMediaDownloader::remoteMediaReceived(
m_remote_file_transfers.erase(it);
}
- assert(m_files.count(name) != 0);
+ sanity_check(m_files.count(name) != 0);
FileStatus *filestatus = m_files[name];
- assert(!filestatus->received);
- assert(filestatus->current_remote >= 0);
+ sanity_check(!filestatus->received);
+ sanity_check(filestatus->current_remote >= 0);
RemoteServerStatus *remote = m_remotes[filestatus->current_remote];
@@ -382,6 +381,7 @@ void ClientMediaDownloader::remoteMediaReceived(
s32 ClientMediaDownloader::selectRemoteServer(FileStatus *filestatus)
{
+ // Pre-conditions
assert(filestatus != NULL);
assert(!filestatus->received);
assert(filestatus->current_remote < 0);
@@ -483,12 +483,12 @@ void ClientMediaDownloader::startRemoteMediaTransfers()
void ClientMediaDownloader::startConventionalTransfers(Client *client)
{
- assert(m_httpfetch_active == 0);
+ assert(m_httpfetch_active == 0); // pre-condition
if (m_uncached_received_count != m_uncached_count) {
// Some media files have not been received yet, use the
// conventional slow method (minetest protocol) to get them
- std::list<std::string> file_requests;
+ std::vector<std::string> file_requests;
for (std::map<std::string, FileStatus*>::iterator
it = m_files.begin();
it != m_files.end(); ++it) {
@@ -616,7 +616,7 @@ std::string ClientMediaDownloader::serializeRequiredHashSet()
it = m_files.begin();
it != m_files.end(); ++it) {
if (!it->second->received) {
- assert(it->second->sha1.size() == 20);
+ FATAL_ERROR_IF(it->second->sha1.size() != 20, "Invalid SHA1 size");
os << it->second->sha1;
}
}
diff --git a/src/clientobject.cpp b/src/clientobject.cpp
index 37f693c5e..ae1be092f 100644
--- a/src/clientobject.cpp
+++ b/src/clientobject.cpp
@@ -39,14 +39,13 @@ ClientActiveObject::~ClientActiveObject()
removeFromScene(true);
}
-ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef,
- ClientEnvironment *env)
+ClientActiveObject* ClientActiveObject::create(ActiveObjectType type,
+ IGameDef *gamedef, ClientEnvironment *env)
{
// Find factory function
std::map<u16, Factory>::iterator n;
n = m_types.find(type);
- if(n == m_types.end())
- {
+ if(n == m_types.end()) {
// If factory is not found, just return.
dstream<<"WARNING: ClientActiveObject: No factory for type="
<<(int)type<<std::endl;
diff --git a/src/clientobject.h b/src/clientobject.h
index 24150628e..be24e1388 100644
--- a/src/clientobject.h
+++ b/src/clientobject.h
@@ -54,6 +54,7 @@ public:
virtual void removeFromScene(bool permanent){}
// 0 <= light_at_pos <= LIGHT_SUN
virtual void updateLight(u8 light_at_pos){}
+ virtual void updateLightNoCheck(u8 light_at_pos){}
virtual v3s16 getLightPosition(){return v3s16(0,0,0);}
virtual core::aabbox3d<f32>* getSelectionBox(){return NULL;}
virtual bool getCollisionBox(aabb3f *toset){return false;}
@@ -86,7 +87,7 @@ public:
virtual void initialize(const std::string &data){}
// Create a certain type of ClientActiveObject
- static ClientActiveObject* create(u8 type, IGameDef *gamedef,
+ static ClientActiveObject* create(ActiveObjectType type, IGameDef *gamedef,
ClientEnvironment *env);
// If returns true, punch will not be sent to the server
diff --git a/src/clouds.cpp b/src/clouds.cpp
index 10ac8f5b3..29210e2b4 100644
--- a/src/clouds.cpp
+++ b/src/clouds.cpp
@@ -21,10 +21,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "noise.h"
#include "constants.h"
#include "debug.h"
-#include "main.h" // For g_profiler and g_settings
#include "profiler.h"
#include "settings.h"
+
+// Menu clouds are created later
+class Clouds;
+Clouds *g_menuclouds = NULL;
+irr::scene::ISceneManager *g_menucloudsmgr = NULL;
+
+static void cloud_3d_setting_changed(const std::string &settingname, void *data)
+{
+ ((Clouds *)data)->readSettings();
+}
+
Clouds::Clouds(
scene::ISceneNode* parent,
scene::ISceneManager* mgr,
@@ -47,8 +57,10 @@ Clouds::Clouds(
//m_material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- m_cloud_y = BS * (cloudheight ? cloudheight :
- g_settings->getS16("cloud_height"));
+ m_passed_cloud_y = cloudheight;
+ readSettings();
+ g_settings->registerChangedCallback("enable_3d_clouds",
+ &cloud_3d_setting_changed, this);
m_box = core::aabbox3d<f32>(-BS*1000000,m_cloud_y-BS,-BS*1000000,
BS*1000000,m_cloud_y+BS,BS*1000000);
@@ -57,6 +69,8 @@ Clouds::Clouds(
Clouds::~Clouds()
{
+ g_settings->deregisterChangedCallback("enable_3d_clouds",
+ &cloud_3d_setting_changed, this);
}
void Clouds::OnRegisterSceneNode()
@@ -82,26 +96,24 @@ void Clouds::render()
ScopeProfiler sp(g_profiler, "Rendering of clouds, avg", SPT_AVG);
- bool enable_3d = g_settings->getBool("enable_3d_clouds");
- int num_faces_to_draw = enable_3d ? 6 : 1;
+ int num_faces_to_draw = m_enable_3d ? 6 : 1;
- m_material.setFlag(video::EMF_BACK_FACE_CULLING, enable_3d);
+ m_material.setFlag(video::EMF_BACK_FACE_CULLING, m_enable_3d);
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
driver->setMaterial(m_material);
/*
- Clouds move from X+ towards X-
+ Clouds move from Z+ towards Z-
*/
- const s16 cloud_radius_i = 12;
- const float cloud_size = BS*64;
- const v2f cloud_speed(0, -BS*2);
+ const float cloud_size = BS * 64;
+ const v2f cloud_speed(0, -BS * 2);
- const float cloud_full_radius = cloud_size * cloud_radius_i;
+ const float cloud_full_radius = cloud_size * m_cloud_radius_i;
// Position of cloud noise origin in world coordinates
- v2f world_cloud_origin_pos_f = m_time*cloud_speed;
+ v2f world_cloud_origin_pos_f = m_time * cloud_speed;
// Position of cloud noise origin from the camera
v2f cloud_origin_from_camera_f = world_cloud_origin_pos_f - m_camera_pos;
// The center point of drawing in the noise
@@ -160,55 +172,50 @@ void Clouds::render()
// Read noise
- bool *grid = new bool[cloud_radius_i*2*cloud_radius_i*2];
+ bool *grid = new bool[m_cloud_radius_i * 2 * m_cloud_radius_i * 2];
- for(s16 zi=-cloud_radius_i; zi<cloud_radius_i; zi++)
- for(s16 xi=-cloud_radius_i; xi<cloud_radius_i; xi++)
- {
- u32 i = (zi+cloud_radius_i)*cloud_radius_i*2 + xi+cloud_radius_i;
-
- v2s16 p_in_noise_i(
- xi+center_of_drawing_in_noise_i.X,
- zi+center_of_drawing_in_noise_i.Y
- );
-
-#if 0
- double noise = noise2d_perlin_abs(
- (float)p_in_noise_i.X*cloud_size/BS/200,
- (float)p_in_noise_i.Y*cloud_size/BS/200,
- m_seed, 3, 0.4);
- grid[i] = (noise >= 0.80);
-#endif
-#if 1
- double noise = noise2d_perlin(
- (float)p_in_noise_i.X*cloud_size/BS/200,
- (float)p_in_noise_i.Y*cloud_size/BS/200,
- m_seed, 3, 0.5);
- grid[i] = (noise >= 0.4);
-#endif
+ float cloud_size_noise = cloud_size / BS / 200;
+
+ for(s16 zi = -m_cloud_radius_i; zi < m_cloud_radius_i; zi++) {
+ u32 si = (zi + m_cloud_radius_i) * m_cloud_radius_i * 2 + m_cloud_radius_i;
+
+ for (s16 xi = -m_cloud_radius_i; xi < m_cloud_radius_i; xi++) {
+ u32 i = si + xi;
+
+ v2s16 p_in_noise_i(
+ xi + center_of_drawing_in_noise_i.X,
+ zi + center_of_drawing_in_noise_i.Y
+ );
+
+ double noise = noise2d_perlin(
+ (float)p_in_noise_i.X * cloud_size_noise,
+ (float)p_in_noise_i.Y * cloud_size_noise,
+ m_seed, 3, 0.5);
+ grid[i] = (noise >= 0.4);
+ }
}
#define GETINDEX(x, z, radius) (((z)+(radius))*(radius)*2 + (x)+(radius))
#define INAREA(x, z, radius) \
((x) >= -(radius) && (x) < (radius) && (z) >= -(radius) && (z) < (radius))
- for(s16 zi0=-cloud_radius_i; zi0<cloud_radius_i; zi0++)
- for(s16 xi0=-cloud_radius_i; xi0<cloud_radius_i; xi0++)
+ for (s16 zi0= -m_cloud_radius_i; zi0 < m_cloud_radius_i; zi0++)
+ for (s16 xi0= -m_cloud_radius_i; xi0 < m_cloud_radius_i; xi0++)
{
s16 zi = zi0;
s16 xi = xi0;
// Draw from front to back (needed for transparency)
/*if(zi <= 0)
- zi = -cloud_radius_i - zi;
+ zi = -m_cloud_radius_i - zi;
if(xi <= 0)
- xi = -cloud_radius_i - xi;*/
+ xi = -m_cloud_radius_i - xi;*/
// Draw from back to front
if(zi >= 0)
- zi = cloud_radius_i - zi - 1;
+ zi = m_cloud_radius_i - zi - 1;
if(xi >= 0)
- xi = cloud_radius_i - xi - 1;
+ xi = m_cloud_radius_i - xi - 1;
- u32 i = GETINDEX(xi, zi, cloud_radius_i);
+ u32 i = GETINDEX(xi, zi, m_cloud_radius_i);
if(grid[i] == false)
continue;
@@ -230,8 +237,8 @@ void Clouds::render()
}*/
f32 rx = cloud_size/2;
- f32 ry = 8*BS;
- f32 rz = cloud_size/2;
+ f32 ry = 8 * BS;
+ f32 rz = cloud_size / 2;
for(int i=0; i<num_faces_to_draw; i++)
{
@@ -247,8 +254,8 @@ void Clouds::render()
v[3].Pos.set( rx, ry,-rz);
break;
case 1: // back
- if(INAREA(xi, zi-1, cloud_radius_i)){
- u32 j = GETINDEX(xi, zi-1, cloud_radius_i);
+ if (INAREA(xi, zi - 1, m_cloud_radius_i)) {
+ u32 j = GETINDEX(xi, zi - 1, m_cloud_radius_i);
if(grid[j])
continue;
}
@@ -262,8 +269,8 @@ void Clouds::render()
v[3].Pos.set(-rx,-ry,-rz);
break;
case 2: //right
- if(INAREA(xi+1, zi, cloud_radius_i)){
- u32 j = GETINDEX(xi+1, zi, cloud_radius_i);
+ if (INAREA(xi + 1, zi, m_cloud_radius_i)) {
+ u32 j = GETINDEX(xi+1, zi, m_cloud_radius_i);
if(grid[j])
continue;
}
@@ -277,8 +284,8 @@ void Clouds::render()
v[3].Pos.set( rx,-ry,-rz);
break;
case 3: // front
- if(INAREA(xi, zi+1, cloud_radius_i)){
- u32 j = GETINDEX(xi, zi+1, cloud_radius_i);
+ if (INAREA(xi, zi + 1, m_cloud_radius_i)) {
+ u32 j = GETINDEX(xi, zi + 1, m_cloud_radius_i);
if(grid[j])
continue;
}
@@ -292,8 +299,8 @@ void Clouds::render()
v[3].Pos.set( rx,-ry, rz);
break;
case 4: // left
- if(INAREA(xi-1, zi, cloud_radius_i)){
- u32 j = GETINDEX(xi-1, zi, cloud_radius_i);
+ if (INAREA(xi-1, zi, m_cloud_radius_i)) {
+ u32 j = GETINDEX(xi-1, zi, m_cloud_radius_i);
if(grid[j])
continue;
}
@@ -349,3 +356,11 @@ void Clouds::update(v2f camera_p, video::SColorf color)
//dstream<<"m_brightness="<<m_brightness<<std::endl;
}
+void Clouds::readSettings()
+{
+ m_cloud_y = BS * (m_passed_cloud_y ? m_passed_cloud_y :
+ g_settings->getS16("cloud_height"));
+ m_cloud_radius_i = g_settings->getU16("cloud_radius");
+ m_enable_3d = g_settings->getBool("enable_3d_clouds");
+}
+
diff --git a/src/clouds.h b/src/clouds.h
index a9e58e0f0..195f48de0 100644
--- a/src/clouds.h
+++ b/src/clouds.h
@@ -24,6 +24,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include "constants.h"
+// Menu clouds
+class Clouds;
+extern Clouds *g_menuclouds;
+
+// Scene manager used for menu clouds
+namespace irr{namespace scene{class ISceneManager;}}
+extern irr::scene::ISceneManager *g_menucloudsmgr;
+
class Clouds : public scene::ISceneNode
{
public:
@@ -75,10 +83,15 @@ public:
BS * 1000000, m_cloud_y + BS - BS * camera_offset.Y, BS * 1000000);
}
+ void readSettings();
+
private:
video::SMaterial m_material;
core::aabbox3d<f32> m_box;
+ s16 m_passed_cloud_y;
float m_cloud_y;
+ u16 m_cloud_radius_i;
+ bool m_enable_3d;
video::SColorf m_color;
u32 m_seed;
v2f m_camera_pos;
diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in
index e111a650d..04f368594 100644
--- a/src/cmake_config.h.in
+++ b/src/cmake_config.h.in
@@ -3,31 +3,27 @@
#ifndef CMAKE_CONFIG_H
#define CMAKE_CONFIG_H
-#define CMAKE_PROJECT_NAME "@PROJECT_NAME@"
-#define CMAKE_VERSION_STRING "@VERSION_STRING@"
-#define CMAKE_PRODUCT_VERSION_STRING "@VERSION_MAJOR@.@VERSION_MINOR@"
-#define CMAKE_RUN_IN_PLACE @RUN_IN_PLACE@
-#define CMAKE_USE_GETTEXT @USE_GETTEXT@
-#define CMAKE_USE_CURL @USE_CURL@
-#define CMAKE_USE_SOUND @USE_SOUND@
-#define CMAKE_USE_FREETYPE @USE_FREETYPE@
-#define CMAKE_STATIC_SHAREDIR "@SHAREDIR@"
-#define CMAKE_USE_LEVELDB @USE_LEVELDB@
-#define CMAKE_USE_LUAJIT @USE_LUAJIT@
-#define CMAKE_USE_REDIS @USE_REDIS@
-#define CMAKE_VERSION_MAJOR @VERSION_MAJOR@
-#define CMAKE_VERSION_MINOR @VERSION_MINOR@
-#define CMAKE_VERSION_PATCH @VERSION_PATCH@
-#define CMAKE_VERSION_PATCH_ORIG @VERSION_PATCH_ORIG@
-#define CMAKE_VERSION_EXTRA_STRING "@VERSION_EXTRA@"
-#define CMAKE_HAVE_ENDIAN_H @HAVE_ENDIAN_H@
-
-#ifdef NDEBUG
- #define CMAKE_BUILD_TYPE "Release"
-#else
- #define CMAKE_BUILD_TYPE "Debug"
-#endif
-#define CMAKE_BUILD_INFO "BUILD_TYPE=" CMAKE_BUILD_TYPE " RUN_IN_PLACE=@RUN_IN_PLACE@ USE_GETTEXT=@USE_GETTEXT@ USE_SOUND=@USE_SOUND@ USE_CURL=@USE_CURL@ USE_FREETYPE=@USE_FREETYPE@ USE_LUAJIT=@USE_LUAJIT@ STATIC_SHAREDIR=@SHAREDIR@"
+#define PROJECT_NAME "@PROJECT_NAME@"
+#define PROJECT_NAME_C "@PROJECT_NAME_CAPITALIZED@"
+#define VERSION_MAJOR @VERSION_MAJOR@
+#define VERSION_MINOR @VERSION_MINOR@
+#define VERSION_PATCH @VERSION_PATCH@
+#define VERSION_EXTRA "@VERSION_EXTRA@"
+#define VERSION_STRING "@VERSION_STRING@"
+#define PRODUCT_VERSION_STRING "@VERSION_MAJOR@.@VERSION_MINOR@"
+#define STATIC_SHAREDIR "@SHAREDIR@"
+#define BUILD_TYPE "@CMAKE_BUILD_TYPE@"
+#cmakedefine01 RUN_IN_PLACE
+#cmakedefine01 USE_GETTEXT
+#cmakedefine01 USE_CURL
+#cmakedefine01 USE_SOUND
+#cmakedefine01 USE_FREETYPE
+#cmakedefine01 USE_LEVELDB
+#cmakedefine01 USE_LUAJIT
+#cmakedefine01 USE_SPATIAL
+#cmakedefine01 USE_SYSTEM_GMP
+#cmakedefine01 USE_REDIS
+#cmakedefine01 HAVE_ENDIAN_H
#endif
diff --git a/src/cmake_config_githash.h.in b/src/cmake_config_githash.h.in
index 4d5fcd60f..c72960c9f 100644
--- a/src/cmake_config_githash.h.in
+++ b/src/cmake_config_githash.h.in
@@ -4,7 +4,7 @@
#ifndef CMAKE_CONFIG_GITHASH_H
#define CMAKE_CONFIG_GITHASH_H
-#define CMAKE_VERSION_GITHASH "@VERSION_GITHASH@"
+#define VERSION_GITHASH "@VERSION_GITHASH@"
#endif
diff --git a/src/collision.cpp b/src/collision.cpp
index 9e0c85531..6afc79ab7 100644
--- a/src/collision.cpp
+++ b/src/collision.cpp
@@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <vector>
#include <set>
#include "util/timetaker.h"
-#include "main.h" // g_profiler
#include "profiler.h"
// float error is 10 - 9.96875 = 0.03125
@@ -173,7 +172,7 @@ bool wouldCollideWithCeiling(
{
//TimeTaker tt("wouldCollideWithCeiling");
- assert(y_increase >= 0);
+ assert(y_increase >= 0); // pre-condition
for(std::vector<aabb3f>::const_iterator
i = staticboxes.begin();
@@ -300,16 +299,14 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
/* add object boxes to cboxes */
- std::list<ActiveObject*> objects;
+ std::vector<ActiveObject*> objects;
#ifndef SERVER
ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env);
- if (c_env != 0)
- {
+ if (c_env != 0) {
f32 distance = speed_f.getLength();
std::vector<DistanceSortedActiveObject> clientobjects;
c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects);
- for (size_t i=0; i < clientobjects.size(); i++)
- {
+ for (size_t i=0; i < clientobjects.size(); i++) {
if ((self == 0) || (self != clientobjects[i].obj)) {
objects.push_back((ActiveObject*)clientobjects[i].obj);
}
@@ -319,12 +316,11 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
#endif
{
ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env);
- if (s_env != 0)
- {
+ if (s_env != 0) {
f32 distance = speed_f.getLength();
- std::set<u16> s_objects = s_env->getObjectsInsideRadius(pos_f,distance * 1.5);
- for (std::set<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); iter++)
- {
+ std::vector<u16> s_objects;
+ s_env->getObjectsInsideRadius(s_objects, pos_f, distance * 1.5);
+ for (std::vector<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); iter++) {
ServerActiveObject *current = s_env->getActiveObject(*iter);
if ((self == 0) || (self != current)) {
objects.push_back((ActiveObject*)current);
@@ -333,16 +329,14 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
}
}
- for (std::list<ActiveObject*>::const_iterator iter = objects.begin();iter != objects.end(); ++iter)
- {
+ for (std::vector<ActiveObject*>::const_iterator iter = objects.begin();
+ iter != objects.end(); ++iter) {
ActiveObject *object = *iter;
- if (object != NULL)
- {
+ if (object != NULL) {
aabb3f object_collisionbox;
if (object->getCollisionBox(&object_collisionbox) &&
- object->collideWithObjects())
- {
+ object->collideWithObjects()) {
cboxes.push_back(object_collisionbox);
is_unloaded.push_back(false);
is_step_up.push_back(false);
@@ -354,11 +348,11 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
}
} //tt3
- assert(cboxes.size() == is_unloaded.size());
- assert(cboxes.size() == is_step_up.size());
- assert(cboxes.size() == bouncy_values.size());
- assert(cboxes.size() == node_positions.size());
- assert(cboxes.size() == is_object.size());
+ assert(cboxes.size() == is_unloaded.size()); // post-condition
+ assert(cboxes.size() == is_step_up.size()); // post-condition
+ assert(cboxes.size() == bouncy_values.size()); // post-condition
+ assert(cboxes.size() == node_positions.size()); // post-condition
+ assert(cboxes.size() == is_object.size()); // post-condition
/*
Collision detection
@@ -373,7 +367,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
//f32 d = 0.15*BS;
// This should always apply, otherwise there are glitches
- assert(d > pos_max_d);
+ assert(d > pos_max_d); // invariant
int loopcount = 0;
@@ -464,7 +458,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
pos_f += speed_f * nearest_dtime;
dtime -= nearest_dtime;
}
-
+
bool is_collision = true;
if(is_unloaded[nearest_boxindex])
is_collision = false;
@@ -567,76 +561,3 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef,
return result;
}
-
-#if 0
-// This doesn't seem to work and isn't used
-collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
- f32 pos_max_d, const aabb3f &box_0,
- f32 stepheight, f32 dtime,
- v3f &pos_f, v3f &speed_f, v3f &accel_f)
-{
- //TimeTaker tt("collisionMovePrecise");
- ScopeProfiler sp(g_profiler, "collisionMovePrecise avg", SPT_AVG);
-
- collisionMoveResult final_result;
-
- // If there is no speed, there are no collisions
- if(speed_f.getLength() == 0)
- return final_result;
-
- // Don't allow overly huge dtime
- if(dtime > 2.0)
- dtime = 2.0;
-
- f32 dtime_downcount = dtime;
-
- u32 loopcount = 0;
- do
- {
- loopcount++;
-
- // Maximum time increment (for collision detection etc)
- // time = distance / speed
- f32 dtime_max_increment = 1.0;
- if(speed_f.getLength() != 0)
- dtime_max_increment = pos_max_d / speed_f.getLength();
-
- // Maximum time increment is 10ms or lower
- if(dtime_max_increment > 0.01)
- dtime_max_increment = 0.01;
-
- f32 dtime_part;
- if(dtime_downcount > dtime_max_increment)
- {
- dtime_part = dtime_max_increment;
- dtime_downcount -= dtime_part;
- }
- else
- {
- dtime_part = dtime_downcount;
- /*
- Setting this to 0 (no -=dtime_part) disables an infinite loop
- when dtime_part is so small that dtime_downcount -= dtime_part
- does nothing
- */
- dtime_downcount = 0;
- }
-
- collisionMoveResult result = collisionMoveSimple(map, gamedef,
- pos_max_d, box_0, stepheight, dtime_part,
- pos_f, speed_f, accel_f);
-
- if(result.touching_ground)
- final_result.touching_ground = true;
- if(result.collides)
- final_result.collides = true;
- if(result.collides_xz)
- final_result.collides_xz = true;
- if(result.standing_on_unloaded)
- final_result.standing_on_unloaded = true;
- }
- while(dtime_downcount > 0.001);
-
- return final_result;
-}
-#endif
diff --git a/src/collision.h b/src/collision.h
index 32086aae3..fc4187eda 100644
--- a/src/collision.h
+++ b/src/collision.h
@@ -75,15 +75,6 @@ collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef,
v3f &accel_f,ActiveObject* self=0,
bool collideWithObjects=true);
-#if 0
-// This doesn't seem to work and isn't used
-// Moves using as many iterations as needed
-collisionMoveResult collisionMovePrecise(Map *map, IGameDef *gamedef,
- f32 pos_max_d, const aabb3f &box_0,
- f32 stepheight, f32 dtime,
- v3f &pos_f, v3f &speed_f, v3f &accel_f);
-#endif
-
// Helper function:
// Checks for collision of a moving aabbox with a static aabbox
// Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision
diff --git a/src/config.h b/src/config.h
index b07aa5d22..0955ea8f5 100644
--- a/src/config.h
+++ b/src/config.h
@@ -6,82 +6,37 @@
#ifndef CONFIG_H
#define CONFIG_H
-#define PROJECT_NAME "Minetest"
-#define RUN_IN_PLACE 0
-#define STATIC_SHAREDIR ""
+#define STRINGIFY(x) #x
+#define STR(x) STRINGIFY(x)
-#define USE_GETTEXT 0
-#ifndef USE_SOUND
- #define USE_SOUND 0
-#endif
-
-#ifndef USE_CURL
- #define USE_CURL 0
-#endif
-
-#ifndef USE_FREETYPE
- #define USE_FREETYPE 0
-#endif
-
-#ifndef USE_LEVELDB
- #define USE_LEVELDB 0
-#endif
-
-#ifndef USE_LUAJIT
- #define USE_LUAJIT 0
-#endif
-
-#ifndef USE_REDIS
- #define USE_REDIS 0
-#endif
-
-#define HAVE_ENDIAN_H 0
-
-#ifdef USE_CMAKE_CONFIG_H
+#if defined USE_CMAKE_CONFIG_H
#include "cmake_config.h"
- #undef PROJECT_NAME
- #define PROJECT_NAME CMAKE_PROJECT_NAME
- #undef RUN_IN_PLACE
- #define RUN_IN_PLACE CMAKE_RUN_IN_PLACE
- #undef USE_GETTEXT
- #define USE_GETTEXT CMAKE_USE_GETTEXT
- #undef USE_SOUND
- #define USE_SOUND CMAKE_USE_SOUND
- #undef USE_CURL
- #define USE_CURL CMAKE_USE_CURL
- #undef USE_FREETYPE
- #define USE_FREETYPE CMAKE_USE_FREETYPE
- #undef STATIC_SHAREDIR
- #define STATIC_SHAREDIR CMAKE_STATIC_SHAREDIR
- #undef USE_LEVELDB
- #define USE_LEVELDB CMAKE_USE_LEVELDB
- #undef USE_LUAJIT
- #define USE_LUAJIT CMAKE_USE_LUAJIT
- #undef USE_REDIS
- #define USE_REDIS CMAKE_USE_REDIS
- #undef VERSION_MAJOR
- #define VERSION_MAJOR CMAKE_VERSION_MAJOR
- #undef VERSION_MINOR
- #define VERSION_MINOR CMAKE_VERSION_MINOR
- #undef VERSION_PATCH
- #define VERSION_PATCH CMAKE_VERSION_PATCH
- #undef VERSION_PATCH_ORIG
- #define VERSION_PATCH_ORIG CMAKE_VERSION_PATCH_ORIG
- #undef VERSION_STRING
- #define VERSION_STRING CMAKE_VERSION_STRING
- #undef PRODUCT_VERSION_STRING
- #define PRODUCT_VERSION_STRING CMAKE_PRODUCT_VERSION_STRING
- #undef VERSION_EXTRA_STRING
- #define VERSION_EXTRA_STRING CMAKE_VERSION_EXTRA_STRING
- #undef HAVE_ENDIAN_H
- #define HAVE_ENDIAN_H CMAKE_HAVE_ENDIAN_H
-#endif
-
-#ifdef __ANDROID__
+#elif defined (__ANDROID__) || defined (ANDROID)
+ #define PROJECT_NAME "minetest"
+ #define PROJECT_NAME_C "Minetest"
+ #define STATIC_SHAREDIR ""
#include "android_version.h"
- #define VERSION_STRING CMAKE_VERSION_STRING
-#endif
+ #ifdef NDEBUG
+ #define BUILD_TYPE "Release"
+ #else
+ #define BUILD_TYPE "Debug"
+ #endif
+#else
+ #ifdef NDEBUG
+ #define BUILD_TYPE "Release"
+ #else
+ #define BUILD_TYPE "Debug"
+ #endif
+#endif
+
+#define BUILD_INFO "BUILD_TYPE=" BUILD_TYPE \
+ " RUN_IN_PLACE=" STR(RUN_IN_PLACE) \
+ " USE_GETTEXT=" STR(USE_GETTEXT) \
+ " USE_SOUND=" STR(USE_SOUND) \
+ " USE_CURL=" STR(USE_CURL) \
+ " USE_FREETYPE=" STR(USE_FREETYPE) \
+ " USE_LUAJIT=" STR(USE_LUAJIT) \
+ " STATIC_SHAREDIR=" STR(STATIC_SHAREDIR)
#endif
-
diff --git a/src/constants.h b/src/constants.h
index d7163bf68..b606fc4fa 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -64,7 +64,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// The absolute working limit is (2^15 - viewing_range).
// I really don't want to make every algorithm to check if it's going near
// the limit or not, so this is lower.
-#define MAP_GENERATION_LIMIT (31000)
+// This is the maximum value the setting map_generation_limit can be
+#define MAX_MAP_GENERATION_LIMIT (31000)
// Size of node in floating-point units
// The original idea behind this is to disallow plain casts between
@@ -97,6 +98,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// TODO: Use case-insensitive player names instead of this hack.
#define PLAYER_FILE_ALTERNATE_TRIES 1000
+// For screenshots a serial number is appended to the filename + datetimestamp
+// if filename + datetimestamp is not unique.
+// This is the maximum number of attempts to try and add a serial to the end of
+// the file attempting to ensure a unique filename
+#define SCREENSHOT_MAX_SERIAL_TRIES 1000
+
/*
GUI related things
*/
@@ -110,4 +117,3 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define DEFAULT_FONT_SIZE (10)
#endif
-
diff --git a/src/content_abm.cpp b/src/content_abm.cpp
index 1ee41b2ec..8694ef981 100644
--- a/src/content_abm.cpp
+++ b/src/content_abm.cpp
@@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "settings.h"
#include "mapblock.h" // For getNodeBlockPos
-#include "main.h" // for g_settings
#include "map.h"
#include "scripting_game.h"
#include "log.h"
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 6d41b2749..0293b7983 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -27,14 +27,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h" // For IntervalLimiter
#include "util/serialize.h"
#include "util/mathconstants.h"
-#include "tile.h"
+#include "client/tile.h"
#include "environment.h"
#include "collision.h"
#include "settings.h"
#include "serialization.h" // For decompressZlib
#include "gamedef.h"
#include "clientobject.h"
-#include "content_object.h"
#include "mesh.h"
#include "itemdef.h"
#include "tool.h"
@@ -43,7 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "localplayer.h"
#include "map.h"
-#include "main.h" // g_settings
#include "camera.h" // CameraModes
#include "wieldmesh.h"
#include "log.h"
@@ -144,12 +142,12 @@ class TestCAO : public ClientActiveObject
public:
TestCAO(IGameDef *gamedef, ClientEnvironment *env);
virtual ~TestCAO();
-
- u8 getType() const
+
+ ActiveObjectType getType() const
{
return ACTIVEOBJECT_TYPE_TEST;
}
-
+
static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
@@ -194,9 +192,9 @@ void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
{
if(m_node != NULL)
return;
-
+
//video::IVideoDriver* driver = smgr->getVideoDriver();
-
+
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
@@ -212,7 +210,7 @@ void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
// Set material
buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
- buf->getMaterial().setTexture(0, tsrc->getTexture("rat.png"));
+ buf->getMaterial().setTexture(0, tsrc->getTextureForMesh("rat.png"));
buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
buf->getMaterial().setFlag(video::EMF_FOG_ENABLE, true);
buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
@@ -288,12 +286,12 @@ class ItemCAO : public ClientActiveObject
public:
ItemCAO(IGameDef *gamedef, ClientEnvironment *env);
virtual ~ItemCAO();
-
- u8 getType() const
+
+ ActiveObjectType getType() const
{
return ACTIVEOBJECT_TYPE_ITEM;
}
-
+
static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env);
void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
@@ -310,12 +308,12 @@ public:
void processMessage(const std::string &data);
void initialize(const std::string &data);
-
+
core::aabbox3d<f32>* getSelectionBox()
{return &m_selection_box;}
v3f getPosition()
{return m_position;}
-
+
std::string infoText()
{return m_infotext;}
@@ -359,9 +357,9 @@ void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
{
if(m_node != NULL)
return;
-
+
//video::IVideoDriver* driver = smgr->getVideoDriver();
-
+
scene::SMesh *mesh = new scene::SMesh();
scene::IMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
@@ -471,7 +469,7 @@ void ItemCAO::updateTexture()
<<": error deSerializing itemstring \""
<<m_itemstring<<std::endl;
}
-
+
// Set meshbuffer texture
m_node->getMaterial(0).setTexture(0, texture);
}
@@ -516,7 +514,7 @@ void ItemCAO::processMessage(const std::string &data)
void ItemCAO::initialize(const std::string &data)
{
infostream<<"ItemCAO: Got init data"<<std::endl;
-
+
{
std::istringstream is(data, std::ios::binary);
// version
@@ -529,7 +527,7 @@ void ItemCAO::initialize(const std::string &data)
// itemstring
m_itemstring = deSerializeString(is);
}
-
+
updateNodePos();
updateInfoText();
}
@@ -545,7 +543,6 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
//
m_is_player(false),
m_is_local_player(false),
- m_id(0),
//
m_smgr(NULL),
m_irr(NULL),
@@ -554,6 +551,7 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
m_animated_meshnode(NULL),
m_wield_meshnode(NULL),
m_spritenode(NULL),
+ m_nametag_color(video::SColor(255, 255, 255, 255)),
m_textnode(NULL),
m_position(v3f(0,10*BS,0)),
m_velocity(v3f(0,0,0)),
@@ -567,6 +565,7 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env):
m_animation_range(v2s32(0,0)),
m_animation_speed(15),
m_animation_blend(0),
+ m_animation_loop(true),
m_bone_position(std::map<std::string, core::vector2d<v3f> >()),
m_attachment_bone(""),
m_attachment_position(v3f(0,0,0)),
@@ -728,6 +727,16 @@ scene::IBillboardSceneNode* GenericCAO::getSpriteSceneNode()
return m_spritenode;
}
+void GenericCAO::setChildrenVisible(bool toset)
+{
+ for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
+ GenericCAO *obj = m_env->getGenericCAO(m_children[i]);
+ if (obj) {
+ obj->setVisible(toset);
+ }
+ }
+}
+
void GenericCAO::setAttachments()
{
updateAttachments();
@@ -737,7 +746,7 @@ ClientActiveObject* GenericCAO::getParent()
{
ClientActiveObject *obj = NULL;
- u16 attached_id = m_env->m_attachements[getId()];
+ u16 attached_id = m_env->attachement_parent_ids[getId()];
if ((attached_id != 0) &&
(attached_id != getId())) {
@@ -749,18 +758,17 @@ ClientActiveObject* GenericCAO::getParent()
void GenericCAO::removeFromScene(bool permanent)
{
// Should be true when removing the object permanently and false when refreshing (eg: updating visuals)
- if((m_env != NULL) && (permanent))
+ if((m_env != NULL) && (permanent))
{
- for(std::vector<u16>::iterator ci = m_children.begin();
- ci != m_children.end(); ci++)
- {
- if (m_env->m_attachements[*ci] == getId()) {
- m_env->m_attachements[*ci] = 0;
+ for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
+ u16 ci = m_children[i];
+ if (m_env->attachement_parent_ids[ci] == getId()) {
+ m_env->attachement_parent_ids[ci] = 0;
}
}
- m_env->m_attachements[getId()] = 0;
-
+ m_env->attachement_parent_ids[getId()] = 0;
+
LocalPlayer* player = m_env->getLocalPlayer();
if (this == player->parent) {
player->parent = NULL;
@@ -823,7 +831,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
NULL, v2f(1, 1), v3f(0,0,0), -1);
m_spritenode->grab();
m_spritenode->setMaterialTexture(0,
- tsrc->getTexture("unknown_node.png"));
+ tsrc->getTextureForMesh("unknown_node.png"));
m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
@@ -899,7 +907,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
m_meshnode = smgr->addMeshSceneNode(mesh, NULL);
m_meshnode->grab();
mesh->drop();
-
+
m_meshnode->setScale(v3f(m_prop.visual_size.X,
m_prop.visual_size.Y,
m_prop.visual_size.X));
@@ -962,9 +970,9 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
if (node && m_is_player && !m_is_local_player) {
// Add a text node for showing the name
gui::IGUIEnvironment* gui = irr->getGUIEnvironment();
- std::wstring wname = narrow_to_wide(m_name);
- m_textnode = smgr->addTextSceneNode(gui->getBuiltInFont(),
- wname.c_str(), video::SColor(255,255,255,255), node);
+ std::wstring wname = utf8_to_wide(m_name);
+ m_textnode = smgr->addTextSceneNode(gui->getSkin()->getFont(),
+ wname.c_str(), m_nametag_color, node);
m_textnode->grab();
m_textnode->setPosition(v3f(0, BS*1.1, 0));
}
@@ -977,19 +985,37 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc,
void GenericCAO::updateLight(u8 light_at_pos)
{
+ // Don't update light of attached one
+ if (getParent() != NULL) {
+ return;
+ }
+
+ updateLightNoCheck(light_at_pos);
+
+ // Update light of all children
+ for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
+ ClientActiveObject *obj = m_env->getActiveObject(m_children[i]);
+ if (obj) {
+ obj->updateLightNoCheck(light_at_pos);
+ }
+ }
+}
+
+void GenericCAO::updateLightNoCheck(u8 light_at_pos)
+{
u8 li = decode_light(light_at_pos);
- if(li != m_last_light)
- {
+ if (li != m_last_light) {
m_last_light = li;
video::SColor color(255,li,li,li);
- if(m_meshnode)
+ if (m_meshnode) {
setMeshColor(m_meshnode->getMesh(), color);
- if(m_animated_meshnode)
+ } else if (m_animated_meshnode) {
setMeshColor(m_animated_meshnode->getMesh(), color);
- if(m_wield_meshnode)
+ } else if (m_wield_meshnode) {
m_wield_meshnode->setColor(color);
- if(m_spritenode)
+ } else if (m_spritenode) {
m_spritenode->setColor(color);
+ }
}
}
@@ -1015,7 +1041,7 @@ void GenericCAO::updateNodePos()
}
}
}
-
+
void GenericCAO::step(float dtime, ClientEnvironment *env)
{
// Handel model of local player instantly to prevent lags
@@ -1101,7 +1127,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
for(std::vector<u16>::iterator ci = m_children.begin();
ci != m_children.end();)
{
- if (m_env->m_attachements[*ci] != getId()) {
+ if (m_env->attachement_parent_ids[*ci] != getId()) {
ci = m_children.erase(ci);
continue;
}
@@ -1118,11 +1144,9 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
addToScene(m_smgr, m_gamedef->tsrc(), m_irr);
// Attachments, part 2: Now that the parent has been refreshed, put its attachments back
- for(std::vector<u16>::iterator ci = m_children.begin();
- ci != m_children.end(); ci++)
- {
+ for (std::vector<u16>::size_type i = 0; i < m_children.size(); i++) {
// Get the object of the child
- ClientActiveObject *obj = m_env->getActiveObject(*ci);
+ ClientActiveObject *obj = m_env->getActiveObject(m_children[i]);
if (obj)
obj->setAttachments();
}
@@ -1300,7 +1324,7 @@ void GenericCAO::updateTextures(const std::string &mod)
texturestring = m_prop.textures[0];
texturestring += mod;
m_spritenode->setMaterialTexture(0,
- tsrc->getTexture(texturestring));
+ tsrc->getTextureForMesh(texturestring));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
@@ -1328,7 +1352,7 @@ void GenericCAO::updateTextures(const std::string &mod)
if(texturestring == "")
continue; // Empty texture string means don't modify that material
texturestring += mod;
- video::ITexture* texture = tsrc->getTexture(texturestring);
+ video::ITexture* texture = tsrc->getTextureForMesh(texturestring);
if(!texture)
{
errorstream<<"GenericCAO::updateTextures(): Could not load texture "<<texturestring<<std::endl;
@@ -1377,7 +1401,7 @@ void GenericCAO::updateTextures(const std::string &mod)
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setTexture(0,
- tsrc->getTexture(texturestring));
+ tsrc->getTextureForMesh(texturestring));
material.getTextureMatrix(0).makeIdentity();
// This allows setting per-material colors. However, until a real lighting
@@ -1405,7 +1429,7 @@ void GenericCAO::updateTextures(const std::string &mod)
tname += mod;
scene::IMeshBuffer *buf = mesh->getMeshBuffer(0);
buf->getMaterial().setTexture(0,
- tsrc->getTexture(tname));
+ tsrc->getTextureForMesh(tname));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
@@ -1430,7 +1454,7 @@ void GenericCAO::updateTextures(const std::string &mod)
tname += mod;
scene::IMeshBuffer *buf = mesh->getMeshBuffer(1);
buf->getMaterial().setTexture(0,
- tsrc->getTexture(tname));
+ tsrc->getTextureForMesh(tname));
// This allows setting per-material colors. However, until a real lighting
// system is added, the code below will have no effect. Once MineTest
@@ -1460,9 +1484,18 @@ void GenericCAO::updateAnimation()
{
if(m_animated_meshnode == NULL)
return;
- m_animated_meshnode->setFrameLoop(m_animation_range.X, m_animation_range.Y);
- m_animated_meshnode->setAnimationSpeed(m_animation_speed);
+
+ if (m_animated_meshnode->getStartFrame() != m_animation_range.X ||
+ m_animated_meshnode->getEndFrame() != m_animation_range.Y)
+ m_animated_meshnode->setFrameLoop(m_animation_range.X, m_animation_range.Y);
+ if (m_animated_meshnode->getAnimationSpeed() != m_animation_speed)
+ m_animated_meshnode->setAnimationSpeed(m_animation_speed);
m_animated_meshnode->setTransitionTime(m_animation_blend);
+// Requires Irrlicht 1.8 or greater
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR > 1
+ if (m_animated_meshnode->getLoopMode() != m_animation_loop)
+ m_animated_meshnode->setLoopMode(m_animation_loop);
+#endif
}
void GenericCAO::updateBonePosition()
@@ -1486,20 +1519,11 @@ void GenericCAO::updateBonePosition()
}
}
}
-
+
void GenericCAO::updateAttachments()
{
- // localplayer itself can't be attached to localplayer
- if (!m_is_local_player)
- {
- m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer();
- // Objects attached to the local player should always be hidden
- m_is_visible = !m_attached_to_local;
- }
-
- if(getParent() == NULL || m_attached_to_local) // Detach or don't attach
- {
+ if (getParent() == NULL) { // Detach or don't attach
scene::ISceneNode *node = getSceneNode();
if (node) {
v3f old_position = node->getAbsolutePosition();
@@ -1637,6 +1661,8 @@ void GenericCAO::processMessage(const std::string &data)
m_animation_range = v2s32((s32)range.X, (s32)range.Y);
m_animation_speed = readF1000(is);
m_animation_blend = readF1000(is);
+ // these are sent inverted so we get true when the server sends nothing
+ m_animation_loop = !readU8(is);
updateAnimation();
} else {
LocalPlayer *player = m_env->getLocalPlayer();
@@ -1645,6 +1671,8 @@ void GenericCAO::processMessage(const std::string &data)
m_animation_range = v2s32((s32)range.X, (s32)range.Y);
m_animation_speed = readF1000(is);
m_animation_blend = readF1000(is);
+ // these are sent inverted so we get true when the server sends nothing
+ m_animation_loop = !readU8(is);
}
// update animation only if local animations present
// and received animation is unknown (except idle animation)
@@ -1668,14 +1696,31 @@ void GenericCAO::processMessage(const std::string &data)
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
updateBonePosition();
- }
- else if(cmd == GENERIC_CMD_SET_ATTACHMENT) {
- m_env->m_attachements[getId()] = readS16(is);
- m_children.push_back(m_env->m_attachements[getId()]);
+ } else if (cmd == GENERIC_CMD_ATTACH_TO) {
+ u16 parentID = readS16(is);
+ u16 oldparent = m_env->attachement_parent_ids[getId()];
+ if (oldparent) {
+ m_children.erase(std::remove(m_children.begin(), m_children.end(),
+ getId()), m_children.end());
+ }
+ m_env->attachement_parent_ids[getId()] = parentID;
+ GenericCAO *parentobj = m_env->getGenericCAO(parentID);
+
+ if (parentobj) {
+ parentobj->m_children.push_back(getId());
+ }
+
m_attachment_bone = deSerializeString(is);
m_attachment_position = readV3F1000(is);
m_attachment_rotation = readV3F1000(is);
+ // localplayer itself can't be attached to localplayer
+ if (!m_is_local_player) {
+ m_attached_to_local = getParent() != NULL && getParent()->isLocalPlayer();
+ // Objects attached to the local player should be hidden by default
+ m_is_visible = !m_attached_to_local;
+ }
+
updateAttachments();
}
else if(cmd == GENERIC_CMD_PUNCHED) {
@@ -1716,13 +1761,26 @@ void GenericCAO::processMessage(const std::string &data)
int rating = readS16(is);
m_armor_groups[name] = rating;
}
+ } else if (cmd == GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) {
+ readU8(is); // version
+ m_nametag_color = readARGB8(is);
+ if (m_textnode != NULL) {
+ m_textnode->setTextColor(m_nametag_color);
+
+ // Enforce hiding nametag,
+ // because if freetype is enabled, a grey
+ // shadow can remain.
+ m_textnode->setVisible(m_nametag_color.getAlpha() > 0);
+ }
}
}
-
+
+/* \pre punchitem != NULL
+ */
bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem,
float time_from_last_punch)
{
- assert(punchitem);
+ assert(punchitem); // pre-condition
const ToolCapabilities *toolcap =
&punchitem->getToolCapabilities(m_gamedef->idef());
PunchDamageResult result = getPunchDamage(
diff --git a/src/content_cao.h b/src/content_cao.h
index 69e2e54a2..299d6c73e 100644
--- a/src/content_cao.h
+++ b/src/content_cao.h
@@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map>
#include "irrlichttypes_extrabloated.h"
-#include "content_object.h"
#include "clientobject.h"
#include "object_properties.h"
#include "itemgroup.h"
@@ -61,7 +60,6 @@ private:
std::string m_name;
bool m_is_player;
bool m_is_local_player;
- int m_id;
// Property-ish things
ObjectProperties m_prop;
//
@@ -72,6 +70,7 @@ private:
scene::IAnimatedMeshSceneNode *m_animated_meshnode;
WieldMeshSceneNode *m_wield_meshnode;
scene::IBillboardSceneNode *m_spritenode;
+ video::SColor m_nametag_color;
scene::ITextSceneNode* m_textnode;
v3f m_position;
v3f m_velocity;
@@ -87,6 +86,7 @@ private:
v2s32 m_animation_range;
int m_animation_speed;
int m_animation_blend;
+ bool m_animation_loop;
std::map<std::string, core::vector2d<v3f> > m_bone_position; // stores position and rotation for each bone name
std::string m_attachment_bone;
v3f m_attachment_position;
@@ -115,7 +115,7 @@ public:
return new GenericCAO(gamedef, env);
}
- inline u8 getType() const
+ inline ActiveObjectType getType() const
{
return ACTIVEOBJECT_TYPE_GENERIC;
}
@@ -162,6 +162,8 @@ public:
m_is_visible = toset;
}
+ void setChildrenVisible(bool toset);
+
void setAttachments();
void removeFromScene(bool permanent);
@@ -176,6 +178,8 @@ public:
void updateLight(u8 light_at_pos);
+ void updateLightNoCheck(u8 light_at_pos);
+
v3s16 getLightPosition();
void updateNodePos();
diff --git a/src/content_cso.cpp b/src/content_cso.cpp
index 4779b20d1..0790024fc 100644
--- a/src/content_cso.cpp
+++ b/src/content_cso.cpp
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_cso.h"
#include <IBillboardSceneNode.h>
-#include "tile.h"
+#include "client/tile.h"
#include "environment.h"
#include "gamedef.h"
#include "log.h"
@@ -50,7 +50,7 @@ public:
m_spritenode = smgr->addBillboardSceneNode(
NULL, v2f(1,1), pos, -1);
m_spritenode->setMaterialTexture(0,
- env->getGameDef()->tsrc()->getTexture("smoke_puff.png"));
+ env->getGameDef()->tsrc()->getTextureForMesh("smoke_puff.png"));
m_spritenode->setMaterialFlag(video::EMF_LIGHTING, false);
m_spritenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, false);
//m_spritenode->setMaterialType(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF);
diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp
index 9af3be789..8fa041312 100644
--- a/src/content_mapblock.cpp
+++ b/src/content_mapblock.cpp
@@ -20,11 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_mapblock.h"
#include "util/numeric.h"
#include "util/directiontables.h"
-#include "main.h" // For g_settings
#include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
#include "settings.h"
#include "nodedef.h"
-#include "tile.h"
+#include "client/tile.h"
#include "mesh.h"
#include <IMeshManipulator.h>
#include "gamedef.h"
@@ -45,18 +44,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// (compatible with ContentFeatures). If you specified 0,0,1,1
// for each face, that would be the same as passing NULL.
void makeCuboid(MeshCollector *collector, const aabb3f &box,
- TileSpec *tiles, int tilecount,
- video::SColor &c, const f32* txc)
+ TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc)
{
- assert(tilecount >= 1 && tilecount <= 6);
+ assert(tilecount >= 1 && tilecount <= 6); // pre-condition
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
- if(txc == NULL)
- {
+ if(txc == NULL) {
static const f32 txc_default[24] = {
0,0,1,1,
0,0,1,1,
@@ -160,14 +157,16 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
- for(s32 j=0; j<24; j+=4)
- {
- int tileindex = MYMIN(j/4, tilecount-1);
- collector->append(tiles[tileindex],
- vertices+j, 4, indices, 6);
+ for (s32 j = 0; j < 24; j += 4) {
+ int tileindex = MYMIN(j / 4, tilecount - 1);
+ collector->append(tiles[tileindex], vertices + j, 4, indices, 6);
}
}
+/*
+ TODO: Fix alpha blending for special nodes
+ Currently only the last element rendered is blended correct
+*/
void mapblock_mesh_generate_special(MeshMakeData *data,
MeshCollector &collector)
{
@@ -191,54 +190,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
- // Create selection mesh
- v3s16 p = data->m_highlighted_pos_relative;
- if (data->m_show_hud &&
- (p.X >= 0) && (p.X < MAP_BLOCKSIZE) &&
- (p.Y >= 0) && (p.Y < MAP_BLOCKSIZE) &&
- (p.Z >= 0) && (p.Z < MAP_BLOCKSIZE)) {
-
- MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
- if(n.getContent() != CONTENT_AIR) {
- // Get selection mesh light level
- static const v3s16 dirs[7] = {
- v3s16( 0, 0, 0),
- v3s16( 0, 1, 0),
- v3s16( 0,-1, 0),
- v3s16( 1, 0, 0),
- v3s16(-1, 0, 0),
- v3s16( 0, 0, 1),
- v3s16( 0, 0,-1)
- };
-
- u16 l = 0;
- u16 l1 = 0;
- for (u8 i = 0; i < 7; i++) {
- MapNode n1 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dirs[i]);
- l1 = getInteriorLight(n1, -4, nodedef);
- if (l1 > l)
- l = l1;
- }
- video::SColor c = MapBlock_LightColor(255, l, 0);
- data->m_highlight_mesh_color = c;
- std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
- TileSpec h_tile;
- h_tile.material_flags |= MATERIAL_FLAG_HIGHLIGHTED;
- h_tile.texture = tsrc->getTexture("halo.png",&h_tile.texture_id);
- v3f pos = intToFloat(p, BS);
- f32 d = 0.05 * BS;
- for(std::vector<aabb3f>::iterator
- i = boxes.begin();
- i != boxes.end(); i++)
- {
- aabb3f box = *i;
- box.MinEdge += v3f(-d, -d, -d) + pos;
- box.MaxEdge += v3f(d, d, d) + pos;
- makeCuboid(&collector, box, &h_tile, 1, c, NULL);
- }
- }
- }
-
for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
@@ -254,8 +205,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
switch(f.drawtype){
default:
- infostream<<"Got "<<f.drawtype<<std::endl;
- assert(0);
+ infostream << "Got " << f.drawtype << std::endl;
+ FATAL_ERROR("Unknown drawtype");
break;
case NDT_AIRLIKE:
break;
@@ -802,7 +753,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
break;}
case NDT_GLASSLIKE_FRAMED_OPTIONAL:
// This is always pre-converted to something else
- assert(0);
+ FATAL_ERROR("NDT_GLASSLIKE_FRAMED_OPTIONAL not pre-converted as expected");
break;
case NDT_GLASSLIKE_FRAMED:
{
@@ -1054,7 +1005,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
break;}
case NDT_ALLFACES_OPTIONAL:
// This is always pre-converted to something else
- assert(0);
+ FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted");
break;
case NDT_TORCHLIKE:
{
@@ -1446,109 +1397,42 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
break;}
case NDT_RAILLIKE:
{
- bool is_rail_x [] = { false, false }; /* x-1, x+1 */
- bool is_rail_z [] = { false, false }; /* z-1, z+1 */
-
- bool is_rail_z_minus_y [] = { false, false }; /* z-1, z+1; y-1 */
- bool is_rail_x_minus_y [] = { false, false }; /* x-1, z+1; y-1 */
- bool is_rail_z_plus_y [] = { false, false }; /* z-1, z+1; y+1 */
- bool is_rail_x_plus_y [] = { false, false }; /* x-1, x+1; y+1 */
-
- MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
- MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
- MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
- MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
- MapNode n_plus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y+1, z));
- MapNode n_plus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y-1, z));
- MapNode n_minus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y+1, z));
- MapNode n_minus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y-1, z));
- MapNode n_plus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z+1));
- MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1));
- MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1));
- MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1));
+ bool is_rail_x[6]; /* (-1,-1,0) X (1,-1,0) (-1,0,0) X (1,0,0) (-1,1,0) X (1,1,0) */
+ bool is_rail_z[6];
content_t thiscontent = n.getContent();
std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
- bool self_connect_to_raillike = ((ItemGroupList) nodedef->get(n).groups)[groupname] != 0;
-
- if ((nodedef->get(n_minus_x).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_minus_x).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_minus_x.getContent() == thiscontent)
- is_rail_x[0] = true;
-
- if ((nodedef->get(n_minus_x_minus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_minus_x_minus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_minus_x_minus_y.getContent() == thiscontent)
- is_rail_x_minus_y[0] = true;
-
- if ((nodedef->get(n_minus_x_plus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_minus_x_plus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_minus_x_plus_y.getContent() == thiscontent)
- is_rail_x_plus_y[0] = true;
-
- if ((nodedef->get(n_plus_x).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_plus_x).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_plus_x.getContent() == thiscontent)
- is_rail_x[1] = true;
-
- if ((nodedef->get(n_plus_x_minus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_plus_x_minus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_plus_x_minus_y.getContent() == thiscontent)
- is_rail_x_minus_y[1] = true;
-
- if ((nodedef->get(n_plus_x_plus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_plus_x_plus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_plus_x_plus_y.getContent() == thiscontent)
- is_rail_x_plus_y[1] = true;
-
- if ((nodedef->get(n_minus_z).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_minus_z).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_minus_z.getContent() == thiscontent)
- is_rail_z[0] = true;
-
- if ((nodedef->get(n_minus_z_minus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_minus_z_minus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_minus_z_minus_y.getContent() == thiscontent)
- is_rail_z_minus_y[0] = true;
-
- if ((nodedef->get(n_minus_z_plus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_minus_z_plus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_minus_z_plus_y.getContent() == thiscontent)
- is_rail_z_plus_y[0] = true;
-
- if ((nodedef->get(n_plus_z).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_plus_z).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_plus_z.getContent() == thiscontent)
- is_rail_z[1] = true;
-
- if ((nodedef->get(n_plus_z_minus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_plus_z_minus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_plus_z_minus_y.getContent() == thiscontent)
- is_rail_z_minus_y[1] = true;
-
- if ((nodedef->get(n_plus_z_plus_y).drawtype == NDT_RAILLIKE
- && ((ItemGroupList) nodedef->get(n_plus_z_plus_y).groups)[groupname] != 0
- && self_connect_to_raillike)
- || n_plus_z_plus_y.getContent() == thiscontent)
- is_rail_z_plus_y[1] = true;
-
- bool is_rail_x_all[] = {false, false};
- bool is_rail_z_all[] = {false, false};
- is_rail_x_all[0]=is_rail_x[0] || is_rail_x_minus_y[0] || is_rail_x_plus_y[0];
- is_rail_x_all[1]=is_rail_x[1] || is_rail_x_minus_y[1] || is_rail_x_plus_y[1];
- is_rail_z_all[0]=is_rail_z[0] || is_rail_z_minus_y[0] || is_rail_z_plus_y[0];
- is_rail_z_all[1]=is_rail_z[1] || is_rail_z_minus_y[1] || is_rail_z_plus_y[1];
+ int self_group = ((ItemGroupList) nodedef->get(n).groups)[groupname];
+
+ u8 index = 0;
+ for (s8 y0 = -1; y0 <= 1; y0++) {
+ // Prevent from indexing never used coordinates
+ for (s8 xz = -1; xz <= 1; xz++) {
+ if (xz == 0)
+ continue;
+ MapNode n_xy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x + xz, y + y0, z));
+ MapNode n_zy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y + y0, z + xz));
+ ContentFeatures def_xy = nodedef->get(n_xy);
+ ContentFeatures def_zy = nodedef->get(n_zy);
+
+ // Check if current node would connect with the rail
+ is_rail_x[index] = ((def_xy.drawtype == NDT_RAILLIKE
+ && ((ItemGroupList) def_xy.groups)[groupname] == self_group)
+ || n_xy.getContent() == thiscontent);
+
+ is_rail_z[index] = ((def_zy.drawtype == NDT_RAILLIKE
+ && ((ItemGroupList) def_zy.groups)[groupname] == self_group)
+ || n_zy.getContent() == thiscontent);
+ index++;
+ }
+ }
+
+ bool is_rail_x_all[2]; // [0] = negative x, [1] = positive x coordinate from the current node position
+ bool is_rail_z_all[2];
+ is_rail_x_all[0] = is_rail_x[0] || is_rail_x[2] || is_rail_x[4];
+ is_rail_x_all[1] = is_rail_x[1] || is_rail_x[3] || is_rail_x[5];
+ is_rail_z_all[0] = is_rail_z[0] || is_rail_z[2] || is_rail_z[4];
+ is_rail_z_all[1] = is_rail_z[1] || is_rail_z[3] || is_rail_z[5];
// reasonable default, flat straight unrotated rail
bool is_straight = true;
@@ -1557,13 +1441,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
u8 tileindex = 0;
// check for sloped rail
- if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
- {
- adjacencies = 5; //5 means sloped
+ if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) {
+ adjacencies = 5; // 5 means sloped
is_straight = true; // sloped is always straight
- }
- else
- {
+ } else {
// is really straight, rails on both sides
is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
@@ -1571,44 +1452,44 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
switch (adjacencies) {
case 1:
- if(is_rail_x_all[0] || is_rail_x_all[1])
+ if (is_rail_x_all[0] || is_rail_x_all[1])
angle = 90;
break;
case 2:
- if(!is_straight)
+ if (!is_straight)
tileindex = 1; // curved
- if(is_rail_x_all[0] && is_rail_x_all[1])
+ if (is_rail_x_all[0] && is_rail_x_all[1])
angle = 90;
- if(is_rail_z_all[0] && is_rail_z_all[1]){
- if (is_rail_z_plus_y[0])
+ if (is_rail_z_all[0] && is_rail_z_all[1]) {
+ if (is_rail_z[4])
angle = 180;
}
- else if(is_rail_x_all[0] && is_rail_z_all[0])
+ else if (is_rail_x_all[0] && is_rail_z_all[0])
angle = 270;
- else if(is_rail_x_all[0] && is_rail_z_all[1])
+ else if (is_rail_x_all[0] && is_rail_z_all[1])
angle = 180;
- else if(is_rail_x_all[1] && is_rail_z_all[1])
+ else if (is_rail_x_all[1] && is_rail_z_all[1])
angle = 90;
break;
case 3:
// here is where the potential to 'switch' a junction is, but not implemented at present
tileindex = 2; // t-junction
if(!is_rail_x_all[1])
- angle=180;
+ angle = 180;
if(!is_rail_z_all[0])
- angle=90;
+ angle = 90;
if(!is_rail_z_all[1])
- angle=270;
+ angle = 270;
break;
case 4:
tileindex = 3; // crossing
break;
case 5: //sloped
- if(is_rail_z_plus_y[0])
+ if (is_rail_z[4])
angle = 180;
- if(is_rail_x_plus_y[0])
+ if (is_rail_x[4])
angle = 90;
- if(is_rail_x_plus_y[1])
+ if (is_rail_x[5])
angle = -90;
break;
default:
@@ -1626,7 +1507,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
float s = BS/2;
short g = -1;
- if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
+ if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5])
g = 1; //Object is at a slope
video::S3DVertex vertices[4] =
@@ -1766,5 +1647,55 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
break;}
}
}
+
+ /*
+ Caused by incorrect alpha blending, selection mesh needs to be created as
+ last element to ensure it gets blended correct over nodes with alpha channel
+ */
+ // Create selection mesh
+ v3s16 p = data->m_highlighted_pos_relative;
+ if (data->m_show_hud &&
+ (p.X >= 0) && (p.X < MAP_BLOCKSIZE) &&
+ (p.Y >= 0) && (p.Y < MAP_BLOCKSIZE) &&
+ (p.Z >= 0) && (p.Z < MAP_BLOCKSIZE)) {
+
+ MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
+ if(n.getContent() != CONTENT_AIR) {
+ // Get selection mesh light level
+ static const v3s16 dirs[7] = {
+ v3s16( 0, 0, 0),
+ v3s16( 0, 1, 0),
+ v3s16( 0,-1, 0),
+ v3s16( 1, 0, 0),
+ v3s16(-1, 0, 0),
+ v3s16( 0, 0, 1),
+ v3s16( 0, 0,-1)
+ };
+
+ u16 l = 0;
+ u16 l1 = 0;
+ for (u8 i = 0; i < 7; i++) {
+ MapNode n1 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dirs[i]);
+ l1 = getInteriorLight(n1, -4, nodedef);
+ if (l1 > l)
+ l = l1;
+ }
+ video::SColor c = MapBlock_LightColor(255, l, 0);
+ data->m_highlight_mesh_color = c;
+ std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
+ TileSpec h_tile;
+ h_tile.material_flags |= MATERIAL_FLAG_HIGHLIGHTED;
+ h_tile.texture = tsrc->getTextureForMesh("halo.png",&h_tile.texture_id);
+ v3f pos = intToFloat(p, BS);
+ f32 d = 0.05 * BS;
+ for (std::vector<aabb3f>::iterator i = boxes.begin();
+ i != boxes.end(); i++) {
+ aabb3f box = *i;
+ box.MinEdge += v3f(-d, -d, -d) + pos;
+ box.MaxEdge += v3f(d, d, d) + pos;
+ makeCuboid(&collector, box, &h_tile, 1, c, NULL);
+ }
+ }
+ }
}
diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp
index 44d0b8e38..3a4a4652a 100644
--- a/src/content_mapnode.cpp
+++ b/src/content_mapnode.cpp
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapnode.h"
#include "nodedef.h"
#include "nameidmapping.h"
-#include <map>
+#include "util/string.h"
/*
Legacy node content type IDs
@@ -218,14 +218,13 @@ public:
}
std::string get(const std::string &old)
{
- std::map<std::string, std::string>::const_iterator i;
- i = old_to_new.find(old);
- if(i == old_to_new.end())
+ StringMap::const_iterator it = old_to_new.find(old);
+ if (it == old_to_new.end())
return "";
- return i->second;
+ return it->second;
}
private:
- std::map<std::string, std::string> old_to_new;
+ StringMap old_to_new;
};
NewNameGetter newnamegetter;
@@ -234,16 +233,3 @@ std::string content_mapnode_get_new_name(const std::string &oldname)
{
return newnamegetter.get(oldname);
}
-
-content_t legacy_get_id(const std::string &oldname, INodeDefManager *ndef)
-{
- std::string newname = content_mapnode_get_new_name(oldname);
- if(newname == "")
- return CONTENT_IGNORE;
- content_t id;
- bool found = ndef->getId(newname, id);
- if(!found)
- return CONTENT_IGNORE;
- return id;
-}
-
diff --git a/src/content_mapnode.h b/src/content_mapnode.h
index 5c9c0b66d..5d68afe59 100644
--- a/src/content_mapnode.h
+++ b/src/content_mapnode.h
@@ -37,8 +37,5 @@ void content_mapnode_get_name_id_mapping(NameIdMapping *nimap);
// Convert "CONTENT_STONE"-style names to dynamic ids
std::string content_mapnode_get_new_name(const std::string &oldname);
class INodeDefManager;
-content_t legacy_get_id(const std::string &oldname, INodeDefManager *ndef);
-#define LEGN(ndef, oldname) legacy_get_id(oldname, ndef)
#endif
-
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index d4b3079ec..add1726fc 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -23,12 +23,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "collision.h"
#include "environment.h"
#include "settings.h"
-#include "main.h" // For g_profiler
-#include "profiler.h"
#include "serialization.h" // For compressZlib
#include "tool.h" // For ToolCapabilities
#include "gamedef.h"
#include "player.h"
+#include "server.h"
#include "scripting_game.h"
#include "genericobject.h"
#include "log.h"
@@ -36,54 +35,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types;
/*
- DummyLoadSAO
-*/
-
-class DummyLoadSAO : public ServerActiveObject
-{
-public:
- DummyLoadSAO(ServerEnvironment *env, v3f pos, u8 type):
- ServerActiveObject(env, pos)
- {
- ServerActiveObject::registerType(type, create);
- }
- // Pretend to be the test object (to fool the client)
- u8 getType() const
- { return ACTIVEOBJECT_TYPE_TEST; }
- // And never save to disk
- bool isStaticAllowed() const
- { return false; }
-
- static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
- const std::string &data)
- {
- return new DummyLoadSAO(env, pos, 0);
- }
-
- void step(float dtime, bool send_recommended)
- {
- m_removed = true;
- infostream<<"DummyLoadSAO step"<<std::endl;
- }
-
- bool getCollisionBox(aabb3f *toset) {
- return false;
- }
-
- bool collideWithObjects() {
- return false;
- }
-
-private:
-};
-
-// Prototype (registers item for deserialization)
-DummyLoadSAO proto1_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_RAT);
-DummyLoadSAO proto2_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_OERKKI1);
-DummyLoadSAO proto3_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_FIREFLY);
-DummyLoadSAO proto4_DummyLoadSAO(NULL, v3f(0,0,0), ACTIVEOBJECT_TYPE_MOBV2);
-
-/*
TestSAO
*/
@@ -97,7 +48,7 @@ public:
{
ServerActiveObject::registerType(getType(), create);
}
- u8 getType() const
+ ActiveObjectType getType() const
{ return ACTIVEOBJECT_TYPE_TEST; }
static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
@@ -138,7 +89,7 @@ public:
data += itos(m_base_position.Z);
ActiveObjectMessage aom(getId(), false, data);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
}
@@ -159,203 +110,6 @@ private:
TestSAO proto_TestSAO(NULL, v3f(0,0,0));
/*
- ItemSAO
-
- DEPRECATED: New dropped items are implemented in Lua; see
- builtin/item_entity.lua.
-*/
-
-class ItemSAO : public ServerActiveObject
-{
-public:
- u8 getType() const
- { return ACTIVEOBJECT_TYPE_ITEM; }
-
- float getMinimumSavedMovement()
- { return 0.1*BS; }
-
- static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
- const std::string &data)
- {
- std::istringstream is(data, std::ios::binary);
- char buf[1];
- // read version
- is.read(buf, 1);
- u8 version = buf[0];
- // check if version is supported
- if(version != 0)
- return NULL;
- std::string itemstring = deSerializeString(is);
- infostream<<"create(): Creating item \""
- <<itemstring<<"\""<<std::endl;
- return new ItemSAO(env, pos, itemstring);
- }
-
- ItemSAO(ServerEnvironment *env, v3f pos,
- const std::string &itemstring):
- ServerActiveObject(env, pos),
- m_itemstring(itemstring),
- m_itemstring_changed(false),
- m_speed_f(0,0,0),
- m_last_sent_position(0,0,0)
- {
- ServerActiveObject::registerType(getType(), create);
- }
-
- void step(float dtime, bool send_recommended)
- {
- ScopeProfiler sp2(g_profiler, "step avg", SPT_AVG);
-
- assert(m_env);
-
- const float interval = 0.2;
- if(m_move_interval.step(dtime, interval)==false)
- return;
- dtime = interval;
-
- core::aabbox3d<f32> box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.);
- collisionMoveResult moveresult;
- // Apply gravity
- m_speed_f += v3f(0, -dtime*9.81*BS, 0);
- // Maximum movement without glitches
- f32 pos_max_d = BS*0.25;
- // Limit speed
- if(m_speed_f.getLength()*dtime > pos_max_d)
- m_speed_f *= pos_max_d / (m_speed_f.getLength()*dtime);
- v3f pos_f = getBasePosition();
- v3f accel_f = v3f(0,0,0);
- f32 stepheight = 0;
- moveresult = collisionMoveSimple(m_env,m_env->getGameDef(),
- pos_max_d, box, stepheight, dtime,
- pos_f, m_speed_f, accel_f);
-
- if(send_recommended == false)
- return;
-
- if(pos_f.getDistanceFrom(m_last_sent_position) > 0.05*BS)
- {
- setBasePosition(pos_f);
- m_last_sent_position = pos_f;
-
- std::ostringstream os(std::ios::binary);
- // command (0 = update position)
- writeU8(os, 0);
- // pos
- writeV3F1000(os, m_base_position);
- // create message and add to list
- ActiveObjectMessage aom(getId(), false, os.str());
- m_messages_out.push_back(aom);
- }
- if(m_itemstring_changed)
- {
- m_itemstring_changed = false;
-
- std::ostringstream os(std::ios::binary);
- // command (1 = update itemstring)
- writeU8(os, 1);
- // itemstring
- os<<serializeString(m_itemstring);
- // create message and add to list
- ActiveObjectMessage aom(getId(), false, os.str());
- m_messages_out.push_back(aom);
- }
- }
-
- std::string getClientInitializationData(u16 protocol_version)
- {
- std::ostringstream os(std::ios::binary);
- // version
- writeU8(os, 0);
- // pos
- writeV3F1000(os, m_base_position);
- // itemstring
- os<<serializeString(m_itemstring);
- return os.str();
- }
-
- std::string getStaticData()
- {
- infostream<<__FUNCTION_NAME<<std::endl;
- std::ostringstream os(std::ios::binary);
- // version
- writeU8(os, 0);
- // itemstring
- os<<serializeString(m_itemstring);
- return os.str();
- }
-
- ItemStack createItemStack()
- {
- try{
- IItemDefManager *idef = m_env->getGameDef()->idef();
- ItemStack item;
- item.deSerialize(m_itemstring, idef);
- infostream<<__FUNCTION_NAME<<": m_itemstring=\""<<m_itemstring
- <<"\" -> item=\""<<item.getItemString()<<"\""
- <<std::endl;
- return item;
- }
- catch(SerializationError &e)
- {
- infostream<<__FUNCTION_NAME<<": serialization error: "
- <<"m_itemstring=\""<<m_itemstring<<"\""<<std::endl;
- return ItemStack();
- }
- }
-
- int punch(v3f dir,
- const ToolCapabilities *toolcap,
- ServerActiveObject *puncher,
- float time_from_last_punch)
- {
- // Take item into inventory
- ItemStack item = createItemStack();
- Inventory *inv = puncher->getInventory();
- if(inv != NULL)
- {
- std::string wieldlist = puncher->getWieldList();
- ItemStack leftover = inv->addItem(wieldlist, item);
- puncher->setInventoryModified();
- if(leftover.empty())
- {
- m_removed = true;
- }
- else
- {
- m_itemstring = leftover.getItemString();
- m_itemstring_changed = true;
- }
- }
-
- return 0;
- }
-
- bool getCollisionBox(aabb3f *toset) {
- return false;
- }
-
- bool collideWithObjects() {
- return false;
- }
-
-private:
- std::string m_itemstring;
- bool m_itemstring_changed;
- v3f m_speed_f;
- v3f m_last_sent_position;
- IntervalLimiter m_move_interval;
-};
-
-// Prototype (registers item for deserialization)
-ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), "");
-
-ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
- const std::string &itemstring)
-{
- return new ItemSAO(env, pos, itemstring);
-}
-
-/*
LuaEntitySAO
*/
@@ -381,6 +135,7 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos,
m_armor_groups_sent(false),
m_animation_speed(0),
m_animation_blend(0),
+ m_animation_loop(true),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_parent_id(0),
@@ -477,7 +232,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
std::string str = getPropertyPacket();
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
// If attached, check that our parent is still there. If it isn't, detach.
@@ -564,15 +319,16 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
m_armor_groups);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
if(m_animation_sent == false){
m_animation_sent = true;
- std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
+ std::string str = gob_cmd_update_animation(
+ m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
if(m_bone_position_sent == false){
@@ -581,7 +337,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
}
@@ -590,7 +346,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
}
@@ -611,7 +367,8 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
- os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
+ os<<serializeLongString(gob_cmd_update_animation(
+ m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
}
@@ -664,19 +421,19 @@ int LuaEntitySAO::punch(v3f dir,
ServerActiveObject *puncher,
float time_from_last_punch)
{
- if(!m_registered){
+ if (!m_registered){
// Delete unknown LuaEntities when punched
m_removed = true;
return 0;
}
// It's best that attachments cannot be punched
- if(isAttached())
+ if (isAttached())
return 0;
ItemStack *punchitem = NULL;
ItemStack punchitem_static;
- if(puncher){
+ if (puncher) {
punchitem_static = puncher->getWieldedItem();
punchitem = &punchitem_static;
}
@@ -687,31 +444,26 @@ int LuaEntitySAO::punch(v3f dir,
punchitem,
time_from_last_punch);
- if(result.did_punch)
- {
+ if (result.did_punch) {
setHP(getHP() - result.damage);
+ if (result.damage > 0) {
+ std::string punchername = puncher ? puncher->getDescription() : "nil";
- std::string punchername = "nil";
-
- if ( puncher != 0 )
- punchername = puncher->getDescription();
-
- actionstream<<getDescription()<<" punched by "
- <<punchername<<", damage "<<result.damage
- <<" hp, health now "<<getHP()<<" hp"<<std::endl;
-
- {
- std::string str = gob_cmd_punched(result.damage, getHP());
- // create message and add to list
- ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ actionstream << getDescription() << " punched by "
+ << punchername << ", damage " << result.damage
+ << " hp, health now " << getHP() << " hp" << std::endl;
}
- if(getHP() == 0)
- m_removed = true;
+ std::string str = gob_cmd_punched(result.damage, getHP());
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push(aom);
}
+ if (getHP() == 0)
+ m_removed = true;
+
m_env->getScriptIface()->luaentity_Punch(m_id, puncher,
time_from_last_punch, toolcap, dir);
@@ -720,10 +472,10 @@ int LuaEntitySAO::punch(v3f dir,
void LuaEntitySAO::rightClick(ServerActiveObject *clicker)
{
- if(!m_registered)
+ if (!m_registered)
return;
// It's best that attachments cannot be clicked
- if(isAttached())
+ if (isAttached())
return;
m_env->getScriptIface()->luaentity_Rightclick(m_id, clicker);
}
@@ -778,21 +530,41 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups)
m_armor_groups_sent = false;
}
-void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
+ItemGroupList LuaEntitySAO::getArmorGroups()
+{
+ return m_armor_groups;
+}
+
+void LuaEntitySAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
{
m_animation_range = frame_range;
m_animation_speed = frame_speed;
m_animation_blend = frame_blend;
+ m_animation_loop = frame_loop;
m_animation_sent = false;
}
-void LuaEntitySAO::setBonePosition(std::string bone, v3f position, v3f rotation)
+void LuaEntitySAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
+{
+ *frame_range = m_animation_range;
+ *frame_speed = m_animation_speed;
+ *frame_blend = m_animation_blend;
+ *frame_loop = m_animation_loop;
+}
+
+void LuaEntitySAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
{
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
m_bone_position_sent = false;
}
-void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
+void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
+{
+ *position = m_bone_position[bone].X;
+ *rotation = m_bone_position[bone].Y;
+}
+
+void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. Attachments
@@ -809,6 +581,30 @@ void LuaEntitySAO::setAttachment(int parent_id, std::string bone, v3f position,
m_attachment_sent = false;
}
+void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
+ v3f *rotation)
+{
+ *parent_id = m_attachment_parent_id;
+ *bone = m_attachment_bone;
+ *position = m_attachment_position;
+ *rotation = m_attachment_rotation;
+}
+
+void LuaEntitySAO::addAttachmentChild(int child_id)
+{
+ m_attachment_child_ids.insert(child_id);
+}
+
+void LuaEntitySAO::removeAttachmentChild(int child_id)
+{
+ m_attachment_child_ids.erase(child_id);
+}
+
+std::set<int> LuaEntitySAO::getAttachmentChildIds()
+{
+ return m_attachment_child_ids;
+}
+
ObjectProperties* LuaEntitySAO::accessObjectProperties()
{
return &m_prop;
@@ -854,7 +650,7 @@ void LuaEntitySAO::setTextureMod(const std::string &mod)
std::string str = gob_cmd_set_texture_mod(mod);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
@@ -868,7 +664,7 @@ void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength,
);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
std::string LuaEntitySAO::getName()
@@ -908,7 +704,7 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end)
);
// create message and add to list
ActiveObjectMessage aom(getId(), false, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
bool LuaEntitySAO::getCollisionBox(aabb3f *toset) {
@@ -956,16 +752,14 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_is_singleplayer(is_singleplayer),
m_animation_speed(0),
m_animation_blend(0),
+ m_animation_loop(true),
m_animation_sent(false),
m_bone_position_sent(false),
m_attachment_parent_id(0),
m_attachment_sent(false),
+ m_nametag_color(video::SColor(255, 255, 255, 255)),
+ m_nametag_sent(false),
// public
- m_moved(false),
- m_inventory_not_sent(false),
- m_hp_not_sent(false),
- m_breath_not_sent(false),
- m_wielded_item_not_sent(false),
m_physics_override_speed(1),
m_physics_override_jump(1),
m_physics_override_gravity(1),
@@ -973,8 +767,8 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
m_physics_override_sneak_glitch(true),
m_physics_override_sent(false)
{
- assert(m_player);
- assert(m_peer_id != 0);
+ assert(m_player); // pre-condition
+ assert(m_peer_id != 0); // pre-condition
setBasePosition(m_player->getPosition());
m_inventory = &m_player->inventory;
m_armor_groups["fleshy"] = 100;
@@ -1051,10 +845,11 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
writeF1000(os, m_player->getYaw());
writeS16(os, getHP());
- writeU8(os, 5 + m_bone_position.size()); // number of messages stuffed in here
+ writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
os<<serializeLongString(getPropertyPacket()); // message 1
os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
- os<<serializeLongString(gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend)); // 3
+ os<<serializeLongString(gob_cmd_update_animation(
+ m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3
for(std::map<std::string, core::vector2d<v3f> >::const_iterator ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii){
os<<serializeLongString(gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y)); // m_bone_position.size
}
@@ -1062,6 +857,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
os<<serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed,
m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
m_physics_override_sneak_glitch)); // 5
+ os << serializeLongString(gob_cmd_update_nametag_attributes(m_nametag_color)); // 6
}
else
{
@@ -1082,7 +878,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
std::string PlayerSAO::getStaticData()
{
- assert(0);
+ FATAL_ERROR("Deprecated function (?)");
return "";
}
@@ -1105,7 +901,7 @@ void PlayerSAO::step(float dtime, bool send_recommended)
std::string str = getPropertyPacket();
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
// If attached, check that our parent is still there. If it isn't, detach.
@@ -1116,7 +912,7 @@ void PlayerSAO::step(float dtime, bool send_recommended)
m_attachment_position = v3f(0,0,0);
m_attachment_rotation = v3f(0,0,0);
m_player->setPosition(m_last_good_position);
- m_moved = true;
+ ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
}
//dstream<<"PlayerSAO::step: dtime: "<<dtime<<std::endl;
@@ -1168,22 +964,16 @@ void PlayerSAO::step(float dtime, bool send_recommended)
);
// create message and add to list
ActiveObjectMessage aom(getId(), false, str);
- m_messages_out.push_back(aom);
- }
-
- if(m_wielded_item_not_sent)
- {
- m_wielded_item_not_sent = false;
- // GenericCAO has no special way to show this
+ m_messages_out.push(aom);
}
- if(m_armor_groups_sent == false){
+ if(m_armor_groups_sent == false) {
m_armor_groups_sent = true;
std::string str = gob_cmd_update_armor_groups(
m_armor_groups);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
if(m_physics_override_sent == false){
@@ -1193,15 +983,16 @@ void PlayerSAO::step(float dtime, bool send_recommended)
m_physics_override_sneak, m_physics_override_sneak_glitch);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
if(m_animation_sent == false){
m_animation_sent = true;
- std::string str = gob_cmd_update_animation(m_animation_range, m_animation_speed, m_animation_blend);
+ std::string str = gob_cmd_update_animation(
+ m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
if(m_bone_position_sent == false){
@@ -1210,7 +1001,7 @@ void PlayerSAO::step(float dtime, bool send_recommended)
std::string str = gob_cmd_update_bone_position((*ii).first, (*ii).second.X, (*ii).second.Y);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
}
}
@@ -1219,7 +1010,15 @@ void PlayerSAO::step(float dtime, bool send_recommended)
std::string str = gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation);
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
+ }
+
+ if (m_nametag_sent == false) {
+ m_nametag_sent = true;
+ std::string str = gob_cmd_update_nametag_attributes(m_nametag_color);
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push(aom);
}
}
@@ -1237,8 +1036,7 @@ void PlayerSAO::setPos(v3f pos)
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
- // Force position change on client
- m_moved = true;
+ ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
}
void PlayerSAO::moveTo(v3f pos, bool continuous)
@@ -1248,22 +1046,19 @@ void PlayerSAO::moveTo(v3f pos, bool continuous)
m_player->setPosition(pos);
// Movement caused by this command is always valid
m_last_good_position = pos;
- // Force position change on client
- m_moved = true;
+ ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
}
void PlayerSAO::setYaw(float yaw)
{
m_player->setYaw(yaw);
- // Force change on client
- m_moved = true;
+ ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
}
void PlayerSAO::setPitch(float pitch)
{
m_player->setPitch(pitch);
- // Force change on client
- m_moved = true;
+ ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id);
}
int PlayerSAO::punch(v3f dir,
@@ -1272,19 +1067,19 @@ int PlayerSAO::punch(v3f dir,
float time_from_last_punch)
{
// It's best that attachments cannot be punched
- if(isAttached())
+ if (isAttached())
return 0;
- if(!toolcap)
+ if (!toolcap)
return 0;
// No effect if PvP disabled
- if(g_settings->getBool("enable_pvp") == false){
- if(puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER){
+ if (g_settings->getBool("enable_pvp") == false) {
+ if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
std::string str = gob_cmd_punched(0, getHP());
// create message and add to list
ActiveObjectMessage aom(getId(), true, str);
- m_messages_out.push_back(aom);
+ m_messages_out.push(aom);
return 0;
}
}
@@ -1294,14 +1089,35 @@ int PlayerSAO::punch(v3f dir,
std::string punchername = "nil";
- if ( puncher != 0 )
+ if (puncher != 0)
punchername = puncher->getDescription();
- actionstream<<"Player "<<m_player->getName()<<" punched by "
- <<punchername<<", damage "<<hitparams.hp
- <<" HP"<<std::endl;
+ PlayerSAO *playersao = m_player->getPlayerSAO();
- setHP(getHP() - hitparams.hp);
+ bool damage_handled = m_env->getScriptIface()->on_punchplayer(playersao,
+ puncher, time_from_last_punch, toolcap, dir,
+ hitparams.hp);
+
+ if (!damage_handled) {
+ setHP(getHP() - hitparams.hp);
+ } else { // override client prediction
+ if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ std::string str = gob_cmd_punched(0, getHP());
+ // create message and add to list
+ ActiveObjectMessage aom(getId(), true, str);
+ m_messages_out.push(aom);
+ }
+ }
+
+
+ actionstream << "Player " << m_player->getName() << " punched by "
+ << punchername;
+ if (!damage_handled) {
+ actionstream << ", damage " << hitparams.hp << " HP";
+ } else {
+ actionstream << ", damage handled by lua";
+ }
+ actionstream << std::endl;
return hitparams.wear;
}
@@ -1326,27 +1142,28 @@ void PlayerSAO::setHP(s16 hp)
{
s16 oldhp = m_player->hp;
- if(hp < 0)
+ s16 hp_change = m_env->getScriptIface()->on_player_hpchange(this,
+ hp - oldhp);
+ if (hp_change == 0)
+ return;
+ hp = oldhp + hp_change;
+
+ if (hp < 0)
hp = 0;
- else if(hp > PLAYER_MAX_HP)
+ else if (hp > PLAYER_MAX_HP)
hp = PLAYER_MAX_HP;
- if(hp < oldhp && g_settings->getBool("enable_damage") == false)
- {
- m_hp_not_sent = true; // fix wrong prediction on client
+ if(hp < oldhp && g_settings->getBool("enable_damage") == false) {
return;
}
m_player->hp = hp;
- if(hp != oldhp) {
- m_hp_not_sent = true;
- if(oldhp > hp)
- m_damage += oldhp - hp;
- }
+ if (oldhp > hp)
+ m_damage += (oldhp - hp);
// Update properties on death
- if((hp == 0) != (oldhp == 0))
+ if ((hp == 0) != (oldhp == 0))
m_properties_sent = false;
}
@@ -1366,23 +1183,43 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
m_armor_groups_sent = false;
}
-void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend)
+ItemGroupList PlayerSAO::getArmorGroups()
+{
+ return m_armor_groups;
+}
+
+void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop)
{
// store these so they can be updated to clients
m_animation_range = frame_range;
m_animation_speed = frame_speed;
m_animation_blend = frame_blend;
+ m_animation_loop = frame_loop;
m_animation_sent = false;
}
-void PlayerSAO::setBonePosition(std::string bone, v3f position, v3f rotation)
+void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop)
+{
+ *frame_range = m_animation_range;
+ *frame_speed = m_animation_speed;
+ *frame_blend = m_animation_blend;
+ *frame_loop = m_animation_loop;
+}
+
+void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation)
{
// store these so they can be updated to clients
m_bone_position[bone] = core::vector2d<v3f>(position, rotation);
m_bone_position_sent = false;
}
-void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
+void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation)
+{
+ *position = m_bone_position[bone].X;
+ *rotation = m_bone_position[bone].Y;
+}
+
+void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
{
// Attachments need to be handled on both the server and client.
// If we just attach on the server, we can only copy the position of the parent. Attachments
@@ -1399,6 +1236,30 @@ void PlayerSAO::setAttachment(int parent_id, std::string bone, v3f position, v3f
m_attachment_sent = false;
}
+void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position,
+ v3f *rotation)
+{
+ *parent_id = m_attachment_parent_id;
+ *bone = m_attachment_bone;
+ *position = m_attachment_position;
+ *rotation = m_attachment_rotation;
+}
+
+void PlayerSAO::addAttachmentChild(int child_id)
+{
+ m_attachment_child_ids.insert(child_id);
+}
+
+void PlayerSAO::removeAttachmentChild(int child_id)
+{
+ m_attachment_child_ids.erase(child_id);
+}
+
+std::set<int> PlayerSAO::getAttachmentChildIds()
+{
+ return m_attachment_child_ids;
+}
+
ObjectProperties* PlayerSAO::accessObjectProperties()
{
return &m_prop;
@@ -1409,6 +1270,17 @@ void PlayerSAO::notifyObjectPropertiesModified()
m_properties_sent = false;
}
+void PlayerSAO::setNametagColor(video::SColor color)
+{
+ m_nametag_color = color;
+ m_nametag_sent = false;
+}
+
+video::SColor PlayerSAO::getNametagColor()
+{
+ return m_nametag_color;
+}
+
Inventory* PlayerSAO::getInventory()
{
return m_inventory;
@@ -1425,11 +1297,6 @@ InventoryLocation PlayerSAO::getInventoryLocation() const
return loc;
}
-void PlayerSAO::setInventoryModified()
-{
- m_inventory_not_sent = true;
-}
-
std::string PlayerSAO::getWieldList() const
{
return "main";
@@ -1442,10 +1309,8 @@ int PlayerSAO::getWieldIndex() const
void PlayerSAO::setWieldIndex(int i)
{
- if(i != m_wield_index)
- {
+ if(i != m_wield_index) {
m_wield_index = i;
- m_wielded_item_not_sent = true;
}
}
@@ -1512,7 +1377,6 @@ bool PlayerSAO::checkMovementCheat()
<<" moved too fast; resetting position"
<<std::endl;
m_player->setPosition(m_last_good_position);
- m_moved = true;
cheated = true;
}
}
diff --git a/src/content_sao.h b/src/content_sao.h
index 38baeab3a..1f0a68cd8 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -21,14 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define CONTENT_SAO_HEADER
#include "serverobject.h"
-#include "content_object.h"
#include "itemgroup.h"
#include "player.h"
#include "object_properties.h"
-ServerActiveObject* createItemSAO(ServerEnvironment *env, v3f pos,
- const std::string &itemstring);
-
/*
LuaEntitySAO needs some internals exposed.
*/
@@ -39,9 +35,9 @@ public:
LuaEntitySAO(ServerEnvironment *env, v3f pos,
const std::string &name, const std::string &state);
~LuaEntitySAO();
- u8 getType() const
+ ActiveObjectType getType() const
{ return ACTIVEOBJECT_TYPE_LUAENTITY; }
- u8 getSendType() const
+ ActiveObjectType getSendType() const
{ return ACTIVEOBJECT_TYPE_GENERIC; }
virtual void addedToEnvironment(u32 dtime_s);
static ServerActiveObject* create(ServerEnvironment *env, v3f pos,
@@ -62,9 +58,16 @@ public:
void setHP(s16 hp);
s16 getHP() const;
void setArmorGroups(const ItemGroupList &armor_groups);
- void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
- void setBonePosition(std::string bone, v3f position, v3f rotation);
- void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
+ ItemGroupList getArmorGroups();
+ void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop);
+ void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop);
+ void setBonePosition(const std::string &bone, v3f position, v3f rotation);
+ void getBonePosition(const std::string &bone, v3f *position, v3f *rotation);
+ void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation);
+ void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation);
+ void addAttachmentChild(int child_id);
+ void removeAttachmentChild(int child_id);
+ std::set<int> getAttachmentChildIds();
ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified();
/* LuaEntitySAO-specific */
@@ -106,12 +109,14 @@ private:
v2f m_animation_range;
float m_animation_speed;
float m_animation_blend;
+ bool m_animation_loop;
bool m_animation_sent;
std::map<std::string, core::vector2d<v3f> > m_bone_position;
bool m_bone_position_sent;
int m_attachment_parent_id;
+ std::set<int> m_attachment_child_ids;
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
@@ -158,9 +163,9 @@ public:
PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
const std::set<std::string> &privs, bool is_singleplayer);
~PlayerSAO();
- u8 getType() const
+ ActiveObjectType getType() const
{ return ACTIVEOBJECT_TYPE_PLAYER; }
- u8 getSendType() const
+ ActiveObjectType getSendType() const
{ return ACTIVEOBJECT_TYPE_GENERIC; }
std::string getDescription();
@@ -196,11 +201,20 @@ public:
u16 getBreath() const;
void setBreath(u16 breath);
void setArmorGroups(const ItemGroupList &armor_groups);
- void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
- void setBonePosition(std::string bone, v3f position, v3f rotation);
- void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation);
+ ItemGroupList getArmorGroups();
+ void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop);
+ void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop);
+ void setBonePosition(const std::string &bone, v3f position, v3f rotation);
+ void getBonePosition(const std::string &bone, v3f *position, v3f *rotation);
+ void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation);
+ void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation);
+ void addAttachmentChild(int child_id);
+ void removeAttachmentChild(int child_id);
+ std::set<int> getAttachmentChildIds();
ObjectProperties* accessObjectProperties();
void notifyObjectPropertiesModified();
+ void setNametagColor(video::SColor color);
+ video::SColor getNametagColor();
/*
Inventory interface
@@ -209,7 +223,6 @@ public:
Inventory* getInventory();
const Inventory* getInventory() const;
InventoryLocation getInventoryLocation() const;
- void setInventoryModified();
std::string getWieldList() const;
int getWieldIndex() const;
void setWieldIndex(int i);
@@ -307,25 +320,23 @@ private:
v2f m_animation_range;
float m_animation_speed;
float m_animation_blend;
+ bool m_animation_loop;
bool m_animation_sent;
std::map<std::string, core::vector2d<v3f> > m_bone_position; // Stores position and rotation for each bone name
bool m_bone_position_sent;
int m_attachment_parent_id;
+ std::set<int> m_attachment_child_ids;
std::string m_attachment_bone;
v3f m_attachment_position;
v3f m_attachment_rotation;
bool m_attachment_sent;
-public:
- // Some flags used by Server
- bool m_moved;
- bool m_inventory_not_sent;
- bool m_hp_not_sent;
- bool m_breath_not_sent;
- bool m_wielded_item_not_sent;
+ video::SColor m_nametag_color;
+ bool m_nametag_sent;
+public:
float m_physics_override_speed;
float m_physics_override_jump;
float m_physics_override_gravity;
diff --git a/src/convert_json.cpp b/src/convert_json.cpp
index cea089623..e03508e21 100644
--- a/src/convert_json.cpp
+++ b/src/convert_json.cpp
@@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mods.h"
#include "config.h"
#include "log.h"
-#include "main.h" // for g_settings
#include "settings.h"
#include "httpfetch.h"
#include "porting.h"
@@ -357,18 +356,10 @@ ModStoreModDetails readModStoreModDetails(Json::Value& details) {
}
//value
- if (details["rating"].asString().size()) {
-
- std::string id_raw = details["rating"].asString();
- char* endptr = 0;
- float numbervalue = strtof(id_raw.c_str(),&endptr);
-
- if ((id_raw != "") && (*endptr == 0)) {
- retval.rating = numbervalue;
- }
- }
- else {
- retval.rating = 0.0;
+ if (details["value"].isInt()) {
+ retval.rating = details["value"].asInt();
+ } else {
+ retval.rating = 0;
}
//depends
diff --git a/src/craftdef.cpp b/src/craftdef.cpp
index afc41303f..409481e64 100644
--- a/src/craftdef.cpp
+++ b/src/craftdef.cpp
@@ -27,31 +27,71 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gamedef.h"
#include "inventory.h"
#include "util/serialize.h"
+#include "util/string.h"
+#include "util/numeric.h"
#include "strfnd.h"
#include "exceptions.h"
+inline bool isGroupRecipeStr(const std::string &rec_name)
+{
+ return str_starts_with(rec_name, std::string("group:"));
+}
+
+inline u64 getHashForString(const std::string &recipe_str)
+{
+ /*errorstream << "Hashing craft string \"" << recipe_str << '"';*/
+ return murmur_hash_64_ua(recipe_str.data(), recipe_str.length(), 0xdeadbeef);
+}
+
+static u64 getHashForGrid(CraftHashType type, const std::vector<std::string> &grid_names)
+{
+ switch (type) {
+ case CRAFT_HASH_TYPE_ITEM_NAMES: {
+ std::ostringstream os;
+ bool is_first = true;
+ for (size_t i = 0; i < grid_names.size(); i++) {
+ if (grid_names[i] != "") {
+ os << (is_first ? "" : "\n") << grid_names[i];
+ is_first = false;
+ }
+ }
+ return getHashForString(os.str());
+ } case CRAFT_HASH_TYPE_COUNT: {
+ u64 cnt = 0;
+ for (size_t i = 0; i < grid_names.size(); i++)
+ if (grid_names[i] != "")
+ cnt++;
+ return cnt;
+ } case CRAFT_HASH_TYPE_UNHASHED:
+ return 0;
+ }
+ // invalid CraftHashType
+ assert(false);
+ return 0;
+}
+
// Check if input matches recipe
// Takes recipe groups into account
static bool inputItemMatchesRecipe(const std::string &inp_name,
const std::string &rec_name, IItemDefManager *idef)
{
// Exact name
- if(inp_name == rec_name)
+ if (inp_name == rec_name)
return true;
// Group
- if(rec_name.substr(0,6) == "group:" && idef->isKnown(inp_name)){
+ if (isGroupRecipeStr(rec_name) && idef->isKnown(inp_name)) {
const struct ItemDefinition &def = idef->get(inp_name);
Strfnd f(rec_name.substr(6));
bool all_groups_match = true;
- do{
+ do {
std::string check_group = f.next(",");
- if(itemgroup_get(def.groups, check_group) == 0){
+ if (itemgroup_get(def.groups, check_group) == 0) {
all_groups_match = false;
break;
}
- }while(!f.atend());
- if(all_groups_match)
+ } while (!f.atend());
+ if (all_groups_match)
return true;
}
@@ -72,11 +112,9 @@ static std::vector<std::string> craftGetItemNames(
const std::vector<std::string> &itemstrings, IGameDef *gamedef)
{
std::vector<std::string> result;
- for(std::vector<std::string>::const_iterator
- i = itemstrings.begin();
- i != itemstrings.end(); i++)
- {
- result.push_back(craftGetItemName(*i, gamedef));
+ for (std::vector<std::string>::size_type i = 0;
+ i < itemstrings.size(); i++) {
+ result.push_back(craftGetItemName(itemstrings[i], gamedef));
}
return result;
}
@@ -86,11 +124,9 @@ static std::vector<std::string> craftGetItemNames(
const std::vector<ItemStack> &items, IGameDef *gamedef)
{
std::vector<std::string> result;
- for(std::vector<ItemStack>::const_iterator
- i = items.begin();
- i != items.end(); i++)
- {
- result.push_back(i->name);
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < items.size(); i++) {
+ result.push_back(items[i].name);
}
return result;
}
@@ -100,11 +136,10 @@ static std::vector<ItemStack> craftGetItems(
const std::vector<std::string> &items, IGameDef *gamedef)
{
std::vector<ItemStack> result;
- for(std::vector<std::string>::const_iterator
- i = items.begin();
- i != items.end(); i++)
- {
- result.push_back(ItemStack(std::string(*i),(u16)1,(u16)0,"",gamedef->getItemDefManager()));
+ for (std::vector<std::string>::size_type i = 0;
+ i < items.size(); i++) {
+ result.push_back(ItemStack(std::string(items[i]), (u16)1,
+ (u16)0, "", gamedef->getItemDefManager()));
}
return result;
}
@@ -118,32 +153,26 @@ static bool craftGetBounds(const std::vector<std::string> &items, unsigned int w
bool success = false;
unsigned int x = 0;
unsigned int y = 0;
- for(std::vector<std::string>::const_iterator
- i = items.begin();
- i != items.end(); i++)
- {
- if(*i != "") // Is this an actual item?
- {
- if(!success)
- {
+ for (std::vector<std::string>::size_type i = 0;
+ i < items.size(); i++) {
+ // Is this an actual item?
+ if (items[i] != "") {
+ if (!success) {
// This is the first nonempty item
min_x = max_x = x;
min_y = max_y = y;
success = true;
- }
- else
- {
- if(x < min_x) min_x = x;
- if(x > max_x) max_x = x;
- if(y < min_y) min_y = y;
- if(y > max_y) max_y = y;
+ } else {
+ if (x < min_x) min_x = x;
+ if (x > max_x) max_x = x;
+ if (y < min_y) min_y = y;
+ if (y > max_y) max_y = y;
}
}
// Step coordinate
x++;
- if(x == width)
- {
+ if (x == width) {
x = 0;
y++;
}
@@ -154,12 +183,10 @@ static bool craftGetBounds(const std::vector<std::string> &items, unsigned int w
// Removes 1 from each item stack
static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
{
- for(std::vector<ItemStack>::iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(i->count != 0)
- i->remove(1);
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ if (input.items[i].count != 0)
+ input.items[i].remove(1);
}
}
@@ -167,11 +194,11 @@ static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
// Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
// a water bucket will not be removed but replaced by an empty bucket.
static void craftDecrementOrReplaceInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements,
const CraftReplacements &replacements,
IGameDef *gamedef)
{
- if(replacements.pairs.empty())
- {
+ if (replacements.pairs.empty()) {
craftDecrementInput(input, gamedef);
return;
}
@@ -179,37 +206,33 @@ static void craftDecrementOrReplaceInput(CraftInput &input,
// Make a copy of the replacements pair list
std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
- for(std::vector<ItemStack>::iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(i->count == 1)
- {
- // Find an appropriate replacement
- bool found_replacement = false;
- for(std::vector<std::pair<std::string, std::string> >::iterator
- j = pairs.begin();
- j != pairs.end(); j++)
- {
- ItemStack from_item;
- from_item.deSerialize(j->first, gamedef->idef());
- if(i->name == from_item.name)
- {
- i->deSerialize(j->second, gamedef->idef());
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ ItemStack &item = input.items[i];
+ // Find an appropriate replacement
+ bool found_replacement = false;
+ for (std::vector<std::pair<std::string, std::string> >::iterator
+ j = pairs.begin();
+ j != pairs.end(); ++j) {
+ if (item.name == craftGetItemName(j->first, gamedef)) {
+ if (item.count == 1) {
+ item.deSerialize(j->second, gamedef->idef());
found_replacement = true;
pairs.erase(j);
break;
+ } else {
+ ItemStack rep;
+ rep.deSerialize(j->second, gamedef->idef());
+ item.remove(1);
+ found_replacement = true;
+ output_replacements.push_back(rep);
+ break;
}
}
- // No replacement was found, simply decrement count to zero
- if(!found_replacement)
- i->remove(1);
- }
- else if(i->count >= 2)
- {
- // Ignore replacements for items with count >= 2
- i->remove(1);
}
+ // No replacement was found, simply decrement count by one
+ if (!found_replacement && item.count > 0)
+ item.remove(1);
}
}
@@ -218,24 +241,19 @@ static std::string craftDumpMatrix(const std::vector<std::string> &items,
unsigned int width)
{
std::ostringstream os(std::ios::binary);
- os<<"{ ";
+ os << "{ ";
unsigned int x = 0;
- for(std::vector<std::string>::const_iterator
- i = items.begin();
- i != items.end(); i++, x++)
- {
- if(x == width)
- {
- os<<"; ";
+ for(std::vector<std::string>::size_type i = 0;
+ i < items.size(); i++, x++) {
+ if (x == width) {
+ os << "; ";
x = 0;
+ } else if (x != 0) {
+ os << ",";
}
- else if(x != 0)
- {
- os<<",";
- }
- os<<"\""<<(*i)<<"\"";
+ os << '"' << items[i] << '"';
}
- os<<" }";
+ os << " }";
return os.str();
}
@@ -244,24 +262,19 @@ std::string craftDumpMatrix(const std::vector<ItemStack> &items,
unsigned int width)
{
std::ostringstream os(std::ios::binary);
- os<<"{ ";
+ os << "{ ";
unsigned int x = 0;
- for(std::vector<ItemStack>::const_iterator
- i = items.begin();
- i != items.end(); i++, x++)
- {
- if(x == width)
- {
- os<<"; ";
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < items.size(); i++, x++) {
+ if (x == width) {
+ os << "; ";
x = 0;
+ } else if (x != 0) {
+ os << ",";
}
- else if(x != 0)
- {
- os<<",";
- }
- os<<"\""<<(i->getItemString())<<"\"";
+ os << '"' << (items[i].getItemString()) << '"';
}
- os<<" }";
+ os << " }";
return os.str();
}
@@ -273,7 +286,8 @@ std::string craftDumpMatrix(const std::vector<ItemStack> &items,
std::string CraftInput::dump() const
{
std::ostringstream os(std::ios::binary);
- os<<"(method="<<((int)method)<<", items="<<craftDumpMatrix(items, width)<<")";
+ os << "(method=" << ((int)method) << ", items="
+ << craftDumpMatrix(items, width) << ")";
return os.str();
}
@@ -284,7 +298,7 @@ std::string CraftInput::dump() const
std::string CraftOutput::dump() const
{
std::ostringstream os(std::ios::binary);
- os<<"(item=\""<<item<<"\", time="<<time<<")";
+ os << "(item=\"" << item << "\", time=" << time << ")";
return os.str();
}
@@ -297,86 +311,18 @@ std::string CraftReplacements::dump() const
std::ostringstream os(std::ios::binary);
os<<"{";
const char *sep = "";
- for(std::vector<std::pair<std::string, std::string> >::const_iterator
- i = pairs.begin();
- i != pairs.end(); i++)
- {
- os<<sep<<"\""<<(i->first)<<"\"=>\""<<(i->second)<<"\"";
+ for (std::vector<std::pair<std::string, std::string> >::size_type i = 0;
+ i < pairs.size(); i++) {
+ const std::pair<std::string, std::string> &repl_p = pairs[i];
+ os << sep
+ << '"' << (repl_p.first)
+ << "\"=>\"" << (repl_p.second) << '"';
sep = ",";
}
- os<<"}";
+ os << "}";
return os.str();
}
-void CraftReplacements::serialize(std::ostream &os) const
-{
- writeU16(os, pairs.size());
- for(u32 i=0; i<pairs.size(); i++)
- {
- os<<serializeString(pairs[i].first);
- os<<serializeString(pairs[i].second);
- }
-}
-
-void CraftReplacements::deSerialize(std::istream &is)
-{
- pairs.clear();
- u32 count = readU16(is);
- for(u32 i=0; i<count; i++)
- {
- std::string first = deSerializeString(is);
- std::string second = deSerializeString(is);
- pairs.push_back(std::make_pair(first, second));
- }
-}
-
-/*
- CraftDefinition
-*/
-
-void CraftDefinition::serialize(std::ostream &os) const
-{
- writeU8(os, 1); // version
- os<<serializeString(getName());
- serializeBody(os);
-}
-
-CraftDefinition* CraftDefinition::deSerialize(std::istream &is)
-{
- int version = readU8(is);
- if(version != 1) throw SerializationError(
- "unsupported CraftDefinition version");
- std::string name = deSerializeString(is);
- CraftDefinition *def = NULL;
- if(name == "shaped")
- {
- def = new CraftDefinitionShaped;
- }
- else if(name == "shapeless")
- {
- def = new CraftDefinitionShapeless;
- }
- else if(name == "toolrepair")
- {
- def = new CraftDefinitionToolRepair;
- }
- else if(name == "cooking")
- {
- def = new CraftDefinitionCooking;
- }
- else if(name == "fuel")
- {
- def = new CraftDefinitionFuel;
- }
- else
- {
- infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl;
- throw SerializationError("Unknown CraftDefinition name");
- }
- def->deSerializeBody(is, version);
- return def;
-}
-
/*
CraftDefinitionShaped
*/
@@ -388,57 +334,64 @@ std::string CraftDefinitionShaped::getName() const
bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
{
- if(input.method != CRAFT_METHOD_NORMAL)
+ if (input.method != CRAFT_METHOD_NORMAL)
return false;
// Get input item matrix
std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
unsigned int inp_width = input.width;
- if(inp_width == 0)
+ if (inp_width == 0)
return false;
- while(inp_names.size() % inp_width != 0)
+ while (inp_names.size() % inp_width != 0)
inp_names.push_back("");
// Get input bounds
- unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0;
- if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
+ unsigned int inp_min_x = 0, inp_max_x = 0, inp_min_y = 0, inp_max_y = 0;
+ if (!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x,
+ inp_min_y, inp_max_y))
return false; // it was empty
+ std::vector<std::string> rec_names;
+ if (hash_inited)
+ rec_names = recipe_names;
+ else
+ rec_names = craftGetItemNames(recipe, gamedef);
+
// Get recipe item matrix
- std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
unsigned int rec_width = width;
- if(rec_width == 0)
+ if (rec_width == 0)
return false;
- while(rec_names.size() % rec_width != 0)
+ while (rec_names.size() % rec_width != 0)
rec_names.push_back("");
// Get recipe bounds
unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
- if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y))
+ if (!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x,
+ rec_min_y, rec_max_y))
return false; // it was empty
// Different sizes?
- if(inp_max_x - inp_min_x != rec_max_x - rec_min_x)
- return false;
- if(inp_max_y - inp_min_y != rec_max_y - rec_min_y)
+ if (inp_max_x - inp_min_x != rec_max_x - rec_min_x ||
+ inp_max_y - inp_min_y != rec_max_y - rec_min_y)
return false;
// Verify that all item names in the bounding box are equal
unsigned int w = inp_max_x - inp_min_x + 1;
unsigned int h = inp_max_y - inp_min_y + 1;
- for(unsigned int y=0; y<h; y++)
- for(unsigned int x=0; x<w; x++)
- {
- unsigned int inp_x = inp_min_x + x;
- unsigned int inp_y = inp_min_y + y;
- unsigned int rec_x = rec_min_x + x;
- unsigned int rec_y = rec_min_y + y;
-
- if(!inputItemMatchesRecipe(
- inp_names[inp_y * inp_width + inp_x],
- rec_names[rec_y * rec_width + rec_x], gamedef->idef())
- ){
- return false;
+
+ for (unsigned int y=0; y < h; y++) {
+ unsigned int inp_y = (inp_min_y + y) * inp_width;
+ unsigned int rec_y = (rec_min_y + y) * rec_width;
+
+ for (unsigned int x=0; x < w; x++) {
+ unsigned int inp_x = inp_min_x + x;
+ unsigned int rec_x = rec_min_x + x;
+
+ if (!inputItemMatchesRecipe(
+ inp_names[inp_y + inp_x],
+ rec_names[rec_y + rec_x], gamedef->idef())) {
+ return false;
+ }
}
}
@@ -455,41 +408,54 @@ CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef *
return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef));
}
-void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
+void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
+ IGameDef *gamedef) const
{
- craftDecrementOrReplaceInput(input, replacements, gamedef);
+ craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}
-std::string CraftDefinitionShaped::dump() const
+CraftHashType CraftDefinitionShaped::getHashType() const
{
- std::ostringstream os(std::ios::binary);
- os<<"(shaped, output=\""<<output
- <<"\", recipe="<<craftDumpMatrix(recipe, width)
- <<", replacements="<<replacements.dump()<<")";
- return os.str();
+ assert(hash_inited); // Pre-condition
+ bool has_group = false;
+ for (size_t i = 0; i < recipe_names.size(); i++) {
+ if (isGroupRecipeStr(recipe_names[i])) {
+ has_group = true;
+ break;
+ }
+ }
+ if (has_group)
+ return CRAFT_HASH_TYPE_COUNT;
+ else
+ return CRAFT_HASH_TYPE_ITEM_NAMES;
}
-void CraftDefinitionShaped::serializeBody(std::ostream &os) const
+u64 CraftDefinitionShaped::getHash(CraftHashType type) const
{
- os<<serializeString(output);
- writeU16(os, width);
- writeU16(os, recipe.size());
- for(u32 i=0; i<recipe.size(); i++)
- os<<serializeString(recipe[i]);
- replacements.serialize(os);
+ assert(hash_inited); // Pre-condition
+ assert((type == CRAFT_HASH_TYPE_ITEM_NAMES)
+ || (type == CRAFT_HASH_TYPE_COUNT)); // Pre-condition
+
+ std::vector<std::string> rec_names = recipe_names;
+ std::sort(rec_names.begin(), rec_names.end());
+ return getHashForGrid(type, rec_names);
}
-void CraftDefinitionShaped::deSerializeBody(std::istream &is, int version)
+void CraftDefinitionShaped::initHash(IGameDef *gamedef)
{
- if(version != 1) throw SerializationError(
- "unsupported CraftDefinitionShaped version");
- output = deSerializeString(is);
- width = readU16(is);
- recipe.clear();
- u32 count = readU16(is);
- for(u32 i=0; i<count; i++)
- recipe.push_back(deSerializeString(is));
- replacements.deSerialize(is);
+ if (hash_inited)
+ return;
+ hash_inited = true;
+ recipe_names = craftGetItemNames(recipe, gamedef);
+}
+
+std::string CraftDefinitionShaped::dump() const
+{
+ std::ostringstream os(std::ios::binary);
+ os << "(shaped, output=\"" << output
+ << "\", recipe=" << craftDumpMatrix(recipe, width)
+ << ", replacements=" << replacements.dump() << ")";
+ return os.str();
}
/*
@@ -503,48 +469,53 @@ std::string CraftDefinitionShapeless::getName() const
bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
{
- if(input.method != CRAFT_METHOD_NORMAL)
+ if (input.method != CRAFT_METHOD_NORMAL)
return false;
-
+
// Filter empty items out of input
std::vector<std::string> input_filtered;
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(i->name != "")
- input_filtered.push_back(i->name);
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ const ItemStack &item = input.items[i];
+ if (item.name != "")
+ input_filtered.push_back(item.name);
}
// If there is a wrong number of items in input, no match
- if(input_filtered.size() != recipe.size()){
+ if (input_filtered.size() != recipe.size()) {
/*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size ("<<recipe.size()<<") "
<<"of recipe with output="<<output<<std::endl;*/
return false;
}
- // Try with all permutations of the recipe
- std::vector<std::string> recipe_copy = craftGetItemNames(recipe, gamedef);
- // Start from the lexicographically first permutation (=sorted)
- std::sort(recipe_copy.begin(), recipe_copy.end());
- //while(std::prev_permutation(recipe_copy.begin(), recipe_copy.end())){}
- do{
+ std::vector<std::string> recipe_copy;
+ if (hash_inited)
+ recipe_copy = recipe_names;
+ else {
+ recipe_copy = craftGetItemNames(recipe, gamedef);
+ std::sort(recipe_copy.begin(), recipe_copy.end());
+ }
+
+ // Try with all permutations of the recipe,
+ // start from the lexicographically first permutation (=sorted),
+ // recipe_names is pre-sorted
+ do {
// If all items match, the recipe matches
bool all_match = true;
//dstream<<"Testing recipe (output="<<output<<"):";
- for(size_t i=0; i<recipe.size(); i++){
+ for (size_t i=0; i<recipe.size(); i++) {
//dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
- if(!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
- gamedef->idef())){
+ if (!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
+ gamedef->idef())) {
all_match = false;
break;
}
}
//dstream<<" -> match="<<all_match<<std::endl;
- if(all_match)
+ if (all_match)
return true;
- }while(std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
+ } while (std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
return false;
}
@@ -556,42 +527,55 @@ CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDe
CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDef *gamedef) const
{
- return CraftInput(CRAFT_METHOD_NORMAL,0,craftGetItems(recipe,gamedef));
+ return CraftInput(CRAFT_METHOD_NORMAL, 0, craftGetItems(recipe, gamedef));
}
-void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
+void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
+ IGameDef *gamedef) const
{
- craftDecrementOrReplaceInput(input, replacements, gamedef);
+ craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}
-std::string CraftDefinitionShapeless::dump() const
+CraftHashType CraftDefinitionShapeless::getHashType() const
{
- std::ostringstream os(std::ios::binary);
- os<<"(shapeless, output=\""<<output
- <<"\", recipe="<<craftDumpMatrix(recipe, recipe.size())
- <<", replacements="<<replacements.dump()<<")";
- return os.str();
+ assert(hash_inited); // Pre-condition
+ bool has_group = false;
+ for (size_t i = 0; i < recipe_names.size(); i++) {
+ if (isGroupRecipeStr(recipe_names[i])) {
+ has_group = true;
+ break;
+ }
+ }
+ if (has_group)
+ return CRAFT_HASH_TYPE_COUNT;
+ else
+ return CRAFT_HASH_TYPE_ITEM_NAMES;
+}
+
+u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
+{
+ assert(hash_inited); // Pre-condition
+ assert(type == CRAFT_HASH_TYPE_ITEM_NAMES
+ || type == CRAFT_HASH_TYPE_COUNT); // Pre-condition
+ return getHashForGrid(type, recipe_names);
}
-void CraftDefinitionShapeless::serializeBody(std::ostream &os) const
+void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
{
- os<<serializeString(output);
- writeU16(os, recipe.size());
- for(u32 i=0; i<recipe.size(); i++)
- os<<serializeString(recipe[i]);
- replacements.serialize(os);
+ if (hash_inited)
+ return;
+ hash_inited = true;
+ recipe_names = craftGetItemNames(recipe, gamedef);
+ std::sort(recipe_names.begin(), recipe_names.end());
}
-void CraftDefinitionShapeless::deSerializeBody(std::istream &is, int version)
+std::string CraftDefinitionShapeless::dump() const
{
- if(version != 1) throw SerializationError(
- "unsupported CraftDefinitionShapeless version");
- output = deSerializeString(is);
- recipe.clear();
- u32 count = readU16(is);
- for(u32 i=0; i<count; i++)
- recipe.push_back(deSerializeString(is));
- replacements.deSerialize(is);
+ std::ostringstream os(std::ios::binary);
+ os << "(shapeless, output=\"" << output
+ << "\", recipe=" << craftDumpMatrix(recipe, recipe.size())
+ << ", replacements=" << replacements.dump() << ")";
+ return os.str();
}
/*
@@ -605,10 +589,9 @@ static ItemStack craftToolRepair(
IGameDef *gamedef)
{
IItemDefManager *idef = gamedef->idef();
- if(item1.count != 1 || item2.count != 1 || item1.name != item2.name
+ if (item1.count != 1 || item2.count != 1 || item1.name != item2.name
|| idef->get(item1.name).type != ITEM_TOOL
- || idef->get(item2.name).type != ITEM_TOOL)
- {
+ || idef->get(item2.name).type != ITEM_TOOL) {
// Failure
return ItemStack();
}
@@ -617,9 +600,9 @@ static ItemStack craftToolRepair(
s32 item2_uses = 65536 - (u32) item2.wear;
s32 new_uses = item1_uses + item2_uses;
s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
- if(new_wear >= 65536)
+ if (new_wear >= 65536)
return ItemStack();
- if(new_wear < 0)
+ if (new_wear < 0)
new_wear = 0;
ItemStack repaired = item1;
@@ -634,21 +617,19 @@ std::string CraftDefinitionToolRepair::getName() const
bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
{
- if(input.method != CRAFT_METHOD_NORMAL)
+ if (input.method != CRAFT_METHOD_NORMAL)
return false;
ItemStack item1;
ItemStack item2;
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(!i->empty())
- {
- if(item1.empty())
- item1 = *i;
- else if(item2.empty())
- item2 = *i;
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ const ItemStack &item = input.items[i];
+ if (!item.empty()) {
+ if (item1.empty())
+ item1 = item;
+ else if (item2.empty())
+ item2 = item;
else
return false;
}
@@ -661,16 +642,14 @@ CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameD
{
ItemStack item1;
ItemStack item2;
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(!i->empty())
- {
- if(item1.empty())
- item1 = *i;
- else if(item2.empty())
- item2 = *i;
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ const ItemStack &item = input.items[i];
+ if (!item.empty()) {
+ if (item1.empty())
+ item1 = item;
+ else if (item2.empty())
+ item2 = item;
}
}
ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
@@ -681,10 +660,11 @@ CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameD
{
std::vector<ItemStack> stack;
stack.push_back(ItemStack());
- return CraftInput(CRAFT_METHOD_COOKING,additional_wear,stack);
+ return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack);
}
-void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
+void CraftDefinitionToolRepair::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
+ IGameDef *gamedef) const
{
craftDecrementInput(input, gamedef);
}
@@ -692,22 +672,10 @@ void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *game
std::string CraftDefinitionToolRepair::dump() const
{
std::ostringstream os(std::ios::binary);
- os<<"(toolrepair, additional_wear="<<additional_wear<<")";
+ os << "(toolrepair, additional_wear=" << additional_wear << ")";
return os.str();
}
-void CraftDefinitionToolRepair::serializeBody(std::ostream &os) const
-{
- writeF1000(os, additional_wear);
-}
-
-void CraftDefinitionToolRepair::deSerializeBody(std::istream &is, int version)
-{
- if(version != 1) throw SerializationError(
- "unsupported CraftDefinitionToolRepair version");
- additional_wear = readF1000(is);
-}
-
/*
CraftDefinitionCooking
*/
@@ -719,27 +687,26 @@ std::string CraftDefinitionCooking::getName() const
bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
{
- if(input.method != CRAFT_METHOD_COOKING)
+ if (input.method != CRAFT_METHOD_COOKING)
return false;
// Filter empty items out of input
std::vector<std::string> input_filtered;
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(i->name != "")
- input_filtered.push_back(i->name);
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ const std::string &name = input.items[i].name;
+ if (name != "")
+ input_filtered.push_back(name);
}
// If there is a wrong number of items in input, no match
- if(input_filtered.size() != 1){
+ if (input_filtered.size() != 1) {
/*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size (1) "
<<"of cooking recipe with output="<<output<<std::endl;*/
return false;
}
-
+
// Check the single input item
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
}
@@ -756,37 +723,49 @@ CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef
return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef));
}
-void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
+void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
+ IGameDef *gamedef) const
{
- craftDecrementOrReplaceInput(input, replacements, gamedef);
+ craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}
-std::string CraftDefinitionCooking::dump() const
+CraftHashType CraftDefinitionCooking::getHashType() const
{
- std::ostringstream os(std::ios::binary);
- os<<"(cooking, output=\""<<output
- <<"\", recipe=\""<<recipe
- <<"\", cooktime="<<cooktime<<")"
- <<", replacements="<<replacements.dump()<<")";
- return os.str();
+ if (isGroupRecipeStr(recipe_name))
+ return CRAFT_HASH_TYPE_COUNT;
+ else
+ return CRAFT_HASH_TYPE_ITEM_NAMES;
+}
+
+u64 CraftDefinitionCooking::getHash(CraftHashType type) const
+{
+ if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
+ return getHashForString(recipe_name);
+ } else if (type == CRAFT_HASH_TYPE_COUNT) {
+ return 1;
+ } else {
+ //illegal hash type for this CraftDefinition (pre-condition)
+ assert(false);
+ return 0;
+ }
}
-void CraftDefinitionCooking::serializeBody(std::ostream &os) const
+void CraftDefinitionCooking::initHash(IGameDef *gamedef)
{
- os<<serializeString(output);
- os<<serializeString(recipe);
- writeF1000(os, cooktime);
- replacements.serialize(os);
+ if (hash_inited)
+ return;
+ hash_inited = true;
+ recipe_name = craftGetItemName(recipe, gamedef);
}
-void CraftDefinitionCooking::deSerializeBody(std::istream &is, int version)
+std::string CraftDefinitionCooking::dump() const
{
- if(version != 1) throw SerializationError(
- "unsupported CraftDefinitionCooking version");
- output = deSerializeString(is);
- recipe = deSerializeString(is);
- cooktime = readF1000(is);
- replacements.deSerialize(is);
+ std::ostringstream os(std::ios::binary);
+ os << "(cooking, output=\"" << output
+ << "\", recipe=\"" << recipe
+ << "\", cooktime=" << cooktime << ")"
+ << ", replacements=" << replacements.dump() << ")";
+ return os.str();
}
/*
@@ -800,27 +779,26 @@ std::string CraftDefinitionFuel::getName() const
bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
{
- if(input.method != CRAFT_METHOD_FUEL)
+ if (input.method != CRAFT_METHOD_FUEL)
return false;
// Filter empty items out of input
std::vector<std::string> input_filtered;
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(i->name != "")
- input_filtered.push_back(i->name);
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ const std::string &name = input.items[i].name;
+ if (name != "")
+ input_filtered.push_back(name);
}
// If there is a wrong number of items in input, no match
- if(input_filtered.size() != 1){
+ if (input_filtered.size() != 1) {
/*dstream<<"Number of input items ("<<input_filtered.size()
<<") does not match recipe size (1) "
<<"of fuel recipe with burntime="<<burntime<<std::endl;*/
return false;
}
-
+
// Check the single input item
return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
}
@@ -837,34 +815,47 @@ CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *ga
return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef));
}
-void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
+void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
+ IGameDef *gamedef) const
{
- craftDecrementOrReplaceInput(input, replacements, gamedef);
+ craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
}
-std::string CraftDefinitionFuel::dump() const
+CraftHashType CraftDefinitionFuel::getHashType() const
{
- std::ostringstream os(std::ios::binary);
- os<<"(fuel, recipe=\""<<recipe
- <<"\", burntime="<<burntime<<")"
- <<", replacements="<<replacements.dump()<<")";
- return os.str();
+ if (isGroupRecipeStr(recipe_name))
+ return CRAFT_HASH_TYPE_COUNT;
+ else
+ return CRAFT_HASH_TYPE_ITEM_NAMES;
}
-void CraftDefinitionFuel::serializeBody(std::ostream &os) const
+u64 CraftDefinitionFuel::getHash(CraftHashType type) const
{
- os<<serializeString(recipe);
- writeF1000(os, burntime);
- replacements.serialize(os);
+ if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
+ return getHashForString(recipe_name);
+ } else if (type == CRAFT_HASH_TYPE_COUNT) {
+ return 1;
+ } else {
+ //illegal hash type for this CraftDefinition (pre-condition)
+ assert(false);
+ return 0;
+ }
}
-void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
+void CraftDefinitionFuel::initHash(IGameDef *gamedef)
{
- if(version != 1) throw SerializationError(
- "unsupported CraftDefinitionFuel version");
- recipe = deSerializeString(is);
- burntime = readF1000(is);
- replacements.deSerialize(is);
+ if (hash_inited)
+ return;
+ hash_inited = true;
+ recipe_name = craftGetItemName(recipe, gamedef);
+}
+std::string CraftDefinitionFuel::dump() const
+{
+ std::ostringstream os(std::ios::binary);
+ os << "(fuel, recipe=\"" << recipe
+ << "\", burntime=" << burntime << ")"
+ << ", replacements=" << replacements.dump() << ")";
+ return os.str();
}
/*
@@ -874,203 +865,171 @@ void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
class CCraftDefManager: public IWritableCraftDefManager
{
public:
+ CCraftDefManager()
+ {
+ m_craft_defs.resize(craft_hash_type_max + 1);
+ }
+
virtual ~CCraftDefManager()
{
clear();
}
+
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
- bool decrementInput, IGameDef *gamedef) const
+ std::vector<ItemStack> &output_replacement, bool decrementInput,
+ IGameDef *gamedef) const
{
output.item = "";
output.time = 0;
// If all input items are empty, abort.
bool all_empty = true;
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++)
- {
- if(!i->empty())
- {
+ for (std::vector<ItemStack>::size_type i = 0;
+ i < input.items.size(); i++) {
+ if (!input.items[i].empty()) {
all_empty = false;
break;
}
}
- if(all_empty)
+ if (all_empty)
return false;
- // Walk crafting definitions from back to front, so that later
- // definitions can override earlier ones.
- for(std::vector<CraftDefinition*>::const_reverse_iterator
- i = m_craft_definitions.rbegin();
- i != m_craft_definitions.rend(); i++)
- {
- CraftDefinition *def = *i;
+ std::vector<std::string> input_names;
+ input_names = craftGetItemNames(input.items, gamedef);
+ std::sort(input_names.begin(), input_names.end());
- /*infostream<<"Checking "<<input.dump()<<std::endl
- <<" against "<<def->dump()<<std::endl;*/
+ // Try hash types with increasing collision rate, and return if found.
+ for (int type = 0; type <= craft_hash_type_max; type++) {
+ u64 hash = getHashForGrid((CraftHashType) type, input_names);
- try {
- if(def->check(input, gamedef))
- {
- // Get output, then decrement input (if requested)
- output = def->getOutput(input, gamedef);
- if(decrementInput)
- def->decrementInput(input, gamedef);
- return true;
- }
- }
- catch(SerializationError &e)
- {
- errorstream<<"getCraftResult: ERROR: "
- <<"Serialization error in recipe "
- <<def->dump()<<std::endl;
- // then go on with the next craft definition
- }
- }
- return false;
- }
- virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
- IGameDef *gamedef) const
- {
- CraftOutput tmpout;
- tmpout.item = "";
- tmpout.time = 0;
+ /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/
- // If output item is empty, abort.
- if(output.item.empty())
- return false;
+ // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];"
+ // but that doesn't compile for some reason. This does.
+ std::map<u64, std::vector<CraftDefinition*> >::const_iterator
+ col_iter = (m_craft_defs[type]).find(hash);
+
+ if (col_iter == (m_craft_defs[type]).end())
+ continue;
- // Walk crafting definitions from back to front, so that later
- // definitions can override earlier ones.
- for(std::vector<CraftDefinition*>::const_reverse_iterator
- i = m_craft_definitions.rbegin();
- i != m_craft_definitions.rend(); i++)
- {
- CraftDefinition *def = *i;
-
- /*infostream<<"Checking "<<input.dump()<<std::endl
- <<" against "<<def->dump()<<std::endl;*/
-
- try {
- tmpout = def->getOutput(input, gamedef);
- if((tmpout.item.substr(0,output.item.length()) == output.item) &&
- ((tmpout.item[output.item.length()] == 0) ||
- (tmpout.item[output.item.length()] == ' ')))
- {
+ const std::vector<CraftDefinition*> &hash_collisions = col_iter->second;
+ // Walk crafting definitions from back to front, so that later
+ // definitions can override earlier ones.
+ for (std::vector<CraftDefinition*>::size_type
+ i = hash_collisions.size(); i > 0; i--) {
+ CraftDefinition *def = hash_collisions[i - 1];
+
+ /*errorstream << "Checking " << input.dump() << std::endl
+ << " against " << def->dump() << std::endl;*/
+
+ if (def->check(input, gamedef)) {
// Get output, then decrement input (if requested)
- input = def->getInput(output, gamedef);
+ output = def->getOutput(input, gamedef);
+ if (decrementInput)
+ def->decrementInput(input, output_replacement, gamedef);
+ /*errorstream << "Check RETURNS TRUE" << std::endl;*/
return true;
}
}
- catch(SerializationError &e)
- {
- errorstream<<"getCraftResult: ERROR: "
- <<"Serialization error in recipe "
- <<def->dump()<<std::endl;
- // then go on with the next craft definition
- }
}
return false;
}
+
virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
- IGameDef *gamedef) const
+ IGameDef *gamedef, unsigned limit=0) const
{
- std::vector<CraftDefinition*> recipes_list;
- CraftInput input;
- CraftOutput tmpout;
- tmpout.item = "";
- tmpout.time = 0;
-
- for(std::vector<CraftDefinition*>::const_reverse_iterator
- i = m_craft_definitions.rbegin();
- i != m_craft_definitions.rend(); i++)
- {
- CraftDefinition *def = *i;
-
- /*infostream<<"Checking "<<input.dump()<<std::endl
- <<" against "<<def->dump()<<std::endl;*/
-
- try {
- tmpout = def->getOutput(input, gamedef);
- if(tmpout.item.substr(0,output.item.length()) == output.item)
- {
- // Get output, then decrement input (if requested)
- input = def->getInput(output, gamedef);
- recipes_list.push_back(*i);
- }
- }
- catch(SerializationError &e)
- {
- errorstream<<"getCraftResult: ERROR: "
- <<"Serialization error in recipe "
- <<def->dump()<<std::endl;
- // then go on with the next craft definition
- }
+ std::vector<CraftDefinition*> recipes;
+
+ std::map<std::string, std::vector<CraftDefinition*> >::const_iterator
+ vec_iter = m_output_craft_definitions.find(output.item);
+
+ if (vec_iter == m_output_craft_definitions.end())
+ return recipes;
+
+ const std::vector<CraftDefinition*> &vec = vec_iter->second;
+
+ recipes.reserve(limit ? MYMIN(limit, vec.size()) : vec.size());
+
+ for (std::vector<CraftDefinition*>::size_type i = vec.size();
+ i > 0; i--) {
+ CraftDefinition *def = vec[i - 1];
+ if (limit && recipes.size() >= limit)
+ break;
+ recipes.push_back(def);
}
- return recipes_list;
+
+ return recipes;
}
virtual std::string dump() const
{
std::ostringstream os(std::ios::binary);
- os<<"Crafting definitions:\n";
- for(std::vector<CraftDefinition*>::const_iterator
- i = m_craft_definitions.begin();
- i != m_craft_definitions.end(); i++)
- {
- os<<(*i)->dump()<<"\n";
+ os << "Crafting definitions:\n";
+ for (int type = 0; type <= craft_hash_type_max; type++) {
+ for (std::map<u64, std::vector<CraftDefinition*> >::const_iterator
+ it = (m_craft_defs[type]).begin();
+ it != (m_craft_defs[type]).end(); it++) {
+ for (std::vector<CraftDefinition*>::size_type i = 0;
+ i < it->second.size(); i++) {
+ os << "type " << type
+ << " hash " << it->first
+ << " def " << it->second[i]->dump()
+ << "\n";
+ }
+ }
}
return os.str();
}
- virtual void registerCraft(CraftDefinition *def)
+ virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef)
{
- verbosestream<<"registerCraft: registering craft definition: "
- <<def->dump()<<std::endl;
- m_craft_definitions.push_back(def);
+ verbosestream << "registerCraft: registering craft definition: "
+ << def->dump() << std::endl;
+ m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);
+
+ CraftInput input;
+ std::string output_name = craftGetItemName(
+ def->getOutput(input, gamedef).item, gamedef);
+ m_output_craft_definitions[output_name].push_back(def);
}
virtual void clear()
{
- for(std::vector<CraftDefinition*>::iterator
- i = m_craft_definitions.begin();
- i != m_craft_definitions.end(); i++){
- delete *i;
- }
- m_craft_definitions.clear();
- }
- virtual void serialize(std::ostream &os) const
- {
- writeU8(os, 0); // version
- u16 count = m_craft_definitions.size();
- writeU16(os, count);
- for(std::vector<CraftDefinition*>::const_iterator
- i = m_craft_definitions.begin();
- i != m_craft_definitions.end(); i++){
- CraftDefinition *def = *i;
- // Serialize wrapped in a string
- std::ostringstream tmp_os(std::ios::binary);
- def->serialize(tmp_os);
- os<<serializeString(tmp_os.str());
+ for (int type = 0; type <= craft_hash_type_max; type++) {
+ for (std::map<u64, std::vector<CraftDefinition*> >::iterator
+ it = m_craft_defs[type].begin();
+ it != m_craft_defs[type].end(); it++) {
+ for (std::vector<CraftDefinition*>::iterator
+ iit = it->second.begin();
+ iit != it->second.end(); ++iit) {
+ delete *iit;
+ }
+ it->second.clear();
+ }
+ m_craft_defs[type].clear();
}
+ m_output_craft_definitions.clear();
}
- virtual void deSerialize(std::istream &is)
+ virtual void initHashes(IGameDef *gamedef)
{
- // Clear everything
- clear();
- // Deserialize
- int version = readU8(is);
- if(version != 0) throw SerializationError(
- "unsupported CraftDefManager version");
- u16 count = readU16(is);
- for(u16 i=0; i<count; i++){
- // Deserialize a string and grab a CraftDefinition from it
- std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
- CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
- // Register
- registerCraft(def);
+ // Move the CraftDefs from the unhashed layer into layers higher up.
+ std::vector<CraftDefinition *> &unhashed =
+ m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
+ for (std::vector<CraftDefinition*>::size_type i = 0;
+ i < unhashed.size(); i++) {
+ CraftDefinition *def = unhashed[i];
+
+ // Initialize and get the definition's hash
+ def->initHash(gamedef);
+ CraftHashType type = def->getHashType();
+ u64 hash = def->getHash(type);
+
+ // Enter the definition
+ m_craft_defs[type][hash].push_back(def);
}
+ unhashed.clear();
}
private:
- std::vector<CraftDefinition*> m_craft_definitions;
+ //TODO: change both maps to unordered_map when c++11 can be used
+ std::vector<std::map<u64, std::vector<CraftDefinition*> > > m_craft_defs;
+ std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
};
IWritableCraftDefManager* createCraftDefManager()
diff --git a/src/craftdef.h b/src/craftdef.h
index 14dc53003..cebb2d7ae 100644
--- a/src/craftdef.h
+++ b/src/craftdef.h
@@ -44,6 +44,28 @@ enum CraftMethod
};
/*
+ The type a hash can be. The earlier a type is mentioned in this enum,
+ the earlier it is tried at crafting, and the less likely is a collision.
+ Changing order causes changes in behaviour, so know what you do.
+ */
+enum CraftHashType
+{
+ // Hashes the normalized names of the recipe's elements.
+ // Only recipes without group usage can be found here,
+ // because groups can't be guessed efficiently.
+ CRAFT_HASH_TYPE_ITEM_NAMES,
+
+ // Counts the non-empty slots.
+ CRAFT_HASH_TYPE_COUNT,
+
+ // This layer both spares an extra variable, and helps to retain (albeit rarely used) functionality. Maps to 0.
+ // Before hashes are "initialized", all hashes reside here, after initialisation, none are.
+ CRAFT_HASH_TYPE_UNHASHED
+
+};
+const int craft_hash_type_max = (int) CRAFT_HASH_TYPE_UNHASHED;
+
+/*
Input: The contents of the crafting slots, arranged in matrix form
*/
struct CraftInput
@@ -106,8 +128,6 @@ struct CraftReplacements
pairs(pairs_)
{}
std::string dump() const;
- void serialize(std::ostream &os) const;
- void deSerialize(std::istream &is);
};
/*
@@ -119,9 +139,6 @@ public:
CraftDefinition(){}
virtual ~CraftDefinition(){}
- void serialize(std::ostream &os) const;
- static CraftDefinition* deSerialize(std::istream &is);
-
// Returns type of crafting definition
virtual std::string getName() const=0;
@@ -133,13 +150,16 @@ public:
// the inverse of the above
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const=0;
// Decreases count of every input item
- virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0;
+ virtual void decrementInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const=0;
- virtual std::string dump() const=0;
+ virtual CraftHashType getHashType() const = 0;
+ virtual u64 getHash(CraftHashType type) const = 0;
+
+ // to be called after all mods are loaded, so that we catch all aliases
+ virtual void initHash(IGameDef *gamedef) = 0;
-protected:
- virtual void serializeBody(std::ostream &os) const=0;
- virtual void deSerializeBody(std::istream &is, int version)=0;
+ virtual std::string dump() const=0;
};
/*
@@ -152,14 +172,15 @@ class CraftDefinitionShaped: public CraftDefinition
{
public:
CraftDefinitionShaped():
- output(""), width(1), recipe(), replacements()
+ output(""), width(1), recipe(), hash_inited(false), replacements()
{}
CraftDefinitionShaped(
const std::string &output_,
unsigned int width_,
const std::vector<std::string> &recipe_,
const CraftReplacements &replacements_):
- output(output_), width(width_), recipe(recipe_), replacements(replacements_)
+ output(output_), width(width_), recipe(recipe_),
+ hash_inited(false), replacements(replacements_)
{}
virtual ~CraftDefinitionShaped(){}
@@ -167,13 +188,15 @@ public:
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
- virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+ virtual void decrementInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
- virtual std::string dump() const;
+ virtual CraftHashType getHashType() const;
+ virtual u64 getHash(CraftHashType type) const;
-protected:
- virtual void serializeBody(std::ostream &os) const;
- virtual void deSerializeBody(std::istream &is, int version);
+ virtual void initHash(IGameDef *gamedef);
+
+ virtual std::string dump() const;
private:
// Output itemstring
@@ -182,6 +205,10 @@ private:
unsigned int width;
// Recipe matrix (itemstrings)
std::vector<std::string> recipe;
+ // Recipe matrix (item names)
+ std::vector<std::string> recipe_names;
+ // bool indicating if initHash has been called already
+ bool hash_inited;
// Replacement items for decrementInput()
CraftReplacements replacements;
};
@@ -195,13 +222,14 @@ class CraftDefinitionShapeless: public CraftDefinition
{
public:
CraftDefinitionShapeless():
- output(""), recipe(), replacements()
+ output(""), recipe(), hash_inited(false), replacements()
{}
CraftDefinitionShapeless(
const std::string &output_,
const std::vector<std::string> &recipe_,
const CraftReplacements &replacements_):
- output(output_), recipe(recipe_), replacements(replacements_)
+ output(output_), recipe(recipe_),
+ hash_inited(false), replacements(replacements_)
{}
virtual ~CraftDefinitionShapeless(){}
@@ -209,19 +237,25 @@ public:
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
- virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+ virtual void decrementInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
- virtual std::string dump() const;
+ virtual CraftHashType getHashType() const;
+ virtual u64 getHash(CraftHashType type) const;
-protected:
- virtual void serializeBody(std::ostream &os) const;
- virtual void deSerializeBody(std::istream &is, int version);
+ virtual void initHash(IGameDef *gamedef);
+
+ virtual std::string dump() const;
private:
// Output itemstring
std::string output;
// Recipe list (itemstrings)
std::vector<std::string> recipe;
+ // Recipe list (item names)
+ std::vector<std::string> recipe_names;
+ // bool indicating if initHash has been called already
+ bool hash_inited;
// Replacement items for decrementInput()
CraftReplacements replacements;
};
@@ -247,13 +281,15 @@ public:
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
- virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+ virtual void decrementInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
- virtual std::string dump() const;
+ virtual CraftHashType getHashType() const { return CRAFT_HASH_TYPE_COUNT; }
+ virtual u64 getHash(CraftHashType type) const { return 2; }
+
+ virtual void initHash(IGameDef *gamedef) {}
-protected:
- virtual void serializeBody(std::ostream &os) const;
- virtual void deSerializeBody(std::istream &is, int version);
+ virtual std::string dump() const;
private:
// This is a constant that is added to the wear of the result.
@@ -272,14 +308,15 @@ class CraftDefinitionCooking: public CraftDefinition
{
public:
CraftDefinitionCooking():
- output(""), recipe(""), cooktime()
+ output(""), recipe(""), hash_inited(false), cooktime()
{}
CraftDefinitionCooking(
const std::string &output_,
const std::string &recipe_,
float cooktime_,
const CraftReplacements &replacements_):
- output(output_), recipe(recipe_), cooktime(cooktime_), replacements(replacements_)
+ output(output_), recipe(recipe_), hash_inited(false),
+ cooktime(cooktime_), replacements(replacements_)
{}
virtual ~CraftDefinitionCooking(){}
@@ -287,19 +324,25 @@ public:
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
- virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+ virtual void decrementInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
- virtual std::string dump() const;
+ virtual CraftHashType getHashType() const;
+ virtual u64 getHash(CraftHashType type) const;
-protected:
- virtual void serializeBody(std::ostream &os) const;
- virtual void deSerializeBody(std::istream &is, int version);
+ virtual void initHash(IGameDef *gamedef);
+
+ virtual std::string dump() const;
private:
// Output itemstring
std::string output;
// Recipe itemstring
std::string recipe;
+ // Recipe item name
+ std::string recipe_name;
+ // bool indicating if initHash has been called already
+ bool hash_inited;
// Time in seconds
float cooktime;
// Replacement items for decrementInput()
@@ -314,12 +357,12 @@ class CraftDefinitionFuel: public CraftDefinition
{
public:
CraftDefinitionFuel():
- recipe(""), burntime()
+ recipe(""), hash_inited(false), burntime()
{}
CraftDefinitionFuel(std::string recipe_,
float burntime_,
const CraftReplacements &replacements_):
- recipe(recipe_), burntime(burntime_), replacements(replacements_)
+ recipe(recipe_), hash_inited(false), burntime(burntime_), replacements(replacements_)
{}
virtual ~CraftDefinitionFuel(){}
@@ -327,17 +370,23 @@ public:
virtual bool check(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const;
virtual CraftInput getInput(const CraftOutput &output, IGameDef *gamedef) const;
- virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const;
+ virtual void decrementInput(CraftInput &input,
+ std::vector<ItemStack> &output_replacements, IGameDef *gamedef) const;
- virtual std::string dump() const;
+ virtual CraftHashType getHashType() const;
+ virtual u64 getHash(CraftHashType type) const;
+
+ virtual void initHash(IGameDef *gamedef);
-protected:
- virtual void serializeBody(std::ostream &os) const;
- virtual void deSerializeBody(std::istream &is, int version);
+ virtual std::string dump() const;
private:
// Recipe itemstring
std::string recipe;
+ // Recipe item name
+ std::string recipe_name;
+ // bool indicating if initHash has been called already
+ bool hash_inited;
// Time in seconds
float burntime;
// Replacement items for decrementInput()
@@ -355,16 +404,13 @@ public:
// The main crafting function
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
+ std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef) const=0;
- virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
- IGameDef *gamedef) const=0;
virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
- IGameDef *gamedef) const=0;
-
+ IGameDef *gamedef, unsigned limit=0) const=0;
+
// Print crafting recipes for debugging
virtual std::string dump() const=0;
-
- virtual void serialize(std::ostream &os) const=0;
};
class IWritableCraftDefManager : public ICraftDefManager
@@ -375,23 +421,23 @@ public:
// The main crafting function
virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
+ std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef) const=0;
- virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output,
- IGameDef *gamedef) const=0;
- virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
- IGameDef *gamedef) const=0;
+ virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
+ IGameDef *gamedef, unsigned limit=0) const=0;
// Print crafting recipes for debugging
virtual std::string dump() const=0;
// Add a crafting definition.
// After calling this, the pointer belongs to the manager.
- virtual void registerCraft(CraftDefinition *def)=0;
+ virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) = 0;
+
// Delete all crafting definitions
virtual void clear()=0;
- virtual void serialize(std::ostream &os) const=0;
- virtual void deSerialize(std::istream &is)=0;
+ // To be called after all mods are loaded, so that we catch all aliases
+ virtual void initHashes(IGameDef *gamedef) = 0;
};
IWritableCraftDefManager* createCraftDefManager();
diff --git a/src/database-dummy.cpp b/src/database-dummy.cpp
index 271d9c975..2e5de5ed1 100644
--- a/src/database-dummy.cpp
+++ b/src/database-dummy.cpp
@@ -18,64 +18,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
/*
-Dummy "database" class
+Dummy database class
*/
-
#include "database-dummy.h"
-#include "map.h"
-#include "mapsector.h"
-#include "mapblock.h"
-#include "serialization.h"
-#include "main.h"
-#include "settings.h"
-#include "log.h"
-
-Database_Dummy::Database_Dummy(ServerMap *map)
-{
- srvmap = map;
-}
-
-int Database_Dummy::Initialized(void)
-{
- return 1;
-}
-
-void Database_Dummy::beginSave() {}
-void Database_Dummy::endSave() {}
-bool Database_Dummy::saveBlock(v3s16 blockpos, std::string &data)
+bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data)
{
- m_database[getBlockAsInteger(blockpos)] = data;
+ m_database[getBlockAsInteger(pos)] = data;
return true;
}
-std::string Database_Dummy::loadBlock(v3s16 blockpos)
+std::string Database_Dummy::loadBlock(const v3s16 &pos)
{
- if (m_database.count(getBlockAsInteger(blockpos)))
- return m_database[getBlockAsInteger(blockpos)];
- else
+ s64 i = getBlockAsInteger(pos);
+ std::map<s64, std::string>::iterator it = m_database.find(i);
+ if (it == m_database.end())
return "";
+ return it->second;
}
-bool Database_Dummy::deleteBlock(v3s16 blockpos)
+bool Database_Dummy::deleteBlock(const v3s16 &pos)
{
- m_database.erase(getBlockAsInteger(blockpos));
+ m_database.erase(getBlockAsInteger(pos));
return true;
}
-void Database_Dummy::listAllLoadableBlocks(std::list<v3s16> &dst)
+void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
- for(std::map<u64, std::string>::iterator x = m_database.begin(); x != m_database.end(); ++x)
- {
- v3s16 p = getIntegerAsBlock(x->first);
- //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
- dst.push_back(p);
+ for (std::map<s64, std::string>::const_iterator x = m_database.begin();
+ x != m_database.end(); ++x) {
+ dst.push_back(getIntegerAsBlock(x->first));
}
}
-Database_Dummy::~Database_Dummy()
-{
- m_database.clear();
-}
diff --git a/src/database-dummy.h b/src/database-dummy.h
index a1535937d..0cf56928e 100644
--- a/src/database-dummy.h
+++ b/src/database-dummy.h
@@ -25,22 +25,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database.h"
#include "irrlichttypes.h"
-class ServerMap;
-
class Database_Dummy : public Database
{
public:
- Database_Dummy(ServerMap *map);
- virtual void beginSave();
- virtual void endSave();
- virtual bool saveBlock(v3s16 blockpos, std::string &data);
- virtual std::string loadBlock(v3s16 blockpos);
- virtual bool deleteBlock(v3s16 blockpos);
- virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
- virtual int Initialized(void);
- ~Database_Dummy();
+ virtual bool saveBlock(const v3s16 &pos, const std::string &data);
+ virtual std::string loadBlock(const v3s16 &pos);
+ virtual bool deleteBlock(const v3s16 &pos);
+ virtual void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
private:
- ServerMap *srvmap;
- std::map<u64, std::string> m_database;
+ std::map<s64, std::string> m_database;
};
+
#endif
+
diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp
index de510e533..e895354a4 100644
--- a/src/database-leveldb.cpp
+++ b/src/database-leveldb.cpp
@@ -22,57 +22,54 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#if USE_LEVELDB
#include "database-leveldb.h"
-#include "leveldb/db.h"
-#include "map.h"
-#include "mapsector.h"
-#include "mapblock.h"
-#include "serialization.h"
-#include "main.h"
-#include "settings.h"
#include "log.h"
#include "filesys.h"
+#include "exceptions.h"
+#include "util/string.h"
+
+#include "leveldb/db.h"
+
#define ENSURE_STATUS_OK(s) \
if (!(s).ok()) { \
- throw FileNotGoodException(std::string("LevelDB error: ") + (s).ToString()); \
+ throw FileNotGoodException(std::string("LevelDB error: ") + \
+ (s).ToString()); \
}
-Database_LevelDB::Database_LevelDB(ServerMap *map, std::string savedir)
+
+Database_LevelDB::Database_LevelDB(const std::string &savedir)
{
leveldb::Options options;
options.create_if_missing = true;
- leveldb::Status status = leveldb::DB::Open(options, savedir + DIR_DELIM + "map.db", &m_database);
+ leveldb::Status status = leveldb::DB::Open(options,
+ savedir + DIR_DELIM + "map.db", &m_database);
ENSURE_STATUS_OK(status);
- srvmap = map;
}
-int Database_LevelDB::Initialized(void)
+Database_LevelDB::~Database_LevelDB()
{
- return 1;
+ delete m_database;
}
-void Database_LevelDB::beginSave() {}
-void Database_LevelDB::endSave() {}
-
-bool Database_LevelDB::saveBlock(v3s16 blockpos, std::string &data)
+bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data)
{
leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
- i64tos(getBlockAsInteger(blockpos)), data);
+ i64tos(getBlockAsInteger(pos)), data);
if (!status.ok()) {
errorstream << "WARNING: saveBlock: LevelDB error saving block "
- << PP(blockpos) << ": " << status.ToString() << std::endl;
+ << PP(pos) << ": " << status.ToString() << std::endl;
return false;
}
return true;
}
-std::string Database_LevelDB::loadBlock(v3s16 blockpos)
+std::string Database_LevelDB::loadBlock(const v3s16 &pos)
{
std::string datastr;
leveldb::Status status = m_database->Get(leveldb::ReadOptions(),
- i64tos(getBlockAsInteger(blockpos)), &datastr);
+ i64tos(getBlockAsInteger(pos)), &datastr);
if(status.ok())
return datastr;
@@ -80,20 +77,20 @@ std::string Database_LevelDB::loadBlock(v3s16 blockpos)
return "";
}
-bool Database_LevelDB::deleteBlock(v3s16 blockpos)
+bool Database_LevelDB::deleteBlock(const v3s16 &pos)
{
leveldb::Status status = m_database->Delete(leveldb::WriteOptions(),
- i64tos(getBlockAsInteger(blockpos)));
+ i64tos(getBlockAsInteger(pos)));
if (!status.ok()) {
errorstream << "WARNING: deleteBlock: LevelDB error deleting block "
- << PP(blockpos) << ": " << status.ToString() << std::endl;
+ << PP(pos) << ": " << status.ToString() << std::endl;
return false;
}
return true;
}
-void Database_LevelDB::listAllLoadableBlocks(std::list<v3s16> &dst)
+void Database_LevelDB::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
@@ -103,8 +100,5 @@ void Database_LevelDB::listAllLoadableBlocks(std::list<v3s16> &dst)
delete it;
}
-Database_LevelDB::~Database_LevelDB()
-{
- delete m_database;
-}
-#endif
+#endif // USE_LEVELDB
+
diff --git a/src/database-leveldb.h b/src/database-leveldb.h
index c195260da..4afe2fdc7 100644
--- a/src/database-leveldb.h
+++ b/src/database-leveldb.h
@@ -28,23 +28,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "leveldb/db.h"
#include <string>
-class ServerMap;
-
class Database_LevelDB : public Database
{
public:
- Database_LevelDB(ServerMap *map, std::string savedir);
- virtual void beginSave();
- virtual void endSave();
- virtual bool saveBlock(v3s16 blockpos, std::string &data);
- virtual std::string loadBlock(v3s16 blockpos);
- virtual bool deleteBlock(v3s16 blockpos);
- virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
- virtual int Initialized(void);
+ Database_LevelDB(const std::string &savedir);
~Database_LevelDB();
+
+ virtual bool saveBlock(const v3s16 &pos, const std::string &data);
+ virtual std::string loadBlock(const v3s16 &pos);
+ virtual bool deleteBlock(const v3s16 &pos);
+ virtual void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
private:
- ServerMap *srvmap;
- leveldb::DB* m_database;
+ leveldb::DB *m_database;
};
+
+#endif // USE_LEVELDB
+
#endif
-#endif
+
diff --git a/src/database-redis.cpp b/src/database-redis.cpp
index b086f899d..cc4e5bade 100644
--- a/src/database-redis.cpp
+++ b/src/database-redis.cpp
@@ -20,85 +20,79 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "config.h"
#if USE_REDIS
-/*
- Redis databases
-*/
-
#include "database-redis.h"
-#include <hiredis.h>
-#include "map.h"
-#include "mapsector.h"
-#include "mapblock.h"
-#include "serialization.h"
-#include "main.h"
#include "settings.h"
#include "log.h"
-#include "filesys.h"
+#include "exceptions.h"
+#include "util/string.h"
+
+#include <hiredis.h>
+#include <cassert>
-Database_Redis::Database_Redis(ServerMap *map, std::string savedir)
+Database_Redis::Database_Redis(Settings &conf)
{
- Settings conf;
- conf.readConfigFile((std::string(savedir) + DIR_DELIM + "world.mt").c_str());
std::string tmp;
try {
- tmp = conf.get("redis_address");
- hash = conf.get("redis_hash");
- } catch(SettingNotFoundException e) {
- throw SettingNotFoundException("Set redis_address and redis_hash in world.mt to use the redis backend");
+ tmp = conf.get("redis_address");
+ hash = conf.get("redis_hash");
+ } catch (SettingNotFoundException) {
+ throw SettingNotFoundException("Set redis_address and "
+ "redis_hash in world.mt to use the redis backend");
}
const char *addr = tmp.c_str();
int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379;
ctx = redisConnect(addr, port);
- if(!ctx)
+ if (!ctx) {
throw FileNotGoodException("Cannot allocate redis context");
- else if(ctx->err) {
+ } else if (ctx->err) {
std::string err = std::string("Connection error: ") + ctx->errstr;
redisFree(ctx);
throw FileNotGoodException(err);
}
- srvmap = map;
}
-int Database_Redis::Initialized(void)
+Database_Redis::~Database_Redis()
{
- return 1;
+ redisFree(ctx);
}
void Database_Redis::beginSave() {
- redisReply *reply;
- reply = (redisReply*) redisCommand(ctx, "MULTI");
- if(!reply)
- throw FileNotGoodException(std::string("redis command 'MULTI' failed: ") + ctx->errstr);
+ redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "MULTI"));
+ if (!reply) {
+ throw FileNotGoodException(std::string(
+ "Redis command 'MULTI' failed: ") + ctx->errstr);
+ }
freeReplyObject(reply);
}
void Database_Redis::endSave() {
- redisReply *reply;
- reply = (redisReply*) redisCommand(ctx, "EXEC");
- if(!reply)
- throw FileNotGoodException(std::string("redis command 'EXEC' failed: ") + ctx->errstr);
+ redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "EXEC"));
+ if (!reply) {
+ throw FileNotGoodException(std::string(
+ "Redis command 'EXEC' failed: ") + ctx->errstr);
+ }
freeReplyObject(reply);
}
-bool Database_Redis::saveBlock(v3s16 blockpos, std::string &data)
+bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data)
{
- std::string tmp = i64tos(getBlockAsInteger(blockpos));
+ std::string tmp = i64tos(getBlockAsInteger(pos));
- redisReply *reply = (redisReply *)redisCommand(ctx, "HSET %s %s %b",
- hash.c_str(), tmp.c_str(), data.c_str(), data.size());
+ redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HSET %s %s %b",
+ hash.c_str(), tmp.c_str(), data.c_str(), data.size()));
if (!reply) {
errorstream << "WARNING: saveBlock: redis command 'HSET' failed on "
- "block " << PP(blockpos) << ": " << ctx->errstr << std::endl;
+ "block " << PP(pos) << ": " << ctx->errstr << std::endl;
freeReplyObject(reply);
return false;
}
if (reply->type == REDIS_REPLY_ERROR) {
- errorstream << "WARNING: saveBlock: saving block " << PP(blockpos)
- << "failed" << std::endl;
+ errorstream << "WARNING: saveBlock: saving block " << PP(pos)
+ << " failed: " << reply->str << std::endl;
freeReplyObject(reply);
return false;
}
@@ -107,38 +101,43 @@ bool Database_Redis::saveBlock(v3s16 blockpos, std::string &data)
return true;
}
-std::string Database_Redis::loadBlock(v3s16 blockpos)
+std::string Database_Redis::loadBlock(const v3s16 &pos)
{
- std::string tmp = i64tos(getBlockAsInteger(blockpos));
- redisReply *reply;
- reply = (redisReply*) redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str());
-
- if(!reply)
- throw FileNotGoodException(std::string("redis command 'HGET %s %s' failed: ") + ctx->errstr);
- if(reply->type != REDIS_REPLY_STRING)
- return "";
-
- std::string str(reply->str, reply->len);
- freeReplyObject(reply); // std::string copies the memory so this won't cause any problems
- return str;
-}
-
-bool Database_Redis::deleteBlock(v3s16 blockpos)
-{
- std::string tmp = i64tos(getBlockAsInteger(blockpos));
+ std::string tmp = i64tos(getBlockAsInteger(pos));
+ redisReply *reply = static_cast<redisReply *>(redisCommand(ctx,
+ "HGET %s %s", hash.c_str(), tmp.c_str()));
- redisReply *reply = (redisReply *)redisCommand(ctx, "HDEL %s %s",
- hash.c_str(), tmp.c_str());
if (!reply) {
- errorstream << "WARNING: deleteBlock: redis command 'HDEL' failed on "
- "block " << PP(blockpos) << ": " << ctx->errstr << std::endl;
+ throw FileNotGoodException(std::string(
+ "Redis command 'HGET %s %s' failed: ") + ctx->errstr);
+ }
+ switch (reply->type) {
+ case REDIS_REPLY_STRING: {
+ std::string str(reply->str, reply->len);
+ // std::string copies the memory so this won't cause any problems
freeReplyObject(reply);
- return false;
+ return str;
+ }
+ case REDIS_REPLY_ERROR:
+ errorstream << "WARNING: loadBlock: loading block " << PP(pos)
+ << " failed: " << reply->str << std::endl;
}
+ freeReplyObject(reply);
+ return "";
+}
- if (reply->type == REDIS_REPLY_ERROR) {
- errorstream << "WARNING: deleteBlock: deleting block " << PP(blockpos)
- << "failed" << std::endl;
+bool Database_Redis::deleteBlock(const v3s16 &pos)
+{
+ std::string tmp = i64tos(getBlockAsInteger(pos));
+
+ redisReply *reply = static_cast<redisReply *>(redisCommand(ctx,
+ "HDEL %s %s", hash.c_str(), tmp.c_str()));
+ if (!reply) {
+ throw FileNotGoodException(std::string(
+ "Redis command 'HDEL %s %s' failed: ") + ctx->errstr);
+ } else if (reply->type == REDIS_REPLY_ERROR) {
+ errorstream << "WARNING: deleteBlock: deleting block " << PP(pos)
+ << " failed: " << reply->str << std::endl;
freeReplyObject(reply);
return false;
}
@@ -147,24 +146,25 @@ bool Database_Redis::deleteBlock(v3s16 blockpos)
return true;
}
-void Database_Redis::listAllLoadableBlocks(std::list<v3s16> &dst)
+void Database_Redis::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
- redisReply *reply;
- reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
- if(!reply)
- throw FileNotGoodException(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr);
- if(reply->type != REDIS_REPLY_ARRAY)
- throw FileNotGoodException("Failed to get keys from database");
- for(size_t i = 0; i < reply->elements; i++)
- {
- assert(reply->element[i]->type == REDIS_REPLY_STRING);
- dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str)));
+ redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HKEYS %s", hash.c_str()));
+ if (!reply) {
+ throw FileNotGoodException(std::string(
+ "Redis command 'HKEYS %s' failed: ") + ctx->errstr);
+ }
+ switch (reply->type) {
+ case REDIS_REPLY_ARRAY:
+ for (size_t i = 0; i < reply->elements; i++) {
+ assert(reply->element[i]->type == REDIS_REPLY_STRING);
+ dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str)));
+ }
+ case REDIS_REPLY_ERROR:
+ throw FileNotGoodException(std::string(
+ "Failed to get keys from database: ") + reply->str);
}
freeReplyObject(reply);
}
-Database_Redis::~Database_Redis()
-{
- redisFree(ctx);
-}
-#endif
+#endif // USE_REDIS
+
diff --git a/src/database-redis.h b/src/database-redis.h
index 34b90fa59..45e702c83 100644
--- a/src/database-redis.h
+++ b/src/database-redis.h
@@ -28,24 +28,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <hiredis.h>
#include <string>
-class ServerMap;
+class Settings;
class Database_Redis : public Database
{
public:
- Database_Redis(ServerMap *map, std::string savedir);
+ Database_Redis(Settings &conf);
+ ~Database_Redis();
+
virtual void beginSave();
virtual void endSave();
- virtual bool saveBlock(v3s16 blockpos, std::string &data);
- virtual std::string loadBlock(v3s16 blockpos);
- virtual bool deleteBlock(v3s16 blockpos);
- virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
- virtual int Initialized(void);
- ~Database_Redis();
+
+ virtual bool saveBlock(const v3s16 &pos, const std::string &data);
+ virtual std::string loadBlock(const v3s16 &pos);
+ virtual bool deleteBlock(const v3s16 &pos);
+ virtual void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
private:
- ServerMap *srvmap;
redisContext *ctx;
std::string hash;
};
+
+#endif // USE_REDIS
+
#endif
-#endif
+
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
index 0679da97d..84b1a7122 100644
--- a/src/database-sqlite3.cpp
+++ b/src/database-sqlite3.cpp
@@ -18,297 +18,231 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
/*
- SQLite format specification:
- - Initially only replaces sectors/ and sectors2/
-
- If map.sqlite does not exist in the save dir
- or the block was not found in the database
- the map will try to load from sectors folder.
- In either case, map.sqlite will be created
- and all future saves will save there.
-
- Structure of map.sqlite:
- Tables:
- blocks
- (PK) INT pos
- BLOB data
+SQLite format specification:
+ blocks:
+ (PK) INT id
+ BLOB data
*/
#include "database-sqlite3.h"
-#include "map.h"
-#include "mapsector.h"
-#include "mapblock.h"
-#include "serialization.h"
-#include "main.h"
-#include "settings.h"
#include "log.h"
#include "filesys.h"
+#include "exceptions.h"
+#include "settings.h"
+#include "util/string.h"
-Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
-{
- m_database = NULL;
- m_database_read = NULL;
- m_database_write = NULL;
- m_database_list = NULL;
- m_database_delete = NULL;
- m_savedir = savedir;
- srvmap = map;
-}
+#include <cassert>
+
+
+#define SQLRES(s, r) \
+ if ((s) != (r)) { \
+ throw FileNotGoodException(std::string(\
+ "SQLite3 database error (" \
+ __FILE__ ":" TOSTRING(__LINE__) \
+ "): ") +\
+ sqlite3_errmsg(m_database)); \
+ }
+#define SQLOK(s) SQLRES(s, SQLITE_OK)
-int Database_SQLite3::Initialized(void)
+#define PREPARE_STATEMENT(name, query) \
+ SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL))
+
+#define FINALIZE_STATEMENT(statement) \
+ if (sqlite3_finalize(statement) != SQLITE_OK) { \
+ throw FileNotGoodException(std::string( \
+ "SQLite3: Failed to finalize " #statement ": ") + \
+ sqlite3_errmsg(m_database)); \
+ }
+
+
+Database_SQLite3::Database_SQLite3(const std::string &savedir) :
+ m_initialized(false),
+ m_savedir(savedir),
+ m_database(NULL),
+ m_stmt_read(NULL),
+ m_stmt_write(NULL),
+ m_stmt_list(NULL),
+ m_stmt_delete(NULL),
+ m_stmt_begin(NULL),
+ m_stmt_end(NULL)
{
- return m_database ? 1 : 0;
}
void Database_SQLite3::beginSave() {
verifyDatabase();
- if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
- errorstream<<"WARNING: beginSave() failed, saving might be slow.";
+ SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE);
+ sqlite3_reset(m_stmt_begin);
}
void Database_SQLite3::endSave() {
verifyDatabase();
- if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
- errorstream<<"WARNING: endSave() failed, map might not have saved.";
+ SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE);
+ sqlite3_reset(m_stmt_end);
}
-void Database_SQLite3::createDirs(std::string path)
+void Database_SQLite3::openDatabase()
{
- if(fs::CreateAllDirs(path) == false)
- {
- infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
- <<"\""<<path<<"\""<<std::endl;
- throw BaseException("Database_SQLite3 failed to create directory");
- }
-}
+ if (m_database) return;
-void Database_SQLite3::verifyDatabase() {
- if(m_database)
- return;
-
- std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
- bool needs_create = false;
- int d;
+ std::string dbp = m_savedir + DIR_DELIM + "map.sqlite";
// Open the database connection
- createDirs(m_savedir); // ?
+ if (!fs::CreateAllDirs(m_savedir)) {
+ infostream << "Database_SQLite3: Failed to create directory \""
+ << m_savedir << "\"" << std::endl;
+ throw FileNotGoodException("Failed to create database "
+ "save directory");
+ }
- if(!fs::PathExists(dbp))
- needs_create = true;
+ bool needs_create = !fs::PathExists(dbp);
- d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
- if(d != SQLITE_OK) {
- errorstream<<"SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
+ if (sqlite3_open_v2(dbp.c_str(), &m_database,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+ NULL) != SQLITE_OK) {
+ errorstream << "SQLite3 database failed to open: "
+ << sqlite3_errmsg(m_database) << std::endl;
throw FileNotGoodException("Cannot open database file");
}
- if(needs_create)
+ if (needs_create) {
createDatabase();
+ }
- std::string querystr = std::string("PRAGMA synchronous = ")
+ std::string query_str = std::string("PRAGMA synchronous = ")
+ itos(g_settings->getU16("sqlite_synchronous"));
- d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
- if(d != SQLITE_OK) {
- errorstream<<"Database pragma set failed: "
- <<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot set pragma");
- }
+ SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL));
+}
- d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
- if(d != SQLITE_OK) {
- errorstream<<"SQLite3 read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare read statement");
- }
+void Database_SQLite3::verifyDatabase()
+{
+ if (m_initialized) return;
+
+ openDatabase();
+
+ PREPARE_STATEMENT(begin, "BEGIN");
+ PREPARE_STATEMENT(end, "COMMIT");
+ PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
#ifdef __ANDROID__
- d = sqlite3_prepare(m_database, "INSERT INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
+ PREPARE_STATEMENT(write, "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
#else
- d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?);", -1, &m_database_write, NULL);
+ PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
#endif
- if(d != SQLITE_OK) {
- errorstream<<"SQLite3 write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare write statement");
- }
+ PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
+ PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
- d = sqlite3_prepare(m_database, "DELETE FROM `blocks` WHERE `pos`=?;", -1, &m_database_delete, NULL);
- if(d != SQLITE_OK) {
- infostream<<"WARNING: SQLite3 database delete statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare delete statement");
- }
+ m_initialized = true;
- d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
- if(d != SQLITE_OK) {
- infostream<<"SQLite3 list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
- throw FileNotGoodException("Cannot prepare read statement");
- }
+ verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
+}
- infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
+inline void Database_SQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
+{
+ SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)));
}
-bool Database_SQLite3::deleteBlock(v3s16 blockpos)
+bool Database_SQLite3::deleteBlock(const v3s16 &pos)
{
verifyDatabase();
- if (sqlite3_bind_int64(m_database_delete, 1,
- getBlockAsInteger(blockpos)) != SQLITE_OK) {
- errorstream << "WARNING: Could not bind block position for delete: "
- << sqlite3_errmsg(m_database) << std::endl;
- }
+ bindPos(m_stmt_delete, pos);
- if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
+ bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE;
+ sqlite3_reset(m_stmt_delete);
+
+ if (!good) {
errorstream << "WARNING: deleteBlock: Block failed to delete "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_delete);
- return false;
+ << PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
}
-
- sqlite3_reset(m_database_delete);
- return true;
+ return good;
}
-bool Database_SQLite3::saveBlock(v3s16 blockpos, std::string &data)
+bool Database_SQLite3::saveBlock(const v3s16 &pos, const std::string &data)
{
verifyDatabase();
- s64 bkey = getBlockAsInteger(blockpos);
-
#ifdef __ANDROID__
/**
- * Note: For some unknown reason sqlite3 fails to REPLACE blocks on android,
- * deleting them and inserting first works.
+ * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
+ * deleting them and then inserting works.
*/
- if (sqlite3_bind_int64(m_database_read, 1, bkey) != SQLITE_OK) {
- infostream << "WARNING: Could not bind block position for load: "
- << sqlite3_errmsg(m_database)<<std::endl;
- }
+ bindPos(m_stmt_read, pos);
- int step_result = sqlite3_step(m_database_read);
- sqlite3_reset(m_database_read);
-
- if (step_result == SQLITE_ROW) {
- if (sqlite3_bind_int64(m_database_delete, 1, bkey) != SQLITE_OK) {
- infostream << "WARNING: Could not bind block position for delete: "
- << sqlite3_errmsg(m_database)<<std::endl;
- }
-
- if (sqlite3_step(m_database_delete) != SQLITE_DONE) {
- errorstream << "WARNING: saveBlock: Block failed to delete "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- return false;
- }
- sqlite3_reset(m_database_delete);
+ if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
+ deleteBlock(pos);
}
+ sqlite3_reset(m_stmt_read);
#endif
- if (sqlite3_bind_int64(m_database_write, 1, bkey) != SQLITE_OK) {
- errorstream << "WARNING: saveBlock: Block position failed to bind: "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_write);
- return false;
- }
-
- if (sqlite3_bind_blob(m_database_write, 2, (void *)data.c_str(),
- data.size(), NULL) != SQLITE_OK) {
- errorstream << "WARNING: saveBlock: Block data failed to bind: "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_write);
- return false;
- }
+ bindPos(m_stmt_write, pos);
+ SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL));
- if (sqlite3_step(m_database_write) != SQLITE_DONE) {
- errorstream << "WARNING: saveBlock: Block failed to save "
- << PP(blockpos) << ": " << sqlite3_errmsg(m_database) << std::endl;
- sqlite3_reset(m_database_write);
- return false;
- }
-
- sqlite3_reset(m_database_write);
+ SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE)
+ sqlite3_reset(m_stmt_write);
return true;
}
-std::string Database_SQLite3::loadBlock(v3s16 blockpos)
+std::string Database_SQLite3::loadBlock(const v3s16 &pos)
{
verifyDatabase();
- if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
- errorstream << "Could not bind block position for load: "
- << sqlite3_errmsg(m_database)<<std::endl;
- }
-
- if (sqlite3_step(m_database_read) == SQLITE_ROW) {
- const char *data = (const char *) sqlite3_column_blob(m_database_read, 0);
- size_t len = sqlite3_column_bytes(m_database_read, 0);
+ bindPos(m_stmt_read, pos);
- std::string s = "";
- if(data)
- s = std::string(data, len);
+ if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
+ sqlite3_reset(m_stmt_read);
+ return "";
+ }
+ const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
+ size_t len = sqlite3_column_bytes(m_stmt_read, 0);
- sqlite3_step(m_database_read);
- // We should never get more than 1 row, so ok to reset
- sqlite3_reset(m_database_read);
+ std::string s;
+ if (data)
+ s = std::string(data, len);
- return s;
- }
+ sqlite3_step(m_stmt_read);
+ // We should never get more than 1 row, so ok to reset
+ sqlite3_reset(m_stmt_read);
- sqlite3_reset(m_database_read);
- return "";
+ return s;
}
void Database_SQLite3::createDatabase()
{
- int e;
- assert(m_database);
- e = sqlite3_exec(m_database,
- "CREATE TABLE IF NOT EXISTS `blocks` ("
- "`pos` INT NOT NULL PRIMARY KEY,"
- "`data` BLOB"
- ");"
- , NULL, NULL, NULL);
- if(e != SQLITE_OK)
- throw FileNotGoodException("Could not create sqlite3 database structure");
- else
- infostream<<"ServerMap: SQLite3 database structure was created";
-
+ assert(m_database); // Pre-condition
+ SQLOK(sqlite3_exec(m_database,
+ "CREATE TABLE IF NOT EXISTS `blocks` (\n"
+ " `pos` INT PRIMARY KEY,\n"
+ " `data` BLOB\n"
+ ");\n",
+ NULL, NULL, NULL));
}
-void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
+void Database_SQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
verifyDatabase();
- while(sqlite3_step(m_database_list) == SQLITE_ROW)
- {
- sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
- v3s16 p = getIntegerAsBlock(block_i);
- //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
- dst.push_back(p);
+ while (sqlite3_step(m_stmt_list) == SQLITE_ROW) {
+ dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
}
+ sqlite3_reset(m_stmt_list);
}
-
-#define FINALIZE_STATEMENT(statement) \
- if ( statement ) \
- rc = sqlite3_finalize(statement); \
- if ( rc != SQLITE_OK ) \
- errorstream << "Database_SQLite3::~Database_SQLite3():" \
- << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
-
Database_SQLite3::~Database_SQLite3()
{
- int rc = SQLITE_OK;
-
- FINALIZE_STATEMENT(m_database_read)
- FINALIZE_STATEMENT(m_database_write)
- FINALIZE_STATEMENT(m_database_list)
- FINALIZE_STATEMENT(m_database_delete)
-
- if(m_database)
- rc = sqlite3_close(m_database);
-
- if (rc != SQLITE_OK) {
+ FINALIZE_STATEMENT(m_stmt_read)
+ FINALIZE_STATEMENT(m_stmt_write)
+ FINALIZE_STATEMENT(m_stmt_list)
+ FINALIZE_STATEMENT(m_stmt_begin)
+ FINALIZE_STATEMENT(m_stmt_end)
+ FINALIZE_STATEMENT(m_stmt_delete)
+
+ if (sqlite3_close(m_database) != SQLITE_OK) {
errorstream << "Database_SQLite3::~Database_SQLite3(): "
- << "Failed to close database: rc=" << rc << std::endl;
+ << "Failed to close database: "
+ << sqlite3_errmsg(m_database) << std::endl;
}
}
+
diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h
index 5035c67c8..a775742be 100644
--- a/src/database-sqlite3.h
+++ b/src/database-sqlite3.h
@@ -27,35 +27,43 @@ extern "C" {
#include "sqlite3.h"
}
-class ServerMap;
-
class Database_SQLite3 : public Database
{
public:
- Database_SQLite3(ServerMap *map, std::string savedir);
+ Database_SQLite3(const std::string &savedir);
+
virtual void beginSave();
virtual void endSave();
- virtual bool saveBlock(v3s16 blockpos, std::string &data);
- virtual std::string loadBlock(v3s16 blockpos);
- virtual bool deleteBlock(v3s16 blockpos);
- virtual void listAllLoadableBlocks(std::list<v3s16> &dst);
- virtual int Initialized(void);
+ virtual bool saveBlock(const v3s16 &pos, const std::string &data);
+ virtual std::string loadBlock(const v3s16 &pos);
+ virtual bool deleteBlock(const v3s16 &pos);
+ virtual void listAllLoadableBlocks(std::vector<v3s16> &dst);
+ virtual bool initialized() const { return m_initialized; }
~Database_SQLite3();
-private:
- ServerMap *srvmap;
- std::string m_savedir;
- sqlite3 *m_database;
- sqlite3_stmt *m_database_read;
- sqlite3_stmt *m_database_write;
- sqlite3_stmt *m_database_delete;
- sqlite3_stmt *m_database_list;
+private:
+ // Open the database
+ void openDatabase();
// Create the database structure
void createDatabase();
- // Verify we can read/write to the database
+ // Open and initialize the database if needed
void verifyDatabase();
- void createDirs(std::string path);
+
+ void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index=1);
+
+ bool m_initialized;
+
+ std::string m_savedir;
+
+ sqlite3 *m_database;
+ sqlite3_stmt *m_stmt_read;
+ sqlite3_stmt *m_stmt_write;
+ sqlite3_stmt *m_stmt_list;
+ sqlite3_stmt *m_stmt_delete;
+ sqlite3_stmt *m_stmt_begin;
+ sqlite3_stmt *m_stmt_end;
};
#endif
+
diff --git a/src/database.cpp b/src/database.cpp
index 26f6992fc..262d475ec 100644
--- a/src/database.cpp
+++ b/src/database.cpp
@@ -48,7 +48,7 @@ static inline s64 pythonmodulo(s64 i, s16 mod)
}
-s64 Database::getBlockAsInteger(const v3s16 pos) const
+s64 Database::getBlockAsInteger(const v3s16 &pos)
{
return (u64) pos.Z * 0x1000000 +
(u64) pos.Y * 0x1000 +
@@ -56,7 +56,7 @@ s64 Database::getBlockAsInteger(const v3s16 pos) const
}
-v3s16 Database::getIntegerAsBlock(s64 i) const
+v3s16 Database::getIntegerAsBlock(s64 i)
{
v3s16 pos;
pos.X = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
diff --git a/src/database.h b/src/database.h
index f04c4aa50..cee7b6fd9 100644
--- a/src/database.h
+++ b/src/database.h
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef DATABASE_HEADER
#define DATABASE_HEADER
-#include <list>
+#include <vector>
#include <string>
#include "irr_v3d.h"
#include "irrlichttypes.h"
@@ -32,16 +32,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Database
{
public:
- virtual void beginSave() = 0;
- virtual void endSave() = 0;
-
- virtual bool saveBlock(v3s16 blockpos, std::string &data) = 0;
- virtual std::string loadBlock(v3s16 blockpos) = 0;
- virtual bool deleteBlock(v3s16 blockpos) = 0;
- s64 getBlockAsInteger(const v3s16 pos) const;
- v3s16 getIntegerAsBlock(s64 i) const;
- virtual void listAllLoadableBlocks(std::list<v3s16> &dst) = 0;
- virtual int Initialized(void)=0;
- virtual ~Database() {};
+ virtual ~Database() {}
+
+ virtual void beginSave() {}
+ virtual void endSave() {}
+
+ virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0;
+ virtual std::string loadBlock(const v3s16 &pos) = 0;
+ virtual bool deleteBlock(const v3s16 &pos) = 0;
+
+ static s64 getBlockAsInteger(const v3s16 &pos);
+ static v3s16 getIntegerAsBlock(s64 i);
+
+ virtual void listAllLoadableBlocks(std::vector<v3s16> &dst) = 0;
+
+ virtual bool initialized() const { return true; }
};
+
#endif
+
diff --git a/src/debug.cpp b/src/debug.cpp
index bd970a8e4..ae2ffadc3 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -133,11 +133,11 @@ Nullstream dummyout;
Assert
*/
-void assert_fail(const char *assertion, const char *file,
+void sanity_check_fn(const char *assertion, const char *file,
unsigned int line, const char *function)
{
DEBUGPRINT("\nIn thread %lx:\n"
- "%s:%u: %s: Assertion '%s' failed.\n",
+ "%s:%u: %s: An engine assumption '%s' failed.\n",
(unsigned long)get_current_thread_id(),
file, line, function, assertion);
@@ -149,6 +149,22 @@ void assert_fail(const char *assertion, const char *file,
abort();
}
+void fatal_error_fn(const char *msg, const char *file,
+ unsigned int line, const char *function)
+{
+ DEBUGPRINT("\nIn thread %lx:\n"
+ "%s:%u: %s: A fatal error occurred: %s\n",
+ (unsigned long)get_current_thread_id(),
+ file, line, function, msg);
+
+ debug_stacks_print();
+
+ if(g_debugstreams[1])
+ fclose(g_debugstreams[1]);
+
+ abort();
+}
+
/*
DebugStack
*/
@@ -369,9 +385,11 @@ long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
MINIDUMP_USER_STREAM_INFORMATION mdusi;
MINIDUMP_USER_STREAM mdus;
bool minidump_created = false;
- std::string version_str("Minetest ");
- std::string dumpfile = porting::path_user + DIR_DELIM "minetest.dmp";
+ std::string dumpfile = porting::path_user + DIR_DELIM PROJECT_NAME ".dmp";
+
+ std::string version_str(PROJECT_NAME " ");
+ version_str += g_version_hash;
HANDLE hFile = CreateFileA(dumpfile.c_str(), GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
@@ -385,8 +403,6 @@ long WINAPI Win32ExceptionHandler(struct _EXCEPTION_POINTERS *pExceptInfo)
mdei.ExceptionPointers = pExceptInfo;
mdei.ThreadId = GetCurrentThreadId();
- version_str += minetest_version_hash;
-
mdus.Type = CommentStreamA;
mdus.BufferSize = version_str.size();
mdus.Buffer = (PVOID)version_str.c_str();
diff --git a/src/debug.h b/src/debug.h
index 1027fde69..9684aa2df 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include <exception>
+#include <assert.h>
#include "gettime.h"
#if (defined(WIN32) || defined(_WIN32_WCE))
@@ -72,28 +73,38 @@ extern std::ostream dstream;
extern std::ostream dstream_no_stderr;
extern Nullstream dummyout;
-/*
- Include assert.h and immediately undef assert so that it can't override
- our assert later on. leveldb/slice.h is a notable offender.
-*/
-#include <assert.h>
-#undef assert
+/* Abort program execution immediately
+ */
+__NORETURN extern void fatal_error_fn(
+ const char *msg, const char *file,
+ unsigned int line, const char *function);
+
+#define FATAL_ERROR(msg) \
+ fatal_error_fn((msg), __FILE__, __LINE__, __FUNCTION_NAME)
+
+#define FATAL_ERROR_IF(expr, msg) \
+ ((expr) \
+ ? fatal_error_fn((msg), __FILE__, __LINE__, __FUNCTION_NAME) \
+ : (void)(0))
/*
- Assert
+ sanity_check()
+ Equivalent to assert() but persists in Release builds (i.e. when NDEBUG is
+ defined)
*/
-__NORETURN extern void assert_fail(
+__NORETURN extern void sanity_check_fn(
const char *assertion, const char *file,
unsigned int line, const char *function);
-#define ASSERT(expr)\
- ((expr)\
- ? (void)(0)\
- : assert_fail(#expr, __FILE__, __LINE__, __FUNCTION_NAME))
+#define SANITY_CHECK(expr) \
+ ((expr) \
+ ? (void)(0) \
+ : sanity_check_fn(#expr, __FILE__, __LINE__, __FUNCTION_NAME))
+
+#define sanity_check(expr) SANITY_CHECK(expr)
-#define assert(expr) ASSERT(expr)
void debug_set_exception_handler();
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index 6504c5155..f0b02b2d9 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -43,11 +43,13 @@ void set_default_settings(Settings *settings)
settings->setDefault("keymap_special1", "KEY_KEY_E");
settings->setDefault("keymap_chat", "KEY_KEY_T");
settings->setDefault("keymap_cmd", "/");
+ settings->setDefault("keymap_minimap", "KEY_F9");
settings->setDefault("keymap_console", "KEY_F10");
settings->setDefault("keymap_rangeselect", "KEY_KEY_R");
settings->setDefault("keymap_freemove", "KEY_KEY_K");
settings->setDefault("keymap_fastmove", "KEY_KEY_J");
settings->setDefault("keymap_noclip", "KEY_KEY_H");
+ settings->setDefault("keymap_cinematic", "KEY_F8");
settings->setDefault("keymap_screenshot", "KEY_F12");
settings->setDefault("keymap_toggle_hud", "KEY_F1");
settings->setDefault("keymap_toggle_chat", "KEY_F2");
@@ -56,7 +58,7 @@ void set_default_settings(Settings *settings)
#if DEBUG
"KEY_F4");
#else
- "none");
+ "");
#endif
settings->setDefault("keymap_toggle_debug", "KEY_F5");
settings->setDefault("keymap_toggle_profiler", "KEY_F6");
@@ -92,6 +94,7 @@ void set_default_settings(Settings *settings)
// A bit more than the server will send around the player, to make fog blend well
settings->setDefault("viewing_range_nodes_max", "240");
settings->setDefault("viewing_range_nodes_min", "35");
+ settings->setDefault("map_generation_limit", "31000");
settings->setDefault("screenW", "800");
settings->setDefault("screenH", "600");
settings->setDefault("fullscreen", "false");
@@ -101,11 +104,12 @@ void set_default_settings(Settings *settings)
settings->setDefault("address", "");
settings->setDefault("random_input", "false");
settings->setDefault("client_unload_unused_data_timeout", "600");
+ settings->setDefault("client_mapblock_limit", "5000");
settings->setDefault("enable_fog", "true");
settings->setDefault("fov", "72");
settings->setDefault("view_bobbing", "true");
settings->setDefault("new_style_water", "false");
- settings->setDefault("new_style_leaves", "true");
+ settings->setDefault("leaves_style", "fancy");
settings->setDefault("connected_glass", "false");
settings->setDefault("smooth_lighting", "true");
settings->setDefault("display_gamma", "1.8");
@@ -115,6 +119,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("free_move", "false");
settings->setDefault("noclip", "false");
settings->setDefault("continuous_forward", "false");
+ settings->setDefault("cinematic", "false");
+ settings->setDefault("camera_smoothing", "0");
+ settings->setDefault("cinematic_camera_smoothing", "0.7");
settings->setDefault("fast_move", "false");
settings->setDefault("invert_mouse", "false");
settings->setDefault("enable_clouds", "true");
@@ -123,6 +130,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("fall_bobbing_amount", "0.0");
settings->setDefault("enable_3d_clouds", "true");
settings->setDefault("cloud_height", "120");
+ settings->setDefault("cloud_radius", "12");
settings->setDefault("menu_clouds", "true");
settings->setDefault("opaque_water", "false");
settings->setDefault("console_color", "(0,0,0)");
@@ -133,6 +141,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("crosshair_alpha", "255");
settings->setDefault("hud_scaling", "1.0");
settings->setDefault("gui_scaling", "1.0");
+ settings->setDefault("gui_scaling_filter", "false");
+ settings->setDefault("gui_scaling_filter_txr2img", "true");
settings->setDefault("mouse_sensitivity", "0.2");
settings->setDefault("enable_sound", "true");
settings->setDefault("sound_volume", "0.8");
@@ -145,14 +155,18 @@ void set_default_settings(Settings *settings)
settings->setDefault("anisotropic_filter", "false");
settings->setDefault("bilinear_filter", "false");
settings->setDefault("trilinear_filter", "false");
+ settings->setDefault("texture_clean_transparent", "false");
+ settings->setDefault("texture_min_size", "64");
settings->setDefault("preload_item_visuals", "false");
settings->setDefault("enable_bumpmapping", "false");
settings->setDefault("enable_parallax_occlusion", "false");
settings->setDefault("generate_normalmaps", "false");
settings->setDefault("normalmaps_strength", "0.6");
settings->setDefault("normalmaps_smooth", "1");
- settings->setDefault("parallax_occlusion_scale", "0.06");
- settings->setDefault("parallax_occlusion_bias", "0.03");
+ settings->setDefault("parallax_occlusion_mode", "1");
+ settings->setDefault("parallax_occlusion_iterations", "4");
+ settings->setDefault("parallax_occlusion_scale", "0.08");
+ settings->setDefault("parallax_occlusion_bias", "0.04");
settings->setDefault("enable_waving_water", "false");
settings->setDefault("water_wave_height", "1.0");
settings->setDefault("water_wave_length", "20.0");
@@ -163,7 +177,11 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_shaders", "true");
settings->setDefault("repeat_rightclick_time", "0.25");
settings->setDefault("enable_particles", "true");
- settings->setDefault("enable_mesh_cache", "true");
+ settings->setDefault("enable_mesh_cache", "false");
+
+ settings->setDefault("enable_minimap", "true");
+ settings->setDefault("minimap_shape_round", "true");
+ settings->setDefault("minimap_double_scan_height", "true");
settings->setDefault("curl_timeout", "5000");
settings->setDefault("curl_parallel_limit", "8");
@@ -236,6 +254,10 @@ void set_default_settings(Settings *settings)
settings->setDefault("deprecated_lua_api_handling", "log");
#endif
+ settings->setDefault("kick_msg_shutdown", "Server shutting down.");
+ settings->setDefault("kick_msg_crash", "This server has experienced an internal error. You will now be disconnected.");
+ settings->setDefault("ask_reconnect_on_crash", "false");
+
settings->setDefault("profiler_print_interval", "0");
settings->setDefault("enable_mapgen_debug_info", "false");
settings->setDefault("active_object_send_range_blocks", "3");
@@ -249,7 +271,6 @@ void set_default_settings(Settings *settings)
settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096");
settings->setDefault("time_send_interval", "5");
settings->setDefault("time_speed", "72");
- settings->setDefault("year_days", "30");
settings->setDefault("server_unload_unused_data_timeout", "29");
settings->setDefault("max_objects_per_block", "49");
settings->setDefault("server_map_save_interval", "5.3");
@@ -263,6 +284,8 @@ void set_default_settings(Settings *settings)
settings->setDefault("emergequeue_limit_diskonly", "32");
settings->setDefault("emergequeue_limit_generate", "32");
settings->setDefault("num_emerge_threads", "1");
+ settings->setDefault("secure.enable_security", "false");
+ settings->setDefault("secure.trusted_mods", "");
// physics stuff
settings->setDefault("movement_acceleration_default", "3");
@@ -287,7 +310,9 @@ void set_default_settings(Settings *settings)
settings->setDefault("mg_name", "v6");
settings->setDefault("water_level", "1");
settings->setDefault("chunksize", "5");
- settings->setDefault("mg_flags", "");
+ settings->setDefault("mg_flags", "dungeons");
+ settings->setDefault("mgv6_spflags", "jungles, snowbiomes");
+ settings->setDefault("enable_floating_dungeons", "true");
// IPv6
settings->setDefault("enable_ipv6", "true");
@@ -312,7 +337,7 @@ void set_default_settings(Settings *settings)
settings->setDefault("enable_particles", "false");
settings->setDefault("video_driver", "ogles1");
settings->setDefault("touchtarget", "true");
- settings->setDefault("TMPFolder","/sdcard/Minetest/tmp/");
+ settings->setDefault("TMPFolder","/sdcard/" PROJECT_NAME_C "/tmp/");
settings->setDefault("touchscreen_threshold","20");
settings->setDefault("smooth_lighting", "false");
settings->setDefault("max_simultaneous_block_sends_per_client", "3");
diff --git a/src/drawscene.cpp b/src/drawscene.cpp
index b089e71e6..509f341d5 100644
--- a/src/drawscene.cpp
+++ b/src/drawscene.cpp
@@ -18,12 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "drawscene.h"
-#include "main.h" // for g_settings
#include "settings.h"
#include "clouds.h"
#include "clientmap.h"
#include "util/timetaker.h"
#include "fontengine.h"
+#include "guiscalingfilter.h"
typedef enum {
LEFT = -1,
@@ -324,19 +324,19 @@ void draw_sidebyside_3d_mode(Camera& camera, bool show_hud,
//makeColorKeyTexture mirrors texture so we do it twice to get it right again
driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
- driver->draw2DImage(left_image,
+ draw2DImageFilterScaled(driver, left_image,
irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
- driver->draw2DImage(hudtexture,
+ draw2DImageFilterScaled(driver, hudtexture,
irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
- driver->draw2DImage(right_image,
+ draw2DImageFilterScaled(driver, right_image,
irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
- driver->draw2DImage(hudtexture,
+ draw2DImageFilterScaled(driver, hudtexture,
irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
@@ -380,19 +380,19 @@ void draw_top_bottom_3d_mode(Camera& camera, bool show_hud,
//makeColorKeyTexture mirrors texture so we do it twice to get it right again
driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
- driver->draw2DImage(left_image,
+ draw2DImageFilterScaled(driver, left_image,
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
- driver->draw2DImage(hudtexture,
+ draw2DImageFilterScaled(driver, hudtexture,
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
- driver->draw2DImage(right_image,
+ draw2DImageFilterScaled(driver, right_image,
irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
- driver->draw2DImage(hudtexture,
+ draw2DImageFilterScaled(driver, hudtexture,
irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
@@ -416,10 +416,11 @@ void draw_plain(Camera& camera, bool show_hud, Hud& hud,
camera.drawWieldedTool();
}
-void draw_scene(video::IVideoDriver* driver, scene::ISceneManager* smgr,
- Camera& camera, Client& client, LocalPlayer* player, Hud& hud,
- gui::IGUIEnvironment* guienv, std::vector<aabb3f> hilightboxes,
- const v2u32& screensize, video::SColor skycolor, bool show_hud)
+void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
+ Camera &camera, Client& client, LocalPlayer *player, Hud &hud,
+ Mapper &mapper, gui::IGUIEnvironment *guienv,
+ std::vector<aabb3f> hilightboxes, const v2u32 &screensize,
+ video::SColor skycolor, bool show_hud, bool show_minimap)
{
TimeTaker timer("smgr");
@@ -484,6 +485,8 @@ void draw_scene(video::IVideoDriver* driver, scene::ISceneManager* smgr,
hud.drawCrosshair();
hud.drawHotbar(client.getPlayerItem());
hud.drawLuaElements(camera.getOffset());
+ if (show_minimap)
+ mapper.drawMinimap();
}
guienv->drawAll();
diff --git a/src/drawscene.h b/src/drawscene.h
index 3268bcbf2..0630f2970 100644
--- a/src/drawscene.h
+++ b/src/drawscene.h
@@ -22,16 +22,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "camera.h"
#include "hud.h"
+#include "minimap.h"
#include "irrlichttypes_extrabloated.h"
-void draw_load_screen(const std::wstring &text, IrrlichtDevice* device,
- gui::IGUIEnvironment* guienv, float dtime=0, int percent=0,
- bool clouds=true);
+void draw_load_screen(const std::wstring &text, IrrlichtDevice *device,
+ gui::IGUIEnvironment *guienv, float dtime = 0, int percent = 0,
+ bool clouds = true);
-void draw_scene(video::IVideoDriver* driver, scene::ISceneManager* smgr,
- Camera& camera, Client& client, LocalPlayer* player, Hud& hud,
- gui::IGUIEnvironment* guienv, std::vector<aabb3f> hilightboxes,
- const v2u32& screensize, video::SColor skycolor, bool show_hud);
+void draw_scene(video::IVideoDriver *driver, scene::ISceneManager *smgr,
+ Camera &camera, Client &client, LocalPlayer *player, Hud &hud,
+ Mapper &mapper, gui::IGUIEnvironment *guienv,
+ std::vector<aabb3f> hilightboxes, const v2u32 &screensize,
+ video::SColor skycolor, bool show_hud, bool show_minimap);
#endif /* DRAWSCENE_H_ */
diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp
index eb452a196..8ce64e1e1 100644
--- a/src/dungeongen.cpp
+++ b/src/dungeongen.cpp
@@ -26,8 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "nodedef.h"
#include "profiler.h"
-#include "settings.h" // For g_settings
-#include "main.h" // For g_profiler
+#include "settings.h"
//#define DGEN_USE_TORCHES
@@ -39,7 +38,8 @@ NoiseParams nparams_dungeon_density(0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4, 2.0
///////////////////////////////////////////////////////////////////////////////
-DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) {
+DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams)
+{
this->mg = mapgen;
this->vm = mapgen->vm;
@@ -56,10 +56,10 @@ DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) {
dp.c_stair = mg->ndef->getId("mapgen_stair_cobble");
dp.diagonal_dirs = false;
- dp.mossratio = 3.0;
- dp.holesize = v3s16(1, 2, 1);
- dp.roomsize = v3s16(0,0,0);
- dp.notifytype = GENNOTIFY_DUNGEON;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
dp.np_rarity = nparams_dungeon_rarity;
dp.np_wetness = nparams_dungeon_wetness;
@@ -68,7 +68,8 @@ DungeonGen::DungeonGen(Mapgen *mapgen, DungeonParams *dparams) {
}
-void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) {
+void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax)
+{
//TimeTaker t("gen dungeons");
if (NoisePerlin3D(&dp.np_rarity, nmin.X, nmin.Y, nmin.Z, mg->seed) < 0.2)
return;
@@ -79,14 +80,17 @@ void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) {
// Dungeon generator doesn't modify places which have this set
vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
- // Set all air and water to be untouchable to make dungeons open
- // to caves and open air
+ bool no_float = !g_settings->getBool("enable_floating_dungeons");
+
+ // Set all air and water (and optionally ignore) to be untouchable
+ // to make dungeons open to caves and open air
for (s16 z = nmin.Z; z <= nmax.Z; z++) {
for (s16 y = nmin.Y; y <= nmax.Y; y++) {
u32 i = vm->m_area.index(nmin.X, y, z);
for (s16 x = nmin.X; x <= nmax.X; x++) {
content_t c = vm->m_data[i].getContent();
- if (c == CONTENT_AIR || c == dp.c_water)
+ if (c == CONTENT_AIR || c == dp.c_water ||
+ (no_float && c == CONTENT_IGNORE))
vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
i++;
}
@@ -94,7 +98,7 @@ void DungeonGen::generate(u32 bseed, v3s16 nmin, v3s16 nmax) {
}
// Add it
- makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE);
+ makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE);
// Convert some cobble to mossy cobble
if (dp.mossratio != 0.0) {
@@ -127,20 +131,19 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
Find place for first room
*/
bool fits = false;
- for (u32 i = 0; i < 100 && !fits; i++)
- {
+ for (u32 i = 0; i < 100 && !fits; i++) {
bool is_large_room = ((random.next() & 3) == 1);
roomsize = is_large_room ?
- v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) :
- v3s16(random.range(4, 8),random.range(4, 6),random.range(4, 8));
+ v3s16(random.range(8, 16), random.range(8, 16), random.range(8, 16)) :
+ v3s16(random.range(4, 8), random.range(4, 6), random.range(4, 8));
roomsize += dp.roomsize;
// start_padding is used to disallow starting the generation of
// a dungeon in a neighboring generation chunk
roomplace = vm->m_area.MinEdge + start_padding + v3s16(
- random.range(0,areasize.X-roomsize.X-1-start_padding.X),
- random.range(0,areasize.Y-roomsize.Y-1-start_padding.Y),
- random.range(0,areasize.Z-roomsize.Z-1-start_padding.Z));
+ random.range(0, areasize.X - roomsize.X - 1 - start_padding.X),
+ random.range(0, areasize.Y - roomsize.Y - 1 - start_padding.Y),
+ random.range(0, areasize.Z - roomsize.Z - 1 - start_padding.Z));
/*
Check that we're not putting the room to an unknown place,
@@ -149,12 +152,11 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
fits = true;
for (s16 z = 1; z < roomsize.Z - 1; z++)
for (s16 y = 1; y < roomsize.Y - 1; y++)
- for (s16 x = 1; x < roomsize.X - 1; x++)
- {
+ for (s16 x = 1; x < roomsize.X - 1; x++) {
v3s16 p = roomplace + v3s16(x, y, z);
u32 vi = vm->m_area.index(p);
if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) ||
- vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+ vm->m_data[vi].getContent() == CONTENT_IGNORE) {
fits = false;
break;
}
@@ -172,8 +174,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
u32 room_count = random.range(2, 16);
- for (u32 i = 0; i < room_count; i++)
- {
+ for (u32 i = 0; i < room_count; i++) {
// Make a room to the determined place
makeRoom(roomsize, roomplace);
@@ -211,7 +212,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
if (!findPlaceForDoor(doorplace, doordir))
return;
- if (random.range(0,1) == 0)
+ if (random.range(0, 1) == 0)
// Make the door
makeDoor(doorplace, doordir);
else
@@ -224,7 +225,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);
// Find a place for a random sized room
- roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8));
+ roomsize = v3s16(random.range(4, 8), random.range(4, 6), random.range(4, 8));
roomsize += dp.roomsize;
m_pos = corridor_end;
@@ -232,7 +233,7 @@ void DungeonGen::makeDungeon(v3s16 start_padding)
if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
return;
- if (random.range(0,1) == 0)
+ if (random.range(0, 1) == 0)
// Make the door
makeDoor(doorplace, doordir);
else
@@ -250,11 +251,10 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
// Make +-X walls
for (s16 z = 0; z < roomsize.Z; z++)
- for (s16 y = 0; y < roomsize.Y; y++)
- {
+ for (s16 y = 0; y < roomsize.Y; y++) {
{
v3s16 p = roomplace + v3s16(0, y, z);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
@@ -263,7 +263,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
}
{
v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
@@ -274,11 +274,10 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
// Make +-Z walls
for (s16 x = 0; x < roomsize.X; x++)
- for (s16 y = 0; y < roomsize.Y; y++)
- {
+ for (s16 y = 0; y < roomsize.Y; y++) {
{
v3s16 p = roomplace + v3s16(x, y, 0);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
@@ -287,7 +286,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
}
{
v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
@@ -298,11 +297,10 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
// Make +-Y walls (floor and ceiling)
for (s16 z = 0; z < roomsize.Z; z++)
- for (s16 x = 0; x < roomsize.X; x++)
- {
+ for (s16 x = 0; x < roomsize.X; x++) {
{
v3s16 p = roomplace + v3s16(x, 0, z);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
@@ -311,7 +309,7 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
}
{
v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
@@ -323,41 +321,39 @@ void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
// Fill with air
for (s16 z = 1; z < roomsize.Z - 1; z++)
for (s16 y = 1; y < roomsize.Y - 1; y++)
- for (s16 x = 1; x < roomsize.X - 1; x++)
- {
+ for (s16 x = 1; x < roomsize.X - 1; x++) {
v3s16 p = roomplace + v3s16(x, y, z);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
- vm->m_data[vi] = n_air;
+ vm->m_data[vi] = n_air;
}
}
void DungeonGen::makeFill(v3s16 place, v3s16 size,
- u8 avoid_flags, MapNode n, u8 or_flags)
+ u8 avoid_flags, MapNode n, u8 or_flags)
{
for (s16 z = 0; z < size.Z; z++)
for (s16 y = 0; y < size.Y; y++)
- for (s16 x = 0; x < size.X; x++)
- {
+ for (s16 x = 0; x < size.X; x++) {
v3s16 p = place + v3s16(x, y, z);
- if (vm->m_area.contains(p) == false)
+ if (!vm->m_area.contains(p))
continue;
u32 vi = vm->m_area.index(p);
if (vm->m_flags[vi] & avoid_flags)
continue;
vm->m_flags[vi] |= or_flags;
- vm->m_data[vi] = n;
+ vm->m_data[vi] = n;
}
}
void DungeonGen::makeHole(v3s16 place)
{
- makeFill(place, dp.holesize, 0,
- MapNode(CONTENT_AIR), VMANIP_FLAG_DUNGEON_INSIDE);
+ makeFill(place, dp.holesize, 0, MapNode(CONTENT_AIR),
+ VMANIP_FLAG_DUNGEON_INSIDE);
}
@@ -372,8 +368,8 @@ void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
}
-void DungeonGen::makeCorridor(v3s16 doorplace,
- v3s16 doordir, v3s16 &result_place, v3s16 &result_dir)
+void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
+ v3s16 &result_place, v3s16 &result_dir)
{
makeHole(doorplace);
v3s16 p0 = doorplace;
@@ -396,22 +392,26 @@ void DungeonGen::makeCorridor(v3s16 doorplace,
if (partcount != 0)
p.Y += make_stairs;
- if (vm->m_area.contains(p) == true &&
- vm->m_area.contains(p + v3s16(0, 1, 0)) == true) {
+ if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) {
if (make_stairs) {
- makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2),
- VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
+ makeFill(p + v3s16(-1, -1, -1),
+ dp.holesize + v3s16(2, 3, 2),
+ VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
+ MapNode(dp.c_cobble),
+ 0);
makeHole(p);
makeHole(p - dir);
- // TODO: fix stairs code so it works 100% (quite difficult)
+ // TODO: fix stairs code so it works 100%
+ // (quite difficult)
// exclude stairs from the bottom step
// exclude stairs from diagonal steps
if (((dir.X ^ dir.Z) & 1) &&
- (((make_stairs == 1) && i != 0) ||
- ((make_stairs == -1) && i != length - 1))) {
- // rotate face 180 deg if making stairs backwards
+ (((make_stairs == 1) && i != 0) ||
+ ((make_stairs == -1) && i != length - 1))) {
+ // rotate face 180 deg if
+ // making stairs backwards
int facedir = dir_to_facedir(dir * make_stairs);
u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z);
@@ -423,8 +423,11 @@ void DungeonGen::makeCorridor(v3s16 doorplace,
vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
}
} else {
- makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 2, 2),
- VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(dp.c_cobble), 0);
+ makeFill(p + v3s16(-1, -1, -1),
+ dp.holesize + v3s16(2, 2, 2),
+ VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
+ MapNode(dp.c_cobble),
+ 0);
makeHole(p);
}
@@ -444,7 +447,7 @@ void DungeonGen::makeCorridor(v3s16 doorplace,
dir = random_turn(random, dir);
- partlength = random.range(1,length);
+ partlength = random.range(1, length);
make_stairs = 0;
if (random.next() % 2 == 0 && partlength >= 3)
@@ -458,20 +461,15 @@ void DungeonGen::makeCorridor(v3s16 doorplace,
bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
{
- for (u32 i = 0; i < 100; i++)
- {
+ for (u32 i = 0; i < 100; i++) {
v3s16 p = m_pos + m_dir;
v3s16 p1 = p + v3s16(0, 1, 0);
- if (vm->m_area.contains(p) == false
- || vm->m_area.contains(p1) == false
- || i % 4 == 0)
- {
+ if (!vm->m_area.contains(p) || !vm->m_area.contains(p1) || i % 4 == 0) {
randomizeDir();
continue;
}
- if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_cobble
- && vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_cobble)
- {
+ if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_cobble &&
+ vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_cobble) {
// Found wall, this is a good place!
result_place = p;
result_dir = m_dir;
@@ -483,19 +481,25 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
Determine where to move next
*/
// Jump one up if the actual space is there
- if (vm->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == dp.c_cobble
- && vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR
- && vm->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR)
+ if (vm->getNodeNoExNoEmerge(p +
+ v3s16(0, 0, 0)).getContent() == dp.c_cobble &&
+ vm->getNodeNoExNoEmerge(p +
+ v3s16(0, 1, 0)).getContent() == CONTENT_AIR &&
+ vm->getNodeNoExNoEmerge(p +
+ v3s16(0, 2, 0)).getContent() == CONTENT_AIR)
p += v3s16(0,1,0);
// Jump one down if the actual space is there
- if (vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == dp.c_cobble
- && vm->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR
- && vm->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR)
- p += v3s16(0,-1,0);
+ if (vm->getNodeNoExNoEmerge(p +
+ v3s16(0, 1, 0)).getContent() == dp.c_cobble &&
+ vm->getNodeNoExNoEmerge(p +
+ v3s16(0, 0, 0)).getContent() == CONTENT_AIR &&
+ vm->getNodeNoExNoEmerge(p +
+ v3s16(0, -1, 0)).getContent() == CONTENT_AIR)
+ p += v3s16(0, -1, 0);
// Check if walking is now possible
- if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR
- || vm->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() != CONTENT_AIR)
- {
+ if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR ||
+ vm->getNodeNoExNoEmerge(p +
+ v3s16(0, 1, 0)).getContent() != CONTENT_AIR) {
// Cannot continue walking here
randomizeDir();
continue;
@@ -508,10 +512,9 @@ bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
- v3s16 &result_doordir, v3s16 &result_roomplace)
+ v3s16 &result_doordir, v3s16 &result_roomplace)
{
- for (s16 trycount = 0; trycount < 30; trycount++)
- {
+ for (s16 trycount = 0; trycount < 30; trycount++) {
v3s16 doorplace;
v3s16 doordir;
bool r = findPlaceForDoor(doorplace, doordir);
@@ -522,16 +525,16 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
#if 1
if (doordir == v3s16(1, 0, 0)) // X+
roomplace = doorplace +
- v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
+ v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
if (doordir == v3s16(-1, 0, 0)) // X-
roomplace = doorplace +
- v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
+ v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
if (doordir == v3s16(0, 0, 1)) // Z+
roomplace = doorplace +
- v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
+ v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
if (doordir == v3s16(0, 0, -1)) // Z-
roomplace = doorplace +
- v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
+ v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
#endif
#if 0
if (doordir == v3s16(1, 0, 0)) // X+
@@ -548,23 +551,18 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
bool fits = true;
for (s16 z = 1; z < roomsize.Z - 1; z++)
for (s16 y = 1; y < roomsize.Y - 1; y++)
- for (s16 x = 1; x < roomsize.X - 1; x++)
- {
+ for (s16 x = 1; x < roomsize.X - 1; x++) {
v3s16 p = roomplace + v3s16(x, y, z);
- if (vm->m_area.contains(p) == false)
- {
+ if (!vm->m_area.contains(p)) {
fits = false;
break;
}
- if (vm->m_flags[vm->m_area.index(p)]
- & VMANIP_FLAG_DUNGEON_INSIDE)
- {
+ if (vm->m_flags[vm->m_area.index(p)] & VMANIP_FLAG_DUNGEON_INSIDE) {
fits = false;
break;
}
}
- if(fits == false)
- {
+ if (fits == false) {
// Find new place
continue;
}
@@ -602,15 +600,12 @@ v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
v3s16 turn_xz(v3s16 olddir, int t)
{
v3s16 dir;
- if (t == 0)
- {
+ if (t == 0) {
// Turn right
dir.X = olddir.Z;
dir.Z = -olddir.X;
dir.Y = olddir.Y;
- }
- else
- {
+ } else {
// Turn left
dir.X = -olddir.Z;
dir.Z = olddir.X;
@@ -625,10 +620,8 @@ v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
int turn = random.range(0, 2);
v3s16 dir;
if (turn == 0)
- {
// Go straight
dir = olddir;
- }
else if (turn == 1)
// Turn right
dir = turn_xz(olddir, 0);
@@ -639,7 +632,8 @@ v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
}
-int dir_to_facedir(v3s16 d) {
+int dir_to_facedir(v3s16 d)
+{
if (abs(d.X) > abs(d.Z))
return d.X < 0 ? 3 : 1;
else
diff --git a/src/emerge.cpp b/src/emerge.cpp
index a697bcb07..d6bda731a 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h"
#include "util/container.h"
#include "util/thread.h"
-#include "main.h"
#include "constants.h"
#include "voxel.h"
#include "config.h"
@@ -166,7 +165,7 @@ EmergeManager::~EmergeManager()
void EmergeManager::loadMapgenParams()
{
- loadParamsFromSettings(g_settings);
+ params.load(*g_settings);
}
@@ -344,9 +343,9 @@ Mapgen *EmergeManager::createMapgen(const std::string &mgname, int mgid,
MapgenSpecificParams *EmergeManager::createMapgenParams(const std::string &mgname)
{
u32 i;
- for (i = 0; i != ARRLEN(reg_mapgens) && mgname != reg_mapgens[i].name; i++);
+ for (i = 0; i < ARRLEN(reg_mapgens) && mgname != reg_mapgens[i].name; i++);
if (i == ARRLEN(reg_mapgens)) {
- errorstream << "EmergeManager; mapgen " << mgname <<
+ errorstream << "EmergeManager: Mapgen " << mgname <<
" not registered" << std::endl;
return NULL;
}
@@ -356,56 +355,6 @@ MapgenSpecificParams *EmergeManager::createMapgenParams(const std::string &mgnam
}
-void EmergeManager::loadParamsFromSettings(Settings *settings)
-{
- std::string seed_str;
- const char *setname = (settings == g_settings) ? "fixed_map_seed" : "seed";
-
- if (!settings->getNoEx("seed", seed_str)) {
- g_settings->getNoEx(setname, seed_str);
- }
- if (!seed_str.empty()) {
- params.seed = read_seed(seed_str.c_str());
- } else {
- params.seed =
- ((u64)(myrand() & 0xffff) << 0) |
- ((u64)(myrand() & 0xffff) << 16) |
- ((u64)(myrand() & 0xffff) << 32) |
- ((u64)(myrand() & 0xffff) << 48);
- }
-
- settings->getNoEx("mg_name", params.mg_name);
- settings->getS16NoEx("water_level", params.water_level);
- settings->getS16NoEx("chunksize", params.chunksize);
- settings->getFlagStrNoEx("mg_flags", params.flags, flagdesc_mapgen);
- settings->getNoiseParams("mg_biome_np_heat", params.np_biome_heat);
- settings->getNoiseParams("mg_biome_np_humidity", params.np_biome_humidity);
-
- delete params.sparams;
- params.sparams = createMapgenParams(params.mg_name);
-
- if (params.sparams) {
- params.sparams->readParams(g_settings);
- params.sparams->readParams(settings);
- }
-}
-
-
-void EmergeManager::saveParamsToSettings(Settings *settings)
-{
- settings->set("mg_name", params.mg_name);
- settings->setU64("seed", params.seed);
- settings->setS16("water_level", params.water_level);
- settings->setS16("chunksize", params.chunksize);
- settings->setFlagStr("mg_flags", params.flags, flagdesc_mapgen, (u32)-1);
- settings->setNoiseParams("mg_biome_np_heat", params.np_biome_heat);
- settings->setNoiseParams("mg_biome_np_humidity", params.np_biome_humidity);
-
- if (params.sparams)
- params.sparams->writeParams(settings);
-}
-
-
////////////////////////////// Emerge Thread //////////////////////////////////
bool EmergeThread::popBlockEmerge(v3s16 *pos, u8 *flags)
@@ -545,8 +494,8 @@ void *EmergeThread::Thread()
try { // takes about 90ms with -O1 on an e3-1230v2
m_server->getScriptIface()->environment_OnGenerated(
minp, maxp, mapgen->blockseed);
- } catch(LuaError &e) {
- m_server->setAsyncFatalError(e.what());
+ } catch (LuaError &e) {
+ m_server->setAsyncFatalError("Lua: " + std::string(e.what()));
}
EMERGE_DBG_OUT("ended up with: " << analyze_block(block));
@@ -569,20 +518,22 @@ void *EmergeThread::Thread()
}
catch (VersionMismatchException &e) {
std::ostringstream err;
- err << "World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
- err << "----"<<std::endl;
- err << "\""<<e.what()<<"\""<<std::endl;
- err << "See debug.txt."<<std::endl;
- err << "World probably saved by a newer version of Minetest."<<std::endl;
+ err << "World data version mismatch in MapBlock " << PP(last_tried_pos) << std::endl
+ << "----" << std::endl
+ << "\"" << e.what() << "\"" << std::endl
+ << "See debug.txt." << std::endl
+ << "World probably saved by a newer version of " PROJECT_NAME_C "."
+ << std::endl;
m_server->setAsyncFatalError(err.str());
}
catch (SerializationError &e) {
std::ostringstream err;
- err << "Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
- err << "----"<<std::endl;
- err << "\""<<e.what()<<"\""<<std::endl;
- err << "See debug.txt."<<std::endl;
- err << "You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
+ err << "Invalid data in MapBlock " << PP(last_tried_pos) << std::endl
+ << "----" << std::endl
+ << "\"" << e.what() << "\"" << std::endl
+ << "See debug.txt." << std::endl
+ << "You can ignore this using [ignore_world_load_errors = true]."
+ << std::endl;
m_server->setAsyncFatalError(err.str());
}
diff --git a/src/emerge.h b/src/emerge.h
index 8bcc96ee0..1653199ec 100644
--- a/src/emerge.h
+++ b/src/emerge.h
@@ -101,19 +101,16 @@ public:
~EmergeManager();
void loadMapgenParams();
+ static MapgenSpecificParams *createMapgenParams(const std::string &mgname);
void initMapgens();
Mapgen *getCurrentMapgen();
Mapgen *createMapgen(const std::string &mgname, int mgid,
MapgenParams *mgparams);
- MapgenSpecificParams *createMapgenParams(const std::string &mgname);
static void getMapgenNames(std::list<const char *> &mgnames);
void startThreads();
void stopThreads();
bool enqueueBlockEmerge(u16 peer_id, v3s16 p, bool allow_generate);
- void loadParamsFromSettings(Settings *settings);
- void saveParamsToSettings(Settings *settings);
-
//mapgen helper methods
Biome *getBiomeAtPoint(v3s16 p);
int getGroundLevelAtPoint(v2s16 p);
diff --git a/src/environment.cpp b/src/environment.cpp
index e1f79803b..dbbfc6f1f 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -17,6 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <fstream>
#include "environment.h"
#include "filesys.h"
#include "porting.h"
@@ -31,7 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "scripting_game.h"
#include "nodedef.h"
#include "nodemetadata.h"
-#include "main.h" // For g_settings, g_profiler
#include "gamedef.h"
#ifndef SERVER
#include "clientmap.h"
@@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock_mesh.h"
#include "event.h"
#endif
+#include "server.h"
#include "daynightratio.h"
#include "map.h"
#include "emerge.h"
@@ -61,9 +62,8 @@ Environment::Environment():
Environment::~Environment()
{
// Deallocate players
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ for(std::vector<Player*>::iterator i = m_players.begin();
+ i != m_players.end(); ++i) {
delete (*i);
}
}
@@ -78,9 +78,9 @@ void Environment::addPlayer(Player *player)
*/
// If peer id is non-zero, it has to be unique.
if(player->peer_id != 0)
- assert(getPlayer(player->peer_id) == NULL);
+ FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique");
// Name has to be unique.
- assert(getPlayer(player->getName()) == NULL);
+ FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique");
// Add.
m_players.push_back(player);
}
@@ -89,7 +89,7 @@ void Environment::removePlayer(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
- for(std::list<Player*>::iterator i = m_players.begin();
+ for(std::vector<Player*>::iterator i = m_players.begin();
i != m_players.end();)
{
Player *player = *i;
@@ -104,7 +104,7 @@ void Environment::removePlayer(u16 peer_id)
void Environment::removePlayer(const char *name)
{
- for (std::list<Player*>::iterator it = m_players.begin();
+ for (std::vector<Player*>::iterator it = m_players.begin();
it != m_players.end(); ++it) {
if (strcmp((*it)->getName(), name) == 0) {
delete *it;
@@ -116,9 +116,8 @@ void Environment::removePlayer(const char *name)
Player * Environment::getPlayer(u16 peer_id)
{
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ for(std::vector<Player*>::iterator i = m_players.begin();
+ i != m_players.end(); ++i) {
Player *player = *i;
if(player->peer_id == peer_id)
return player;
@@ -128,9 +127,8 @@ Player * Environment::getPlayer(u16 peer_id)
Player * Environment::getPlayer(const char *name)
{
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ for(std::vector<Player*>::iterator i = m_players.begin();
+ i != m_players.end(); ++i) {
Player *player = *i;
if(strcmp(player->getName(), name) == 0)
return player;
@@ -140,15 +138,13 @@ Player * Environment::getPlayer(const char *name)
Player * Environment::getRandomConnectedPlayer()
{
- std::list<Player*> connected_players = getPlayers(true);
+ std::vector<Player*> connected_players = getPlayers(true);
u32 chosen_one = myrand() % connected_players.size();
u32 j = 0;
- for(std::list<Player*>::iterator
+ for(std::vector<Player*>::iterator
i = connected_players.begin();
- i != connected_players.end(); ++i)
- {
- if(j == chosen_one)
- {
+ i != connected_players.end(); ++i) {
+ if(j == chosen_one) {
Player *player = *i;
return player;
}
@@ -159,17 +155,15 @@ Player * Environment::getRandomConnectedPlayer()
Player * Environment::getNearestConnectedPlayer(v3f pos)
{
- std::list<Player*> connected_players = getPlayers(true);
+ std::vector<Player*> connected_players = getPlayers(true);
f32 nearest_d = 0;
Player *nearest_player = NULL;
- for(std::list<Player*>::iterator
+ for(std::vector<Player*>::iterator
i = connected_players.begin();
- i != connected_players.end(); ++i)
- {
+ i != connected_players.end(); ++i) {
Player *player = *i;
f32 d = player->getPosition().getDistanceFrom(pos);
- if(d < nearest_d || nearest_player == NULL)
- {
+ if(d < nearest_d || nearest_player == NULL) {
nearest_d = d;
nearest_player = player;
}
@@ -177,22 +171,20 @@ Player * Environment::getNearestConnectedPlayer(v3f pos)
return nearest_player;
}
-std::list<Player*> Environment::getPlayers()
+std::vector<Player*> Environment::getPlayers()
{
return m_players;
}
-std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
+std::vector<Player*> Environment::getPlayers(bool ignore_disconnected)
{
- std::list<Player*> newlist;
- for(std::list<Player*>::iterator
+ std::vector<Player*> newlist;
+ for(std::vector<Player*>::iterator
i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ i != m_players.end(); ++i) {
Player *player = *i;
-
- if(ignore_disconnected)
- {
+
+ if(ignore_disconnected) {
// Ignore disconnected players
if(player->peer_id == 0)
continue;
@@ -212,25 +204,43 @@ u32 Environment::getDayNightRatio()
void Environment::setTimeOfDaySpeed(float speed)
{
- JMutexAutoLock(this->m_lock);
+ JMutexAutoLock(this->m_timeofday_lock);
m_time_of_day_speed = speed;
}
float Environment::getTimeOfDaySpeed()
{
- JMutexAutoLock(this->m_lock);
+ JMutexAutoLock(this->m_timeofday_lock);
float retval = m_time_of_day_speed;
return retval;
}
+void Environment::setTimeOfDay(u32 time)
+{
+ JMutexAutoLock(this->m_time_lock);
+ m_time_of_day = time;
+ m_time_of_day_f = (float)time / 24000.0;
+}
+
+u32 Environment::getTimeOfDay()
+{
+ JMutexAutoLock(this->m_time_lock);
+ u32 retval = m_time_of_day;
+ return retval;
+}
+
+float Environment::getTimeOfDayF()
+{
+ JMutexAutoLock(this->m_time_lock);
+ float retval = m_time_of_day_f;
+ return retval;
+}
+
void Environment::stepTimeOfDay(float dtime)
{
- float day_speed = 0;
- {
- JMutexAutoLock(this->m_lock);
- day_speed = m_time_of_day_speed;
- }
-
+ // getTimeOfDaySpeed lock the value we need to prevent MT problems
+ float day_speed = getTimeOfDaySpeed();
+
m_time_counter += dtime;
f32 speed = day_speed * 24000./(24.*3600);
u32 units = (u32)(m_time_counter*speed);
@@ -287,7 +297,7 @@ void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
}
}
-void ActiveBlockList::update(std::list<v3s16> &active_positions,
+void ActiveBlockList::update(std::vector<v3s16> &active_positions,
s16 radius,
std::set<v3s16> &blocks_removed,
std::set<v3s16> &blocks_added)
@@ -296,7 +306,7 @@ void ActiveBlockList::update(std::list<v3s16> &active_positions,
Create the new list
*/
std::set<v3s16> newlist = m_forceloaded_list;
- for(std::list<v3s16>::iterator i = active_positions.begin();
+ for(std::vector<v3s16>::iterator i = active_positions.begin();
i != active_positions.end(); ++i)
{
fillRadiusBlock(*i, radius, newlist);
@@ -373,7 +383,7 @@ ServerEnvironment::~ServerEnvironment()
m_map->drop();
// Delete ActiveBlockModifiers
- for(std::list<ABMWithState>::iterator
+ for(std::vector<ABMWithState>::iterator
i = m_abms.begin(); i != m_abms.end(); ++i){
delete i->abm;
}
@@ -395,8 +405,8 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16
//calculate normalized direction vector
v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
- (pos2.Y - pos1.Y)/distance,
- (pos2.Z - pos1.Z)/distance);
+ (pos2.Y - pos1.Y)/distance,
+ (pos2.Z - pos1.Z)/distance);
//find out if there's a node on path between pos1 and pos2
for (float i = 1; i < distance; i += stepsize) {
@@ -416,12 +426,24 @@ bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16
return true;
}
+void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
+ const std::string &str_reason, bool reconnect)
+{
+ for (std::vector<Player*>::iterator it = m_players.begin();
+ it != m_players.end();
+ ++it) {
+ ((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
+ (*it)->protocol_version, (AccessDeniedCode)reason,
+ str_reason, reconnect);
+ }
+}
+
void ServerEnvironment::saveLoadedPlayers()
{
std::string players_path = m_path_world + DIR_DELIM "players";
fs::CreateDir(players_path);
- for (std::list<Player*>::iterator it = m_players.begin();
+ for (std::vector<Player*>::iterator it = m_players.begin();
it != m_players.end();
++it) {
RemotePlayer *player = static_cast<RemotePlayer*>(*it);
@@ -444,41 +466,43 @@ void ServerEnvironment::savePlayer(const std::string &playername)
Player *ServerEnvironment::loadPlayer(const std::string &playername)
{
- std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
-
- RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
bool newplayer = false;
bool found = false;
+ std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
+ std::string path = players_path + playername;
+
+ RemotePlayer *player = static_cast<RemotePlayer *>(getPlayer(playername.c_str()));
if (!player) {
- player = new RemotePlayer(m_gamedef, playername.c_str());
+ player = new RemotePlayer(m_gamedef, "");
newplayer = true;
}
- RemotePlayer testplayer(m_gamedef, "");
- std::string path = players_path + playername;
for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
- // Open file and deserialize
+ //// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
- if (!is.good()) {
- return NULL;
- }
- testplayer.deSerialize(is, path);
+ if (!is.good())
+ continue;
+ player->deSerialize(is, path);
is.close();
- if (testplayer.getName() == playername) {
- *player = testplayer;
+
+ if (player->getName() == playername) {
found = true;
break;
}
+
path = players_path + playername + itos(i);
}
+
if (!found) {
infostream << "Player file for player " << playername
<< " not found" << std::endl;
+ if (newplayer)
+ delete player;
return NULL;
}
- if (newplayer) {
+
+ if (newplayer)
addPlayer(player);
- }
player->setModified(false);
return player;
}
@@ -549,9 +573,9 @@ class ABMHandler
{
private:
ServerEnvironment *m_env;
- std::map<content_t, std::list<ActiveABM> > m_aabms;
+ std::map<content_t, std::vector<ActiveABM> > m_aabms;
public:
- ABMHandler(std::list<ABMWithState> &abms,
+ ABMHandler(std::vector<ABMWithState> &abms,
float dtime_s, ServerEnvironment *env,
bool use_timers):
m_env(env)
@@ -559,8 +583,8 @@ public:
if(dtime_s < 0.001)
return;
INodeDefManager *ndef = env->getGameDef()->ndef();
- for(std::list<ABMWithState>::iterator
- i = abms.begin(); i != abms.end(); ++i){
+ for(std::vector<ABMWithState>::iterator
+ i = abms.begin(); i != abms.end(); ++i) {
ActiveBlockModifier *abm = i->abm;
float trigger_interval = abm->getTriggerInterval();
if(trigger_interval < 0.001)
@@ -604,10 +628,10 @@ public:
k != ids.end(); k++)
{
content_t c = *k;
- std::map<content_t, std::list<ActiveABM> >::iterator j;
+ std::map<content_t, std::vector<ActiveABM> >::iterator j;
j = m_aabms.find(c);
if(j == m_aabms.end()){
- std::list<ActiveABM> aabmlist;
+ std::vector<ActiveABM> aabmlist;
m_aabms[c] = aabmlist;
j = m_aabms.find(c);
}
@@ -664,14 +688,13 @@ public:
content_t c = n.getContent();
v3s16 p = p0 + block->getPosRelative();
- std::map<content_t, std::list<ActiveABM> >::iterator j;
+ std::map<content_t, std::vector<ActiveABM> >::iterator j;
j = m_aabms.find(c);
if(j == m_aabms.end())
continue;
- for(std::list<ActiveABM>::iterator
- i = j->second.begin(); i != j->second.end(); i++)
- {
+ for(std::vector<ActiveABM>::iterator
+ i = j->second.begin(); i != j->second.end(); i++) {
if(myrand() % i->chance != 0)
continue;
@@ -738,7 +761,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
/*infostream<<"ServerEnvironment::activateBlock(): block is "
<<dtime_s<<" seconds old."<<std::endl;*/
-
+
// Activate stored objects
activateObjects(block, dtime_s);
@@ -830,9 +853,8 @@ bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
return true;
}
-std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
+void ServerEnvironment::getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius)
{
- std::set<u16> objects;
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
i != m_active_objects.end(); ++i)
@@ -842,20 +864,18 @@ std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
v3f objectpos = obj->getBasePosition();
if(objectpos.getDistanceFrom(pos) > radius)
continue;
- objects.insert(id);
+ objects.push_back(id);
}
- return objects;
}
void ServerEnvironment::clearAllObjects()
{
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Removing all active objects"<<std::endl;
- std::list<u16> objects_to_remove;
+ std::vector<u16> objects_to_remove;
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
- i != m_active_objects.end(); ++i)
- {
+ i != m_active_objects.end(); ++i) {
ServerActiveObject* obj = i->second;
if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
continue;
@@ -866,7 +886,7 @@ void ServerEnvironment::clearAllObjects()
if(block){
block->m_static_objects.remove(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "clearAllObjects");
+ MOD_REASON_CLEAR_ALL_OBJECTS);
obj->m_static_exists = false;
}
}
@@ -888,15 +908,15 @@ void ServerEnvironment::clearAllObjects()
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
+
// Remove references from m_active_objects
- for(std::list<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = objects_to_remove.begin();
+ i != objects_to_remove.end(); ++i) {
m_active_objects.erase(*i);
}
// Get list of loaded blocks
- std::list<v3s16> loaded_blocks;
+ std::vector<v3s16> loaded_blocks;
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Listing all loaded blocks"<<std::endl;
m_map->listAllLoadedBlocks(loaded_blocks);
@@ -905,7 +925,7 @@ void ServerEnvironment::clearAllObjects()
<<loaded_blocks.size()<<std::endl;
// Get list of loadable blocks
- std::list<v3s16> loadable_blocks;
+ std::vector<v3s16> loadable_blocks;
infostream<<"ServerEnvironment::clearAllObjects(): "
<<"Listing all loadable blocks"<<std::endl;
m_map->listAllLoadableBlocks(loadable_blocks);
@@ -915,12 +935,11 @@ void ServerEnvironment::clearAllObjects()
<<", now clearing"<<std::endl;
// Grab a reference on each loaded block to avoid unloading it
- for(std::list<v3s16>::iterator i = loaded_blocks.begin();
- i != loaded_blocks.end(); ++i)
- {
+ for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+ i != loaded_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
- assert(block);
+ assert(block != NULL);
block->refGrab();
}
@@ -931,9 +950,8 @@ void ServerEnvironment::clearAllObjects()
u32 num_blocks_checked = 0;
u32 num_blocks_cleared = 0;
u32 num_objs_cleared = 0;
- for(std::list<v3s16>::iterator i = loadable_blocks.begin();
- i != loadable_blocks.end(); ++i)
- {
+ for(std::vector<v3s16>::iterator i = loadable_blocks.begin();
+ i != loadable_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->emergeBlock(p, false);
if(!block){
@@ -947,7 +965,7 @@ void ServerEnvironment::clearAllObjects()
block->m_static_objects.m_stored.clear();
block->m_static_objects.m_active.clear();
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "clearAllObjects");
+ MOD_REASON_CLEAR_ALL_OBJECTS);
num_objs_cleared += num_stored + num_active;
num_blocks_cleared++;
}
@@ -969,9 +987,8 @@ void ServerEnvironment::clearAllObjects()
m_map->unloadUnreferencedBlocks();
// Drop references that were added above
- for(std::list<v3s16>::iterator i = loaded_blocks.begin();
- i != loaded_blocks.end(); ++i)
- {
+ for(std::vector<v3s16>::iterator i = loaded_blocks.begin();
+ i != loaded_blocks.end(); ++i) {
v3s16 p = *i;
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
assert(block);
@@ -986,7 +1003,7 @@ void ServerEnvironment::clearAllObjects()
void ServerEnvironment::step(float dtime)
{
DSTACK(__FUNCTION_NAME);
-
+
//TimeTaker timer("ServerEnv step");
/* Step time of day */
@@ -1006,21 +1023,21 @@ void ServerEnvironment::step(float dtime)
m_game_time += inc_i;
m_game_time_fraction_counter -= (float)inc_i;
}
-
+
/*
Handle players
*/
{
ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
- for(std::list<Player*>::iterator i = m_players.begin();
+ for(std::vector<Player*>::iterator i = m_players.begin();
i != m_players.end(); ++i)
{
Player *player = *i;
-
+
// Ignore disconnected players
if(player->peer_id == 0)
continue;
-
+
// Move
player->move(dtime, this, 100*BS);
}
@@ -1035,20 +1052,20 @@ void ServerEnvironment::step(float dtime)
/*
Get player block positions
*/
- std::list<v3s16> players_blockpos;
- for(std::list<Player*>::iterator
+ std::vector<v3s16> players_blockpos;
+ for(std::vector<Player*>::iterator
i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ i != m_players.end(); ++i) {
Player *player = *i;
// Ignore disconnected players
if(player->peer_id == 0)
continue;
+
v3s16 blockpos = getNodeBlockPos(
floatToInt(player->getPosition(), BS));
players_blockpos.push_back(blockpos);
}
-
+
/*
Update list of active blocks, collecting changes
*/
@@ -1064,7 +1081,7 @@ void ServerEnvironment::step(float dtime)
// Convert active objects that are no more in active blocks to static
deactivateFarObjects(false);
-
+
for(std::set<v3s16>::iterator
i = blocks_removed.begin();
i != blocks_removed.end(); ++i)
@@ -1073,11 +1090,11 @@ void ServerEnvironment::step(float dtime)
/* infostream<<"Server: Block " << PP(p)
<< " became inactive"<<std::endl; */
-
+
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
if(block==NULL)
continue;
-
+
// Set current time as timestamp (and let it set ChangedFlag)
block->setTimestamp(m_game_time);
}
@@ -1110,7 +1127,7 @@ void ServerEnvironment::step(float dtime)
if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
{
ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
-
+
float dtime = 1.0;
for(std::set<v3s16>::iterator
@@ -1118,7 +1135,7 @@ void ServerEnvironment::step(float dtime)
i != m_active_blocks.m_list.end(); ++i)
{
v3s16 p = *i;
-
+
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") being handled"<<std::endl;*/
@@ -1128,14 +1145,14 @@ void ServerEnvironment::step(float dtime)
// Reset block usage timer
block->resetUsageTimer();
-
+
// Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time);
// If time has changed much from the one on disk,
// set block to be saved when it is unloaded
if(block->getTimestamp() > block->getDiskTimestamp() + 60)
block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
- "Timestamp older than 60s (step)");
+ MOD_REASON_BLOCK_EXPIRED);
// Run node timers
std::map<v3s16, NodeTimer> elapsed_timers =
@@ -1153,7 +1170,7 @@ void ServerEnvironment::step(float dtime)
}
}
}
-
+
const float abm_interval = 1.0;
if(m_active_block_modifier_interval.step(dtime, abm_interval))
do{ // breakable
@@ -1164,7 +1181,7 @@ void ServerEnvironment::step(float dtime)
}
ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
TimeTaker timer("modify in active blocks");
-
+
// Initialize handling of ActiveBlockModifiers
ABMHandler abmhandler(m_abms, abm_interval, this, true);
@@ -1173,14 +1190,14 @@ void ServerEnvironment::step(float dtime)
i != m_active_blocks.m_list.end(); ++i)
{
v3s16 p = *i;
-
+
/*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
<<") being handled"<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
- if(block==NULL)
+ if(block == NULL)
continue;
-
+
// Set current time as timestamp
block->setTimestampNoChangedFlag(m_game_time);
@@ -1197,7 +1214,7 @@ void ServerEnvironment::step(float dtime)
m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
}
}while(0);
-
+
/*
Step script environment (run global on_step())
*/
@@ -1211,7 +1228,7 @@ void ServerEnvironment::step(float dtime)
//TimeTaker timer("Step active objects");
g_profiler->avg("SEnv: num of objects", m_active_objects.size());
-
+
// This helps the objects to send data at the same time
bool send_recommended = false;
m_send_recommended_timer += dtime;
@@ -1234,12 +1251,13 @@ void ServerEnvironment::step(float dtime)
// Read messages from object
while(!obj->m_messages_out.empty())
{
- m_active_object_messages.push_back(
- obj->m_messages_out.pop_front());
+ m_active_object_messages.push(
+ obj->m_messages_out.front());
+ obj->m_messages_out.pop();
}
}
}
-
+
/*
Manage active objects
*/
@@ -1282,7 +1300,7 @@ u16 getFreeServerActiveObjectId(
last_used_id ++;
if(isFreeServerActiveObjectId(last_used_id, objects))
return last_used_id;
-
+
if(last_used_id == startid)
return 0;
}
@@ -1290,57 +1308,12 @@ u16 getFreeServerActiveObjectId(
u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
{
- assert(object);
+ assert(object); // Pre-condition
m_added_objects++;
u16 id = addActiveObjectRaw(object, true, 0);
return id;
}
-#if 0
-bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
-{
- assert(obj);
-
- v3f objectpos = obj->getBasePosition();
-
- // The block in which the object resides in
- v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
-
- /*
- Update the static data
- */
-
- // Create new static object
- std::string staticdata = obj->getStaticData();
- StaticObject s_obj(obj->getType(), objectpos, staticdata);
- // Add to the block where the object is located in
- v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
- // Get or generate the block
- MapBlock *block = m_map->emergeBlock(blockpos);
-
- bool succeeded = false;
-
- if(block)
- {
- block->m_static_objects.insert(0, s_obj);
- block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
- "addActiveObjectAsStatic");
- succeeded = true;
- }
- else{
- infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
- <<"Could not find or generate "
- <<"a block for storing static object"<<std::endl;
- succeeded = false;
- }
-
- if(obj->environmentDeletes())
- delete obj;
-
- return succeeded;
-}
-#endif
-
/*
Finds out what new objects have been added to
inside a radius around a position
@@ -1438,7 +1411,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
removed_objects.insert(id);
continue;
}
-
+
f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
if (distance_f <= player_radius_f || player_radius_f == 0)
@@ -1451,13 +1424,40 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
}
}
+void ServerEnvironment::setStaticForActiveObjectsInBlock(
+ v3s16 blockpos, bool static_exists, v3s16 static_block)
+{
+ MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
+ if (!block)
+ return;
+
+ for (std::map<u16, StaticObject>::iterator
+ so_it = block->m_static_objects.m_active.begin();
+ so_it != block->m_static_objects.m_active.end(); ++so_it) {
+ // Get the ServerActiveObject counterpart to this StaticObject
+ std::map<u16, ServerActiveObject *>::iterator ao_it;
+ ao_it = m_active_objects.find(so_it->first);
+ if (ao_it == m_active_objects.end()) {
+ // If this ever happens, there must be some kind of nasty bug.
+ errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): "
+ "Object from MapBlock::m_static_objects::m_active not found "
+ "in m_active_objects";
+ continue;
+ }
+
+ ServerActiveObject *sao = ao_it->second;
+ sao->m_static_exists = static_exists;
+ sao->m_static_block = static_block;
+ }
+}
+
ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
{
if(m_active_object_messages.empty())
return ActiveObjectMessage(0);
-
+
ActiveObjectMessage message = m_active_object_messages.front();
- m_active_object_messages.pop_front();
+ m_active_object_messages.pop();
return message;
}
@@ -1468,7 +1468,7 @@ ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
bool set_changed, u32 dtime_s)
{
- assert(object);
+ assert(object); // Pre-condition
if(object->getId() == 0){
u16 new_id = getFreeServerActiveObjectId(m_active_objects);
if(new_id == 0)
@@ -1495,19 +1495,19 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
}
/*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"added (id="<<object->getId()<<")"<<std::endl;*/
-
+
m_active_objects[object->getId()] = object;
-
+
verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
<<"Added id="<<object->getId()<<"; there are now "
<<m_active_objects.size()<<" active objects."
<<std::endl;
-
+
// Register reference in scripting api (must be done before post-init)
m_script->addObjectReference(object);
// Post-initialize object
object->addedToEnvironment(dtime_s);
-
+
// Add static data to block
if(object->isStaticAllowed())
{
@@ -1525,7 +1525,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
if(set_changed)
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "addActiveObjectRaw");
+ MOD_REASON_ADD_ACTIVE_OBJECT_RAW);
} else {
v3s16 p = floatToInt(objectpos, BS);
errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
@@ -1533,7 +1533,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
<<" statically (pos="<<PP(p)<<")"<<std::endl;
}
}
-
+
return object->getId();
}
@@ -1542,11 +1542,10 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
*/
void ServerEnvironment::removeRemovedObjects()
{
- std::list<u16> objects_to_remove;
+ std::vector<u16> objects_to_remove;
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
- i != m_active_objects.end(); ++i)
- {
+ i != m_active_objects.end(); ++i) {
u16 id = i->first;
ServerActiveObject* obj = i->second;
// This shouldn't happen but check it
@@ -1575,7 +1574,7 @@ void ServerEnvironment::removeRemovedObjects()
if (block) {
block->m_static_objects.remove(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "removeRemovedObjects/remove");
+ MOD_REASON_REMOVE_OBJECTS_REMOVE);
obj->m_static_exists = false;
} else {
infostream<<"Failed to emerge block from which an object to "
@@ -1600,7 +1599,7 @@ void ServerEnvironment::removeRemovedObjects()
block->m_static_objects.m_stored.push_back(i->second);
block->m_static_objects.m_active.erase(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "removeRemovedObjects/deactivate");
+ MOD_REASON_REMOVE_OBJECTS_DEACTIVATE);
}
} else {
infostream<<"Failed to emerge block from which an object to "
@@ -1616,13 +1615,13 @@ void ServerEnvironment::removeRemovedObjects()
// Delete
if(obj->environmentDeletes())
delete obj;
+
// Id to be removed from m_active_objects
objects_to_remove.push_back(id);
}
// Remove references from m_active_objects
- for(std::list<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = objects_to_remove.begin();
+ i != objects_to_remove.end(); ++i) {
m_active_objects.erase(*i);
}
}
@@ -1666,17 +1665,19 @@ static void print_hexdump(std::ostream &o, const std::string &data)
*/
void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
{
- if(block==NULL)
+ if(block == NULL)
return;
+
// Ignore if no stored objects (to not set changed flag)
if(block->m_static_objects.m_stored.empty())
return;
+
verbosestream<<"ServerEnvironment::activateObjects(): "
<<"activating objects of block "<<PP(block->getPos())
<<" ("<<block->m_static_objects.m_stored.size()
<<" objects)"<<std::endl;
bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
- if(large_amount){
+ if (large_amount) {
errorstream<<"suspiciously large amount of objects detected: "
<<block->m_static_objects.m_stored.size()<<" in "
<<PP(block->getPos())
@@ -1684,32 +1685,28 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
// Clear stored list
block->m_static_objects.m_stored.clear();
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "stored list cleared in activateObjects due to "
- "large amount of objects");
+ MOD_REASON_TOO_MANY_OBJECTS);
return;
}
// Activate stored objects
- std::list<StaticObject> new_stored;
- for(std::list<StaticObject>::iterator
+ std::vector<StaticObject> new_stored;
+ for (std::vector<StaticObject>::iterator
i = block->m_static_objects.m_stored.begin();
- i != block->m_static_objects.m_stored.end(); ++i)
- {
- /*infostream<<"Server: Creating an active object from "
- <<"static data"<<std::endl;*/
+ i != block->m_static_objects.m_stored.end(); ++i) {
StaticObject &s_obj = *i;
+
// Create an active object from the data
ServerActiveObject *obj = ServerActiveObject::create
- (s_obj.type, this, 0, s_obj.pos, s_obj.data);
+ ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data);
// If couldn't create object, store static data back.
- if(obj==NULL)
- {
+ if(obj == NULL) {
errorstream<<"ServerEnvironment::activateObjects(): "
<<"failed to create active object from static object "
<<"in block "<<PP(s_obj.pos/BS)
<<" type="<<(int)s_obj.type<<" data:"<<std::endl;
print_hexdump(verbosestream, s_obj.data);
-
+
new_stored.push_back(s_obj);
continue;
}
@@ -1722,10 +1719,9 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
// Clear stored list
block->m_static_objects.m_stored.clear();
// Add leftover failed stuff to stored list
- for(std::list<StaticObject>::iterator
+ for(std::vector<StaticObject>::iterator
i = new_stored.begin();
- i != new_stored.end(); ++i)
- {
+ i != new_stored.end(); ++i) {
StaticObject &s_obj = *i;
block->m_static_objects.m_stored.push_back(s_obj);
}
@@ -1765,14 +1761,13 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
*/
void ServerEnvironment::deactivateFarObjects(bool force_delete)
{
- std::list<u16> objects_to_remove;
+ std::vector<u16> objects_to_remove;
for(std::map<u16, ServerActiveObject*>::iterator
i = m_active_objects.begin();
- i != m_active_objects.end(); ++i)
- {
+ i != m_active_objects.end(); ++i) {
ServerActiveObject* obj = i->second;
assert(obj);
-
+
// Do not deactivate if static data creation not allowed
if(!force_delete && !obj->isStaticAllowed())
continue;
@@ -1811,7 +1806,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
block->m_static_objects.insert(id, s_obj);
obj->m_static_block = blockpos_o;
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "deactivateFarObjects: Static data moved in");
+ MOD_REASON_STATIC_DATA_ADDED);
// Delete from block where object was located
block = m_map->emergeBlock(old_static_block, false);
@@ -1824,7 +1819,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
}
block->m_static_objects.remove(id);
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "deactivateFarObjects: Static data moved out");
+ MOD_REASON_STATIC_DATA_REMOVED);
continue;
}
@@ -1848,36 +1843,38 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
// Create new static object
std::string staticdata_new = obj->getStaticData();
StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
-
+
bool stays_in_same_block = false;
bool data_changed = true;
- if(obj->m_static_exists){
- if(obj->m_static_block == blockpos_o)
+ if (obj->m_static_exists) {
+ if (obj->m_static_block == blockpos_o)
stays_in_same_block = true;
MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
-
- std::map<u16, StaticObject>::iterator n =
+
+ if (block) {
+ std::map<u16, StaticObject>::iterator n =
block->m_static_objects.m_active.find(id);
- if(n != block->m_static_objects.m_active.end()){
- StaticObject static_old = n->second;
+ if (n != block->m_static_objects.m_active.end()) {
+ StaticObject static_old = n->second;
- float save_movem = obj->getMinimumSavedMovement();
+ float save_movem = obj->getMinimumSavedMovement();
- if(static_old.data == staticdata_new &&
- (static_old.pos - objectpos).getLength() < save_movem)
- data_changed = false;
- } else {
- errorstream<<"ServerEnvironment::deactivateFarObjects(): "
+ if (static_old.data == staticdata_new &&
+ (static_old.pos - objectpos).getLength() < save_movem)
+ data_changed = false;
+ } else {
+ errorstream<<"ServerEnvironment::deactivateFarObjects(): "
<<"id="<<id<<" m_static_exists=true but "
<<"static data doesn't actually exist in "
<<PP(obj->m_static_block)<<std::endl;
+ }
}
}
bool shall_be_written = (!stays_in_same_block || data_changed);
-
+
// Delete old static object
if(obj->m_static_exists)
{
@@ -1889,8 +1886,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
// Only mark block as modified if data changed considerably
if(shall_be_written)
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "deactivateFarObjects: Static data "
- "changed considerably");
+ MOD_REASON_STATIC_DATA_CHANGED);
}
}
@@ -1932,13 +1928,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
// Store static data
u16 store_id = pending_delete ? id : 0;
block->m_static_objects.insert(store_id, s_obj);
-
+
// Only mark block as modified if data changed considerably
if(shall_be_written)
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "deactivateFarObjects: Static data "
- "changed considerably");
-
+ MOD_REASON_STATIC_DATA_CHANGED);
+
obj->m_static_exists = true;
obj->m_static_block = block->getPos();
}
@@ -1968,7 +1963,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
obj->m_pending_deactivation = true;
continue;
}
-
+
verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
<<"object id="<<id<<" is not known by clients"
<<"; deleting"<<std::endl;
@@ -1986,14 +1981,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete)
}
// Remove references from m_active_objects
- for(std::list<u16>::iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = objects_to_remove.begin();
+ i != objects_to_remove.end(); ++i) {
m_active_objects.erase(*i);
}
}
-
#ifndef SERVER
#include "clientsimpleobject.h"
@@ -2012,7 +2005,7 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
m_irr(irr)
{
char zero = 0;
- memset(m_attachements, zero, sizeof(m_attachements));
+ memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids));
}
ClientEnvironment::~ClientEnvironment()
@@ -2025,9 +2018,8 @@ ClientEnvironment::~ClientEnvironment()
delete i->second;
}
- for(std::list<ClientSimpleObject*>::iterator
- i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
- {
+ for(std::vector<ClientSimpleObject*>::iterator
+ i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) {
delete *i;
}
@@ -2052,16 +2044,16 @@ void ClientEnvironment::addPlayer(Player *player)
It is a failure if player is local and there already is a local
player
*/
- assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
+ FATAL_ERROR_IF(player->isLocal() == true && getLocalPlayer() != NULL,
+ "Player is local but there is already a local player");
Environment::addPlayer(player);
}
LocalPlayer * ClientEnvironment::getLocalPlayer()
{
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ for(std::vector<Player*>::iterator i = m_players.begin();
+ i != m_players.end(); ++i) {
Player *player = *i;
if(player->isLocal())
return (LocalPlayer*)player;
@@ -2084,15 +2076,15 @@ void ClientEnvironment::step(float dtime)
LocalPlayer *lplayer = getLocalPlayer();
assert(lplayer);
// collision info queue
- std::list<CollisionInfo> player_collisions;
-
+ std::vector<CollisionInfo> player_collisions;
+
/*
Get the speed the player is going
*/
bool is_climbing = lplayer->is_climbing;
-
+
f32 player_speed = lplayer->getSpeed().getLength();
-
+
/*
Maximum position increment
*/
@@ -2104,15 +2096,15 @@ void ClientEnvironment::step(float dtime)
f32 dtime_max_increment = 1;
if(player_speed > 0.001)
dtime_max_increment = position_max_increment / player_speed;
-
+
// Maximum time increment is 10ms or lower
if(dtime_max_increment > 0.01)
dtime_max_increment = 0.01;
-
+
// Don't allow overly huge dtime
if(dtime > 0.5)
dtime = 0.5;
-
+
f32 dtime_downcount = dtime;
/*
@@ -2140,11 +2132,11 @@ void ClientEnvironment::step(float dtime)
*/
dtime_downcount = 0;
}
-
+
/*
Handle local player
*/
-
+
{
// Apply physics
if(free_move == false && is_climbing == false)
@@ -2170,18 +2162,9 @@ void ClientEnvironment::step(float dtime)
if(dl > lplayer->movement_liquid_fluidity_smooth)
dl = lplayer->movement_liquid_fluidity_smooth;
dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
-
+
v3f d = d_wanted.normalize() * dl;
speed += d;
-
-#if 0 // old code
- if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
- if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
- if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
- if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
- if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
- if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
-#endif
}
lplayer->setSpeed(speed);
@@ -2196,13 +2179,11 @@ void ClientEnvironment::step(float dtime)
}
}
while(dtime_downcount > 0.001);
-
+
//std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
-
- for(std::list<CollisionInfo>::iterator
- i = player_collisions.begin();
- i != player_collisions.end(); ++i)
- {
+
+ for(std::vector<CollisionInfo>::iterator i = player_collisions.begin();
+ i != player_collisions.end(); ++i) {
CollisionInfo &info = *i;
v3f speed_diff = info.new_speed - info.old_speed;;
// Handle only fall damage
@@ -2235,14 +2216,14 @@ void ClientEnvironment::step(float dtime)
}
}
}
-
+
/*
A quick draft of lava damage
*/
if(m_lava_hurt_interval.step(dtime, 1.0))
{
v3f pf = lplayer->getPosition();
-
+
// Feet, middle and head
v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
MapNode n1 = m_map->getNodeNoEx(p1);
@@ -2258,7 +2239,7 @@ void ClientEnvironment::step(float dtime)
m_gamedef->ndef()->get(n2).damage_per_second);
damage_per_second = MYMAX(damage_per_second,
m_gamedef->ndef()->get(n3).damage_per_second);
-
+
if(damage_per_second != 0)
{
damageLocalPlayer(damage_per_second, true);
@@ -2317,16 +2298,14 @@ void ClientEnvironment::step(float dtime)
/*
Stuff that can be done in an arbitarily large dtime
*/
- for(std::list<Player*>::iterator i = m_players.begin();
- i != m_players.end(); ++i)
- {
+ for(std::vector<Player*>::iterator i = m_players.begin();
+ i != m_players.end(); ++i) {
Player *player = *i;
-
+
/*
Handle non-local players
*/
- if(player->isLocal() == false)
- {
+ if(player->isLocal() == false) {
// Move
player->move(dtime, this, 100*BS);
@@ -2354,7 +2333,7 @@ void ClientEnvironment::step(float dtime)
/*
Step active objects and update lighting of them
*/
-
+
g_profiler->avg("CEnv: num of objects", m_active_objects.size());
bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
for(std::map<u16, ClientActiveObject*>::iterator
@@ -2387,25 +2366,36 @@ void ClientEnvironment::step(float dtime)
Step and handle simple objects
*/
g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
- for(std::list<ClientSimpleObject*>::iterator
- i = m_simple_objects.begin(); i != m_simple_objects.end();)
- {
- ClientSimpleObject *simple = *i;
- std::list<ClientSimpleObject*>::iterator cur = i;
- ++i;
+ for(std::vector<ClientSimpleObject*>::iterator
+ i = m_simple_objects.begin(); i != m_simple_objects.end();) {
+ std::vector<ClientSimpleObject*>::iterator cur = i;
+ ClientSimpleObject *simple = *cur;
+
simple->step(dtime);
- if(simple->m_to_be_removed){
+ if(simple->m_to_be_removed) {
delete simple;
- m_simple_objects.erase(cur);
+ i = m_simple_objects.erase(cur);
+ }
+ else {
+ ++i;
}
}
}
-
+
void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
{
m_simple_objects.push_back(simple);
}
+GenericCAO* ClientEnvironment::getGenericCAO(u16 id)
+{
+ ClientActiveObject *obj = getActiveObject(id);
+ if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC)
+ return (GenericCAO*) obj;
+ else
+ return NULL;
+}
+
ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
{
std::map<u16, ClientActiveObject*>::iterator n;
@@ -2435,7 +2425,7 @@ u16 getFreeClientActiveObjectId(
last_used_id ++;
if(isFreeClientActiveObjectId(last_used_id, objects))
return last_used_id;
-
+
if(last_used_id == startid)
return 0;
}
@@ -2443,7 +2433,7 @@ u16 getFreeClientActiveObjectId(
u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
{
- assert(object);
+ assert(object); // Pre-condition
if(object->getId() == 0)
{
u16 new_id = getFreeClientActiveObjectId(m_active_objects);
@@ -2488,7 +2478,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
const std::string &init_data)
{
ClientActiveObject* obj =
- ClientActiveObject::create(type, m_gamedef, this);
+ ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this);
if(obj == NULL)
{
infostream<<"ClientEnvironment::addActiveObject(): "
@@ -2496,7 +2486,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type,
<<std::endl;
return;
}
-
+
obj->setId(id);
try
@@ -2532,28 +2522,23 @@ void ClientEnvironment::removeActiveObject(u16 id)
m_active_objects.erase(id);
}
-void ClientEnvironment::processActiveObjectMessage(u16 id,
- const std::string &data)
+void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data)
{
- ClientActiveObject* obj = getActiveObject(id);
- if(obj == NULL)
- {
- infostream<<"ClientEnvironment::processActiveObjectMessage():"
- <<" got message for id="<<id<<", which doesn't exist."
- <<std::endl;
+ ClientActiveObject *obj = getActiveObject(id);
+ if (obj == NULL) {
+ infostream << "ClientEnvironment::processActiveObjectMessage():"
+ << " got message for id=" << id << ", which doesn't exist."
+ << std::endl;
return;
}
- try
- {
+
+ try {
obj->processMessage(data);
- }
- catch(SerializationError &e)
- {
+ } catch (SerializationError &e) {
errorstream<<"ClientEnvironment::processActiveObjectMessage():"
- <<" id="<<id<<" type="<<obj->getType()
- <<" SerializationError in processMessage(),"
- <<" message="<<serializeJsonString(data)
- <<std::endl;
+ << " id=" << id << " type=" << obj->getType()
+ << " SerializationError in processMessage(): " << e.what()
+ << std::endl;
}
}
@@ -2565,11 +2550,9 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
{
LocalPlayer *lplayer = getLocalPlayer();
assert(lplayer);
-
- if(handle_hp){
- if (lplayer->hp == 0) // Don't damage a dead player
- return;
- if(lplayer->hp > damage)
+
+ if (handle_hp) {
+ if (lplayer->hp > damage)
lplayer->hp -= damage;
else
lplayer->hp = 0;
@@ -2593,7 +2576,7 @@ void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
/*
Client likes to call these
*/
-
+
void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
std::vector<DistanceSortedActiveObject> &dest)
{
diff --git a/src/environment.h b/src/environment.h
index 13c0c0efe..c70694316 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <set>
#include <list>
+#include <queue>
#include <map>
#include "irr_v3d.h"
#include "activeobject.h"
@@ -39,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapnode.h"
#include "mapblock.h"
#include "jthread/jmutex.h"
+#include "network/networkprotocol.h" // for AccessDeniedCode
class ServerEnvironment;
class ActiveBlockModifier;
@@ -75,28 +77,19 @@ public:
Player * getPlayer(const char *name);
Player * getRandomConnectedPlayer();
Player * getNearestConnectedPlayer(v3f pos);
- std::list<Player*> getPlayers();
- std::list<Player*> getPlayers(bool ignore_disconnected);
+ std::vector<Player*> getPlayers();
+ std::vector<Player*> getPlayers(bool ignore_disconnected);
u32 getDayNightRatio();
// 0-23999
- virtual void setTimeOfDay(u32 time)
- {
- m_time_of_day = time;
- m_time_of_day_f = (float)time / 24000.0;
- }
-
- u32 getTimeOfDay()
- { return m_time_of_day; }
-
- float getTimeOfDayF()
- { return m_time_of_day_f; }
+ virtual void setTimeOfDay(u32 time);
+ u32 getTimeOfDay();
+ float getTimeOfDayF();
void stepTimeOfDay(float dtime);
void setTimeOfDaySpeed(float speed);
-
float getTimeOfDaySpeed();
void setDayNightRatioOverride(bool enable, u32 value)
@@ -110,7 +103,7 @@ public:
protected:
// peer_ids in here should be unique, except that there may be many 0s
- std::list<Player*> m_players;
+ std::vector<Player*> m_players;
// Time of day in milli-hours (0-23999); determines day and night
u32 m_time_of_day;
// Time of day in 0...1
@@ -134,7 +127,8 @@ protected:
bool m_cache_enable_shaders;
private:
- JMutex m_lock;
+ JMutex m_timeofday_lock;
+ JMutex m_time_lock;
};
@@ -182,7 +176,7 @@ struct ABMWithState
class ActiveBlockList
{
public:
- void update(std::list<v3s16> &active_positions,
+ void update(std::vector<v3s16> &active_positions,
s16 radius,
std::set<v3s16> &blocks_removed,
std::set<v3s16> &blocks_added);
@@ -228,6 +222,8 @@ public:
float getSendRecommendedInterval()
{ return m_recommended_send_interval; }
+ void kickAllPlayers(AccessDeniedCode reason,
+ const std::string &str_reason, bool reconnect);
// Save players
void saveLoadedPlayers();
void savePlayer(const std::string &playername);
@@ -313,7 +309,7 @@ public:
bool swapNode(v3s16 p, const MapNode &n);
// Find all active objects inside a radius around a point
- std::set<u16> getObjectsInsideRadius(v3f pos, float radius);
+ void getObjectsInsideRadius(std::vector<u16> &objects, v3f pos, float radius);
// Clear all objects, loading and going through every MapBlock
void clearAllObjects();
@@ -331,6 +327,11 @@ public:
std::set<v3s16>* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; };
+ // Sets the static object status all the active objects in the specified block
+ // This is only really needed for deleting blocks from the map
+ void setStaticForActiveObjectsInBlock(v3s16 blockpos,
+ bool static_exists, v3s16 static_block=v3s16(0,0,0));
+
private:
/*
@@ -386,7 +387,7 @@ private:
// Active object list
std::map<u16, ServerActiveObject*> m_active_objects;
// Outgoing network message buffer for active objects
- std::list<ActiveObjectMessage> m_active_object_messages;
+ std::queue<ActiveObjectMessage> m_active_object_messages;
// Some timers
float m_send_recommended_timer;
IntervalLimiter m_object_management_interval;
@@ -401,7 +402,7 @@ private:
u32 m_game_time;
// A helper variable for incrementing the latter
float m_game_time_fraction_counter;
- std::list<ABMWithState> m_abms;
+ std::vector<ABMWithState> m_abms;
// An interval for generally sending object positions and stuff
float m_recommended_send_interval;
// Estimate for general maximum lag as determined by server.
@@ -412,6 +413,8 @@ private:
#ifndef SERVER
#include "clientobject.h"
+#include "content_cao.h"
+
class ClientSimpleObject;
/*
@@ -474,6 +477,7 @@ public:
ActiveObjects
*/
+ GenericCAO* getGenericCAO(u16 id);
ClientActiveObject* getActiveObject(u16 id);
/*
@@ -509,7 +513,7 @@ public:
// Get event from queue. CEE_NONE is returned if queue is empty.
ClientEnvEvent getClientEvent();
- u16 m_attachements[USHRT_MAX];
+ u16 attachement_parent_ids[USHRT_MAX + 1];
std::list<std::string> getPlayerNames()
{ return m_player_names; }
@@ -529,7 +533,7 @@ private:
IGameDef *m_gamedef;
IrrlichtDevice *m_irr;
std::map<u16, ClientActiveObject*> m_active_objects;
- std::list<ClientSimpleObject*> m_simple_objects;
+ std::vector<ClientSimpleObject*> m_simple_objects;
std::list<ClientEnvEvent> m_client_event_queue;
IntervalLimiter m_active_object_light_update_interval;
IntervalLimiter m_lava_hurt_interval;
diff --git a/src/exceptions.h b/src/exceptions.h
index 5b716c2ca..6bf832828 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -70,6 +70,11 @@ public:
SerializationError(const std::string &s): BaseException(s) {}
};
+class PacketError : public BaseException {
+public:
+ PacketError(const std::string &s): BaseException(s) {}
+};
+
class LoadError : public BaseException {
public:
LoadError(const std::string &s): BaseException(s) {}
@@ -115,6 +120,11 @@ public:
ClientStateError(std::string s): BaseException(s) {}
};
+class PrngException : public BaseException {
+public:
+ PrngException(std::string s): BaseException(s) {}
+};
+
/*
Some "old-style" interrupts:
*/
diff --git a/src/filecache.cpp b/src/filecache.cpp
index 33677cc83..f1694d8d5 100644
--- a/src/filecache.cpp
+++ b/src/filecache.cpp
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filecache.h"
-#include "clientserver.h"
+#include "network/networkprotocol.h"
#include "log.h"
#include "filesys.h"
#include <string>
diff --git a/src/filesys.cpp b/src/filesys.cpp
index 784715617..4cefdb807 100644
--- a/src/filesys.cpp
+++ b/src/filesys.cpp
@@ -34,8 +34,9 @@ namespace fs
#define _WIN32_WINNT 0x0501
#include <windows.h>
+#include <shlwapi.h>
-std::vector<DirListNode> GetDirListing(std::string pathstring)
+std::vector<DirListNode> GetDirListing(const std::string &pathstring)
{
std::vector<DirListNode> listing;
@@ -44,7 +45,7 @@ std::vector<DirListNode> GetDirListing(std::string pathstring)
DWORD dwError;
std::string dirSpec = pathstring + "\\*";
-
+
// Find the first file in the directory.
hFind = FindFirstFile(dirSpec.c_str(), &FindFileData);
@@ -86,7 +87,7 @@ std::vector<DirListNode> GetDirListing(std::string pathstring)
return listing;
}
-bool CreateDir(std::string path)
+bool CreateDir(const std::string &path)
{
bool r = CreateDirectory(path.c_str(), NULL);
if(r == true)
@@ -96,12 +97,17 @@ bool CreateDir(std::string path)
return false;
}
-bool PathExists(std::string path)
+bool PathExists(const std::string &path)
{
return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
}
-bool IsDir(std::string path)
+bool IsPathAbsolute(const std::string &path)
+{
+ return !PathIsRelative(path.c_str());
+}
+
+bool IsDir(const std::string &path)
{
DWORD attr = GetFileAttributes(path.c_str());
return (attr != INVALID_FILE_ATTRIBUTES &&
@@ -113,7 +119,7 @@ bool IsDirDelimiter(char c)
return c == '/' || c == '\\';
}
-bool RecursiveDelete(std::string path)
+bool RecursiveDelete(const std::string &path)
{
infostream<<"Recursively deleting \""<<path<<"\""<<std::endl;
@@ -136,7 +142,7 @@ bool RecursiveDelete(std::string path)
infostream<<"RecursiveDelete: Deleting content of directory "
<<path<<std::endl;
std::vector<DirListNode> content = GetDirListing(path);
- for(int i=0; i<content.size(); i++){
+ for(size_t i=0; i<content.size(); i++){
const DirListNode &n = content[i];
std::string fullpath = path + DIR_DELIM + n.name;
bool did = RecursiveDelete(fullpath);
@@ -158,7 +164,7 @@ bool RecursiveDelete(std::string path)
return true;
}
-bool DeleteSingleFileOrEmptyDirectory(std::string path)
+bool DeleteSingleFileOrEmptyDirectory(const std::string &path)
{
DWORD attr = GetFileAttributes(path.c_str());
bool is_directory = (attr != INVALID_FILE_ATTRIBUTES &&
@@ -177,7 +183,7 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path)
std::string TempPath()
{
- DWORD bufsize = GetTempPath(0, "");
+ DWORD bufsize = GetTempPath(0, NULL);
if(bufsize == 0){
errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl;
return "";
@@ -199,7 +205,7 @@ std::string TempPath()
#include <sys/wait.h>
#include <unistd.h>
-std::vector<DirListNode> GetDirListing(std::string pathstring)
+std::vector<DirListNode> GetDirListing(const std::string &pathstring)
{
std::vector<DirListNode> listing;
@@ -252,7 +258,7 @@ std::vector<DirListNode> GetDirListing(std::string pathstring)
return listing;
}
-bool CreateDir(std::string path)
+bool CreateDir(const std::string &path)
{
int r = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if(r == 0)
@@ -268,13 +274,18 @@ bool CreateDir(std::string path)
}
}
-bool PathExists(std::string path)
+bool PathExists(const std::string &path)
{
struct stat st;
return (stat(path.c_str(),&st) == 0);
}
-bool IsDir(std::string path)
+bool IsPathAbsolute(const std::string &path)
+{
+ return path[0] == '/';
+}
+
+bool IsDir(const std::string &path)
{
struct stat statbuf;
if(stat(path.c_str(), &statbuf))
@@ -287,16 +298,16 @@ bool IsDirDelimiter(char c)
return c == '/';
}
-bool RecursiveDelete(std::string path)
+bool RecursiveDelete(const std::string &path)
{
/*
Execute the 'rm' command directly, by fork() and execve()
*/
-
+
infostream<<"Removing \""<<path<<"\""<<std::endl;
//return false;
-
+
pid_t child_pid = fork();
if(child_pid == 0)
@@ -314,9 +325,9 @@ bool RecursiveDelete(std::string path)
verbosestream<<"Executing '"<<argv[0]<<"' '"<<argv[1]<<"' '"
<<argv[2]<<"'"<<std::endl;
-
+
execv(argv[0], argv);
-
+
// Execv shouldn't return. Failed.
_exit(1);
}
@@ -333,7 +344,7 @@ bool RecursiveDelete(std::string path)
}
}
-bool DeleteSingleFileOrEmptyDirectory(std::string path)
+bool DeleteSingleFileOrEmptyDirectory(const std::string &path)
{
if(IsDir(path)){
bool did = (rmdir(path.c_str()) == 0);
@@ -370,7 +381,7 @@ std::string TempPath()
#endif
-void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst)
+void GetRecursiveSubPaths(const std::string &path, std::vector<std::string> &dst)
{
std::vector<DirListNode> content = GetDirListing(path);
for(unsigned int i=0; i<content.size(); i++){
@@ -398,7 +409,7 @@ bool DeletePaths(const std::vector<std::string> &paths)
return success;
}
-bool RecursiveDeleteContent(std::string path)
+bool RecursiveDeleteContent(const std::string &path)
{
infostream<<"Removing content of \""<<path<<"\""<<std::endl;
std::vector<DirListNode> list = GetDirListing(path);
@@ -417,7 +428,7 @@ bool RecursiveDeleteContent(std::string path)
return true;
}
-bool CreateAllDirs(std::string path)
+bool CreateAllDirs(const std::string &path)
{
std::vector<std::string> tocreate;
@@ -435,7 +446,7 @@ bool CreateAllDirs(std::string path)
return true;
}
-bool CopyFileContents(std::string source, std::string target)
+bool CopyFileContents(const std::string &source, const std::string &target)
{
FILE *sourcefile = fopen(source.c_str(), "rb");
if(sourcefile == NULL){
@@ -489,7 +500,7 @@ bool CopyFileContents(std::string source, std::string target)
return retval;
}
-bool CopyDir(std::string source, std::string target)
+bool CopyDir(const std::string &source, const std::string &target)
{
if(PathExists(source)){
if(!PathExists(target)){
@@ -519,7 +530,7 @@ bool CopyDir(std::string source, std::string target)
}
}
-bool PathStartsWith(std::string path, std::string prefix)
+bool PathStartsWith(const std::string &path, const std::string &prefix)
{
size_t pathsize = path.size();
size_t pathpos = 0;
@@ -569,7 +580,7 @@ bool PathStartsWith(std::string path, std::string prefix)
}
}
-std::string RemoveLastPathComponent(std::string path,
+std::string RemoveLastPathComponent(const std::string &path,
std::string *removed, int count)
{
if(removed)
@@ -651,6 +662,25 @@ std::string RemoveRelativePathComponents(std::string path)
return path.substr(0, pos);
}
+std::string AbsolutePath(const std::string &path)
+{
+#ifdef _WIN32
+ char *abs_path = _fullpath(NULL, path.c_str(), MAX_PATH);
+#else
+ char *abs_path = realpath(path.c_str(), NULL);
+#endif
+ if (!abs_path) return "";
+ std::string abs_path_str(abs_path);
+ free(abs_path);
+ return abs_path_str;
+}
+
+const char *GetFilenameFromPath(const char *path)
+{
+ const char *filename = strrchr(path, DIR_DELIM_CHAR);
+ return filename ? filename + 1 : path;
+}
+
bool safeWriteToFile(const std::string &path, const std::string &content)
{
std::string tmp_file = path + ".~mt";
diff --git a/src/filesys.h b/src/filesys.h
index 1b3659afe..19fcbb673 100644
--- a/src/filesys.h
+++ b/src/filesys.h
@@ -26,9 +26,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifdef _WIN32 // WINDOWS
#define DIR_DELIM "\\"
+#define DIR_DELIM_CHAR '\\'
#define FILESYS_CASE_INSENSITIVE 1
#else // POSIX
#define DIR_DELIM "/"
+#define DIR_DELIM_CHAR '/'
#define FILESYS_CASE_INSENSITIVE 0
#endif
@@ -40,22 +42,25 @@ struct DirListNode
std::string name;
bool dir;
};
-std::vector<DirListNode> GetDirListing(std::string path);
+
+std::vector<DirListNode> GetDirListing(const std::string &path);
// Returns true if already exists
-bool CreateDir(std::string path);
+bool CreateDir(const std::string &path);
+
+bool PathExists(const std::string &path);
-bool PathExists(std::string path);
+bool IsPathAbsolute(const std::string &path);
-bool IsDir(std::string path);
+bool IsDir(const std::string &path);
bool IsDirDelimiter(char c);
// Only pass full paths to this one. True on success.
// NOTE: The WIN32 version returns always true.
-bool RecursiveDelete(std::string path);
+bool RecursiveDelete(const std::string &path);
-bool DeleteSingleFileOrEmptyDirectory(std::string path);
+bool DeleteSingleFileOrEmptyDirectory(const std::string &path);
// Returns path to temp directory, can return "" on error
std::string TempPath();
@@ -63,34 +68,34 @@ std::string TempPath();
/* Multiplatform */
// The path itself not included
-void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst);
+void GetRecursiveSubPaths(const std::string &path, std::vector<std::string> &dst);
// Tries to delete all, returns false if any failed
bool DeletePaths(const std::vector<std::string> &paths);
// Only pass full paths to this one. True on success.
-bool RecursiveDeleteContent(std::string path);
+bool RecursiveDeleteContent(const std::string &path);
// Create all directories on the given path that don't already exist.
-bool CreateAllDirs(std::string path);
+bool CreateAllDirs(const std::string &path);
// Copy a regular file
-bool CopyFileContents(std::string source, std::string target);
+bool CopyFileContents(const std::string &source, const std::string &target);
// Copy directory and all subdirectories
// Omits files and subdirectories that start with a period
-bool CopyDir(std::string source, std::string target);
+bool CopyDir(const std::string &source, const std::string &target);
// Check if one path is prefix of another
// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2"
// Ignores case differences and '/' vs. '\\' on Windows
-bool PathStartsWith(std::string path, std::string prefix);
+bool PathStartsWith(const std::string &path, const std::string &prefix);
// Remove last path component and the dir delimiter before and/or after it,
// returns "" if there is only one path component.
// removed: If non-NULL, receives the removed component(s).
// count: Number of components to remove
-std::string RemoveLastPathComponent(std::string path,
+std::string RemoveLastPathComponent(const std::string &path,
std::string *removed = NULL, int count = 1);
// Remove "." and ".." path components and for every ".." removed, remove
@@ -98,9 +103,17 @@ std::string RemoveLastPathComponent(std::string path,
// this does not resolve symlinks and check for existence of directories.
std::string RemoveRelativePathComponents(std::string path);
+// Returns the absolute path for the passed path, with "." and ".." path
+// components and symlinks removed. Returns "" on error.
+std::string AbsolutePath(const std::string &path);
+
+// Returns the filename from a path or the entire path if no directory
+// delimiter is found.
+const char *GetFilenameFromPath(const char *path);
+
bool safeWriteToFile(const std::string &path, const std::string &content);
-}//fs
+} // namespace fs
#endif
diff --git a/src/fontengine.cpp b/src/fontengine.cpp
index 3b82a3c47..f881701e0 100644
--- a/src/fontengine.cpp
+++ b/src/fontengine.cpp
@@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "fontengine.h"
#include "log.h"
-#include "main.h"
#include "config.h"
#include "porting.h"
#include "constants.h"
@@ -36,7 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
FontEngine* g_fontengine = NULL;
/** callback to be used on change of font size setting */
-static void font_setting_changed(const std::string, void *userdata) {
+static void font_setting_changed(const std::string &name, void *userdata)
+{
g_fontengine->readSettings();
}
@@ -55,9 +55,9 @@ FontEngine::FontEngine(Settings* main_settings, gui::IGUIEnvironment* env) :
m_default_size[i] = (FontMode) FONT_SIZE_UNSPECIFIED;
}
- assert(m_settings != NULL);
- assert(m_env != NULL);
- assert(m_env->getSkin() != NULL);
+ assert(m_settings != NULL); // pre-condition
+ assert(m_env != NULL); // pre-condition
+ assert(m_env->getSkin() != NULL); // pre-condition
m_currentMode = FM_Simple;
@@ -172,7 +172,7 @@ unsigned int FontEngine::getTextHeight(unsigned int font_size, FontMode mode)
if (font == NULL) {
font = m_env->getSkin()->getFont();
}
- assert(font != NULL);
+ FATAL_ERROR_IF(font == NULL, "Could not get skin font");
return font->getDimension(L"Some unimportant example String").Height;
}
@@ -187,7 +187,7 @@ unsigned int FontEngine::getTextWidth(const std::wstring& text,
if (font == NULL) {
font = m_env->getSkin()->getFont();
}
- assert(font != NULL);
+ FATAL_ERROR_IF(font == NULL, "Could not get font");
return font->getDimension(text.c_str()).Width;
}
@@ -202,7 +202,7 @@ unsigned int FontEngine::getLineHeight(unsigned int font_size, FontMode mode)
if (font == NULL) {
font = m_env->getSkin()->getFont();
}
- assert(font != NULL);
+ FATAL_ERROR_IF(font == NULL, "Could not get font");
return font->getDimension(L"Some unimportant example String").Height
+ font->getKerningHeight();
@@ -255,7 +255,7 @@ void FontEngine::updateSkin()
// If we did fail to create a font our own make irrlicht find a default one
font = m_env->getSkin()->getFont();
- assert(font);
+ FATAL_ERROR_IF(font == NULL, "Could not create/get font");
u32 text_height = font->getDimension(L"Hello, world!").Height;
infostream << "text_height=" << text_height << std::endl;
@@ -355,7 +355,7 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode)
/** initialize a font without freetype */
void FontEngine::initSimpleFont(unsigned int basesize, FontMode mode)
{
- assert((mode == FM_Simple) || (mode == FM_SimpleMono));
+ assert(mode == FM_Simple || mode == FM_SimpleMono); // pre-condition
std::string font_path = "";
if (mode == FM_Simple) {
diff --git a/src/fontengine.h b/src/fontengine.h
index 9edde05f8..9f7266775 100644
--- a/src/fontengine.h
+++ b/src/fontengine.h
@@ -59,7 +59,7 @@ public:
unsigned int font_size=FONT_SIZE_UNSPECIFIED,
FontMode mode=FM_Unspecified)
{
- return getTextWidth(narrow_to_wide(text));
+ return getTextWidth(utf8_to_wide(text));
}
/** get text width if a text for a specific font */
diff --git a/src/game.cpp b/src/game.cpp
index 7fb99ef3e..11e868a80 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -18,64 +18,60 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "game.h"
-#include "irrlichttypes_extrabloated.h"
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IMaterialRendererServices.h>
-#include "IMeshCache.h"
+
+#include <iomanip>
+#include "camera.h"
#include "client.h"
-#include "server.h"
-#include "guiPasswordChange.h"
-#include "guiVolumeChange.h"
-#include "guiKeyChangeMenu.h"
-#include "guiFormSpecMenu.h"
-#include "tool.h"
-#include "guiChatConsole.h"
-#include "config.h"
-#include "version.h"
+#include "client/tile.h" // For TextureSource
+#include "clientmap.h"
#include "clouds.h"
-#include "particles.h"
-#include "camera.h"
-#include "mapblock.h"
-#include "settings.h"
-#include "profiler.h"
-#include "mainmenumanager.h"
-#include "gettext.h"
+#include "config.h"
+#include "content_cao.h"
+#include "drawscene.h"
+#include "event_manager.h"
+#include "fontengine.h"
+#include "itemdef.h"
#include "log.h"
#include "filesys.h"
-// Needed for determining pointing to nodes
-#include "nodedef.h"
-#include "nodemetadata.h"
-#include "main.h" // For g_settings
-#include "itemdef.h"
-#include "tile.h" // For TextureSource
-#include "shader.h" // For ShaderSource
+#include "gettext.h"
+#include "guiChatConsole.h"
+#include "guiFormSpecMenu.h"
+#include "guiKeyChangeMenu.h"
+#include "guiPasswordChange.h"
+#include "guiVolumeChange.h"
+#include "hud.h"
#include "logoutputbuffer.h"
-#include "subgame.h"
+#include "mainmenumanager.h"
+#include "mapblock.h"
+#include "nodedef.h" // Needed for determining pointing to nodes
+#include "nodemetadata.h"
+#include "particles.h"
+#include "profiler.h"
#include "quicktune_shortcutter.h"
-#include "clientmap.h"
-#include "hud.h"
+#include "server.h"
+#include "settings.h"
+#include "shader.h" // For ShaderSource
#include "sky.h"
+#include "subgame.h"
+#include "tool.h"
+#include "util/directiontables.h"
+#include "util/pointedthing.h"
+#include "version.h"
+#include "minimap.h"
+
#include "sound.h"
+
#if USE_SOUND
-#include "sound_openal.h"
+ #include "sound_openal.h"
#endif
-#include "event_manager.h"
-#include <iomanip>
-#include <list>
-#include "util/directiontables.h"
-#include "util/pointedthing.h"
-#include "drawscene.h"
-#include "content_cao.h"
-#include "fontengine.h"
#ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
+ #include "touchscreengui.h"
#endif
+extern Settings *g_settings;
+extern Profiler *g_profiler;
+
/*
Text input system
*/
@@ -89,14 +85,14 @@ struct TextDestNodeMetadata : public TextDest {
// This is deprecated I guess? -celeron55
void gotText(std::wstring text)
{
- std::string ntext = wide_to_narrow(text);
+ std::string ntext = wide_to_utf8(text);
infostream << "Submitting 'text' field of node at (" << m_p.X << ","
<< m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
- std::map<std::string, std::string> fields;
+ StringMap fields;
fields["text"] = ntext;
m_client->sendNodemetaFields(m_p, "", fields);
}
- void gotText(std::map<std::string, std::string> fields)
+ void gotText(const StringMap &fields)
{
m_client->sendNodemetaFields(m_p, "", fields);
}
@@ -116,7 +112,7 @@ struct TextDestPlayerInventory : public TextDest {
m_client = client;
m_formname = formname;
}
- void gotText(std::map<std::string, std::string> fields)
+ void gotText(const StringMap &fields)
{
m_client->sendInventoryFields(m_formname, fields);
}
@@ -143,7 +139,7 @@ struct LocalFormspecHandler : public TextDest {
errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
}
- void gotText(std::map<std::string, std::string> fields)
+ void gotText(const StringMap &fields)
{
if (m_formname == "MT_PAUSE_MENU") {
if (fields.find("btn_sound") != fields.end()) {
@@ -185,9 +181,9 @@ struct LocalFormspecHandler : public TextDest {
if ((fields.find("btn_send") != fields.end()) ||
(fields.find("quit") != fields.end())) {
- if (fields.find("f_text") != fields.end()) {
- m_client->typeChatMessage(narrow_to_wide(fields["f_text"]));
- }
+ StringMap::const_iterator it = fields.find("f_text");
+ if (it != fields.end())
+ m_client->typeChatMessage(utf8_to_wide(it->second));
return;
}
@@ -215,12 +211,14 @@ struct LocalFormspecHandler : public TextDest {
return;
}
- errorstream << "LocalFormspecHandler::gotText unhandled >" << m_formname << "< event" << std::endl;
- int i = 0;
+ errorstream << "LocalFormspecHandler::gotText unhandled >"
+ << m_formname << "< event" << std::endl;
- for (std::map<std::string, std::string>::iterator iter = fields.begin();
- iter != fields.end(); iter++) {
- errorstream << "\t" << i << ": " << iter->first << "=" << iter->second << std::endl;
+ int i = 0;
+ StringMap::const_iterator it;
+ for (it = fields.begin(); it != fields.end(); ++it) {
+ errorstream << "\t" << i << ": " << it->first
+ << "=" << it->second << std::endl;
i++;
}
}
@@ -447,7 +445,7 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
std::ostringstream os(std::ios_base::binary);
g_profiler->printPage(os, show_profiler, show_profiler_max);
- std::wstring text = narrow_to_wide(os.str());
+ std::wstring text = utf8_to_wide(os.str());
guitext_profiler->setText(text.c_str());
guitext_profiler->setVisible(true);
@@ -492,7 +490,7 @@ private:
color(color)
{}
};
- std::list<Piece> m_log;
+ std::vector<Piece> m_log;
public:
u32 m_log_max_size;
@@ -515,7 +513,7 @@ public:
{
std::map<std::string, Meta> m_meta;
- for (std::list<Piece>::const_iterator k = m_log.begin();
+ for (std::vector<Piece>::const_iterator k = m_log.begin();
k != m_log.end(); k++) {
const Piece &piece = *k;
@@ -565,16 +563,6 @@ public:
s32 graphh = 50;
s32 textx = x_left + m_log_max_size + 15;
s32 textx2 = textx + 200 - 15;
-
- // Draw background
- /*{
- u32 num_graphs = m_meta.size();
- core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
- textx2, y_bottom);
- video::SColor bgcolor(120,0,0,0);
- driver->draw2DRectangle(bgcolor, rect, NULL);
- }*/
-
s32 meta_i = 0;
for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
@@ -594,16 +582,16 @@ public:
s32 texth = 15;
char buf[10];
snprintf(buf, 10, "%.3g", show_max);
- font->draw(narrow_to_wide(buf).c_str(),
+ font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - graphh,
textx2, y - graphh + texth),
meta.color);
snprintf(buf, 10, "%.3g", show_min);
- font->draw(narrow_to_wide(buf).c_str(),
+ font->draw(utf8_to_wide(buf).c_str(),
core::rect<s32>(textx, y - texth,
textx2, y),
meta.color);
- font->draw(narrow_to_wide(id).c_str(),
+ font->draw(utf8_to_wide(id).c_str(),
core::rect<s32>(textx, y - graphh / 2 - texth / 2,
textx2, y - graphh / 2 + texth / 2),
meta.color);
@@ -613,7 +601,7 @@ public:
float lastscaledvalue = 0.0;
bool lastscaledvalue_exists = false;
- for (std::list<Piece>::const_iterator j = m_log.begin();
+ for (std::vector<Piece>::const_iterator j = m_log.begin();
j != m_log.end(); j++) {
const Piece &piece = *j;
float value = 0;
@@ -818,7 +806,7 @@ public:
m_fogEnabled = g_settings->getBool("enable_fog");
}
- static void SettingsCallback(const std::string name, void *userdata)
+ static void SettingsCallback(const std::string &name, void *userdata)
{
reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
}
@@ -879,6 +867,9 @@ public:
services->setPixelShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
services->setVertexShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
+ v3f minimap_yaw_vec = m_client->getMapper()->getYawVec();
+ services->setPixelShaderConstant("yawVec", (irr::f32 *)&minimap_yaw_vec, 3);
+
// Uniform sampler layers
int layer0 = 0;
int layer1 = 1;
@@ -887,11 +878,11 @@ public:
#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1);
services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1);
- services->setPixelShaderConstant("useNormalmap" , (irr::f32 *)&layer2, 1);
+ services->setPixelShaderConstant("textureFlags" , (irr::f32 *)&layer2, 1);
#else
services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1);
services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1);
- services->setPixelShaderConstant("useNormalmap" , (irr::s32 *)&layer2, 1);
+ services->setPixelShaderConstant("textureFlags" , (irr::s32 *)&layer2, 1);
#endif
}
};
@@ -1058,7 +1049,7 @@ static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
FORMSPEC_VERSION_STRING
SIZE_TAG
"field[3,2.35;6,0.5;f_text;;" + text + "]"
- "button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
+ "button_exit[4,3;3,0.5;btn_send;" + wide_to_utf8(wstrgettext("Proceed")) + "]"
;
/* Create menu */
@@ -1098,7 +1089,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
bool singleplayermode)
{
#ifdef __ANDROID__
- std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
+ std::string control_text = wide_to_utf8(wstrgettext("Default Controls:\n"
"No menu visible:\n"
"- single tap: button activate\n"
"- double tap: place/use\n"
@@ -1112,7 +1103,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
" --> place single item to slot\n"
));
#else
- std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
+ std::string control_text = wide_to_utf8(wstrgettext("Default Controls:\n"
"- WASD: move\n"
"- Space: jump/climb\n"
"- Shift: sneak/go down\n"
@@ -1131,26 +1122,26 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
os << FORMSPEC_VERSION_STRING << SIZE_TAG
<< "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
- << wide_to_narrow(wstrgettext("Continue")) << "]";
+ << wide_to_utf8(wstrgettext("Continue")) << "]";
if (!singleplayermode) {
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
- << wide_to_narrow(wstrgettext("Change Password")) << "]";
+ << wide_to_utf8(wstrgettext("Change Password")) << "]";
}
-
+
#ifndef __ANDROID__
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
- << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
+ << wide_to_utf8(wstrgettext("Sound Volume")) << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
- << wide_to_narrow(wstrgettext("Change Keys")) << "]";
+ << wide_to_utf8(wstrgettext("Change Keys")) << "]";
#endif
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
- << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
+ << wide_to_utf8(wstrgettext("Exit to Menu")) << "]";
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
- << wide_to_narrow(wstrgettext("Exit to OS")) << "]"
+ << wide_to_utf8(wstrgettext("Exit to OS")) << "]"
<< "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
- << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
- << minetest_build_info << "\n"
+ << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME_C "\n"
+ << g_build_info << "\n"
<< "path_user = " << wrap_rows(porting::path_user, 20)
<< "\n;]";
@@ -1161,7 +1152,8 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
- (*cur_formspec)->setFocus(L"btn_continue");
+ std::string con("btn_continue");
+ (*cur_formspec)->setFocus(con);
(*cur_formspec)->doPause = true;
}
@@ -1175,7 +1167,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
// Get new messages from error log buffer
while (!chat_log_error_buf.empty()) {
- chat_backend.addMessage(L"", narrow_to_wide(chat_log_error_buf.get()));
+ chat_backend.addMessage(L"", utf8_to_wide(chat_log_error_buf.get()));
}
// Get new messages from client
@@ -1250,9 +1242,11 @@ struct KeyCache {
KEYMAP_ID_CHAT,
KEYMAP_ID_CMD,
KEYMAP_ID_CONSOLE,
+ KEYMAP_ID_MINIMAP,
KEYMAP_ID_FREEMOVE,
KEYMAP_ID_FASTMOVE,
KEYMAP_ID_NOCLIP,
+ KEYMAP_ID_CINEMATIC,
KEYMAP_ID_SCREENSHOT,
KEYMAP_ID_TOGGLE_HUD,
KEYMAP_ID_TOGGLE_CHAT,
@@ -1298,9 +1292,11 @@ void KeyCache::populate()
key[KEYMAP_ID_CHAT] = getKeySetting("keymap_chat");
key[KEYMAP_ID_CMD] = getKeySetting("keymap_cmd");
key[KEYMAP_ID_CONSOLE] = getKeySetting("keymap_console");
+ key[KEYMAP_ID_MINIMAP] = getKeySetting("keymap_minimap");
key[KEYMAP_ID_FREEMOVE] = getKeySetting("keymap_freemove");
key[KEYMAP_ID_FASTMOVE] = getKeySetting("keymap_fastmove");
key[KEYMAP_ID_NOCLIP] = getKeySetting("keymap_noclip");
+ key[KEYMAP_ID_CINEMATIC] = getKeySetting("keymap_cinematic");
key[KEYMAP_ID_SCREENSHOT] = getKeySetting("keymap_screenshot");
key[KEYMAP_ID_TOGGLE_HUD] = getKeySetting("keymap_toggle_hud");
key[KEYMAP_ID_TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat");
@@ -1402,6 +1398,7 @@ struct VolatileRunFlags {
bool invert_mouse;
bool show_chat;
bool show_hud;
+ bool show_minimap;
bool force_fog_off;
bool show_debug;
bool show_profiler_graph;
@@ -1420,8 +1417,7 @@ struct VolatileRunFlags {
* hides most of the stuff in this class (nothing in this class is required
* by any other file) but exposes the public methods/data only.
*/
-class Game
-{
+class Game {
public:
Game();
~Game();
@@ -1436,7 +1432,8 @@ public:
// If address is "", local server is used and address is updated
std::string *address,
u16 port,
- std::wstring *error_message,
+ std::string &error_message,
+ bool *reconnect,
ChatBackend *chat_backend,
const SubgameSpec &gamespec, // Used for local game
bool simple_singleplayer_mode);
@@ -1458,9 +1455,8 @@ protected:
// Client creation
bool createClient(const std::string &playername,
- const std::string &password, std::string *address, u16 port,
- std::wstring *error_message);
- bool initGui(std::wstring *error_message);
+ const std::string &password, std::string *address, u16 port);
+ bool initGui();
// Client connection
bool connectToServer(const std::string &playername,
@@ -1470,17 +1466,17 @@ protected:
// Main loop
- void updateInteractTimers(GameRunData *args, f32 dtime);
+ void updateInteractTimers(GameRunData *runData, f32 dtime);
bool checkConnection();
bool handleCallbacks();
void processQueues();
- void updateProfilers(const GameRunData &run_data, const RunStats &stats,
+ void updateProfilers(const GameRunData &runData, const RunStats &stats,
const FpsControl &draw_times, f32 dtime);
void addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times,
f32 dtime);
void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
- void processUserInput(VolatileRunFlags *flags, GameRunData *interact_args,
+ void processUserInput(VolatileRunFlags *flags, GameRunData *runData,
f32 dtime);
void processKeyboardInput(VolatileRunFlags *flags,
float *statustext_time,
@@ -1497,9 +1493,12 @@ protected:
void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
void toggleFast(float *statustext_time);
void toggleNoClip(float *statustext_time);
+ void toggleCinematic(float *statustext_time);
void toggleChat(float *statustext_time, bool *flag);
void toggleHud(float *statustext_time, bool *flag);
+ void toggleMinimap(float *statustext_time, bool *flag, bool show_hud,
+ bool shift_pressed);
void toggleFog(float *statustext_time, bool *flag);
void toggleDebug(float *statustext_time, bool *show_debug,
bool *show_profiler_graph);
@@ -1546,6 +1545,9 @@ protected:
void showOverlayMessage(const wchar_t *msg, float dtime, int percent,
bool draw_clouds = true);
+ static void settingChangedCallback(const std::string &setting_name, void *data);
+ void readSettings();
+
private:
InputHandler *input;
@@ -1578,6 +1580,7 @@ private:
Sky *sky; // Free using ->Drop()
Inventory *local_inventory;
Hud *hud;
+ Mapper *mapper;
/* 'cache'
This class does take ownership/responsibily for cleaning up etc of any of
@@ -1587,7 +1590,8 @@ private:
video::IVideoDriver *driver;
scene::ISceneManager *smgr;
bool *kill;
- std::wstring *error_message;
+ std::string *error_message;
+ bool *reconnect_requested;
IGameDef *gamedef; // Convenience (same as *client)
scene::ISceneNode *skybox;
@@ -1615,10 +1619,7 @@ private:
IntervalLimiter profiler_interval;
- /* TODO: Add a callback function so these can be updated when a setting
- * changes. At this point in time it doesn't matter (e.g. /set
- * is documented to change server settings only)
- *
+ /*
* TODO: Local caching of settings is not optimal and should at some stage
* be updated to use a global settings object for getting thse values
* (as opposed to the this local caching). This can be addressed in
@@ -1631,6 +1632,11 @@ private:
bool m_cache_enable_fog;
f32 m_cache_mouse_sensitivity;
f32 m_repeat_right_click_time;
+
+#ifdef __ANDROID__
+ bool m_cache_hold_aux1;
+#endif
+
};
Game::Game() :
@@ -1653,17 +1659,30 @@ Game::Game() :
clouds(NULL),
sky(NULL),
local_inventory(NULL),
- hud(NULL)
+ hud(NULL),
+ mapper(NULL)
{
- m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
- m_cache_enable_node_highlighting = g_settings->getBool("enable_node_highlighting");
- m_cache_enable_clouds = g_settings->getBool("enable_clouds");
- m_cache_enable_particles = g_settings->getBool("enable_particles");
- m_cache_enable_fog = g_settings->getBool("enable_fog");
- m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
- m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
+ g_settings->registerChangedCallback("doubletap_jump",
+ &settingChangedCallback, this);
+ g_settings->registerChangedCallback("enable_node_highlighting",
+ &settingChangedCallback, this);
+ g_settings->registerChangedCallback("enable_clouds",
+ &settingChangedCallback, this);
+ g_settings->registerChangedCallback("enable_particles",
+ &settingChangedCallback, this);
+ g_settings->registerChangedCallback("enable_fog",
+ &settingChangedCallback, this);
+ g_settings->registerChangedCallback("mouse_sensitivity",
+ &settingChangedCallback, this);
+ g_settings->registerChangedCallback("repeat_rightclick_time",
+ &settingChangedCallback, this);
+
+ readSettings();
+
+#ifdef __ANDROID__
+ m_cache_hold_aux1 = false; // This is initialised properly later
+#endif
- m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
}
@@ -1693,6 +1712,21 @@ Game::~Game()
delete draw_control;
extendedResourceCleanup();
+
+ g_settings->deregisterChangedCallback("doubletap_jump",
+ &settingChangedCallback, this);
+ g_settings->deregisterChangedCallback("enable_node_highlighting",
+ &settingChangedCallback, this);
+ g_settings->deregisterChangedCallback("enable_clouds",
+ &settingChangedCallback, this);
+ g_settings->deregisterChangedCallback("enable_particles",
+ &settingChangedCallback, this);
+ g_settings->deregisterChangedCallback("enable_fog",
+ &settingChangedCallback, this);
+ g_settings->deregisterChangedCallback("mouse_sensitivity",
+ &settingChangedCallback, this);
+ g_settings->deregisterChangedCallback("repeat_rightclick_time",
+ &settingChangedCallback, this);
}
bool Game::startup(bool *kill,
@@ -1704,18 +1738,20 @@ bool Game::startup(bool *kill,
const std::string &password,
std::string *address, // can change if simple_singleplayer_mode
u16 port,
- std::wstring *error_message,
+ std::string &error_message,
+ bool *reconnect,
ChatBackend *chat_backend,
const SubgameSpec &gamespec,
bool simple_singleplayer_mode)
{
// "cache"
- this->device = device;
- this->kill = kill;
- this->error_message = error_message;
- this->random_input = random_input;
- this->input = input;
- this->chat_backend = chat_backend;
+ this->device = device;
+ this->kill = kill;
+ this->error_message = &error_message;
+ this->reconnect_requested = reconnect;
+ this->random_input = random_input;
+ this->input = input;
+ this->chat_backend = chat_backend;
this->simple_singleplayer_mode = simple_singleplayer_mode;
driver = device->getVideoDriver();
@@ -1726,7 +1762,7 @@ bool Game::startup(bool *kill,
if (!init(map_dir, address, port, gamespec))
return false;
- if (!createClient(playername, password, address, port, error_message))
+ if (!createClient(playername, password, address, port))
return false;
return true;
@@ -1737,6 +1773,7 @@ void Game::run()
{
ProfilerGraph graph;
RunStats stats = { 0 };
+ CameraOrientation cam_view_target = { 0 };
CameraOrientation cam_view = { 0 };
GameRunData runData = { 0 };
FpsControl draw_times = { 0 };
@@ -1749,6 +1786,7 @@ void Game::run()
flags.show_chat = true;
flags.show_hud = true;
+ flags.show_minimap = g_settings->getBool("enable_minimap");
flags.show_debug = g_settings->getBool("show_debug");
flags.invert_mouse = g_settings->getBool("invert_mouse");
flags.first_loop_after_window_activation = true;
@@ -1769,6 +1807,11 @@ void Game::run()
set_light_table(g_settings->getFloat("display_gamma"));
+#ifdef __ANDROID__
+ m_cache_hold_aux1 = g_settings->getBool("fast_move")
+ && client->checkPrivilege("fast");
+#endif
+
while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
/* Must be called immediately after a device->run() call because it
@@ -1792,10 +1835,20 @@ void Game::run()
updateProfilers(runData, stats, draw_times, dtime);
processUserInput(&flags, &runData, dtime);
// Update camera before player movement to avoid camera lag of one frame
- updateCameraDirection(&cam_view, &flags);
+ updateCameraDirection(&cam_view_target, &flags);
+ float cam_smoothing = 0;
+ if (g_settings->getBool("cinematic"))
+ cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
+ else
+ cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
+ cam_smoothing = rangelim(cam_smoothing, 0.01f, 1.0f);
+ cam_view.camera_yaw += (cam_view_target.camera_yaw -
+ cam_view.camera_yaw) * cam_smoothing;
+ cam_view.camera_pitch += (cam_view_target.camera_pitch -
+ cam_view.camera_pitch) * cam_smoothing;
updatePlayerControl(cam_view);
step(&dtime);
- processClientEvents(&cam_view, &runData.damage_flash);
+ processClientEvents(&cam_view_target, &runData.damage_flash);
updateCamera(&flags, draw_times.busy_time, dtime,
runData.time_from_last_punch);
updateSound(dtime);
@@ -1848,10 +1901,11 @@ void Game::shutdown()
}
-
+/****************************************************************************/
/****************************************************************************
Startup
****************************************************************************/
+/****************************************************************************/
bool Game::init(
const std::string &map_dir,
@@ -1934,10 +1988,10 @@ bool Game::createSingleplayerServer(const std::string map_dir,
}
if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
- *error_message = L"Unable to listen on " +
- narrow_to_wide(bind_addr.serializeString()) +
- L" because IPv6 is disabled";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Unable to listen on " +
+ bind_addr.serializeString() +
+ " because IPv6 is disabled";
+ errorstream << *error_message << std::endl;
return false;
}
@@ -1950,8 +2004,7 @@ bool Game::createSingleplayerServer(const std::string map_dir,
}
bool Game::createClient(const std::string &playername,
- const std::string &password, std::string *address, u16 port,
- std::wstring *error_message)
+ const std::string &password, std::string *address, u16 port)
{
showOverlayMessage(wgettext("Creating client..."), 0, 10);
@@ -1966,25 +2019,25 @@ bool Game::createClient(const std::string &playername,
return false;
if (!could_connect) {
- if (*error_message == L"" && !connect_aborted) {
+ if (error_message->empty() && !connect_aborted) {
// Should not happen if error messages are set properly
- *error_message = L"Connection failed for unknown reason";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Connection failed for unknown reason";
+ errorstream << *error_message << std::endl;
}
return false;
}
if (!getServerContent(&connect_aborted)) {
- if (*error_message == L"" && !connect_aborted) {
+ if (error_message->empty() && !connect_aborted) {
// Should not happen if error messages are set properly
- *error_message = L"Connection failed for unknown reason";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Connection failed for unknown reason";
+ errorstream << *error_message << std::endl;
}
return false;
}
// Update cached textures, meshes and materials
- client->afterContentReceived(device, g_fontengine->getFont());
+ client->afterContentReceived(device);
/* Camera
*/
@@ -1997,9 +2050,8 @@ bool Game::createClient(const std::string &playername,
if (m_cache_enable_clouds) {
clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
if (!clouds) {
- *error_message = L"Memory allocation error";
- *error_message += narrow_to_wide(" (clouds)");
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Memory allocation error (clouds)";
+ errorstream << *error_message << std::endl;
return false;
}
}
@@ -2012,9 +2064,8 @@ bool Game::createClient(const std::string &playername,
local_inventory = new Inventory(itemdef_manager);
if (!(sky && local_inventory)) {
- *error_message = L"Memory allocation error";
- *error_message += narrow_to_wide(" (sky or local inventory)");
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Memory allocation error (sky or local inventory)";
+ errorstream << *error_message << std::endl;
return false;
}
@@ -2028,14 +2079,15 @@ bool Game::createClient(const std::string &playername,
crack_animation_length = 5;
}
- if (!initGui(error_message))
+ if (!initGui())
return false;
/* Set window caption
*/
- core::stringw str = L"Minetest [";
+ std::wstring str = utf8_to_wide(PROJECT_NAME_C);
+ str += L" [";
str += driver->getName();
- str += "]";
+ str += L"]";
device->setWindowCaption(str.c_str());
LocalPlayer *player = client->getEnv().getLocalPlayer();
@@ -2045,19 +2097,22 @@ bool Game::createClient(const std::string &playername,
hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory);
if (!hud) {
- *error_message = L"Memory error: could not create HUD";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Memory error: could not create HUD";
+ errorstream << *error_message << std::endl;
return false;
}
+ mapper = client->getMapper();
+ mapper->setMinimapMode(MINIMAP_MODE_OFF);
+
return true;
}
-bool Game::initGui(std::wstring *error_message)
+bool Game::initGui()
{
// First line of debug text
guitext = guienv->addStaticText(
- L"Minetest",
+ utf8_to_wide(PROJECT_NAME_C).c_str(),
core::rect<s32>(0, 0, 0, 0),
false, false, guiroot);
@@ -2094,8 +2149,8 @@ bool Game::initGui(std::wstring *error_message)
gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
-1, chat_backend, client);
if (!gui_chat_console) {
- *error_message = L"Could not allocate memory for chat console";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Could not allocate memory for chat console";
+ errorstream << *error_message << std::endl;
return false;
}
@@ -2145,16 +2200,16 @@ bool Game::connectToServer(const std::string &playername,
local_server_mode = true;
}
} catch (ResolveError &e) {
- *error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = std::string("Couldn't resolve address: ") + e.what();
+ errorstream << *error_message << std::endl;
return false;
}
if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
- *error_message = L"Unable to connect to " +
- narrow_to_wide(connect_address.serializeString()) +
- L" because IPv6 is disabled";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Unable to connect to " +
+ connect_address.serializeString() +
+ " because IPv6 is disabled";
+ errorstream << *error_message << std::endl;
return false;
}
@@ -2184,7 +2239,10 @@ bool Game::connectToServer(const std::string &playername,
input->clear();
FpsControl fps_control = { 0 };
- f32 dtime; // in seconds
+ f32 dtime;
+ f32 wait_time = 0; // in seconds
+
+ fps_control.last_time = device->getTimer()->getTime();
while (device->run()) {
@@ -2204,9 +2262,10 @@ bool Game::connectToServer(const std::string &playername,
// Break conditions
if (client->accessDenied()) {
- *error_message = L"Access denied. Reason: "
+ *error_message = "Access denied. Reason: "
+ client->accessDeniedReason();
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *reconnect_requested = client->reconnectRequested();
+ errorstream << *error_message << std::endl;
break;
}
@@ -2216,6 +2275,14 @@ bool Game::connectToServer(const std::string &playername,
break;
}
+ wait_time += dtime;
+ // Only time out if we aren't waiting for the server we started
+ if ((*address != "") && (wait_time > 10)) {
+ *error_message = "Connection timed out.";
+ errorstream << *error_message << std::endl;
+ break;
+ }
+
// Update status
showOverlayMessage(wgettext("Connecting to server..."), dtime, 20);
}
@@ -2235,6 +2302,8 @@ bool Game::getServerContent(bool *aborted)
FpsControl fps_control = { 0 };
f32 dtime; // in seconds
+ fps_control.last_time = device->getTimer()->getTime();
+
while (device->run()) {
limitFps(&fps_control, &dtime);
@@ -2252,16 +2321,12 @@ bool Game::getServerContent(bool *aborted)
}
// Error conditions
- if (client->accessDenied()) {
- *error_message = L"Access denied. Reason: "
- + client->accessDeniedReason();
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ if (!checkConnection())
return false;
- }
if (client->getState() < LC_Init) {
- *error_message = L"Client disconnected";
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *error_message = "Client disconnected";
+ errorstream << *error_message << std::endl;
return false;
}
@@ -2292,18 +2357,18 @@ bool Game::getServerContent(bool *aborted)
if ((USE_CURL == 0) ||
(!g_settings->getBool("enable_remote_media_server"))) {
float cur = client->getCurRate();
- std::string cur_unit = gettext(" KB/s");
+ std::string cur_unit = gettext("KiB/s");
if (cur > 900) {
cur /= 1024.0;
- cur_unit = gettext(" MB/s");
+ cur_unit = gettext("MiB/s");
}
- message << " ( " << cur << cur_unit << " )";
+ message << " (" << cur << ' ' << cur_unit << ")";
}
progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
- draw_load_screen(narrow_to_wide(message.str()), device,
+ draw_load_screen(utf8_to_wide(message.str()), device,
guienv, dtime, progress);
}
}
@@ -2312,20 +2377,21 @@ bool Game::getServerContent(bool *aborted)
}
-
+/****************************************************************************/
/****************************************************************************
Run
****************************************************************************/
+/****************************************************************************/
-inline void Game::updateInteractTimers(GameRunData *args, f32 dtime)
+inline void Game::updateInteractTimers(GameRunData *runData, f32 dtime)
{
- if (args->nodig_delay_timer >= 0)
- args->nodig_delay_timer -= dtime;
+ if (runData->nodig_delay_timer >= 0)
+ runData->nodig_delay_timer -= dtime;
- if (args->object_hit_delay_timer >= 0)
- args->object_hit_delay_timer -= dtime;
+ if (runData->object_hit_delay_timer >= 0)
+ runData->object_hit_delay_timer -= dtime;
- args->time_from_last_punch += dtime;
+ runData->time_from_last_punch += dtime;
}
@@ -2334,9 +2400,10 @@ inline void Game::updateInteractTimers(GameRunData *args, f32 dtime)
inline bool Game::checkConnection()
{
if (client->accessDenied()) {
- *error_message = L"Access denied. Reason: "
+ *error_message = "Access denied. Reason: "
+ client->accessDeniedReason();
- errorstream << wide_to_narrow(*error_message) << std::endl;
+ *reconnect_requested = client->reconnectRequested();
+ errorstream << *error_message << std::endl;
return false;
}
@@ -2388,7 +2455,7 @@ void Game::processQueues()
}
-void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
+void Game::updateProfilers(const GameRunData &runData, const RunStats &stats,
const FpsControl &draw_times, f32 dtime)
{
float profiler_print_interval =
@@ -2407,7 +2474,7 @@ void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
}
update_profiler_gui(guitext_profiler, g_fontengine,
- run_data.profiler_current_page, run_data.profiler_max_page,
+ runData.profiler_current_page, runData.profiler_max_page,
driver->getScreenSize().Height);
g_profiler->clear();
@@ -2488,7 +2555,7 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
****************************************************************************/
void Game::processUserInput(VolatileRunFlags *flags,
- GameRunData *interact_args, f32 dtime)
+ GameRunData *runData, f32 dtime)
{
// Reset input if window not active or some menu is active
if (device->isWindowActive() == false
@@ -2519,18 +2586,18 @@ void Game::processUserInput(VolatileRunFlags *flags,
#endif
// Increase timer for double tap of "keymap_jump"
- if (m_cache_doubletap_jump && interact_args->jump_timer <= 0.2)
- interact_args->jump_timer += dtime;
+ if (m_cache_doubletap_jump && runData->jump_timer <= 0.2)
+ runData->jump_timer += dtime;
processKeyboardInput(
flags,
- &interact_args->statustext_time,
- &interact_args->jump_timer,
- &interact_args->reset_jump_timer,
- &interact_args->profiler_current_page,
- interact_args->profiler_max_page);
+ &runData->statustext_time,
+ &runData->jump_timer,
+ &runData->reset_jump_timer,
+ &runData->profiler_current_page,
+ runData->profiler_max_page);
- processItemSelection(&interact_args->new_playeritem);
+ processItemSelection(&runData->new_playeritem);
}
@@ -2568,10 +2635,15 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
toggleFast(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) {
toggleNoClip(statustext_time);
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CINEMATIC])) {
+ toggleCinematic(statustext_time);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) {
client->makeScreenshot(device);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) {
toggleHud(statustext_time, &flags->show_hud);
+ } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_MINIMAP])) {
+ toggleMinimap(statustext_time, &flags->show_minimap, flags->show_hud,
+ input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]));
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_CHAT])) {
toggleChat(statustext_time, &flags->show_chat);
} else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_FORCE_FOG_OFF])) {
@@ -2615,7 +2687,7 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
if (quicktune->hasMessage()) {
std::string msg = quicktune->getMessage();
- statustext = narrow_to_wide(msg);
+ statustext = utf8_to_wide(msg);
*statustext_time = 0;
}
}
@@ -2673,6 +2745,15 @@ void Game::dropSelectedItem()
void Game::openInventory()
{
+ /*
+ * Don't permit to open inventory is CAO or player doesn't exists.
+ * This prevent showing an empty inventory at player load
+ */
+
+ LocalPlayer *player = client->getEnv().getLocalPlayer();
+ if (player == NULL || player->getCAO() == NULL)
+ return;
+
infostream << "the_game: " << "Launching inventory" << std::endl;
PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
@@ -2727,8 +2808,14 @@ void Game::toggleFast(float *statustext_time)
*statustext_time = 0;
statustext = msg[fast_move];
- if (fast_move && !client->checkPrivilege("fast"))
+ bool has_fast_privs = client->checkPrivilege("fast");
+
+ if (fast_move && !has_fast_privs)
statustext += L" (note: no 'fast' privilege)";
+
+#ifdef __ANDROID__
+ m_cache_hold_aux1 = fast_move && has_fast_privs;
+#endif
}
@@ -2745,6 +2832,16 @@ void Game::toggleNoClip(float *statustext_time)
statustext += L" (note: no 'noclip' privilege)";
}
+void Game::toggleCinematic(float *statustext_time)
+{
+ static const wchar_t *msg[] = { L"cinematic disabled", L"cinematic enabled" };
+ bool cinematic = !g_settings->getBool("cinematic");
+ g_settings->set("cinematic", bool_to_cstr(cinematic));
+
+ *statustext_time = 0;
+ statustext = msg[cinematic];
+}
+
void Game::toggleChat(float *statustext_time, bool *flag)
{
@@ -2767,6 +2864,55 @@ void Game::toggleHud(float *statustext_time, bool *flag)
client->setHighlighted(client->getHighlighted(), *flag);
}
+void Game::toggleMinimap(float *statustext_time, bool *flag,
+ bool show_hud, bool shift_pressed)
+{
+ if (!show_hud || !g_settings->getBool("enable_minimap"))
+ return;
+
+ if (shift_pressed) {
+ mapper->toggleMinimapShape();
+ return;
+ }
+
+ u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags;
+
+ MinimapMode mode = MINIMAP_MODE_OFF;
+ if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) {
+ mode = mapper->getMinimapMode();
+ mode = (MinimapMode)((int)mode + 1);
+ }
+
+ *flag = true;
+ switch (mode) {
+ case MINIMAP_MODE_SURFACEx1:
+ statustext = L"Minimap in surface mode, Zoom x1";
+ break;
+ case MINIMAP_MODE_SURFACEx2:
+ statustext = L"Minimap in surface mode, Zoom x2";
+ break;
+ case MINIMAP_MODE_SURFACEx4:
+ statustext = L"Minimap in surface mode, Zoom x4";
+ break;
+ case MINIMAP_MODE_RADARx1:
+ statustext = L"Minimap in radar mode, Zoom x1";
+ break;
+ case MINIMAP_MODE_RADARx2:
+ statustext = L"Minimap in radar mode, Zoom x2";
+ break;
+ case MINIMAP_MODE_RADARx4:
+ statustext = L"Minimap in radar mode, Zoom x4";
+ break;
+ default:
+ mode = MINIMAP_MODE_OFF;
+ *flag = false;
+ statustext = (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) ?
+ L"Minimap hidden" : L"Minimap disabled by server";
+ }
+
+ *statustext_time = 0;
+ mapper->setMinimapMode(mode);
+}
void Game::toggleFog(float *statustext_time, bool *flag)
{
@@ -2839,7 +2985,7 @@ void Game::increaseViewRange(float *statustext_time)
s16 range = g_settings->getS16("viewing_range_nodes_min");
s16 range_new = range + 10;
g_settings->set("viewing_range_nodes_min", itos(range_new));
- statustext = narrow_to_wide("Minimum viewing range changed to "
+ statustext = utf8_to_wide("Minimum viewing range changed to "
+ itos(range_new));
*statustext_time = 0;
}
@@ -2854,7 +3000,7 @@ void Game::decreaseViewRange(float *statustext_time)
range_new = range;
g_settings->set("viewing_range_nodes_min", itos(range_new));
- statustext = narrow_to_wide("Minimum viewing range changed to "
+ statustext = utf8_to_wide("Minimum viewing range changed to "
+ itos(range_new));
*statustext_time = 0;
}
@@ -2954,19 +3100,34 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
cam.camera_pitch,
cam.camera_yaw
);
+
+ u32 keypress_bits =
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
+ ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) |
+ ( (u32)(input->getLeftState() & 0x1) << 7) |
+ ( (u32)(input->getRightState() & 0x1) << 8
+ );
+
+#ifdef ANDROID
+ /* For Android, simulate holding down AUX1 (fast move) if the user has
+ * the fast_move setting toggled on. If there is an aux1 key defined for
+ * Android then its meaning is inverted (i.e. holding aux1 means walk and
+ * not fast)
+ */
+ if (m_cache_hold_aux1) {
+ control.aux1 = control.aux1 ^ true;
+ keypress_bits ^= ((u32)(1U << 5));
+ }
+#endif
+
client->setPlayerControl(control);
LocalPlayer *player = client->getEnv().getLocalPlayer();
- player->keyPressed =
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
- ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) |
- ( (u32)(input->getLeftState() & 0x1) << 7) |
- ( (u32)(input->getRightState() & 0x1) << 8
- );
+ player->keyPressed = keypress_bits;
//tt.stop();
}
@@ -3077,7 +3238,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
u32 new_id = player->addHud(e);
//if this isn't true our huds aren't consistent
- assert(new_id == id);
+ sanity_check(new_id == id);
delete event.hudadd.pos;
delete event.hudadd.name;
@@ -3169,12 +3330,12 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
event.set_sky.params->size() == 6) {
sky->setFallbackBgColor(*event.set_sky.bgcolor);
skybox = smgr->addSkyBoxSceneNode(
- texture_src->getTexture((*event.set_sky.params)[0]),
- texture_src->getTexture((*event.set_sky.params)[1]),
- texture_src->getTexture((*event.set_sky.params)[2]),
- texture_src->getTexture((*event.set_sky.params)[3]),
- texture_src->getTexture((*event.set_sky.params)[4]),
- texture_src->getTexture((*event.set_sky.params)[5]));
+ texture_src->getTextureForMesh((*event.set_sky.params)[0]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[1]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[2]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[3]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[4]),
+ texture_src->getTextureForMesh((*event.set_sky.params)[5]));
}
// Handle everything else as plain color
else {
@@ -3231,6 +3392,7 @@ void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time,
camera->toggleCameraMode();
playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
+ playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
}
float full_punch_interval = playeritem_toolcap.full_punch_interval;
@@ -3453,13 +3615,13 @@ void Game::handlePointingAtNode(GameRunData *runData,
NodeMetadata *meta = map.getNodeMetadata(nodepos);
if (meta) {
- infotext = narrow_to_wide(meta->getString("infotext"));
+ infotext = utf8_to_wide(meta->getString("infotext"));
} else {
MapNode n = map.getNodeNoEx(nodepos);
if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
infotext = L"Unknown node: ";
- infotext += narrow_to_wide(nodedef_manager->get(n).name);
+ infotext += utf8_to_wide(nodedef_manager->get(n).name);
}
}
@@ -3525,10 +3687,10 @@ void Game::handlePointingAtObject(GameRunData *runData,
const v3f &player_position,
bool show_debug)
{
- infotext = narrow_to_wide(runData->selected_object->infoText());
+ infotext = utf8_to_wide(runData->selected_object->infoText());
if (infotext == L"" && show_debug) {
- infotext = narrow_to_wide(runData->selected_object->debugInfoText());
+ infotext = utf8_to_wide(runData->selected_object->debugInfoText());
}
if (input->getLeftState()) {
@@ -3885,8 +4047,9 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
stats->beginscenetime = timer.stop(true);
}
- draw_scene(driver, smgr, *camera, *client, player, *hud, guienv,
- highlight_boxes, screensize, skycolor, flags.show_hud);
+ draw_scene(driver, smgr, *camera, *client, player, *hud, *mapper,
+ guienv, highlight_boxes, screensize, skycolor, flags.show_hud,
+ flags.show_minimap);
/*
Profiler graph
@@ -3920,6 +4083,14 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
}
/*
+ Update minimap pos and rotation
+ */
+ if (flags.show_minimap && flags.show_hud) {
+ mapper->setPos(floatToInt(player->getPosition(), BS));
+ mapper->setAngle(player->getYaw());
+ }
+
+ /*
End scene
*/
{
@@ -3933,6 +4104,28 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
}
+inline static const char *yawToDirectionString(int yaw)
+{
+ // NOTE: TODO: This can be done mathematically without the else/else-if
+ // cascade.
+
+ const char *player_direction;
+
+ yaw = wrapDegrees_0_360(yaw);
+
+ if (yaw >= 45 && yaw < 135)
+ player_direction = "West [-X]";
+ else if (yaw >= 135 && yaw < 225)
+ player_direction = "South [-Z]";
+ else if (yaw >= 225 && yaw < 315)
+ player_direction = "East [+X]";
+ else
+ player_direction = "North [+Z]";
+
+ return player_direction;
+}
+
+
void Game::updateGui(float *statustext_time, const RunStats &stats,
const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
const CameraOrientation &cam)
@@ -3950,7 +4143,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
std::ostringstream os(std::ios_base::binary);
os << std::fixed
- << "Minetest " << minetest_version_hash
+ << PROJECT_NAME_C " " << g_version_hash
<< " FPS = " << fps
<< " (R: range_all=" << draw_control->range_all << ")"
<< std::setprecision(0)
@@ -3962,12 +4155,12 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
<< ", v_range = " << draw_control->wanted_range
<< std::setprecision(3)
<< ", RTT = " << client->getRTT();
- guitext->setText(narrow_to_wide(os.str()).c_str());
+ guitext->setText(utf8_to_wide(os.str()).c_str());
guitext->setVisible(true);
} else if (flags.show_hud || flags.show_chat) {
std::ostringstream os(std::ios_base::binary);
- os << "Minetest " << minetest_version_hash;
- guitext->setText(narrow_to_wide(os.str()).c_str());
+ os << PROJECT_NAME_C " " << g_version_hash;
+ guitext->setText(utf8_to_wide(os.str()).c_str());
guitext->setVisible(true);
} else {
guitext->setVisible(false);
@@ -3988,6 +4181,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
<< ", " << (player_position.Y / BS)
<< ", " << (player_position.Z / BS)
<< ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
+ << " " << yawToDirectionString(cam.camera_yaw)
<< ") (seed = " << ((u64)client->getMapSeed())
<< ")";
@@ -4003,7 +4197,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
}
}
- guitext2->setText(narrow_to_wide(os.str()).c_str());
+ guitext2->setText(utf8_to_wide(os.str()).c_str());
guitext2->setVisible(true);
core::rect<s32> rect(
@@ -4081,7 +4275,6 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
// not using getRealTime is necessary for wine
device->getTimer()->tick(); // Maker sure device time is up-to-date
u32 time = device->getTimer()->getTime();
-
u32 last_time = fps_timings->last_time;
if (time > last_time) // Make sure time hasn't overflowed
@@ -4127,10 +4320,29 @@ void Game::showOverlayMessage(const wchar_t *msg, float dtime,
delete[] msg;
}
+void Game::settingChangedCallback(const std::string &setting_name, void *data)
+{
+ ((Game *)data)->readSettings();
+}
+void Game::readSettings()
+{
+ m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
+ m_cache_enable_node_highlighting = g_settings->getBool("enable_node_highlighting");
+ m_cache_enable_clouds = g_settings->getBool("enable_clouds");
+ m_cache_enable_particles = g_settings->getBool("enable_particles");
+ m_cache_enable_fog = g_settings->getBool("enable_fog");
+ m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
+ m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
+
+ m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
+}
+
+/****************************************************************************/
/****************************************************************************
Shutdown / cleanup
****************************************************************************/
+/****************************************************************************/
void Game::extendedResourceCleanup()
{
@@ -4154,10 +4366,11 @@ void Game::extendedResourceCleanup()
}
-
+/****************************************************************************/
/****************************************************************************
extern function for launching the game
****************************************************************************/
+/****************************************************************************/
void the_game(bool *kill,
bool random_input,
@@ -4170,8 +4383,9 @@ void the_game(bool *kill,
const std::string &address, // If empty local server is created
u16 port,
- std::wstring &error_message,
+ std::string &error_message,
ChatBackend &chat_backend,
+ bool *reconnect_requested,
const SubgameSpec &gamespec, // Used for local game
bool simple_singleplayer_mode)
{
@@ -4186,25 +4400,24 @@ void the_game(bool *kill,
try {
if (game.startup(kill, random_input, input, device, map_dir,
- playername, password, &server_address, port,
- &error_message, &chat_backend, gamespec,
- simple_singleplayer_mode)) {
-
+ playername, password, &server_address, port, error_message,
+ reconnect_requested, &chat_backend, gamespec,
+ simple_singleplayer_mode)) {
game.run();
game.shutdown();
}
} catch (SerializationError &e) {
- error_message = L"A serialization error occurred:\n"
- + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
- L" running a different version of Minetest.";
- errorstream << wide_to_narrow(error_message) << std::endl;
+ error_message = std::string("A serialization error occurred:\n")
+ + e.what() + "\n\nThe server is probably "
+ " running a different version of " PROJECT_NAME_C ".";
+ errorstream << error_message << std::endl;
} catch (ServerError &e) {
- error_message = narrow_to_wide(e.what());
- errorstream << "ServerError: " << e.what() << std::endl;
+ error_message = e.what();
+ errorstream << "ServerError: " << error_message << std::endl;
} catch (ModError &e) {
- errorstream << "ModError: " << e.what() << std::endl;
- error_message = narrow_to_wide(e.what()) + wstrgettext("\nCheck debug.txt for details.");
+ error_message = e.what() + strgettext("\nCheck debug.txt for details.");
+ errorstream << "ModError: " << error_message << std::endl;
}
}
diff --git a/src/game.h b/src/game.h
index 61f780bee..e1f4e9346 100644
--- a/src/game.h
+++ b/src/game.h
@@ -145,8 +145,9 @@ void the_game(bool *kill,
const std::string &password,
const std::string &address, // If "", local server is used
u16 port,
- std::wstring &error_message,
+ std::string &error_message,
ChatBackend &chat_backend,
+ bool *reconnect_requested,
const SubgameSpec &gamespec, // Used for local game
bool simple_singleplayer_mode);
diff --git a/src/gamedef.h b/src/gamedef.h
index 793d85b39..a5f6b5968 100644
--- a/src/gamedef.h
+++ b/src/gamedef.h
@@ -31,6 +31,7 @@ class ISoundManager;
class IShaderSource;
class MtEventManager;
class IRollbackManager;
+class EmergeManager;
namespace irr { namespace scene {
class IAnimatedMesh;
class ISceneManager;
@@ -55,10 +56,10 @@ public:
virtual ITextureSource* getTextureSource()=0;
virtual IShaderSource* getShaderSource()=0;
-
+
// Used for keeping track of names/ids of unknown nodes
virtual u16 allocateUnknownNodeId(const std::string &name)=0;
-
+
// Only usable on the client
virtual ISoundManager* getSoundManager()=0;
virtual MtEventManager* getEventManager()=0;
@@ -69,20 +70,24 @@ public:
// Only usable on the server, and NOT thread-safe. It is usable from the
// environment thread.
virtual IRollbackManager* getRollbackManager(){return NULL;}
-
+
+ // Only usable on the server. Thread safe if not written while running threads.
+ virtual EmergeManager *getEmergeManager() { return NULL; }
+
// Used on the client
virtual bool checkLocalPrivilege(const std::string &priv)
{ return false; }
-
+
// Shorthands
- IItemDefManager* idef(){return getItemDefManager();}
- INodeDefManager* ndef(){return getNodeDefManager();}
- ICraftDefManager* cdef(){return getCraftDefManager();}
- ITextureSource* tsrc(){return getTextureSource();}
- ISoundManager* sound(){return getSoundManager();}
- IShaderSource* shsrc(){return getShaderSource();}
- MtEventManager* event(){return getEventManager();}
- IRollbackManager* rollback(){return getRollbackManager();}
+ IItemDefManager *idef() { return getItemDefManager(); }
+ INodeDefManager *ndef() { return getNodeDefManager(); }
+ ICraftDefManager *cdef() { return getCraftDefManager(); }
+ ITextureSource *tsrc() { return getTextureSource(); }
+ ISoundManager *sound() { return getSoundManager(); }
+ IShaderSource *shsrc() { return getShaderSource(); }
+ MtEventManager *event() { return getEventManager(); }
+ IRollbackManager *rollback() { return getRollbackManager();}
+ EmergeManager *emerge() { return getEmergeManager(); }
};
#endif
diff --git a/src/content_object.h b/src/gameparams.h
index 65a829773..b50e943d2 100644
--- a/src/content_object.h
+++ b/src/gameparams.h
@@ -17,23 +17,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CONTENT_OBJECT_HEADER
-#define CONTENT_OBJECT_HEADER
+#ifndef __GAME_PARAMS_H__
+#define __GAME_PARAMS_H__
-#define ACTIVEOBJECT_TYPE_TEST 1
-#define ACTIVEOBJECT_TYPE_ITEM 2
-#define ACTIVEOBJECT_TYPE_RAT 3
-#define ACTIVEOBJECT_TYPE_OERKKI1 4
-#define ACTIVEOBJECT_TYPE_FIREFLY 5
-#define ACTIVEOBJECT_TYPE_MOBV2 6
+#include "irrlichttypes_extrabloated.h"
-#define ACTIVEOBJECT_TYPE_LUAENTITY 7
-
-// Special type, not stored as a static object
-#define ACTIVEOBJECT_TYPE_PLAYER 100
-
-// Special type, only exists as CAO
-#define ACTIVEOBJECT_TYPE_GENERIC 101
+struct GameParams {
+ u16 socket_port;
+ std::string world_path;
+ SubgameSpec game_spec;
+ bool is_dedicated_server;
+ int log_level;
+};
#endif
-
diff --git a/src/genericobject.cpp b/src/genericobject.cpp
index 9a1b9d8d0..90e8cf3d3 100644
--- a/src/genericobject.cpp
+++ b/src/genericobject.cpp
@@ -133,7 +133,7 @@ std::string gob_cmd_update_physics_override(float physics_override_speed, float
return os.str();
}
-std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend)
+std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop)
{
std::ostringstream os(std::ios::binary);
// command
@@ -142,6 +142,8 @@ std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_
writeV2F1000(os, frames);
writeF1000(os, frame_speed);
writeF1000(os, frame_blend);
+ // these are sent inverted so we get true when the server sends nothing
+ writeU8(os, !frame_loop);
return os.str();
}
@@ -161,7 +163,7 @@ std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f posit
{
std::ostringstream os(std::ios::binary);
// command
- writeU8(os, GENERIC_CMD_SET_ATTACHMENT);
+ writeU8(os, GENERIC_CMD_ATTACH_TO);
// parameters
writeS16(os, parent_id);
os<<serializeString(bone);
@@ -170,3 +172,13 @@ std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f posit
return os.str();
}
+std::string gob_cmd_update_nametag_attributes(video::SColor color)
+{
+ std::ostringstream os(std::ios::binary);
+ // command
+ writeU8(os, GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES);
+ // parameters
+ writeU8(os, 1); // version for forward compatibility
+ writeARGB8(os, color);
+ return os.str();
+}
diff --git a/src/genericobject.h b/src/genericobject.h
index 29e5e29cb..b92570831 100644
--- a/src/genericobject.h
+++ b/src/genericobject.h
@@ -24,16 +24,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include <iostream>
-#define GENERIC_CMD_SET_PROPERTIES 0
-#define GENERIC_CMD_UPDATE_POSITION 1
-#define GENERIC_CMD_SET_TEXTURE_MOD 2
-#define GENERIC_CMD_SET_SPRITE 3
-#define GENERIC_CMD_PUNCHED 4
-#define GENERIC_CMD_UPDATE_ARMOR_GROUPS 5
-#define GENERIC_CMD_SET_ANIMATION 6
-#define GENERIC_CMD_SET_BONE_POSITION 7
-#define GENERIC_CMD_SET_ATTACHMENT 8
-#define GENERIC_CMD_SET_PHYSICS_OVERRIDE 9
+enum GenericCMD {
+ GENERIC_CMD_SET_PROPERTIES,
+ GENERIC_CMD_UPDATE_POSITION,
+ GENERIC_CMD_SET_TEXTURE_MOD,
+ GENERIC_CMD_SET_SPRITE,
+ GENERIC_CMD_PUNCHED,
+ GENERIC_CMD_UPDATE_ARMOR_GROUPS,
+ GENERIC_CMD_SET_ANIMATION,
+ GENERIC_CMD_SET_BONE_POSITION,
+ GENERIC_CMD_ATTACH_TO,
+ GENERIC_CMD_SET_PHYSICS_OVERRIDE,
+ GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES
+};
#include "object_properties.h"
std::string gob_cmd_set_properties(const ObjectProperties &prop);
@@ -66,11 +69,13 @@ std::string gob_cmd_update_armor_groups(const ItemGroupList &armor_groups);
std::string gob_cmd_update_physics_override(float physics_override_speed,
float physics_override_jump, float physics_override_gravity, bool sneak, bool sneak_glitch);
-std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend);
+std::string gob_cmd_update_animation(v2f frames, float frame_speed, float frame_blend, bool frame_loop);
std::string gob_cmd_update_bone_position(std::string bone, v3f position, v3f rotation);
std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f position, v3f rotation);
+std::string gob_cmd_update_nametag_attributes(video::SColor color);
+
#endif
diff --git a/src/gettext.cpp b/src/gettext.cpp
index fc7569418..0fd6b574e 100644
--- a/src/gettext.cpp
+++ b/src/gettext.cpp
@@ -102,8 +102,9 @@ const char* MSVC_LocaleLookup(const char* raw_shortname) {
last_raw_value = shortname;
- if (glb_supported_locales.find(narrow_to_wide(shortname)) != glb_supported_locales.end()) {
- last_full_name = wide_to_narrow(glb_supported_locales[narrow_to_wide(shortname)]);
+ if (glb_supported_locales.find(utf8_to_wide(shortname)) != glb_supported_locales.end()) {
+ last_full_name = wide_to_utf8(
+ glb_supported_locales[utf8_to_wide(shortname)]);
return last_full_name.c_str();
}
@@ -236,8 +237,9 @@ void init_gettext(const char *path, const std::string &configured_language) {
#endif
#endif
- bindtextdomain(PROJECT_NAME, path);
- textdomain(PROJECT_NAME);
+ static std::string name = lowercase(PROJECT_NAME);
+ bindtextdomain(name.c_str(), path);
+ textdomain(name.c_str());
#if defined(_WIN32)
// Set character encoding for Win32
diff --git a/src/gettext.h b/src/gettext.h
index 8235efa8a..9b4e801f7 100644
--- a/src/gettext.h
+++ b/src/gettext.h
@@ -39,13 +39,13 @@ void init_gettext(const char *path, const std::string &configured_language,
void init_gettext(const char *path, const std::string &configured_language);
#endif
-extern wchar_t *narrow_to_wide_c(const char *str);
+extern wchar_t *utf8_to_wide_c(const char *str);
// You must free the returned string!
// The returned string is allocated using new
inline const wchar_t *wgettext(const char *str)
{
- return narrow_to_wide_c(gettext(str));
+ return utf8_to_wide_c(gettext(str));
}
inline std::wstring wstrgettext(const std::string &text)
diff --git a/src/gettime.h b/src/gettime.h
index 2a6a211b8..44c159026 100644
--- a/src/gettime.h
+++ b/src/gettime.h
@@ -54,8 +54,8 @@ inline std::string getTimestamp()
// This is not really thread-safe but it won't break anything
// except its own output, so just go with it.
struct tm *tm = localtime(&t);
- char cs[20];
- strftime(cs, 20, "%H:%M:%S", tm);
+ char cs[20]; //YYYY-MM-DD HH:MM:SS + '\0'
+ strftime(cs, 20, "%Y-%m-%d %H:%M:%S", tm);
return cs;
}
diff --git a/src/gmp/CMakeLists.txt b/src/gmp/CMakeLists.txt
new file mode 100644
index 000000000..96ae8191d
--- /dev/null
+++ b/src/gmp/CMakeLists.txt
@@ -0,0 +1,7 @@
+if(MSVC)
+ set(CMAKE_C_FLAGS_RELEASE "/MT /O2 /Ob2 /D NDEBUG")
+endif()
+
+add_library(gmp mini-gmp.c)
+target_link_libraries(gmp)
+
diff --git a/src/gmp/mini-gmp.c b/src/gmp/mini-gmp.c
new file mode 100644
index 000000000..f3b43fbe8
--- /dev/null
+++ b/src/gmp/mini-gmp.c
@@ -0,0 +1,4130 @@
+/* mini-gmp, a minimalistic implementation of a GNU GMP subset.
+
+ Contributed to the GNU project by Niels Möller
+
+Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, 2001,
+2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
+Free Software Foundation, Inc.
+
+This file is part of the GNU MP Library.
+
+The GNU MP Library 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 3 of the License, or (at your
+option) any later version.
+
+The GNU MP Library 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 the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
+
+/* NOTE: All functions in this file which are not declared in
+ mini-gmp.h are internal, and are not intended to be compatible
+ neither with GMP nor with future versions of mini-gmp. */
+
+/* Much of the material copied from GMP files, including: gmp-impl.h,
+ longlong.h, mpn/generic/add_n.c, mpn/generic/addmul_1.c,
+ mpn/generic/lshift.c, mpn/generic/mul_1.c,
+ mpn/generic/mul_basecase.c, mpn/generic/rshift.c,
+ mpn/generic/sbpi1_div_qr.c, mpn/generic/sub_n.c,
+ mpn/generic/submul_1.c. */
+
+#include <assert.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mini-gmp.h"
+
+
+/* Macros */
+#define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT)
+
+#define GMP_LIMB_MAX (~ (mp_limb_t) 0)
+#define GMP_LIMB_HIGHBIT ((mp_limb_t) 1 << (GMP_LIMB_BITS - 1))
+
+#define GMP_HLIMB_BIT ((mp_limb_t) 1 << (GMP_LIMB_BITS / 2))
+#define GMP_LLIMB_MASK (GMP_HLIMB_BIT - 1)
+
+#define GMP_ULONG_BITS (sizeof(unsigned long) * CHAR_BIT)
+#define GMP_ULONG_HIGHBIT ((unsigned long) 1 << (GMP_ULONG_BITS - 1))
+
+#define GMP_ABS(x) ((x) >= 0 ? (x) : -(x))
+#define GMP_NEG_CAST(T,x) (-((T)((x) + 1) - 1))
+
+#define GMP_MIN(a, b) ((a) < (b) ? (a) : (b))
+#define GMP_MAX(a, b) ((a) > (b) ? (a) : (b))
+
+#define gmp_assert_nocarry(x) do { \
+ mp_limb_t __cy = x; \
+ assert (__cy == 0); \
+ } while (0)
+
+#define gmp_clz(count, x) do { \
+ mp_limb_t __clz_x = (x); \
+ unsigned __clz_c; \
+ for (__clz_c = 0; \
+ (__clz_x & ((mp_limb_t) 0xff << (GMP_LIMB_BITS - 8))) == 0; \
+ __clz_c += 8) \
+ __clz_x <<= 8; \
+ for (; (__clz_x & GMP_LIMB_HIGHBIT) == 0; __clz_c++) \
+ __clz_x <<= 1; \
+ (count) = __clz_c; \
+ } while (0)
+
+#define gmp_ctz(count, x) do { \
+ mp_limb_t __ctz_x = (x); \
+ unsigned __ctz_c = 0; \
+ gmp_clz (__ctz_c, __ctz_x & - __ctz_x); \
+ (count) = GMP_LIMB_BITS - 1 - __ctz_c; \
+ } while (0)
+
+#define gmp_add_ssaaaa(sh, sl, ah, al, bh, bl) \
+ do { \
+ mp_limb_t __x; \
+ __x = (al) + (bl); \
+ (sh) = (ah) + (bh) + (__x < (al)); \
+ (sl) = __x; \
+ } while (0)
+
+#define gmp_sub_ddmmss(sh, sl, ah, al, bh, bl) \
+ do { \
+ mp_limb_t __x; \
+ __x = (al) - (bl); \
+ (sh) = (ah) - (bh) - ((al) < (bl)); \
+ (sl) = __x; \
+ } while (0)
+
+#define gmp_umul_ppmm(w1, w0, u, v) \
+ do { \
+ mp_limb_t __x0, __x1, __x2, __x3; \
+ unsigned __ul, __vl, __uh, __vh; \
+ mp_limb_t __u = (u), __v = (v); \
+ \
+ __ul = __u & GMP_LLIMB_MASK; \
+ __uh = __u >> (GMP_LIMB_BITS / 2); \
+ __vl = __v & GMP_LLIMB_MASK; \
+ __vh = __v >> (GMP_LIMB_BITS / 2); \
+ \
+ __x0 = (mp_limb_t) __ul * __vl; \
+ __x1 = (mp_limb_t) __ul * __vh; \
+ __x2 = (mp_limb_t) __uh * __vl; \
+ __x3 = (mp_limb_t) __uh * __vh; \
+ \
+ __x1 += __x0 >> (GMP_LIMB_BITS / 2);/* this can't give carry */ \
+ __x1 += __x2; /* but this indeed can */ \
+ if (__x1 < __x2) /* did we get it? */ \
+ __x3 += GMP_HLIMB_BIT; /* yes, add it in the proper pos. */ \
+ \
+ (w1) = __x3 + (__x1 >> (GMP_LIMB_BITS / 2)); \
+ (w0) = (__x1 << (GMP_LIMB_BITS / 2)) + (__x0 & GMP_LLIMB_MASK); \
+ } while (0)
+
+#define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \
+ do { \
+ mp_limb_t _qh, _ql, _r, _mask; \
+ gmp_umul_ppmm (_qh, _ql, (nh), (di)); \
+ gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \
+ _r = (nl) - _qh * (d); \
+ _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \
+ _qh += _mask; \
+ _r += _mask & (d); \
+ if (_r >= (d)) \
+ { \
+ _r -= (d); \
+ _qh++; \
+ } \
+ \
+ (r) = _r; \
+ (q) = _qh; \
+ } while (0)
+
+#define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \
+ do { \
+ mp_limb_t _q0, _t1, _t0, _mask; \
+ gmp_umul_ppmm ((q), _q0, (n2), (dinv)); \
+ gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \
+ \
+ /* Compute the two most significant limbs of n - q'd */ \
+ (r1) = (n1) - (d1) * (q); \
+ gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \
+ gmp_umul_ppmm (_t1, _t0, (d0), (q)); \
+ gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \
+ (q)++; \
+ \
+ /* Conditionally adjust q and the remainders */ \
+ _mask = - (mp_limb_t) ((r1) >= _q0); \
+ (q) += _mask; \
+ gmp_add_ssaaaa ((r1), (r0), (r1), (r0), _mask & (d1), _mask & (d0)); \
+ if ((r1) >= (d1)) \
+ { \
+ if ((r1) > (d1) || (r0) >= (d0)) \
+ { \
+ (q)++; \
+ gmp_sub_ddmmss ((r1), (r0), (r1), (r0), (d1), (d0)); \
+ } \
+ } \
+ } while (0)
+
+/* Swap macros. */
+#define MP_LIMB_T_SWAP(x, y) \
+ do { \
+ mp_limb_t __mp_limb_t_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_limb_t_swap__tmp; \
+ } while (0)
+#define MP_SIZE_T_SWAP(x, y) \
+ do { \
+ mp_size_t __mp_size_t_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_size_t_swap__tmp; \
+ } while (0)
+#define MP_BITCNT_T_SWAP(x,y) \
+ do { \
+ mp_bitcnt_t __mp_bitcnt_t_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_bitcnt_t_swap__tmp; \
+ } while (0)
+#define MP_PTR_SWAP(x, y) \
+ do { \
+ mp_ptr __mp_ptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_ptr_swap__tmp; \
+ } while (0)
+#define MP_SRCPTR_SWAP(x, y) \
+ do { \
+ mp_srcptr __mp_srcptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mp_srcptr_swap__tmp; \
+ } while (0)
+
+#define MPN_PTR_SWAP(xp,xs, yp,ys) \
+ do { \
+ MP_PTR_SWAP (xp, yp); \
+ MP_SIZE_T_SWAP (xs, ys); \
+ } while(0)
+#define MPN_SRCPTR_SWAP(xp,xs, yp,ys) \
+ do { \
+ MP_SRCPTR_SWAP (xp, yp); \
+ MP_SIZE_T_SWAP (xs, ys); \
+ } while(0)
+
+#define MPZ_PTR_SWAP(x, y) \
+ do { \
+ mpz_ptr __mpz_ptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mpz_ptr_swap__tmp; \
+ } while (0)
+#define MPZ_SRCPTR_SWAP(x, y) \
+ do { \
+ mpz_srcptr __mpz_srcptr_swap__tmp = (x); \
+ (x) = (y); \
+ (y) = __mpz_srcptr_swap__tmp; \
+ } while (0)
+
+
+/* Memory allocation and other helper functions. */
+static void
+gmp_die (const char *msg)
+{
+ fprintf (stderr, "%s\n", msg);
+ abort();
+}
+
+static void *
+gmp_default_alloc (size_t size)
+{
+ void *p;
+
+ assert (size > 0);
+
+ p = malloc (size);
+ if (!p)
+ gmp_die("gmp_default_alloc: Virtual memory exhausted.");
+
+ return p;
+}
+
+static void *
+gmp_default_realloc (void *old, size_t old_size, size_t new_size)
+{
+ mp_ptr p;
+
+ p = realloc (old, new_size);
+
+ if (!p)
+ gmp_die("gmp_default_realoc: Virtual memory exhausted.");
+
+ return p;
+}
+
+static void
+gmp_default_free (void *p, size_t size)
+{
+ free (p);
+}
+
+static void * (*gmp_allocate_func) (size_t) = gmp_default_alloc;
+static void * (*gmp_reallocate_func) (void *, size_t, size_t) = gmp_default_realloc;
+static void (*gmp_free_func) (void *, size_t) = gmp_default_free;
+
+void
+mp_get_memory_functions (void *(**alloc_func) (size_t),
+ void *(**realloc_func) (void *, size_t, size_t),
+ void (**free_func) (void *, size_t))
+{
+ if (alloc_func)
+ *alloc_func = gmp_allocate_func;
+
+ if (realloc_func)
+ *realloc_func = gmp_reallocate_func;
+
+ if (free_func)
+ *free_func = gmp_free_func;
+}
+
+void
+mp_set_memory_functions (void *(*alloc_func) (size_t),
+ void *(*realloc_func) (void *, size_t, size_t),
+ void (*free_func) (void *, size_t))
+{
+ if (!alloc_func)
+ alloc_func = gmp_default_alloc;
+ if (!realloc_func)
+ realloc_func = gmp_default_realloc;
+ if (!free_func)
+ free_func = gmp_default_free;
+
+ gmp_allocate_func = alloc_func;
+ gmp_reallocate_func = realloc_func;
+ gmp_free_func = free_func;
+}
+
+#define gmp_xalloc(size) ((*gmp_allocate_func)((size)))
+#define gmp_free(p) ((*gmp_free_func) ((p), 0))
+
+static mp_ptr
+gmp_xalloc_limbs (mp_size_t size)
+{
+ return gmp_xalloc (size * sizeof (mp_limb_t));
+}
+
+static mp_ptr
+gmp_xrealloc_limbs (mp_ptr old, mp_size_t size)
+{
+ assert (size > 0);
+ return (*gmp_reallocate_func) (old, 0, size * sizeof (mp_limb_t));
+}
+
+
+/* MPN interface */
+
+void
+mpn_copyi (mp_ptr d, mp_srcptr s, mp_size_t n)
+{
+ mp_size_t i;
+ for (i = 0; i < n; i++)
+ d[i] = s[i];
+}
+
+void
+mpn_copyd (mp_ptr d, mp_srcptr s, mp_size_t n)
+{
+ while (n-- > 0)
+ d[n] = s[n];
+}
+
+int
+mpn_cmp (mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ for (; n > 0; n--)
+ {
+ if (ap[n-1] < bp[n-1])
+ return -1;
+ else if (ap[n-1] > bp[n-1])
+ return 1;
+ }
+ return 0;
+}
+
+static int
+mpn_cmp4 (mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
+{
+ if (an > bn)
+ return 1;
+ else if (an < bn)
+ return -1;
+ else
+ return mpn_cmp (ap, bp, an);
+}
+
+static mp_size_t
+mpn_normalized_size (mp_srcptr xp, mp_size_t n)
+{
+ for (; n > 0 && xp[n-1] == 0; n--)
+ ;
+ return n;
+}
+
+#define mpn_zero_p(xp, n) (mpn_normalized_size ((xp), (n)) == 0)
+
+mp_limb_t
+mpn_add_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b)
+{
+ mp_size_t i;
+
+ assert (n > 0);
+
+ for (i = 0; i < n; i++)
+ {
+ mp_limb_t r = ap[i] + b;
+ /* Carry out */
+ b = (r < b);
+ rp[i] = r;
+ }
+ return b;
+}
+
+mp_limb_t
+mpn_add_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ mp_size_t i;
+ mp_limb_t cy;
+
+ for (i = 0, cy = 0; i < n; i++)
+ {
+ mp_limb_t a, b, r;
+ a = ap[i]; b = bp[i];
+ r = a + cy;
+ cy = (r < cy);
+ r += b;
+ cy += (r < b);
+ rp[i] = r;
+ }
+ return cy;
+}
+
+mp_limb_t
+mpn_add (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
+{
+ mp_limb_t cy;
+
+ assert (an >= bn);
+
+ cy = mpn_add_n (rp, ap, bp, bn);
+ if (an > bn)
+ cy = mpn_add_1 (rp + bn, ap + bn, an - bn, cy);
+ return cy;
+}
+
+mp_limb_t
+mpn_sub_1 (mp_ptr rp, mp_srcptr ap, mp_size_t n, mp_limb_t b)
+{
+ mp_size_t i;
+
+ assert (n > 0);
+
+ for (i = 0; i < n; i++)
+ {
+ mp_limb_t a = ap[i];
+ /* Carry out */
+ mp_limb_t cy = a < b;;
+ rp[i] = a - b;
+ b = cy;
+ }
+ return b;
+}
+
+mp_limb_t
+mpn_sub_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ mp_size_t i;
+ mp_limb_t cy;
+
+ for (i = 0, cy = 0; i < n; i++)
+ {
+ mp_limb_t a, b;
+ a = ap[i]; b = bp[i];
+ b += cy;
+ cy = (b < cy);
+ cy += (a < b);
+ rp[i] = a - b;
+ }
+ return cy;
+}
+
+mp_limb_t
+mpn_sub (mp_ptr rp, mp_srcptr ap, mp_size_t an, mp_srcptr bp, mp_size_t bn)
+{
+ mp_limb_t cy;
+
+ assert (an >= bn);
+
+ cy = mpn_sub_n (rp, ap, bp, bn);
+ if (an > bn)
+ cy = mpn_sub_1 (rp + bn, ap + bn, an - bn, cy);
+ return cy;
+}
+
+mp_limb_t
+mpn_mul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl)
+{
+ mp_limb_t ul, cl, hpl, lpl;
+
+ assert (n >= 1);
+
+ cl = 0;
+ do
+ {
+ ul = *up++;
+ gmp_umul_ppmm (hpl, lpl, ul, vl);
+
+ lpl += cl;
+ cl = (lpl < cl) + hpl;
+
+ *rp++ = lpl;
+ }
+ while (--n != 0);
+
+ return cl;
+}
+
+mp_limb_t
+mpn_addmul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl)
+{
+ mp_limb_t ul, cl, hpl, lpl, rl;
+
+ assert (n >= 1);
+
+ cl = 0;
+ do
+ {
+ ul = *up++;
+ gmp_umul_ppmm (hpl, lpl, ul, vl);
+
+ lpl += cl;
+ cl = (lpl < cl) + hpl;
+
+ rl = *rp;
+ lpl = rl + lpl;
+ cl += lpl < rl;
+ *rp++ = lpl;
+ }
+ while (--n != 0);
+
+ return cl;
+}
+
+mp_limb_t
+mpn_submul_1 (mp_ptr rp, mp_srcptr up, mp_size_t n, mp_limb_t vl)
+{
+ mp_limb_t ul, cl, hpl, lpl, rl;
+
+ assert (n >= 1);
+
+ cl = 0;
+ do
+ {
+ ul = *up++;
+ gmp_umul_ppmm (hpl, lpl, ul, vl);
+
+ lpl += cl;
+ cl = (lpl < cl) + hpl;
+
+ rl = *rp;
+ lpl = rl - lpl;
+ cl += lpl > rl;
+ *rp++ = lpl;
+ }
+ while (--n != 0);
+
+ return cl;
+}
+
+mp_limb_t
+mpn_mul (mp_ptr rp, mp_srcptr up, mp_size_t un, mp_srcptr vp, mp_size_t vn)
+{
+ assert (un >= vn);
+ assert (vn >= 1);
+
+ /* We first multiply by the low order limb. This result can be
+ stored, not added, to rp. We also avoid a loop for zeroing this
+ way. */
+
+ rp[un] = mpn_mul_1 (rp, up, un, vp[0]);
+ rp += 1, vp += 1, vn -= 1;
+
+ /* Now accumulate the product of up[] and the next higher limb from
+ vp[]. */
+
+ while (vn >= 1)
+ {
+ rp[un] = mpn_addmul_1 (rp, up, un, vp[0]);
+ rp += 1, vp += 1, vn -= 1;
+ }
+ return rp[un - 1];
+}
+
+void
+mpn_mul_n (mp_ptr rp, mp_srcptr ap, mp_srcptr bp, mp_size_t n)
+{
+ mpn_mul (rp, ap, n, bp, n);
+}
+
+void
+mpn_sqr (mp_ptr rp, mp_srcptr ap, mp_size_t n)
+{
+ mpn_mul (rp, ap, n, ap, n);
+}
+
+mp_limb_t
+mpn_lshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt)
+{
+ mp_limb_t high_limb, low_limb;
+ unsigned int tnc;
+ mp_size_t i;
+ mp_limb_t retval;
+
+ assert (n >= 1);
+ assert (cnt >= 1);
+ assert (cnt < GMP_LIMB_BITS);
+
+ up += n;
+ rp += n;
+
+ tnc = GMP_LIMB_BITS - cnt;
+ low_limb = *--up;
+ retval = low_limb >> tnc;
+ high_limb = (low_limb << cnt);
+
+ for (i = n - 1; i != 0; i--)
+ {
+ low_limb = *--up;
+ *--rp = high_limb | (low_limb >> tnc);
+ high_limb = (low_limb << cnt);
+ }
+ *--rp = high_limb;
+
+ return retval;
+}
+
+mp_limb_t
+mpn_rshift (mp_ptr rp, mp_srcptr up, mp_size_t n, unsigned int cnt)
+{
+ mp_limb_t high_limb, low_limb;
+ unsigned int tnc;
+ mp_size_t i;
+ mp_limb_t retval;
+
+ assert (n >= 1);
+ assert (cnt >= 1);
+ assert (cnt < GMP_LIMB_BITS);
+
+ tnc = GMP_LIMB_BITS - cnt;
+ high_limb = *up++;
+ retval = (high_limb << tnc);
+ low_limb = high_limb >> cnt;
+
+ for (i = n - 1; i != 0; i--)
+ {
+ high_limb = *up++;
+ *rp++ = low_limb | (high_limb << tnc);
+ low_limb = high_limb >> cnt;
+ }
+ *rp = low_limb;
+
+ return retval;
+}
+
+
+/* MPN division interface. */
+mp_limb_t
+mpn_invert_3by2 (mp_limb_t u1, mp_limb_t u0)
+{
+ mp_limb_t r, p, m;
+ unsigned ul, uh;
+ unsigned ql, qh;
+
+ /* First, do a 2/1 inverse. */
+ /* The inverse m is defined as floor( (B^2 - 1 - u1)/u1 ), so that 0 <
+ * B^2 - (B + m) u1 <= u1 */
+ assert (u1 >= GMP_LIMB_HIGHBIT);
+
+ ul = u1 & GMP_LLIMB_MASK;
+ uh = u1 >> (GMP_LIMB_BITS / 2);
+
+ qh = ~u1 / uh;
+ r = ((~u1 - (mp_limb_t) qh * uh) << (GMP_LIMB_BITS / 2)) | GMP_LLIMB_MASK;
+
+ p = (mp_limb_t) qh * ul;
+ /* Adjustment steps taken from udiv_qrnnd_c */
+ if (r < p)
+ {
+ qh--;
+ r += u1;
+ if (r >= u1) /* i.e. we didn't get carry when adding to r */
+ if (r < p)
+ {
+ qh--;
+ r += u1;
+ }
+ }
+ r -= p;
+
+ /* Do a 3/2 division (with half limb size) */
+ p = (r >> (GMP_LIMB_BITS / 2)) * qh + r;
+ ql = (p >> (GMP_LIMB_BITS / 2)) + 1;
+
+ /* By the 3/2 method, we don't need the high half limb. */
+ r = (r << (GMP_LIMB_BITS / 2)) + GMP_LLIMB_MASK - ql * u1;
+
+ if (r >= (p << (GMP_LIMB_BITS / 2)))
+ {
+ ql--;
+ r += u1;
+ }
+ m = ((mp_limb_t) qh << (GMP_LIMB_BITS / 2)) + ql;
+ if (r >= u1)
+ {
+ m++;
+ r -= u1;
+ }
+
+ if (u0 > 0)
+ {
+ mp_limb_t th, tl;
+ r = ~r;
+ r += u0;
+ if (r < u0)
+ {
+ m--;
+ if (r >= u1)
+ {
+ m--;
+ r -= u1;
+ }
+ r -= u1;
+ }
+ gmp_umul_ppmm (th, tl, u0, m);
+ r += th;
+ if (r < th)
+ {
+ m--;
+ if (r > u1 || (r == u1 && tl > u0))
+ m--;
+ }
+ }
+
+ return m;
+}
+
+struct gmp_div_inverse
+{
+ /* Normalization shift count. */
+ unsigned shift;
+ /* Normalized divisor (d0 unused for mpn_div_qr_1) */
+ mp_limb_t d1, d0;
+ /* Inverse, for 2/1 or 3/2. */
+ mp_limb_t di;
+};
+
+static void
+mpn_div_qr_1_invert (struct gmp_div_inverse *inv, mp_limb_t d)
+{
+ unsigned shift;
+
+ assert (d > 0);
+ gmp_clz (shift, d);
+ inv->shift = shift;
+ inv->d1 = d << shift;
+ inv->di = mpn_invert_limb (inv->d1);
+}
+
+static void
+mpn_div_qr_2_invert (struct gmp_div_inverse *inv,
+ mp_limb_t d1, mp_limb_t d0)
+{
+ unsigned shift;
+
+ assert (d1 > 0);
+ gmp_clz (shift, d1);
+ inv->shift = shift;
+ if (shift > 0)
+ {
+ d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift));
+ d0 <<= shift;
+ }
+ inv->d1 = d1;
+ inv->d0 = d0;
+ inv->di = mpn_invert_3by2 (d1, d0);
+}
+
+static void
+mpn_div_qr_invert (struct gmp_div_inverse *inv,
+ mp_srcptr dp, mp_size_t dn)
+{
+ assert (dn > 0);
+
+ if (dn == 1)
+ mpn_div_qr_1_invert (inv, dp[0]);
+ else if (dn == 2)
+ mpn_div_qr_2_invert (inv, dp[1], dp[0]);
+ else
+ {
+ unsigned shift;
+ mp_limb_t d1, d0;
+
+ d1 = dp[dn-1];
+ d0 = dp[dn-2];
+ assert (d1 > 0);
+ gmp_clz (shift, d1);
+ inv->shift = shift;
+ if (shift > 0)
+ {
+ d1 = (d1 << shift) | (d0 >> (GMP_LIMB_BITS - shift));
+ d0 = (d0 << shift) | (dp[dn-3] >> (GMP_LIMB_BITS - shift));
+ }
+ inv->d1 = d1;
+ inv->d0 = d0;
+ inv->di = mpn_invert_3by2 (d1, d0);
+ }
+}
+
+/* Not matching current public gmp interface, rather corresponding to
+ the sbpi1_div_* functions. */
+static mp_limb_t
+mpn_div_qr_1_preinv (mp_ptr qp, mp_srcptr np, mp_size_t nn,
+ const struct gmp_div_inverse *inv)
+{
+ mp_limb_t d, di;
+ mp_limb_t r;
+ mp_ptr tp = NULL;
+
+ if (inv->shift > 0)
+ {
+ tp = gmp_xalloc_limbs (nn);
+ r = mpn_lshift (tp, np, nn, inv->shift);
+ np = tp;
+ }
+ else
+ r = 0;
+
+ d = inv->d1;
+ di = inv->di;
+ while (nn-- > 0)
+ {
+ mp_limb_t q;
+
+ gmp_udiv_qrnnd_preinv (q, r, r, np[nn], d, di);
+ if (qp)
+ qp[nn] = q;
+ }
+ if (inv->shift > 0)
+ gmp_free (tp);
+
+ return r >> inv->shift;
+}
+
+static mp_limb_t
+mpn_div_qr_1 (mp_ptr qp, mp_srcptr np, mp_size_t nn, mp_limb_t d)
+{
+ assert (d > 0);
+
+ /* Special case for powers of two. */
+ if (d > 1 && (d & (d-1)) == 0)
+ {
+ unsigned shift;
+ mp_limb_t r = np[0] & (d-1);
+ gmp_ctz (shift, d);
+ if (qp)
+ mpn_rshift (qp, np, nn, shift);
+
+ return r;
+ }
+ else
+ {
+ struct gmp_div_inverse inv;
+ mpn_div_qr_1_invert (&inv, d);
+ return mpn_div_qr_1_preinv (qp, np, nn, &inv);
+ }
+}
+
+static void
+mpn_div_qr_2_preinv (mp_ptr qp, mp_ptr rp, mp_srcptr np, mp_size_t nn,
+ const struct gmp_div_inverse *inv)
+{
+ unsigned shift;
+ mp_size_t i;
+ mp_limb_t d1, d0, di, r1, r0;
+ mp_ptr tp;
+
+ assert (nn >= 2);
+ shift = inv->shift;
+ d1 = inv->d1;
+ d0 = inv->d0;
+ di = inv->di;
+
+ if (shift > 0)
+ {
+ tp = gmp_xalloc_limbs (nn);
+ r1 = mpn_lshift (tp, np, nn, shift);
+ np = tp;
+ }
+ else
+ r1 = 0;
+
+ r0 = np[nn - 1];
+
+ for (i = nn - 2; i >= 0; i--)
+ {
+ mp_limb_t n0, q;
+ n0 = np[i];
+ gmp_udiv_qr_3by2 (q, r1, r0, r1, r0, n0, d1, d0, di);
+
+ if (qp)
+ qp[i] = q;
+ }
+
+ if (shift > 0)
+ {
+ assert ((r0 << (GMP_LIMB_BITS - shift)) == 0);
+ r0 = (r0 >> shift) | (r1 << (GMP_LIMB_BITS - shift));
+ r1 >>= shift;
+
+ gmp_free (tp);
+ }
+
+ rp[1] = r1;
+ rp[0] = r0;
+}
+
+#if 0
+static void
+mpn_div_qr_2 (mp_ptr qp, mp_ptr rp, mp_srcptr np, mp_size_t nn,
+ mp_limb_t d1, mp_limb_t d0)
+{
+ struct gmp_div_inverse inv;
+ assert (nn >= 2);
+
+ mpn_div_qr_2_invert (&inv, d1, d0);
+ mpn_div_qr_2_preinv (qp, rp, np, nn, &inv);
+}
+#endif
+
+static void
+mpn_div_qr_pi1 (mp_ptr qp,
+ mp_ptr np, mp_size_t nn, mp_limb_t n1,
+ mp_srcptr dp, mp_size_t dn,
+ mp_limb_t dinv)
+{
+ mp_size_t i;
+
+ mp_limb_t d1, d0;
+ mp_limb_t cy, cy1;
+ mp_limb_t q;
+
+ assert (dn > 2);
+ assert (nn >= dn);
+
+ d1 = dp[dn - 1];
+ d0 = dp[dn - 2];
+
+ assert ((d1 & GMP_LIMB_HIGHBIT) != 0);
+ /* Iteration variable is the index of the q limb.
+ *
+ * We divide <n1, np[dn-1+i], np[dn-2+i], np[dn-3+i],..., np[i]>
+ * by <d1, d0, dp[dn-3], ..., dp[0] >
+ */
+
+ for (i = nn - dn; i >= 0; i--)
+ {
+ mp_limb_t n0 = np[dn-1+i];
+
+ if (n1 == d1 && n0 == d0)
+ {
+ q = GMP_LIMB_MAX;
+ mpn_submul_1 (np+i, dp, dn, q);
+ n1 = np[dn-1+i]; /* update n1, last loop's value will now be invalid */
+ }
+ else
+ {
+ gmp_udiv_qr_3by2 (q, n1, n0, n1, n0, np[dn-2+i], d1, d0, dinv);
+
+ cy = mpn_submul_1 (np + i, dp, dn-2, q);
+
+ cy1 = n0 < cy;
+ n0 = n0 - cy;
+ cy = n1 < cy1;
+ n1 = n1 - cy1;
+ np[dn-2+i] = n0;
+
+ if (cy != 0)
+ {
+ n1 += d1 + mpn_add_n (np + i, np + i, dp, dn - 1);
+ q--;
+ }
+ }
+
+ if (qp)
+ qp[i] = q;
+ }
+
+ np[dn - 1] = n1;
+}
+
+static void
+mpn_div_qr_preinv (mp_ptr qp, mp_ptr np, mp_size_t nn,
+ mp_srcptr dp, mp_size_t dn,
+ const struct gmp_div_inverse *inv)
+{
+ assert (dn > 0);
+ assert (nn >= dn);
+
+ if (dn == 1)
+ np[0] = mpn_div_qr_1_preinv (qp, np, nn, inv);
+ else if (dn == 2)
+ mpn_div_qr_2_preinv (qp, np, np, nn, inv);
+ else
+ {
+ mp_limb_t nh;
+ unsigned shift;
+
+ assert (inv->d1 == dp[dn-1]);
+ assert (inv->d0 == dp[dn-2]);
+ assert ((inv->d1 & GMP_LIMB_HIGHBIT) != 0);
+
+ shift = inv->shift;
+ if (shift > 0)
+ nh = mpn_lshift (np, np, nn, shift);
+ else
+ nh = 0;
+
+ mpn_div_qr_pi1 (qp, np, nn, nh, dp, dn, inv->di);
+
+ if (shift > 0)
+ gmp_assert_nocarry (mpn_rshift (np, np, dn, shift));
+ }
+}
+
+static void
+mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn)
+{
+ struct gmp_div_inverse inv;
+ mp_ptr tp = NULL;
+
+ assert (dn > 0);
+ assert (nn >= dn);
+
+ mpn_div_qr_invert (&inv, dp, dn);
+ if (dn > 2 && inv.shift > 0)
+ {
+ tp = gmp_xalloc_limbs (dn);
+ gmp_assert_nocarry (mpn_lshift (tp, dp, dn, inv.shift));
+ dp = tp;
+ }
+ mpn_div_qr_preinv (qp, np, nn, dp, dn, &inv);
+ if (tp)
+ gmp_free (tp);
+}
+
+
+/* MPN base conversion. */
+static unsigned
+mpn_base_power_of_two_p (unsigned b)
+{
+ switch (b)
+ {
+ case 2: return 1;
+ case 4: return 2;
+ case 8: return 3;
+ case 16: return 4;
+ case 32: return 5;
+ case 64: return 6;
+ case 128: return 7;
+ case 256: return 8;
+ default: return 0;
+ }
+}
+
+struct mpn_base_info
+{
+ /* bb is the largest power of the base which fits in one limb, and
+ exp is the corresponding exponent. */
+ unsigned exp;
+ mp_limb_t bb;
+};
+
+static void
+mpn_get_base_info (struct mpn_base_info *info, mp_limb_t b)
+{
+ mp_limb_t m;
+ mp_limb_t p;
+ unsigned exp;
+
+ m = GMP_LIMB_MAX / b;
+ for (exp = 1, p = b; p <= m; exp++)
+ p *= b;
+
+ info->exp = exp;
+ info->bb = p;
+}
+
+static mp_bitcnt_t
+mpn_limb_size_in_base_2 (mp_limb_t u)
+{
+ unsigned shift;
+
+ assert (u > 0);
+ gmp_clz (shift, u);
+ return GMP_LIMB_BITS - shift;
+}
+
+static size_t
+mpn_get_str_bits (unsigned char *sp, unsigned bits, mp_srcptr up, mp_size_t un)
+{
+ unsigned char mask;
+ size_t sn, j;
+ mp_size_t i;
+ int shift;
+
+ sn = ((un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1])
+ + bits - 1) / bits;
+
+ mask = (1U << bits) - 1;
+
+ for (i = 0, j = sn, shift = 0; j-- > 0;)
+ {
+ unsigned char digit = up[i] >> shift;
+
+ shift += bits;
+
+ if (shift >= GMP_LIMB_BITS && ++i < un)
+ {
+ shift -= GMP_LIMB_BITS;
+ digit |= up[i] << (bits - shift);
+ }
+ sp[j] = digit & mask;
+ }
+ return sn;
+}
+
+/* We generate digits from the least significant end, and reverse at
+ the end. */
+static size_t
+mpn_limb_get_str (unsigned char *sp, mp_limb_t w,
+ const struct gmp_div_inverse *binv)
+{
+ mp_size_t i;
+ for (i = 0; w > 0; i++)
+ {
+ mp_limb_t h, l, r;
+
+ h = w >> (GMP_LIMB_BITS - binv->shift);
+ l = w << binv->shift;
+
+ gmp_udiv_qrnnd_preinv (w, r, h, l, binv->d1, binv->di);
+ assert ( (r << (GMP_LIMB_BITS - binv->shift)) == 0);
+ r >>= binv->shift;
+
+ sp[i] = r;
+ }
+ return i;
+}
+
+static size_t
+mpn_get_str_other (unsigned char *sp,
+ int base, const struct mpn_base_info *info,
+ mp_ptr up, mp_size_t un)
+{
+ struct gmp_div_inverse binv;
+ size_t sn;
+ size_t i;
+
+ mpn_div_qr_1_invert (&binv, base);
+
+ sn = 0;
+
+ if (un > 1)
+ {
+ struct gmp_div_inverse bbinv;
+ mpn_div_qr_1_invert (&bbinv, info->bb);
+
+ do
+ {
+ mp_limb_t w;
+ size_t done;
+ w = mpn_div_qr_1_preinv (up, up, un, &bbinv);
+ un -= (up[un-1] == 0);
+ done = mpn_limb_get_str (sp + sn, w, &binv);
+
+ for (sn += done; done < info->exp; done++)
+ sp[sn++] = 0;
+ }
+ while (un > 1);
+ }
+ sn += mpn_limb_get_str (sp + sn, up[0], &binv);
+
+ /* Reverse order */
+ for (i = 0; 2*i + 1 < sn; i++)
+ {
+ unsigned char t = sp[i];
+ sp[i] = sp[sn - i - 1];
+ sp[sn - i - 1] = t;
+ }
+
+ return sn;
+}
+
+size_t
+mpn_get_str (unsigned char *sp, int base, mp_ptr up, mp_size_t un)
+{
+ unsigned bits;
+
+ assert (un > 0);
+ assert (up[un-1] > 0);
+
+ bits = mpn_base_power_of_two_p (base);
+ if (bits)
+ return mpn_get_str_bits (sp, bits, up, un);
+ else
+ {
+ struct mpn_base_info info;
+
+ mpn_get_base_info (&info, base);
+ return mpn_get_str_other (sp, base, &info, up, un);
+ }
+}
+
+static mp_size_t
+mpn_set_str_bits (mp_ptr rp, const unsigned char *sp, size_t sn,
+ unsigned bits)
+{
+ mp_size_t rn;
+ size_t j;
+ unsigned shift;
+
+ for (j = sn, rn = 0, shift = 0; j-- > 0; )
+ {
+ if (shift == 0)
+ {
+ rp[rn++] = sp[j];
+ shift += bits;
+ }
+ else
+ {
+ rp[rn-1] |= (mp_limb_t) sp[j] << shift;
+ shift += bits;
+ if (shift >= GMP_LIMB_BITS)
+ {
+ shift -= GMP_LIMB_BITS;
+ if (shift > 0)
+ rp[rn++] = (mp_limb_t) sp[j] >> (bits - shift);
+ }
+ }
+ }
+ rn = mpn_normalized_size (rp, rn);
+ return rn;
+}
+
+static mp_size_t
+mpn_set_str_other (mp_ptr rp, const unsigned char *sp, size_t sn,
+ mp_limb_t b, const struct mpn_base_info *info)
+{
+ mp_size_t rn;
+ mp_limb_t w;
+ unsigned first;
+ unsigned k;
+ size_t j;
+
+ first = 1 + (sn - 1) % info->exp;
+
+ j = 0;
+ w = sp[j++];
+ for (k = 1; k < first; k++)
+ w = w * b + sp[j++];
+
+ rp[0] = w;
+
+ for (rn = (w > 0); j < sn;)
+ {
+ mp_limb_t cy;
+
+ w = sp[j++];
+ for (k = 1; k < info->exp; k++)
+ w = w * b + sp[j++];
+
+ cy = mpn_mul_1 (rp, rp, rn, info->bb);
+ cy += mpn_add_1 (rp, rp, rn, w);
+ if (cy > 0)
+ rp[rn++] = cy;
+ }
+ assert (j == sn);
+
+ return rn;
+}
+
+mp_size_t
+mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base)
+{
+ unsigned bits;
+
+ if (sn == 0)
+ return 0;
+
+ bits = mpn_base_power_of_two_p (base);
+ if (bits)
+ return mpn_set_str_bits (rp, sp, sn, bits);
+ else
+ {
+ struct mpn_base_info info;
+
+ mpn_get_base_info (&info, base);
+ return mpn_set_str_other (rp, sp, sn, base, &info);
+ }
+}
+
+
+/* MPZ interface */
+void
+mpz_init (mpz_t r)
+{
+ r->_mp_alloc = 1;
+ r->_mp_size = 0;
+ r->_mp_d = gmp_xalloc_limbs (1);
+}
+
+/* The utility of this function is a bit limited, since many functions
+ assings the result variable using mpz_swap. */
+void
+mpz_init2 (mpz_t r, mp_bitcnt_t bits)
+{
+ mp_size_t rn;
+
+ bits -= (bits != 0); /* Round down, except if 0 */
+ rn = 1 + bits / GMP_LIMB_BITS;
+
+ r->_mp_alloc = rn;
+ r->_mp_size = 0;
+ r->_mp_d = gmp_xalloc_limbs (rn);
+}
+
+void
+mpz_clear (mpz_t r)
+{
+ gmp_free (r->_mp_d);
+}
+
+static void *
+mpz_realloc (mpz_t r, mp_size_t size)
+{
+ size = GMP_MAX (size, 1);
+
+ r->_mp_d = gmp_xrealloc_limbs (r->_mp_d, size);
+ r->_mp_alloc = size;
+
+ if (GMP_ABS (r->_mp_size) > size)
+ r->_mp_size = 0;
+
+ return r->_mp_d;
+}
+
+/* Realloc for an mpz_t WHAT if it has less than NEEDED limbs. */
+#define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \
+ ? mpz_realloc(z,n) \
+ : (z)->_mp_d)
+
+/* MPZ assignment and basic conversions. */
+void
+mpz_set_si (mpz_t r, signed long int x)
+{
+ if (x >= 0)
+ mpz_set_ui (r, x);
+ else /* (x < 0) */
+ {
+ r->_mp_size = -1;
+ r->_mp_d[0] = GMP_NEG_CAST (unsigned long int, x);
+ }
+}
+
+void
+mpz_set_ui (mpz_t r, unsigned long int x)
+{
+ if (x > 0)
+ {
+ r->_mp_size = 1;
+ r->_mp_d[0] = x;
+ }
+ else
+ r->_mp_size = 0;
+}
+
+void
+mpz_set (mpz_t r, const mpz_t x)
+{
+ /* Allow the NOP r == x */
+ if (r != x)
+ {
+ mp_size_t n;
+ mp_ptr rp;
+
+ n = GMP_ABS (x->_mp_size);
+ rp = MPZ_REALLOC (r, n);
+
+ mpn_copyi (rp, x->_mp_d, n);
+ r->_mp_size = x->_mp_size;
+ }
+}
+
+void
+mpz_init_set_si (mpz_t r, signed long int x)
+{
+ mpz_init (r);
+ mpz_set_si (r, x);
+}
+
+void
+mpz_init_set_ui (mpz_t r, unsigned long int x)
+{
+ mpz_init (r);
+ mpz_set_ui (r, x);
+}
+
+void
+mpz_init_set (mpz_t r, const mpz_t x)
+{
+ mpz_init (r);
+ mpz_set (r, x);
+}
+
+int
+mpz_fits_slong_p (const mpz_t u)
+{
+ mp_size_t us = u->_mp_size;
+
+ if (us == 0)
+ return 1;
+ else if (us == 1)
+ return u->_mp_d[0] < GMP_LIMB_HIGHBIT;
+ else if (us == -1)
+ return u->_mp_d[0] <= GMP_LIMB_HIGHBIT;
+ else
+ return 0;
+}
+
+int
+mpz_fits_ulong_p (const mpz_t u)
+{
+ mp_size_t us = u->_mp_size;
+
+ return us == 0 || us == 1;
+}
+
+long int
+mpz_get_si (const mpz_t u)
+{
+ mp_size_t us = u->_mp_size;
+
+ if (us > 0)
+ return (long) (u->_mp_d[0] & ~GMP_LIMB_HIGHBIT);
+ else if (us < 0)
+ return (long) (- u->_mp_d[0] | GMP_LIMB_HIGHBIT);
+ else
+ return 0;
+}
+
+unsigned long int
+mpz_get_ui (const mpz_t u)
+{
+ return u->_mp_size == 0 ? 0 : u->_mp_d[0];
+}
+
+size_t
+mpz_size (const mpz_t u)
+{
+ return GMP_ABS (u->_mp_size);
+}
+
+mp_limb_t
+mpz_getlimbn (const mpz_t u, mp_size_t n)
+{
+ if (n >= 0 && n < GMP_ABS (u->_mp_size))
+ return u->_mp_d[n];
+ else
+ return 0;
+}
+
+
+/* Conversions and comparison to double. */
+void
+mpz_set_d (mpz_t r, double x)
+{
+ int sign;
+ mp_ptr rp;
+ mp_size_t rn, i;
+ double B;
+ double Bi;
+ mp_limb_t f;
+
+ /* x != x is true when x is a NaN, and x == x * 0.5 is true when x is
+ zero or infinity. */
+ if (x == 0.0 || x != x || x == x * 0.5)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ if (x < 0.0)
+ {
+ x = - x;
+ sign = 1;
+ }
+ else
+ sign = 0;
+
+ if (x < 1.0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+ B = 2.0 * (double) GMP_LIMB_HIGHBIT;
+ Bi = 1.0 / B;
+ for (rn = 1; x >= B; rn++)
+ x *= Bi;
+
+ rp = MPZ_REALLOC (r, rn);
+
+ f = (mp_limb_t) x;
+ x -= f;
+ assert (x < 1.0);
+ rp[rn-1] = f;
+ for (i = rn-1; i-- > 0; )
+ {
+ x = B * x;
+ f = (mp_limb_t) x;
+ x -= f;
+ assert (x < 1.0);
+ rp[i] = f;
+ }
+
+ r->_mp_size = sign ? - rn : rn;
+}
+
+void
+mpz_init_set_d (mpz_t r, double x)
+{
+ mpz_init (r);
+ mpz_set_d (r, x);
+}
+
+double
+mpz_get_d (const mpz_t u)
+{
+ mp_size_t un;
+ double x;
+ double B = 2.0 * (double) GMP_LIMB_HIGHBIT;
+
+ un = GMP_ABS (u->_mp_size);
+
+ if (un == 0)
+ return 0.0;
+
+ x = u->_mp_d[--un];
+ while (un > 0)
+ x = B*x + u->_mp_d[--un];
+
+ if (u->_mp_size < 0)
+ x = -x;
+
+ return x;
+}
+
+int
+mpz_cmpabs_d (const mpz_t x, double d)
+{
+ mp_size_t xn;
+ double B, Bi;
+ mp_size_t i;
+
+ xn = x->_mp_size;
+ d = GMP_ABS (d);
+
+ if (xn != 0)
+ {
+ xn = GMP_ABS (xn);
+
+ B = 2.0 * (double) GMP_LIMB_HIGHBIT;
+ Bi = 1.0 / B;
+
+ /* Scale d so it can be compared with the top limb. */
+ for (i = 1; i < xn; i++)
+ d *= Bi;
+
+ if (d >= B)
+ return -1;
+
+ /* Compare floor(d) to top limb, subtract and cancel when equal. */
+ for (i = xn; i-- > 0;)
+ {
+ mp_limb_t f, xl;
+
+ f = (mp_limb_t) d;
+ xl = x->_mp_d[i];
+ if (xl > f)
+ return 1;
+ else if (xl < f)
+ return -1;
+ d = B * (d - f);
+ }
+ }
+ return - (d > 0.0);
+}
+
+int
+mpz_cmp_d (const mpz_t x, double d)
+{
+ if (x->_mp_size < 0)
+ {
+ if (d >= 0.0)
+ return -1;
+ else
+ return -mpz_cmpabs_d (x, d);
+ }
+ else
+ {
+ if (d < 0.0)
+ return 1;
+ else
+ return mpz_cmpabs_d (x, d);
+ }
+}
+
+
+/* MPZ comparisons and the like. */
+int
+mpz_sgn (const mpz_t u)
+{
+ mp_size_t usize = u->_mp_size;
+
+ if (usize > 0)
+ return 1;
+ else if (usize < 0)
+ return -1;
+ else
+ return 0;
+}
+
+int
+mpz_cmp_si (const mpz_t u, long v)
+{
+ mp_size_t usize = u->_mp_size;
+
+ if (usize < -1)
+ return -1;
+ else if (v >= 0)
+ return mpz_cmp_ui (u, v);
+ else if (usize >= 0)
+ return 1;
+ else /* usize == -1 */
+ {
+ mp_limb_t ul = u->_mp_d[0];
+ if ((mp_limb_t)GMP_NEG_CAST (unsigned long int, v) < ul)
+ return -1;
+ else if ( (mp_limb_t)GMP_NEG_CAST (unsigned long int, v) > ul)
+ return 1;
+ }
+ return 0;
+}
+
+int
+mpz_cmp_ui (const mpz_t u, unsigned long v)
+{
+ mp_size_t usize = u->_mp_size;
+
+ if (usize > 1)
+ return 1;
+ else if (usize < 0)
+ return -1;
+ else
+ {
+ mp_limb_t ul = (usize > 0) ? u->_mp_d[0] : 0;
+ if (ul > v)
+ return 1;
+ else if (ul < v)
+ return -1;
+ }
+ return 0;
+}
+
+int
+mpz_cmp (const mpz_t a, const mpz_t b)
+{
+ mp_size_t asize = a->_mp_size;
+ mp_size_t bsize = b->_mp_size;
+
+ if (asize > bsize)
+ return 1;
+ else if (asize < bsize)
+ return -1;
+ else if (asize > 0)
+ return mpn_cmp (a->_mp_d, b->_mp_d, asize);
+ else if (asize < 0)
+ return -mpn_cmp (a->_mp_d, b->_mp_d, -asize);
+ else
+ return 0;
+}
+
+int
+mpz_cmpabs_ui (const mpz_t u, unsigned long v)
+{
+ mp_size_t un = GMP_ABS (u->_mp_size);
+ mp_limb_t ul;
+
+ if (un > 1)
+ return 1;
+
+ ul = (un == 1) ? u->_mp_d[0] : 0;
+
+ if (ul > v)
+ return 1;
+ else if (ul < v)
+ return -1;
+ else
+ return 0;
+}
+
+int
+mpz_cmpabs (const mpz_t u, const mpz_t v)
+{
+ return mpn_cmp4 (u->_mp_d, GMP_ABS (u->_mp_size),
+ v->_mp_d, GMP_ABS (v->_mp_size));
+}
+
+void
+mpz_abs (mpz_t r, const mpz_t u)
+{
+ if (r != u)
+ mpz_set (r, u);
+
+ r->_mp_size = GMP_ABS (r->_mp_size);
+}
+
+void
+mpz_neg (mpz_t r, const mpz_t u)
+{
+ if (r != u)
+ mpz_set (r, u);
+
+ r->_mp_size = -r->_mp_size;
+}
+
+void
+mpz_swap (mpz_t u, mpz_t v)
+{
+ MP_SIZE_T_SWAP (u->_mp_size, v->_mp_size);
+ MP_SIZE_T_SWAP (u->_mp_alloc, v->_mp_alloc);
+ MP_PTR_SWAP (u->_mp_d, v->_mp_d);
+}
+
+
+/* MPZ addition and subtraction */
+
+/* Adds to the absolute value. Returns new size, but doesn't store it. */
+static mp_size_t
+mpz_abs_add_ui (mpz_t r, const mpz_t a, unsigned long b)
+{
+ mp_size_t an;
+ mp_ptr rp;
+ mp_limb_t cy;
+
+ an = GMP_ABS (a->_mp_size);
+ if (an == 0)
+ {
+ r->_mp_d[0] = b;
+ return b > 0;
+ }
+
+ rp = MPZ_REALLOC (r, an + 1);
+
+ cy = mpn_add_1 (rp, a->_mp_d, an, b);
+ rp[an] = cy;
+ an += (cy > 0);
+
+ return an;
+}
+
+/* Subtract from the absolute value. Returns new size, (or -1 on underflow),
+ but doesn't store it. */
+static mp_size_t
+mpz_abs_sub_ui (mpz_t r, const mpz_t a, unsigned long b)
+{
+ mp_size_t an = GMP_ABS (a->_mp_size);
+ mp_ptr rp = MPZ_REALLOC (r, an);
+
+ if (an == 0)
+ {
+ rp[0] = b;
+ return -(b > 0);
+ }
+ else if (an == 1 && a->_mp_d[0] < b)
+ {
+ rp[0] = b - a->_mp_d[0];
+ return -1;
+ }
+ else
+ {
+ gmp_assert_nocarry (mpn_sub_1 (rp, a->_mp_d, an, b));
+ return mpn_normalized_size (rp, an);
+ }
+}
+
+void
+mpz_add_ui (mpz_t r, const mpz_t a, unsigned long b)
+{
+ if (a->_mp_size >= 0)
+ r->_mp_size = mpz_abs_add_ui (r, a, b);
+ else
+ r->_mp_size = -mpz_abs_sub_ui (r, a, b);
+}
+
+void
+mpz_sub_ui (mpz_t r, const mpz_t a, unsigned long b)
+{
+ if (a->_mp_size < 0)
+ r->_mp_size = -mpz_abs_add_ui (r, a, b);
+ else
+ r->_mp_size = mpz_abs_sub_ui (r, a, b);
+}
+
+void
+mpz_ui_sub (mpz_t r, unsigned long a, const mpz_t b)
+{
+ if (b->_mp_size < 0)
+ r->_mp_size = mpz_abs_add_ui (r, b, a);
+ else
+ r->_mp_size = -mpz_abs_sub_ui (r, b, a);
+}
+
+static mp_size_t
+mpz_abs_add (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t an = GMP_ABS (a->_mp_size);
+ mp_size_t bn = GMP_ABS (b->_mp_size);
+ mp_size_t rn;
+ mp_ptr rp;
+ mp_limb_t cy;
+
+ rn = GMP_MAX (an, bn);
+ rp = MPZ_REALLOC (r, rn + 1);
+ if (an >= bn)
+ cy = mpn_add (rp, a->_mp_d, an, b->_mp_d, bn);
+ else
+ cy = mpn_add (rp, b->_mp_d, bn, a->_mp_d, an);
+
+ rp[rn] = cy;
+
+ return rn + (cy > 0);
+}
+
+static mp_size_t
+mpz_abs_sub (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t an = GMP_ABS (a->_mp_size);
+ mp_size_t bn = GMP_ABS (b->_mp_size);
+ int cmp;
+ mp_ptr rp;
+
+ cmp = mpn_cmp4 (a->_mp_d, an, b->_mp_d, bn);
+ if (cmp > 0)
+ {
+ rp = MPZ_REALLOC (r, an);
+ gmp_assert_nocarry (mpn_sub (rp, a->_mp_d, an, b->_mp_d, bn));
+ return mpn_normalized_size (rp, an);
+ }
+ else if (cmp < 0)
+ {
+ rp = MPZ_REALLOC (r, bn);
+ gmp_assert_nocarry (mpn_sub (rp, b->_mp_d, bn, a->_mp_d, an));
+ return -mpn_normalized_size (rp, bn);
+ }
+ else
+ return 0;
+}
+
+void
+mpz_add (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t rn;
+
+ if ( (a->_mp_size ^ b->_mp_size) >= 0)
+ rn = mpz_abs_add (r, a, b);
+ else
+ rn = mpz_abs_sub (r, a, b);
+
+ r->_mp_size = a->_mp_size >= 0 ? rn : - rn;
+}
+
+void
+mpz_sub (mpz_t r, const mpz_t a, const mpz_t b)
+{
+ mp_size_t rn;
+
+ if ( (a->_mp_size ^ b->_mp_size) >= 0)
+ rn = mpz_abs_sub (r, a, b);
+ else
+ rn = mpz_abs_add (r, a, b);
+
+ r->_mp_size = a->_mp_size >= 0 ? rn : - rn;
+}
+
+
+/* MPZ multiplication */
+void
+mpz_mul_si (mpz_t r, const mpz_t u, long int v)
+{
+ if (v < 0)
+ {
+ mpz_mul_ui (r, u, GMP_NEG_CAST (unsigned long int, v));
+ mpz_neg (r, r);
+ }
+ else
+ mpz_mul_ui (r, u, (unsigned long int) v);
+}
+
+void
+mpz_mul_ui (mpz_t r, const mpz_t u, unsigned long int v)
+{
+ mp_size_t un;
+ mpz_t t;
+ mp_ptr tp;
+ mp_limb_t cy;
+
+ un = GMP_ABS (u->_mp_size);
+
+ if (un == 0 || v == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ mpz_init2 (t, (un + 1) * GMP_LIMB_BITS);
+
+ tp = t->_mp_d;
+ cy = mpn_mul_1 (tp, u->_mp_d, un, v);
+ tp[un] = cy;
+
+ t->_mp_size = un + (cy > 0);
+ if (u->_mp_size < 0)
+ t->_mp_size = - t->_mp_size;
+
+ mpz_swap (r, t);
+ mpz_clear (t);
+}
+
+void
+mpz_mul (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ int sign;
+ mp_size_t un, vn, rn;
+ mpz_t t;
+ mp_ptr tp;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+
+ if (un == 0 || vn == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ sign = (u->_mp_size ^ v->_mp_size) < 0;
+
+ mpz_init2 (t, (un + vn) * GMP_LIMB_BITS);
+
+ tp = t->_mp_d;
+ if (un >= vn)
+ mpn_mul (tp, u->_mp_d, un, v->_mp_d, vn);
+ else
+ mpn_mul (tp, v->_mp_d, vn, u->_mp_d, un);
+
+ rn = un + vn;
+ rn -= tp[rn-1] == 0;
+
+ t->_mp_size = sign ? - rn : rn;
+ mpz_swap (r, t);
+ mpz_clear (t);
+}
+
+void
+mpz_mul_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bits)
+{
+ mp_size_t un, rn;
+ mp_size_t limbs;
+ unsigned shift;
+ mp_ptr rp;
+
+ un = GMP_ABS (u->_mp_size);
+ if (un == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ limbs = bits / GMP_LIMB_BITS;
+ shift = bits % GMP_LIMB_BITS;
+
+ rn = un + limbs + (shift > 0);
+ rp = MPZ_REALLOC (r, rn);
+ if (shift > 0)
+ {
+ mp_limb_t cy = mpn_lshift (rp + limbs, u->_mp_d, un, shift);
+ rp[rn-1] = cy;
+ rn -= (cy == 0);
+ }
+ else
+ mpn_copyd (rp + limbs, u->_mp_d, un);
+
+ while (limbs > 0)
+ rp[--limbs] = 0;
+
+ r->_mp_size = (u->_mp_size < 0) ? - rn : rn;
+}
+
+
+/* MPZ division */
+enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC };
+
+/* Allows q or r to be zero. Returns 1 iff remainder is non-zero. */
+static int
+mpz_div_qr (mpz_t q, mpz_t r,
+ const mpz_t n, const mpz_t d, enum mpz_div_round_mode mode)
+{
+ mp_size_t ns, ds, nn, dn, qs;
+ ns = n->_mp_size;
+ ds = d->_mp_size;
+
+ if (ds == 0)
+ gmp_die("mpz_div_qr: Divide by zero.");
+
+ if (ns == 0)
+ {
+ if (q)
+ q->_mp_size = 0;
+ if (r)
+ r->_mp_size = 0;
+ return 0;
+ }
+
+ nn = GMP_ABS (ns);
+ dn = GMP_ABS (ds);
+
+ qs = ds ^ ns;
+
+ if (nn < dn)
+ {
+ if (mode == GMP_DIV_CEIL && qs >= 0)
+ {
+ /* q = 1, r = n - d */
+ if (r)
+ mpz_sub (r, n, d);
+ if (q)
+ mpz_set_ui (q, 1);
+ }
+ else if (mode == GMP_DIV_FLOOR && qs < 0)
+ {
+ /* q = -1, r = n + d */
+ if (r)
+ mpz_add (r, n, d);
+ if (q)
+ mpz_set_si (q, -1);
+ }
+ else
+ {
+ /* q = 0, r = d */
+ if (r)
+ mpz_set (r, n);
+ if (q)
+ q->_mp_size = 0;
+ }
+ return 1;
+ }
+ else
+ {
+ mp_ptr np, qp;
+ mp_size_t qn, rn;
+ mpz_t tq, tr;
+
+ mpz_init (tr);
+ mpz_set (tr, n);
+ np = tr->_mp_d;
+
+ qn = nn - dn + 1;
+
+ if (q)
+ {
+ mpz_init2 (tq, qn * GMP_LIMB_BITS);
+ qp = tq->_mp_d;
+ }
+ else
+ qp = NULL;
+
+ mpn_div_qr (qp, np, nn, d->_mp_d, dn);
+
+ if (qp)
+ {
+ qn -= (qp[qn-1] == 0);
+
+ tq->_mp_size = qs < 0 ? -qn : qn;
+ }
+ rn = mpn_normalized_size (np, dn);
+ tr->_mp_size = ns < 0 ? - rn : rn;
+
+ if (mode == GMP_DIV_FLOOR && qs < 0 && rn != 0)
+ {
+ if (q)
+ mpz_sub_ui (tq, tq, 1);
+ if (r)
+ mpz_add (tr, tr, d);
+ }
+ else if (mode == GMP_DIV_CEIL && qs >= 0 && rn != 0)
+ {
+ if (q)
+ mpz_add_ui (tq, tq, 1);
+ if (r)
+ mpz_sub (tr, tr, d);
+ }
+
+ if (q)
+ {
+ mpz_swap (tq, q);
+ mpz_clear (tq);
+ }
+ if (r)
+ mpz_swap (tr, r);
+
+ mpz_clear (tr);
+
+ return rn != 0;
+ }
+}
+
+void
+mpz_cdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, r, n, d, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, r, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_qr (mpz_t q, mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, r, n, d, GMP_DIV_TRUNC);
+}
+
+void
+mpz_cdiv_q (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, NULL, n, d, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_q (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, NULL, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_q (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC);
+}
+
+void
+mpz_cdiv_r (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_r (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_r (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_TRUNC);
+}
+
+void
+mpz_mod (mpz_t r, const mpz_t n, const mpz_t d)
+{
+ if (d->_mp_size >= 0)
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_FLOOR);
+ else
+ mpz_div_qr (NULL, r, n, d, GMP_DIV_CEIL);
+}
+
+static void
+mpz_div_q_2exp (mpz_t q, const mpz_t u, mp_bitcnt_t bit_index,
+ enum mpz_div_round_mode mode)
+{
+ mp_size_t un, qn;
+ mp_size_t limb_cnt;
+ mp_ptr qp;
+ mp_limb_t adjust;
+
+ un = u->_mp_size;
+ if (un == 0)
+ {
+ q->_mp_size = 0;
+ return;
+ }
+ limb_cnt = bit_index / GMP_LIMB_BITS;
+ qn = GMP_ABS (un) - limb_cnt;
+ bit_index %= GMP_LIMB_BITS;
+
+ if (mode == ((un > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* un != 0 here. */
+ /* Note: Below, the final indexing at limb_cnt is valid because at
+ that point we have qn > 0. */
+ adjust = (qn <= 0
+ || !mpn_zero_p (u->_mp_d, limb_cnt)
+ || (u->_mp_d[limb_cnt]
+ & (((mp_limb_t) 1 << bit_index) - 1)));
+ else
+ adjust = 0;
+
+ if (qn <= 0)
+ qn = 0;
+
+ else
+ {
+ qp = MPZ_REALLOC (q, qn);
+
+ if (bit_index != 0)
+ {
+ mpn_rshift (qp, u->_mp_d + limb_cnt, qn, bit_index);
+ qn -= qp[qn - 1] == 0;
+ }
+ else
+ {
+ mpn_copyi (qp, u->_mp_d + limb_cnt, qn);
+ }
+ }
+
+ q->_mp_size = qn;
+
+ mpz_add_ui (q, q, adjust);
+ if (un < 0)
+ mpz_neg (q, q);
+}
+
+static void
+mpz_div_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t bit_index,
+ enum mpz_div_round_mode mode)
+{
+ mp_size_t us, un, rn;
+ mp_ptr rp;
+ mp_limb_t mask;
+
+ us = u->_mp_size;
+ if (us == 0 || bit_index == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+ rn = (bit_index + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS;
+ assert (rn > 0);
+
+ rp = MPZ_REALLOC (r, rn);
+ un = GMP_ABS (us);
+
+ mask = GMP_LIMB_MAX >> (rn * GMP_LIMB_BITS - bit_index);
+
+ if (rn > un)
+ {
+ /* Quotient (with truncation) is zero, and remainder is
+ non-zero */
+ if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */
+ {
+ /* Have to negate and sign extend. */
+ mp_size_t i;
+ mp_limb_t cy;
+
+ for (cy = 1, i = 0; i < un; i++)
+ {
+ mp_limb_t s = ~u->_mp_d[i] + cy;
+ cy = s < cy;
+ rp[i] = s;
+ }
+ assert (cy == 0);
+ for (; i < rn - 1; i++)
+ rp[i] = GMP_LIMB_MAX;
+
+ rp[rn-1] = mask;
+ us = -us;
+ }
+ else
+ {
+ /* Just copy */
+ if (r != u)
+ mpn_copyi (rp, u->_mp_d, un);
+
+ rn = un;
+ }
+ }
+ else
+ {
+ if (r != u)
+ mpn_copyi (rp, u->_mp_d, rn - 1);
+
+ rp[rn-1] = u->_mp_d[rn-1] & mask;
+
+ if (mode == ((us > 0) ? GMP_DIV_CEIL : GMP_DIV_FLOOR)) /* us != 0 here. */
+ {
+ /* If r != 0, compute 2^{bit_count} - r. */
+ mp_size_t i;
+
+ for (i = 0; i < rn && rp[i] == 0; i++)
+ ;
+ if (i < rn)
+ {
+ /* r > 0, need to flip sign. */
+ rp[i] = ~rp[i] + 1;
+ for (i++; i < rn; i++)
+ rp[i] = ~rp[i];
+
+ rp[rn-1] &= mask;
+
+ /* us is not used for anything else, so we can modify it
+ here to indicate flipped sign. */
+ us = -us;
+ }
+ }
+ }
+ rn = mpn_normalized_size (rp, rn);
+ r->_mp_size = us < 0 ? -rn : rn;
+}
+
+void
+mpz_cdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_q_2exp (r, u, cnt, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_q_2exp (r, u, cnt, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_q_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_q_2exp (r, u, cnt, GMP_DIV_TRUNC);
+}
+
+void
+mpz_cdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_r_2exp (r, u, cnt, GMP_DIV_CEIL);
+}
+
+void
+mpz_fdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_r_2exp (r, u, cnt, GMP_DIV_FLOOR);
+}
+
+void
+mpz_tdiv_r_2exp (mpz_t r, const mpz_t u, mp_bitcnt_t cnt)
+{
+ mpz_div_r_2exp (r, u, cnt, GMP_DIV_TRUNC);
+}
+
+void
+mpz_divexact (mpz_t q, const mpz_t n, const mpz_t d)
+{
+ gmp_assert_nocarry (mpz_div_qr (q, NULL, n, d, GMP_DIV_TRUNC));
+}
+
+int
+mpz_divisible_p (const mpz_t n, const mpz_t d)
+{
+ return mpz_div_qr (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0;
+}
+
+static unsigned long
+mpz_div_qr_ui (mpz_t q, mpz_t r,
+ const mpz_t n, unsigned long d, enum mpz_div_round_mode mode)
+{
+ mp_size_t ns, qn;
+ mp_ptr qp;
+ mp_limb_t rl;
+ mp_size_t rs;
+
+ ns = n->_mp_size;
+ if (ns == 0)
+ {
+ if (q)
+ q->_mp_size = 0;
+ if (r)
+ r->_mp_size = 0;
+ return 0;
+ }
+
+ qn = GMP_ABS (ns);
+ if (q)
+ qp = MPZ_REALLOC (q, qn);
+ else
+ qp = NULL;
+
+ rl = mpn_div_qr_1 (qp, n->_mp_d, qn, d);
+ assert (rl < d);
+
+ rs = rl > 0;
+ rs = (ns < 0) ? -rs : rs;
+
+ if (rl > 0 && ( (mode == GMP_DIV_FLOOR && ns < 0)
+ || (mode == GMP_DIV_CEIL && ns >= 0)))
+ {
+ if (q)
+ gmp_assert_nocarry (mpn_add_1 (qp, qp, qn, 1));
+ rl = d - rl;
+ rs = -rs;
+ }
+
+ if (r)
+ {
+ r->_mp_d[0] = rl;
+ r->_mp_size = rs;
+ }
+ if (q)
+ {
+ qn -= (qp[qn-1] == 0);
+ assert (qn == 0 || qp[qn-1] > 0);
+
+ q->_mp_size = (ns < 0) ? - qn : qn;
+ }
+
+ return rl;
+}
+
+unsigned long
+mpz_cdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, r, n, d, GMP_DIV_CEIL);
+}
+
+unsigned long
+mpz_fdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, r, n, d, GMP_DIV_FLOOR);
+}
+
+unsigned long
+mpz_tdiv_qr_ui (mpz_t q, mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, r, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_cdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_CEIL);
+}
+
+unsigned long
+mpz_fdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_FLOOR);
+}
+
+unsigned long
+mpz_tdiv_q_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_cdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_CEIL);
+}
+unsigned long
+mpz_fdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR);
+}
+unsigned long
+mpz_tdiv_r_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_cdiv_ui (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_CEIL);
+}
+
+unsigned long
+mpz_fdiv_ui (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_FLOOR);
+}
+
+unsigned long
+mpz_tdiv_ui (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC);
+}
+
+unsigned long
+mpz_mod_ui (mpz_t r, const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, r, n, d, GMP_DIV_FLOOR);
+}
+
+void
+mpz_divexact_ui (mpz_t q, const mpz_t n, unsigned long d)
+{
+ gmp_assert_nocarry (mpz_div_qr_ui (q, NULL, n, d, GMP_DIV_TRUNC));
+}
+
+int
+mpz_divisible_ui_p (const mpz_t n, unsigned long d)
+{
+ return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0;
+}
+
+
+/* GCD */
+static mp_limb_t
+mpn_gcd_11 (mp_limb_t u, mp_limb_t v)
+{
+ unsigned shift;
+
+ assert ( (u | v) > 0);
+
+ if (u == 0)
+ return v;
+ else if (v == 0)
+ return u;
+
+ gmp_ctz (shift, u | v);
+
+ u >>= shift;
+ v >>= shift;
+
+ if ( (u & 1) == 0)
+ MP_LIMB_T_SWAP (u, v);
+
+ while ( (v & 1) == 0)
+ v >>= 1;
+
+ while (u != v)
+ {
+ if (u > v)
+ {
+ u -= v;
+ do
+ u >>= 1;
+ while ( (u & 1) == 0);
+ }
+ else
+ {
+ v -= u;
+ do
+ v >>= 1;
+ while ( (v & 1) == 0);
+ }
+ }
+ return u << shift;
+}
+
+unsigned long
+mpz_gcd_ui (mpz_t g, const mpz_t u, unsigned long v)
+{
+ mp_size_t un;
+
+ if (v == 0)
+ {
+ if (g)
+ mpz_abs (g, u);
+ }
+ else
+ {
+ un = GMP_ABS (u->_mp_size);
+ if (un != 0)
+ v = mpn_gcd_11 (mpn_div_qr_1 (NULL, u->_mp_d, un, v), v);
+
+ if (g)
+ mpz_set_ui (g, v);
+ }
+
+ return v;
+}
+
+static mp_bitcnt_t
+mpz_make_odd (mpz_t r, const mpz_t u)
+{
+ mp_size_t un, rn, i;
+ mp_ptr rp;
+ unsigned shift;
+
+ un = GMP_ABS (u->_mp_size);
+ assert (un > 0);
+
+ for (i = 0; u->_mp_d[i] == 0; i++)
+ ;
+
+ gmp_ctz (shift, u->_mp_d[i]);
+
+ rn = un - i;
+ rp = MPZ_REALLOC (r, rn);
+ if (shift > 0)
+ {
+ mpn_rshift (rp, u->_mp_d + i, rn, shift);
+ rn -= (rp[rn-1] == 0);
+ }
+ else
+ mpn_copyi (rp, u->_mp_d + i, rn);
+
+ r->_mp_size = rn;
+ return i * GMP_LIMB_BITS + shift;
+}
+
+void
+mpz_gcd (mpz_t g, const mpz_t u, const mpz_t v)
+{
+ mpz_t tu, tv;
+ mp_bitcnt_t uz, vz, gz;
+
+ if (u->_mp_size == 0)
+ {
+ mpz_abs (g, v);
+ return;
+ }
+ if (v->_mp_size == 0)
+ {
+ mpz_abs (g, u);
+ return;
+ }
+
+ mpz_init (tu);
+ mpz_init (tv);
+
+ uz = mpz_make_odd (tu, u);
+ vz = mpz_make_odd (tv, v);
+ gz = GMP_MIN (uz, vz);
+
+ if (tu->_mp_size < tv->_mp_size)
+ mpz_swap (tu, tv);
+
+ mpz_tdiv_r (tu, tu, tv);
+ if (tu->_mp_size == 0)
+ {
+ mpz_swap (g, tv);
+ }
+ else
+ for (;;)
+ {
+ int c;
+
+ mpz_make_odd (tu, tu);
+ c = mpz_cmp (tu, tv);
+ if (c == 0)
+ {
+ mpz_swap (g, tu);
+ break;
+ }
+ if (c < 0)
+ mpz_swap (tu, tv);
+
+ if (tv->_mp_size == 1)
+ {
+ mp_limb_t vl = tv->_mp_d[0];
+ mp_limb_t ul = mpz_tdiv_ui (tu, vl);
+ mpz_set_ui (g, mpn_gcd_11 (ul, vl));
+ break;
+ }
+ mpz_sub (tu, tu, tv);
+ }
+ mpz_clear (tu);
+ mpz_clear (tv);
+ mpz_mul_2exp (g, g, gz);
+}
+
+void
+mpz_gcdext (mpz_t g, mpz_t s, mpz_t t, const mpz_t u, const mpz_t v)
+{
+ mpz_t tu, tv, s0, s1, t0, t1;
+ mp_bitcnt_t uz, vz, gz;
+ mp_bitcnt_t power;
+
+ if (u->_mp_size == 0)
+ {
+ /* g = 0 u + sgn(v) v */
+ signed long sign = mpz_sgn (v);
+ mpz_abs (g, v);
+ if (s)
+ mpz_set_ui (s, 0);
+ if (t)
+ mpz_set_si (t, sign);
+ return;
+ }
+
+ if (v->_mp_size == 0)
+ {
+ /* g = sgn(u) u + 0 v */
+ signed long sign = mpz_sgn (u);
+ mpz_abs (g, u);
+ if (s)
+ mpz_set_si (s, sign);
+ if (t)
+ mpz_set_ui (t, 0);
+ return;
+ }
+
+ mpz_init (tu);
+ mpz_init (tv);
+ mpz_init (s0);
+ mpz_init (s1);
+ mpz_init (t0);
+ mpz_init (t1);
+
+ uz = mpz_make_odd (tu, u);
+ vz = mpz_make_odd (tv, v);
+ gz = GMP_MIN (uz, vz);
+
+ uz -= gz;
+ vz -= gz;
+
+ /* Cofactors corresponding to odd gcd. gz handled later. */
+ if (tu->_mp_size < tv->_mp_size)
+ {
+ mpz_swap (tu, tv);
+ MPZ_SRCPTR_SWAP (u, v);
+ MPZ_PTR_SWAP (s, t);
+ MP_BITCNT_T_SWAP (uz, vz);
+ }
+
+ /* Maintain
+ *
+ * u = t0 tu + t1 tv
+ * v = s0 tu + s1 tv
+ *
+ * where u and v denote the inputs with common factors of two
+ * eliminated, and det (s0, t0; s1, t1) = 2^p. Then
+ *
+ * 2^p tu = s1 u - t1 v
+ * 2^p tv = -s0 u + t0 v
+ */
+
+ /* After initial division, tu = q tv + tu', we have
+ *
+ * u = 2^uz (tu' + q tv)
+ * v = 2^vz tv
+ *
+ * or
+ *
+ * t0 = 2^uz, t1 = 2^uz q
+ * s0 = 0, s1 = 2^vz
+ */
+
+ mpz_setbit (t0, uz);
+ mpz_tdiv_qr (t1, tu, tu, tv);
+ mpz_mul_2exp (t1, t1, uz);
+
+ mpz_setbit (s1, vz);
+ power = uz + vz;
+
+ if (tu->_mp_size > 0)
+ {
+ mp_bitcnt_t shift;
+ shift = mpz_make_odd (tu, tu);
+ mpz_mul_2exp (t0, t0, shift);
+ mpz_mul_2exp (s0, s0, shift);
+ power += shift;
+
+ for (;;)
+ {
+ int c;
+ c = mpz_cmp (tu, tv);
+ if (c == 0)
+ break;
+
+ if (c < 0)
+ {
+ /* tv = tv' + tu
+ *
+ * u = t0 tu + t1 (tv' + tu) = (t0 + t1) tu + t1 tv'
+ * v = s0 tu + s1 (tv' + tu) = (s0 + s1) tu + s1 tv' */
+
+ mpz_sub (tv, tv, tu);
+ mpz_add (t0, t0, t1);
+ mpz_add (s0, s0, s1);
+
+ shift = mpz_make_odd (tv, tv);
+ mpz_mul_2exp (t1, t1, shift);
+ mpz_mul_2exp (s1, s1, shift);
+ }
+ else
+ {
+ mpz_sub (tu, tu, tv);
+ mpz_add (t1, t0, t1);
+ mpz_add (s1, s0, s1);
+
+ shift = mpz_make_odd (tu, tu);
+ mpz_mul_2exp (t0, t0, shift);
+ mpz_mul_2exp (s0, s0, shift);
+ }
+ power += shift;
+ }
+ }
+
+ /* Now tv = odd part of gcd, and -s0 and t0 are corresponding
+ cofactors. */
+
+ mpz_mul_2exp (tv, tv, gz);
+ mpz_neg (s0, s0);
+
+ /* 2^p g = s0 u + t0 v. Eliminate one factor of two at a time. To
+ adjust cofactors, we need u / g and v / g */
+
+ mpz_divexact (s1, v, tv);
+ mpz_abs (s1, s1);
+ mpz_divexact (t1, u, tv);
+ mpz_abs (t1, t1);
+
+ while (power-- > 0)
+ {
+ /* s0 u + t0 v = (s0 - v/g) u - (t0 + u/g) v */
+ if (mpz_odd_p (s0) || mpz_odd_p (t0))
+ {
+ mpz_sub (s0, s0, s1);
+ mpz_add (t0, t0, t1);
+ }
+ mpz_divexact_ui (s0, s0, 2);
+ mpz_divexact_ui (t0, t0, 2);
+ }
+
+ /* Arrange so that |s| < |u| / 2g */
+ mpz_add (s1, s0, s1);
+ if (mpz_cmpabs (s0, s1) > 0)
+ {
+ mpz_swap (s0, s1);
+ mpz_sub (t0, t0, t1);
+ }
+ if (u->_mp_size < 0)
+ mpz_neg (s0, s0);
+ if (v->_mp_size < 0)
+ mpz_neg (t0, t0);
+
+ mpz_swap (g, tv);
+ if (s)
+ mpz_swap (s, s0);
+ if (t)
+ mpz_swap (t, t0);
+
+ mpz_clear (tu);
+ mpz_clear (tv);
+ mpz_clear (s0);
+ mpz_clear (s1);
+ mpz_clear (t0);
+ mpz_clear (t1);
+}
+
+void
+mpz_lcm (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mpz_t g;
+
+ if (u->_mp_size == 0 || v->_mp_size == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ mpz_init (g);
+
+ mpz_gcd (g, u, v);
+ mpz_divexact (g, u, g);
+ mpz_mul (r, g, v);
+
+ mpz_clear (g);
+ mpz_abs (r, r);
+}
+
+void
+mpz_lcm_ui (mpz_t r, const mpz_t u, unsigned long v)
+{
+ if (v == 0 || u->_mp_size == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ v /= mpz_gcd_ui (NULL, u, v);
+ mpz_mul_ui (r, u, v);
+
+ mpz_abs (r, r);
+}
+
+int
+mpz_invert (mpz_t r, const mpz_t u, const mpz_t m)
+{
+ mpz_t g, tr;
+ int invertible;
+
+ if (u->_mp_size == 0 || mpz_cmpabs_ui (m, 1) <= 0)
+ return 0;
+
+ mpz_init (g);
+ mpz_init (tr);
+
+ mpz_gcdext (g, tr, NULL, u, m);
+ invertible = (mpz_cmp_ui (g, 1) == 0);
+
+ if (invertible)
+ {
+ if (tr->_mp_size < 0)
+ {
+ if (m->_mp_size >= 0)
+ mpz_add (tr, tr, m);
+ else
+ mpz_sub (tr, tr, m);
+ }
+ mpz_swap (r, tr);
+ }
+
+ mpz_clear (g);
+ mpz_clear (tr);
+ return invertible;
+}
+
+
+/* Higher level operations (sqrt, pow and root) */
+
+void
+mpz_pow_ui (mpz_t r, const mpz_t b, unsigned long e)
+{
+ unsigned long bit;
+ mpz_t tr;
+ mpz_init_set_ui (tr, 1);
+
+ for (bit = GMP_ULONG_HIGHBIT; bit > 0; bit >>= 1)
+ {
+ mpz_mul (tr, tr, tr);
+ if (e & bit)
+ mpz_mul (tr, tr, b);
+ }
+ mpz_swap (r, tr);
+ mpz_clear (tr);
+}
+
+void
+mpz_ui_pow_ui (mpz_t r, unsigned long blimb, unsigned long e)
+{
+ mpz_t b;
+ mpz_init_set_ui (b, blimb);
+ mpz_pow_ui (r, b, e);
+ mpz_clear (b);
+}
+
+void
+mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const mpz_t m)
+{
+ mpz_t tr;
+ mpz_t base;
+ mp_size_t en, mn;
+ mp_srcptr mp;
+ struct gmp_div_inverse minv;
+ unsigned shift;
+ mp_ptr tp = NULL;
+
+ en = GMP_ABS (e->_mp_size);
+ mn = GMP_ABS (m->_mp_size);
+ if (mn == 0)
+ gmp_die ("mpz_powm: Zero modulo.");
+
+ if (en == 0)
+ {
+ mpz_set_ui (r, 1);
+ return;
+ }
+
+ mp = m->_mp_d;
+ mpn_div_qr_invert (&minv, mp, mn);
+ shift = minv.shift;
+
+ if (shift > 0)
+ {
+ /* To avoid shifts, we do all our reductions, except the final
+ one, using a *normalized* m. */
+ minv.shift = 0;
+
+ tp = gmp_xalloc_limbs (mn);
+ gmp_assert_nocarry (mpn_lshift (tp, mp, mn, shift));
+ mp = tp;
+ }
+
+ mpz_init (base);
+
+ if (e->_mp_size < 0)
+ {
+ if (!mpz_invert (base, b, m))
+ gmp_die ("mpz_powm: Negative exponent and non-invertibe base.");
+ }
+ else
+ {
+ mp_size_t bn;
+ mpz_abs (base, b);
+
+ bn = base->_mp_size;
+ if (bn >= mn)
+ {
+ mpn_div_qr_preinv (NULL, base->_mp_d, base->_mp_size, mp, mn, &minv);
+ bn = mn;
+ }
+
+ /* We have reduced the absolute value. Now take care of the
+ sign. Note that we get zero represented non-canonically as
+ m. */
+ if (b->_mp_size < 0)
+ {
+ mp_ptr bp = MPZ_REALLOC (base, mn);
+ gmp_assert_nocarry (mpn_sub (bp, mp, mn, bp, bn));
+ bn = mn;
+ }
+ base->_mp_size = mpn_normalized_size (base->_mp_d, bn);
+ }
+ mpz_init_set_ui (tr, 1);
+
+ while (en-- > 0)
+ {
+ mp_limb_t w = e->_mp_d[en];
+ mp_limb_t bit;
+
+ for (bit = GMP_LIMB_HIGHBIT; bit > 0; bit >>= 1)
+ {
+ mpz_mul (tr, tr, tr);
+ if (w & bit)
+ mpz_mul (tr, tr, base);
+ if (tr->_mp_size > mn)
+ {
+ mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv);
+ tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn);
+ }
+ }
+ }
+
+ /* Final reduction */
+ if (tr->_mp_size >= mn)
+ {
+ minv.shift = shift;
+ mpn_div_qr_preinv (NULL, tr->_mp_d, tr->_mp_size, mp, mn, &minv);
+ tr->_mp_size = mpn_normalized_size (tr->_mp_d, mn);
+ }
+ if (tp)
+ gmp_free (tp);
+
+ mpz_swap (r, tr);
+ mpz_clear (tr);
+ mpz_clear (base);
+}
+
+void
+mpz_powm_ui (mpz_t r, const mpz_t b, unsigned long elimb, const mpz_t m)
+{
+ mpz_t e;
+ mpz_init_set_ui (e, elimb);
+ mpz_powm (r, b, e, m);
+ mpz_clear (e);
+}
+
+/* x=trunc(y^(1/z)), r=y-x^z */
+void
+mpz_rootrem (mpz_t x, mpz_t r, const mpz_t y, unsigned long z)
+{
+ int sgn;
+ mpz_t t, u;
+
+ sgn = y->_mp_size < 0;
+ if (sgn && (z & 1) == 0)
+ gmp_die ("mpz_rootrem: Negative argument, with even root.");
+ if (z == 0)
+ gmp_die ("mpz_rootrem: Zeroth root.");
+
+ if (mpz_cmpabs_ui (y, 1) <= 0) {
+ mpz_set (x, y);
+ if (r)
+ r->_mp_size = 0;
+ return;
+ }
+
+ mpz_init (t);
+ mpz_init (u);
+ mpz_setbit (t, mpz_sizeinbase (y, 2) / z + 1);
+
+ if (z == 2) /* simplify sqrt loop: z-1 == 1 */
+ do {
+ mpz_swap (u, t); /* u = x */
+ mpz_tdiv_q (t, y, u); /* t = y/x */
+ mpz_add (t, t, u); /* t = y/x + x */
+ mpz_tdiv_q_2exp (t, t, 1); /* x'= (y/x + x)/2 */
+ } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */
+ else /* z != 2 */ {
+ mpz_t v;
+
+ mpz_init (v);
+ if (sgn)
+ mpz_neg (t, t);
+
+ do {
+ mpz_swap (u, t); /* u = x */
+ mpz_pow_ui (t, u, z - 1); /* t = x^(z-1) */
+ mpz_tdiv_q (t, y, t); /* t = y/x^(z-1) */
+ mpz_mul_ui (v, u, z - 1); /* v = x*(z-1) */
+ mpz_add (t, t, v); /* t = y/x^(z-1) + x*(z-1) */
+ mpz_tdiv_q_ui (t, t, z); /* x'=(y/x^(z-1) + x*(z-1))/z */
+ } while (mpz_cmpabs (t, u) < 0); /* |x'| < |x| */
+
+ mpz_clear (v);
+ }
+
+ if (r) {
+ mpz_pow_ui (t, u, z);
+ mpz_sub (r, y, t);
+ }
+ mpz_swap (x, u);
+ mpz_clear (u);
+ mpz_clear (t);
+}
+
+int
+mpz_root (mpz_t x, const mpz_t y, unsigned long z)
+{
+ int res;
+ mpz_t r;
+
+ mpz_init (r);
+ mpz_rootrem (x, r, y, z);
+ res = r->_mp_size == 0;
+ mpz_clear (r);
+
+ return res;
+}
+
+/* Compute s = floor(sqrt(u)) and r = u - s^2. Allows r == NULL */
+void
+mpz_sqrtrem (mpz_t s, mpz_t r, const mpz_t u)
+{
+ mpz_rootrem (s, r, u, 2);
+}
+
+void
+mpz_sqrt (mpz_t s, const mpz_t u)
+{
+ mpz_rootrem (s, NULL, u, 2);
+}
+
+
+/* Combinatorics */
+
+void
+mpz_fac_ui (mpz_t x, unsigned long n)
+{
+ if (n < 2) {
+ mpz_set_ui (x, 1);
+ return;
+ }
+ mpz_set_ui (x, n);
+ for (;--n > 1;)
+ mpz_mul_ui (x, x, n);
+}
+
+void
+mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k)
+{
+ mpz_t t;
+
+ if (k > n) {
+ r->_mp_size = 0;
+ return;
+ }
+ mpz_fac_ui (r, n);
+ mpz_init (t);
+ mpz_fac_ui (t, k);
+ mpz_divexact (r, r, t);
+ mpz_fac_ui (t, n - k);
+ mpz_divexact (r, r, t);
+ mpz_clear (t);
+}
+
+
+/* Logical operations and bit manipulation. */
+
+/* Numbers are treated as if represented in two's complement (and
+ infinitely sign extended). For a negative values we get the two's
+ complement from -x = ~x + 1, where ~ is bitwise complementt.
+ Negation transforms
+
+ xxxx10...0
+
+ into
+
+ yyyy10...0
+
+ where yyyy is the bitwise complement of xxxx. So least significant
+ bits, up to and including the first one bit, are unchanged, and
+ the more significant bits are all complemented.
+
+ To change a bit from zero to one in a negative number, subtract the
+ corresponding power of two from the absolute value. This can never
+ underflow. To change a bit from one to zero, add the corresponding
+ power of two, and this might overflow. E.g., if x = -001111, the
+ two's complement is 110001. Clearing the least significant bit, we
+ get two's complement 110000, and -010000. */
+
+int
+mpz_tstbit (const mpz_t d, mp_bitcnt_t bit_index)
+{
+ mp_size_t limb_index;
+ unsigned shift;
+ mp_size_t ds;
+ mp_size_t dn;
+ mp_limb_t w;
+ int bit;
+
+ ds = d->_mp_size;
+ dn = GMP_ABS (ds);
+ limb_index = bit_index / GMP_LIMB_BITS;
+ if (limb_index >= dn)
+ return ds < 0;
+
+ shift = bit_index % GMP_LIMB_BITS;
+ w = d->_mp_d[limb_index];
+ bit = (w >> shift) & 1;
+
+ if (ds < 0)
+ {
+ /* d < 0. Check if any of the bits below is set: If so, our bit
+ must be complemented. */
+ if (shift > 0 && (w << (GMP_LIMB_BITS - shift)) > 0)
+ return bit ^ 1;
+ while (limb_index-- > 0)
+ if (d->_mp_d[limb_index] > 0)
+ return bit ^ 1;
+ }
+ return bit;
+}
+
+static void
+mpz_abs_add_bit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ mp_size_t dn, limb_index;
+ mp_limb_t bit;
+ mp_ptr dp;
+
+ dn = GMP_ABS (d->_mp_size);
+
+ limb_index = bit_index / GMP_LIMB_BITS;
+ bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS);
+
+ if (limb_index >= dn)
+ {
+ mp_size_t i;
+ /* The bit should be set outside of the end of the number.
+ We have to increase the size of the number. */
+ dp = MPZ_REALLOC (d, limb_index + 1);
+
+ dp[limb_index] = bit;
+ for (i = dn; i < limb_index; i++)
+ dp[i] = 0;
+ dn = limb_index + 1;
+ }
+ else
+ {
+ mp_limb_t cy;
+
+ dp = d->_mp_d;
+
+ cy = mpn_add_1 (dp + limb_index, dp + limb_index, dn - limb_index, bit);
+ if (cy > 0)
+ {
+ dp = MPZ_REALLOC (d, dn + 1);
+ dp[dn++] = cy;
+ }
+ }
+
+ d->_mp_size = (d->_mp_size < 0) ? - dn : dn;
+}
+
+static void
+mpz_abs_sub_bit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ mp_size_t dn, limb_index;
+ mp_ptr dp;
+ mp_limb_t bit;
+
+ dn = GMP_ABS (d->_mp_size);
+ dp = d->_mp_d;
+
+ limb_index = bit_index / GMP_LIMB_BITS;
+ bit = (mp_limb_t) 1 << (bit_index % GMP_LIMB_BITS);
+
+ assert (limb_index < dn);
+
+ gmp_assert_nocarry (mpn_sub_1 (dp + limb_index, dp + limb_index,
+ dn - limb_index, bit));
+ dn -= (dp[dn-1] == 0);
+ d->_mp_size = (d->_mp_size < 0) ? - dn : dn;
+}
+
+void
+mpz_setbit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ if (!mpz_tstbit (d, bit_index))
+ {
+ if (d->_mp_size >= 0)
+ mpz_abs_add_bit (d, bit_index);
+ else
+ mpz_abs_sub_bit (d, bit_index);
+ }
+}
+
+void
+mpz_clrbit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ if (mpz_tstbit (d, bit_index))
+ {
+ if (d->_mp_size >= 0)
+ mpz_abs_sub_bit (d, bit_index);
+ else
+ mpz_abs_add_bit (d, bit_index);
+ }
+}
+
+void
+mpz_combit (mpz_t d, mp_bitcnt_t bit_index)
+{
+ if (mpz_tstbit (d, bit_index) ^ (d->_mp_size < 0))
+ mpz_abs_sub_bit (d, bit_index);
+ else
+ mpz_abs_add_bit (d, bit_index);
+}
+
+void
+mpz_com (mpz_t r, const mpz_t u)
+{
+ mpz_neg (r, u);
+ mpz_sub_ui (r, r, 1);
+}
+
+void
+mpz_and (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, rn, i;
+ mp_ptr up, vp, rp;
+
+ mp_limb_t ux, vx, rx;
+ mp_limb_t uc, vc, rc;
+ mp_limb_t ul, vl, rl;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+ if (un < vn)
+ {
+ MPZ_SRCPTR_SWAP (u, v);
+ MP_SIZE_T_SWAP (un, vn);
+ }
+ if (vn == 0)
+ {
+ r->_mp_size = 0;
+ return;
+ }
+
+ uc = u->_mp_size < 0;
+ vc = v->_mp_size < 0;
+ rc = uc & vc;
+
+ ux = -uc;
+ vx = -vc;
+ rx = -rc;
+
+ /* If the smaller input is positive, higher limbs don't matter. */
+ rn = vx ? un : vn;
+
+ rp = MPZ_REALLOC (r, rn + rc);
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ for (i = 0; i < vn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ vx) + vc;
+ vc = vl < vc;
+
+ rl = ( (ul & vl) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ assert (vc == 0);
+
+ for (; i < rn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ rl = ( (ul & vx) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ if (rc)
+ rp[rn++] = rc;
+ else
+ rn = mpn_normalized_size (rp, rn);
+
+ r->_mp_size = rx ? -rn : rn;
+}
+
+void
+mpz_ior (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, rn, i;
+ mp_ptr up, vp, rp;
+
+ mp_limb_t ux, vx, rx;
+ mp_limb_t uc, vc, rc;
+ mp_limb_t ul, vl, rl;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+ if (un < vn)
+ {
+ MPZ_SRCPTR_SWAP (u, v);
+ MP_SIZE_T_SWAP (un, vn);
+ }
+ if (vn == 0)
+ {
+ mpz_set (r, u);
+ return;
+ }
+
+ uc = u->_mp_size < 0;
+ vc = v->_mp_size < 0;
+ rc = uc | vc;
+
+ ux = -uc;
+ vx = -vc;
+ rx = -rc;
+
+ /* If the smaller input is negative, by sign extension higher limbs
+ don't matter. */
+ rn = vx ? vn : un;
+
+ rp = MPZ_REALLOC (r, rn + rc);
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ for (i = 0; i < vn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ vx) + vc;
+ vc = vl < vc;
+
+ rl = ( (ul | vl) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ assert (vc == 0);
+
+ for (; i < rn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ rl = ( (ul | vx) ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ if (rc)
+ rp[rn++] = rc;
+ else
+ rn = mpn_normalized_size (rp, rn);
+
+ r->_mp_size = rx ? -rn : rn;
+}
+
+void
+mpz_xor (mpz_t r, const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, i;
+ mp_ptr up, vp, rp;
+
+ mp_limb_t ux, vx, rx;
+ mp_limb_t uc, vc, rc;
+ mp_limb_t ul, vl, rl;
+
+ un = GMP_ABS (u->_mp_size);
+ vn = GMP_ABS (v->_mp_size);
+ if (un < vn)
+ {
+ MPZ_SRCPTR_SWAP (u, v);
+ MP_SIZE_T_SWAP (un, vn);
+ }
+ if (vn == 0)
+ {
+ mpz_set (r, u);
+ return;
+ }
+
+ uc = u->_mp_size < 0;
+ vc = v->_mp_size < 0;
+ rc = uc ^ vc;
+
+ ux = -uc;
+ vx = -vc;
+ rx = -rc;
+
+ rp = MPZ_REALLOC (r, un + rc);
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ for (i = 0; i < vn; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ vx) + vc;
+ vc = vl < vc;
+
+ rl = (ul ^ vl ^ rx) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ assert (vc == 0);
+
+ for (; i < un; i++)
+ {
+ ul = (up[i] ^ ux) + uc;
+ uc = ul < uc;
+
+ rl = (ul ^ ux) + rc;
+ rc = rl < rc;
+ rp[i] = rl;
+ }
+ if (rc)
+ rp[un++] = rc;
+ else
+ un = mpn_normalized_size (rp, un);
+
+ r->_mp_size = rx ? -un : un;
+}
+
+static unsigned
+gmp_popcount_limb (mp_limb_t x)
+{
+ unsigned c;
+
+ /* Do 16 bits at a time, to avoid limb-sized constants. */
+ for (c = 0; x > 0; x >>= 16)
+ {
+ unsigned w = ((x >> 1) & 0x5555) + (x & 0x5555);
+ w = ((w >> 2) & 0x3333) + (w & 0x3333);
+ w = ((w >> 4) & 0x0f0f) + (w & 0x0f0f);
+ w = (w >> 8) + (w & 0x00ff);
+ c += w;
+ }
+ return c;
+}
+
+mp_bitcnt_t
+mpz_popcount (const mpz_t u)
+{
+ mp_size_t un, i;
+ mp_bitcnt_t c;
+
+ un = u->_mp_size;
+
+ if (un < 0)
+ return ~(mp_bitcnt_t) 0;
+
+ for (c = 0, i = 0; i < un; i++)
+ c += gmp_popcount_limb (u->_mp_d[i]);
+
+ return c;
+}
+
+mp_bitcnt_t
+mpz_hamdist (const mpz_t u, const mpz_t v)
+{
+ mp_size_t un, vn, i;
+ mp_limb_t uc, vc, ul, vl, comp;
+ mp_srcptr up, vp;
+ mp_bitcnt_t c;
+
+ un = u->_mp_size;
+ vn = v->_mp_size;
+
+ if ( (un ^ vn) < 0)
+ return ~(mp_bitcnt_t) 0;
+
+ if (un < 0)
+ {
+ assert (vn < 0);
+ un = -un;
+ vn = -vn;
+ uc = vc = 1;
+ comp = - (mp_limb_t) 1;
+ }
+ else
+ uc = vc = comp = 0;
+
+ up = u->_mp_d;
+ vp = v->_mp_d;
+
+ if (un < vn)
+ MPN_SRCPTR_SWAP (up, un, vp, vn);
+
+ for (i = 0, c = 0; i < vn; i++)
+ {
+ ul = (up[i] ^ comp) + uc;
+ uc = ul < uc;
+
+ vl = (vp[i] ^ comp) + vc;
+ vc = vl < vc;
+
+ c += gmp_popcount_limb (ul ^ vl);
+ }
+ assert (vc == 0);
+
+ for (; i < un; i++)
+ {
+ ul = (up[i] ^ comp) + uc;
+ uc = ul < uc;
+
+ c += gmp_popcount_limb (ul ^ comp);
+ }
+
+ return c;
+}
+
+mp_bitcnt_t
+mpz_scan1 (const mpz_t u, mp_bitcnt_t starting_bit)
+{
+ mp_ptr up;
+ mp_size_t us, un, i;
+ mp_limb_t limb, ux, uc;
+ unsigned cnt;
+
+ up = u->_mp_d;
+ us = u->_mp_size;
+ un = GMP_ABS (us);
+ i = starting_bit / GMP_LIMB_BITS;
+
+ /* Past the end there's no 1 bits for u>=0, or an immediate 1 bit
+ for u<0. Notice this test picks up any u==0 too. */
+ if (i >= un)
+ return (us >= 0 ? ~(mp_bitcnt_t) 0 : starting_bit);
+
+ if (us < 0)
+ {
+ ux = GMP_LIMB_MAX;
+ uc = mpn_zero_p (up, i);
+ }
+ else
+ ux = uc = 0;
+
+ limb = (ux ^ up[i]) + uc;
+ uc = limb < uc;
+
+ /* Mask to 0 all bits before starting_bit, thus ignoring them. */
+ limb &= (GMP_LIMB_MAX << (starting_bit % GMP_LIMB_BITS));
+
+ while (limb == 0)
+ {
+ i++;
+ if (i == un)
+ {
+ assert (uc == 0);
+ /* For the u > 0 case, this can happen only for the first
+ masked limb. For the u < 0 case, it happens when the
+ highest limbs of the absolute value are all ones. */
+ return (us >= 0 ? ~(mp_bitcnt_t) 0 : un * GMP_LIMB_BITS);
+ }
+ limb = (ux ^ up[i]) + uc;
+ uc = limb < uc;
+ }
+ gmp_ctz (cnt, limb);
+ return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt;
+}
+
+mp_bitcnt_t
+mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit)
+{
+ mp_ptr up;
+ mp_size_t us, un, i;
+ mp_limb_t limb, ux, uc;
+ unsigned cnt;
+
+ up = u->_mp_d;
+ us = u->_mp_size;
+ un = GMP_ABS (us);
+ i = starting_bit / GMP_LIMB_BITS;
+
+ /* When past end, there's an immediate 0 bit for u>=0, or no 0 bits for
+ u<0. Notice this test picks up all cases of u==0 too. */
+ if (i >= un)
+ return (us >= 0 ? starting_bit : ~(mp_bitcnt_t) 0);
+
+ if (us < 0)
+ {
+ ux = GMP_LIMB_MAX;
+ uc = mpn_zero_p (up, i);
+ }
+ else
+ ux = uc = 0;
+
+ limb = (ux ^ up[i]) + uc;
+ uc = limb < uc;
+
+ /* Mask to 1 all bits before starting_bit, thus ignoring them. */
+ limb |= ((mp_limb_t) 1 << (starting_bit % GMP_LIMB_BITS)) - 1;
+
+ while (limb == GMP_LIMB_MAX)
+ {
+ i++;
+ if (i == un)
+ {
+ assert (uc == 0);
+ return (us >= 0 ? un * GMP_LIMB_BITS : ~(mp_bitcnt_t) 0);
+ }
+ limb = (ux ^ up[i]) + uc;
+ uc = limb < uc;
+ }
+ gmp_ctz (cnt, ~limb);
+ return (mp_bitcnt_t) i * GMP_LIMB_BITS + cnt;
+}
+
+
+/* MPZ base conversion. */
+
+size_t
+mpz_sizeinbase (const mpz_t u, int base)
+{
+ mp_size_t un;
+ mp_srcptr up;
+ mp_ptr tp;
+ mp_bitcnt_t bits;
+ struct gmp_div_inverse bi;
+ size_t ndigits;
+
+ assert (base >= 2);
+ assert (base <= 36);
+
+ un = GMP_ABS (u->_mp_size);
+ if (un == 0)
+ return 1;
+
+ up = u->_mp_d;
+
+ bits = (un - 1) * GMP_LIMB_BITS + mpn_limb_size_in_base_2 (up[un-1]);
+ switch (base)
+ {
+ case 2:
+ return bits;
+ case 4:
+ return (bits + 1) / 2;
+ case 8:
+ return (bits + 2) / 3;
+ case 16:
+ return (bits + 3) / 4;
+ case 32:
+ return (bits + 4) / 5;
+ /* FIXME: Do something more clever for the common case of base
+ 10. */
+ }
+
+ tp = gmp_xalloc_limbs (un);
+ mpn_copyi (tp, up, un);
+ mpn_div_qr_1_invert (&bi, base);
+
+ for (ndigits = 0; un > 0; ndigits++)
+ {
+ mpn_div_qr_1_preinv (tp, tp, un, &bi);
+ un -= (tp[un-1] == 0);
+ }
+ gmp_free (tp);
+ return ndigits;
+}
+
+char *
+mpz_get_str (char *sp, int base, const mpz_t u)
+{
+ unsigned bits;
+ const char *digits;
+ mp_size_t un;
+ size_t i, sn;
+
+ if (base >= 0)
+ {
+ digits = "0123456789abcdefghijklmnopqrstuvwxyz";
+ }
+ else
+ {
+ base = -base;
+ digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ }
+ if (base <= 1)
+ base = 10;
+ if (base > 36)
+ return NULL;
+
+ sn = 1 + mpz_sizeinbase (u, base);
+ if (!sp)
+ sp = gmp_xalloc (1 + sn);
+
+ un = GMP_ABS (u->_mp_size);
+
+ if (un == 0)
+ {
+ sp[0] = '0';
+ sp[1] = '\0';
+ return sp;
+ }
+
+ i = 0;
+
+ if (u->_mp_size < 0)
+ sp[i++] = '-';
+
+ bits = mpn_base_power_of_two_p (base);
+
+ if (bits)
+ /* Not modified in this case. */
+ sn = i + mpn_get_str_bits ((unsigned char *) sp + i, bits, u->_mp_d, un);
+ else
+ {
+ struct mpn_base_info info;
+ mp_ptr tp;
+
+ mpn_get_base_info (&info, base);
+ tp = gmp_xalloc_limbs (un);
+ mpn_copyi (tp, u->_mp_d, un);
+
+ sn = i + mpn_get_str_other ((unsigned char *) sp + i, base, &info, tp, un);
+ gmp_free (tp);
+ }
+
+ for (; i < sn; i++)
+ sp[i] = digits[(unsigned char) sp[i]];
+
+ sp[sn] = '\0';
+ return sp;
+}
+
+int
+mpz_set_str (mpz_t r, const char *sp, int base)
+{
+ unsigned bits;
+ mp_size_t rn, alloc;
+ mp_ptr rp;
+ size_t sn;
+ size_t dn;
+ int sign;
+ unsigned char *dp;
+
+ assert (base == 0 || (base >= 2 && base <= 36));
+
+ while (isspace( (unsigned char) *sp))
+ sp++;
+
+ if (*sp == '-')
+ {
+ sign = 1;
+ sp++;
+ }
+ else
+ sign = 0;
+
+ if (base == 0)
+ {
+ if (*sp == '0')
+ {
+ sp++;
+ if (*sp == 'x' || *sp == 'X')
+ {
+ base = 16;
+ sp++;
+ }
+ else if (*sp == 'b' || *sp == 'B')
+ {
+ base = 2;
+ sp++;
+ }
+ else
+ base = 8;
+ }
+ else
+ base = 10;
+ }
+
+ sn = strlen (sp);
+ dp = gmp_xalloc (sn + (sn == 0));
+
+ for (dn = 0; *sp; sp++)
+ {
+ unsigned digit;
+
+ if (isspace ((unsigned char) *sp))
+ continue;
+ if (*sp >= '0' && *sp <= '9')
+ digit = *sp - '0';
+ else if (*sp >= 'a' && *sp <= 'z')
+ digit = *sp - 'a' + 10;
+ else if (*sp >= 'A' && *sp <= 'Z')
+ digit = *sp - 'A' + 10;
+ else
+ digit = base; /* fail */
+
+ if (digit >= base)
+ {
+ gmp_free (dp);
+ r->_mp_size = 0;
+ return -1;
+ }
+
+ dp[dn++] = digit;
+ }
+
+ bits = mpn_base_power_of_two_p (base);
+
+ if (bits > 0)
+ {
+ alloc = (sn * bits + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS;
+ rp = MPZ_REALLOC (r, alloc);
+ rn = mpn_set_str_bits (rp, dp, dn, bits);
+ }
+ else
+ {
+ struct mpn_base_info info;
+ mpn_get_base_info (&info, base);
+ alloc = (sn + info.exp - 1) / info.exp;
+ rp = MPZ_REALLOC (r, alloc);
+ rn = mpn_set_str_other (rp, dp, dn, base, &info);
+ }
+ assert (rn <= alloc);
+ gmp_free (dp);
+
+ r->_mp_size = sign ? - rn : rn;
+
+ return 0;
+}
+
+int
+mpz_init_set_str (mpz_t r, const char *sp, int base)
+{
+ mpz_init (r);
+ return mpz_set_str (r, sp, base);
+}
+
+size_t
+mpz_out_str (FILE *stream, int base, const mpz_t x)
+{
+ char *str;
+ size_t len;
+
+ str = mpz_get_str (NULL, base, x);
+ len = strlen (str);
+ len = fwrite (str, 1, len, stream);
+ gmp_free (str);
+ return len;
+}
+
+
+static int
+gmp_detect_endian (void)
+{
+ static const int i = 1;
+ const unsigned char *p = (const unsigned char *) &i;
+ if (*p == 1)
+ /* Little endian */
+ return -1;
+ else
+ /* Big endian */
+ return 1;
+}
+
+/* Import and export. Does not support nails. */
+void
+mpz_import (mpz_t r, size_t count, int order, size_t size, int endian,
+ size_t nails, const void *src)
+{
+ const unsigned char *p;
+ ptrdiff_t word_step;
+ mp_ptr rp;
+ mp_size_t rn;
+
+ /* The current (partial) limb. */
+ mp_limb_t limb;
+ /* The number of bytes already copied to this limb (starting from
+ the low end). */
+ size_t bytes;
+ /* The index where the limb should be stored, when completed. */
+ mp_size_t i;
+
+ if (nails != 0)
+ gmp_die ("mpz_import: Nails not supported.");
+
+ assert (order == 1 || order == -1);
+ assert (endian >= -1 && endian <= 1);
+
+ if (endian == 0)
+ endian = gmp_detect_endian ();
+
+ p = (unsigned char *) src;
+
+ word_step = (order != endian) ? 2 * size : 0;
+
+ /* Process bytes from the least significant end, so point p at the
+ least significant word. */
+ if (order == 1)
+ {
+ p += size * (count - 1);
+ word_step = - word_step;
+ }
+
+ /* And at least significant byte of that word. */
+ if (endian == 1)
+ p += (size - 1);
+
+ rn = (size * count + sizeof(mp_limb_t) - 1) / sizeof(mp_limb_t);
+ rp = MPZ_REALLOC (r, rn);
+
+ for (limb = 0, bytes = 0, i = 0; count > 0; count--, p += word_step)
+ {
+ size_t j;
+ for (j = 0; j < size; j++, p -= (ptrdiff_t) endian)
+ {
+ limb |= (mp_limb_t) *p << (bytes++ * CHAR_BIT);
+ if (bytes == sizeof(mp_limb_t))
+ {
+ rp[i++] = limb;
+ bytes = 0;
+ limb = 0;
+ }
+ }
+ }
+ if (bytes > 0)
+ rp[i++] = limb;
+ assert (i == rn);
+
+ r->_mp_size = mpn_normalized_size (rp, i);
+}
+
+void *
+mpz_export (void *r, size_t *countp, int order, size_t size, int endian,
+ size_t nails, const mpz_t u)
+{
+ unsigned char *p;
+ ptrdiff_t word_step;
+ size_t count, k;
+ mp_size_t un;
+
+ /* The current (partial) limb. */
+ mp_limb_t limb;
+ /* The number of bytes left to to in this limb. */
+ size_t bytes;
+ /* The index where the limb was read. */
+ mp_size_t i;
+
+ if (nails != 0)
+ gmp_die ("mpz_import: Nails not supported.");
+
+ assert (order == 1 || order == -1);
+ assert (endian >= -1 && endian <= 1);
+ assert (size > 0 || u->_mp_size == 0);
+
+ un = GMP_ABS (u->_mp_size);
+ if (un == 0)
+ {
+ if (countp)
+ *countp = 0;
+ return r;
+ }
+
+ /* Count bytes in top limb. */
+ for (limb = u->_mp_d[un-1], k = 0; limb > 0; k++, limb >>= CHAR_BIT)
+ ;
+
+ assert (k > 0);
+
+ count = (k + (un-1) * sizeof (mp_limb_t) + size - 1) / size;
+
+ if (!r)
+ r = gmp_xalloc (count * size);
+
+ if (endian == 0)
+ endian = gmp_detect_endian ();
+
+ p = (unsigned char *) r;
+
+ word_step = (order != endian) ? 2 * size : 0;
+
+ /* Process bytes from the least significant end, so point p at the
+ least significant word. */
+ if (order == 1)
+ {
+ p += size * (count - 1);
+ word_step = - word_step;
+ }
+
+ /* And at least significant byte of that word. */
+ if (endian == 1)
+ p += (size - 1);
+
+ for (bytes = 0, i = 0, k = 0; k < count; k++, p += word_step)
+ {
+ size_t j;
+ for (j = 0; j < size; j++, p -= (ptrdiff_t) endian)
+ {
+ if (bytes == 0)
+ {
+ if (i < un)
+ limb = u->_mp_d[i++];
+ bytes = sizeof (mp_limb_t);
+ }
+ *p = limb;
+ limb >>= CHAR_BIT;
+ bytes--;
+ }
+ }
+ assert (i == un);
+ assert (k == count);
+
+ if (countp)
+ *countp = count;
+
+ return r;
+}
diff --git a/src/gmp/mini-gmp.h b/src/gmp/mini-gmp.h
new file mode 100644
index 000000000..8c94ca2ed
--- /dev/null
+++ b/src/gmp/mini-gmp.h
@@ -0,0 +1,256 @@
+/* mini-gmp, a minimalistic implementation of a GNU GMP subset.
+
+Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
+
+This file is part of the GNU MP Library.
+
+The GNU MP Library 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 3 of the License, or (at your
+option) any later version.
+
+The GNU MP Library 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 the GNU MP Library. If not, see http://www.gnu.org/licenses/. */
+
+/* About mini-gmp: This is a minimal implementation of a subset of the
+ GMP interface. It is intended for inclusion into applications which
+ have modest bignums needs, as a fallback when the real GMP library
+ is not installed.
+
+ This file defines the public interface. */
+
+#ifndef __MINI_GMP_H__
+#define __MINI_GMP_H__
+
+/* For size_t */
+#include <stddef.h>
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+void mp_set_memory_functions (void *(*) (size_t),
+ void *(*) (void *, size_t, size_t),
+ void (*) (void *, size_t));
+
+void mp_get_memory_functions (void *(**) (size_t),
+ void *(**) (void *, size_t, size_t),
+ void (**) (void *, size_t));
+
+typedef unsigned long mp_limb_t;
+typedef long mp_size_t;
+typedef unsigned long mp_bitcnt_t;
+
+typedef mp_limb_t *mp_ptr;
+typedef const mp_limb_t *mp_srcptr;
+
+typedef struct
+{
+ int _mp_alloc; /* Number of *limbs* allocated and pointed
+ to by the _mp_d field. */
+ int _mp_size; /* abs(_mp_size) is the number of limbs the
+ last field points to. If _mp_size is
+ negative this is a negative number. */
+ mp_limb_t *_mp_d; /* Pointer to the limbs. */
+} __mpz_struct;
+
+typedef __mpz_struct mpz_t[1];
+
+typedef __mpz_struct *mpz_ptr;
+typedef const __mpz_struct *mpz_srcptr;
+
+void mpn_copyi (mp_ptr, mp_srcptr, mp_size_t);
+void mpn_copyd (mp_ptr, mp_srcptr, mp_size_t);
+
+int mpn_cmp (mp_srcptr, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_add_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_add_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t);
+mp_limb_t mpn_add (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_sub_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_sub_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t);
+mp_limb_t mpn_sub (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_mul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_addmul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+mp_limb_t mpn_submul_1 (mp_ptr, mp_srcptr, mp_size_t, mp_limb_t);
+
+mp_limb_t mpn_mul (mp_ptr, mp_srcptr, mp_size_t, mp_srcptr, mp_size_t);
+void mpn_mul_n (mp_ptr, mp_srcptr, mp_srcptr, mp_size_t);
+void mpn_sqr (mp_ptr, mp_srcptr, mp_size_t);
+
+mp_limb_t mpn_lshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int);
+mp_limb_t mpn_rshift (mp_ptr, mp_srcptr, mp_size_t, unsigned int);
+
+mp_limb_t mpn_invert_3by2 (mp_limb_t, mp_limb_t);
+#define mpn_invert_limb(x) mpn_invert_3by2 ((x), 0)
+
+size_t mpn_get_str (unsigned char *, int, mp_ptr, mp_size_t);
+mp_size_t mpn_set_str (mp_ptr, const unsigned char *, size_t, int);
+
+void mpz_init (mpz_t);
+void mpz_init2 (mpz_t, mp_bitcnt_t);
+void mpz_clear (mpz_t);
+
+#define mpz_odd_p(z) (((z)->_mp_size != 0) & (int) (z)->_mp_d[0])
+#define mpz_even_p(z) (! mpz_odd_p (z))
+
+int mpz_sgn (const mpz_t);
+int mpz_cmp_si (const mpz_t, long);
+int mpz_cmp_ui (const mpz_t, unsigned long);
+int mpz_cmp (const mpz_t, const mpz_t);
+int mpz_cmpabs_ui (const mpz_t, unsigned long);
+int mpz_cmpabs (const mpz_t, const mpz_t);
+int mpz_cmp_d (const mpz_t, double);
+int mpz_cmpabs_d (const mpz_t, double);
+
+void mpz_abs (mpz_t, const mpz_t);
+void mpz_neg (mpz_t, const mpz_t);
+void mpz_swap (mpz_t, mpz_t);
+
+void mpz_add_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_add (mpz_t, const mpz_t, const mpz_t);
+void mpz_sub_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_ui_sub (mpz_t, unsigned long, const mpz_t);
+void mpz_sub (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_mul_si (mpz_t, const mpz_t, long int);
+void mpz_mul_ui (mpz_t, const mpz_t, unsigned long int);
+void mpz_mul (mpz_t, const mpz_t, const mpz_t);
+void mpz_mul_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+
+void mpz_cdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_fdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_tdiv_qr (mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_cdiv_q (mpz_t, const mpz_t, const mpz_t);
+void mpz_fdiv_q (mpz_t, const mpz_t, const mpz_t);
+void mpz_tdiv_q (mpz_t, const mpz_t, const mpz_t);
+void mpz_cdiv_r (mpz_t, const mpz_t, const mpz_t);
+void mpz_fdiv_r (mpz_t, const mpz_t, const mpz_t);
+void mpz_tdiv_r (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_cdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_fdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_tdiv_q_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_cdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_fdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+void mpz_tdiv_r_2exp (mpz_t, const mpz_t, mp_bitcnt_t);
+
+void mpz_mod (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_divexact (mpz_t, const mpz_t, const mpz_t);
+
+int mpz_divisible_p (const mpz_t, const mpz_t);
+
+unsigned long mpz_cdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_fdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_tdiv_qr_ui (mpz_t, mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_cdiv_q_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_fdiv_q_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_tdiv_q_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_cdiv_r_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_fdiv_r_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_tdiv_r_ui (mpz_t, const mpz_t, unsigned long);
+unsigned long mpz_cdiv_ui (const mpz_t, unsigned long);
+unsigned long mpz_fdiv_ui (const mpz_t, unsigned long);
+unsigned long mpz_tdiv_ui (const mpz_t, unsigned long);
+
+unsigned long mpz_mod_ui (mpz_t, const mpz_t, unsigned long);
+
+void mpz_divexact_ui (mpz_t, const mpz_t, unsigned long);
+
+int mpz_divisible_ui_p (const mpz_t, unsigned long);
+
+unsigned long mpz_gcd_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_gcd (mpz_t, const mpz_t, const mpz_t);
+void mpz_gcdext (mpz_t, mpz_t, mpz_t, const mpz_t, const mpz_t);
+void mpz_lcm_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_lcm (mpz_t, const mpz_t, const mpz_t);
+int mpz_invert (mpz_t, const mpz_t, const mpz_t);
+
+void mpz_sqrtrem (mpz_t, mpz_t, const mpz_t);
+void mpz_sqrt (mpz_t, const mpz_t);
+
+void mpz_pow_ui (mpz_t, const mpz_t, unsigned long);
+void mpz_ui_pow_ui (mpz_t, unsigned long, unsigned long);
+void mpz_powm (mpz_t, const mpz_t, const mpz_t, const mpz_t);
+void mpz_powm_ui (mpz_t, const mpz_t, unsigned long, const mpz_t);
+
+void mpz_rootrem (mpz_t, mpz_t, const mpz_t, unsigned long);
+int mpz_root (mpz_t, const mpz_t, unsigned long);
+
+void mpz_fac_ui (mpz_t, unsigned long);
+void mpz_bin_uiui (mpz_t, unsigned long, unsigned long);
+
+int mpz_tstbit (const mpz_t, mp_bitcnt_t);
+void mpz_setbit (mpz_t, mp_bitcnt_t);
+void mpz_clrbit (mpz_t, mp_bitcnt_t);
+void mpz_combit (mpz_t, mp_bitcnt_t);
+
+void mpz_com (mpz_t, const mpz_t);
+void mpz_and (mpz_t, const mpz_t, const mpz_t);
+void mpz_ior (mpz_t, const mpz_t, const mpz_t);
+void mpz_xor (mpz_t, const mpz_t, const mpz_t);
+
+mp_bitcnt_t mpz_popcount (const mpz_t);
+mp_bitcnt_t mpz_hamdist (const mpz_t, const mpz_t);
+mp_bitcnt_t mpz_scan0 (const mpz_t, mp_bitcnt_t);
+mp_bitcnt_t mpz_scan1 (const mpz_t, mp_bitcnt_t);
+
+int mpz_fits_slong_p (const mpz_t);
+int mpz_fits_ulong_p (const mpz_t);
+long int mpz_get_si (const mpz_t);
+unsigned long int mpz_get_ui (const mpz_t);
+double mpz_get_d (const mpz_t);
+size_t mpz_size (const mpz_t);
+mp_limb_t mpz_getlimbn (const mpz_t, mp_size_t);
+
+void mpz_set_si (mpz_t, signed long int);
+void mpz_set_ui (mpz_t, unsigned long int);
+void mpz_set (mpz_t, const mpz_t);
+void mpz_set_d (mpz_t, double);
+
+void mpz_init_set_si (mpz_t, signed long int);
+void mpz_init_set_ui (mpz_t, unsigned long int);
+void mpz_init_set (mpz_t, const mpz_t);
+void mpz_init_set_d (mpz_t, double);
+
+size_t mpz_sizeinbase (const mpz_t, int);
+char *mpz_get_str (char *, int, const mpz_t);
+int mpz_set_str (mpz_t, const char *, int);
+int mpz_init_set_str (mpz_t, const char *, int);
+
+/* This long list taken from gmp.h. */
+/* For reference, "defined(EOF)" cannot be used here. In g++ 2.95.4,
+ <iostream> defines EOF but not FILE. */
+#if defined (FILE) \
+ || defined (H_STDIO) \
+ || defined (_H_STDIO) /* AIX */ \
+ || defined (_STDIO_H) /* glibc, Sun, SCO */ \
+ || defined (_STDIO_H_) /* BSD, OSF */ \
+ || defined (__STDIO_H) /* Borland */ \
+ || defined (__STDIO_H__) /* IRIX */ \
+ || defined (_STDIO_INCLUDED) /* HPUX */ \
+ || defined (__dj_include_stdio_h_) /* DJGPP */ \
+ || defined (_FILE_DEFINED) /* Microsoft */ \
+ || defined (__STDIO__) /* Apple MPW MrC */ \
+ || defined (_MSL_STDIO_H) /* Metrowerks */ \
+ || defined (_STDIO_H_INCLUDED) /* QNX4 */ \
+ || defined (_ISO_STDIO_ISO_H) /* Sun C++ */ \
+ || defined (__STDIO_LOADED) /* VMS */
+size_t mpz_out_str (FILE *, int, const mpz_t);
+#endif
+
+void mpz_import (mpz_t, size_t, int, size_t, int, size_t, const void *);
+void *mpz_export (void *, size_t *, int, size_t, int, size_t, const mpz_t);
+
+#if defined (__cplusplus)
+}
+#endif
+#endif /* __MINI_GMP_H__ */
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
index 8210e0bf4..3937e405c 100644
--- a/src/guiChatConsole.cpp
+++ b/src/guiChatConsole.cpp
@@ -24,9 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gettime.h"
#include "keycode.h"
#include "settings.h"
-#include "main.h" // for g_settings
#include "porting.h"
-#include "tile.h"
+#include "client/tile.h"
#include "fontengine.h"
#include "log.h"
#include "gettext.h"
diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h
index 2bf45fdf4..652b265a4 100644
--- a/src/guiChatConsole.h
+++ b/src/guiChatConsole.h
@@ -122,9 +122,6 @@ private:
// font
gui::IGUIFont* m_font;
v2u32 m_fontsize;
-#if USE_FREETYPE
- bool m_use_freetype;
-#endif
};
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
index de6f44134..c616bc322 100644
--- a/src/guiEngine.cpp
+++ b/src/guiEngine.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiEngine.h"
+#include <fstream>
#include <IGUIStaticText.h>
#include <ICameraSceneNode.h>
#include "scripting_mainmenu.h"
@@ -27,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "version.h"
#include "porting.h"
#include "filesys.h"
-#include "main.h"
#include "settings.h"
#include "guiMainMenu.h"
#include "sound.h"
@@ -36,9 +36,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "httpfetch.h"
#include "log.h"
#include "fontengine.h"
+#include "guiscalingfilter.h"
#ifdef __ANDROID__
-#include "tile.h"
+#include "client/tile.h"
#include <GLES/gl.h>
#endif
@@ -52,7 +53,7 @@ TextDestGuiEngine::TextDestGuiEngine(GUIEngine* engine)
}
/******************************************************************************/
-void TextDestGuiEngine::gotText(std::map<std::string, std::string> fields)
+void TextDestGuiEngine::gotText(const StringMap &fields)
{
m_engine->getScriptIface()->handleMainMenuButtons(fields);
}
@@ -60,7 +61,7 @@ void TextDestGuiEngine::gotText(std::map<std::string, std::string> fields)
/******************************************************************************/
void TextDestGuiEngine::gotText(std::wstring text)
{
- m_engine->getScriptIface()->handleMainMenuEvent(wide_to_narrow(text));
+ m_engine->getScriptIface()->handleMainMenuEvent(wide_to_utf8(text));
}
/******************************************************************************/
@@ -171,8 +172,8 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
m_sound_manager = &dummySoundManager;
//create topleft header
- std::wstring t = narrow_to_wide(std::string("Minetest ") +
- minetest_version_hash);
+ std::wstring t = utf8_to_wide(std::string(PROJECT_NAME_C " ") +
+ g_version_hash);
core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(t), g_fontengine->getTextHeight());
rect += v2s32(4, 0);
@@ -194,7 +195,8 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
m_texture_source,
m_formspecgui,
m_buttonhandler,
- NULL);
+ NULL,
+ false);
m_menu->allowClose(false);
m_menu->lockSize(true,v2u32(800,600));
@@ -206,10 +208,8 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
m_script = new MainMenuScripting(this);
try {
- if (m_data->errormessage != "") {
- m_script->setMainMenuErrorMessage(m_data->errormessage);
- m_data->errormessage = "";
- }
+ m_script->setMainMenuData(&m_data->script_data);
+ m_data->script_data.errormessage = "";
if (!loadMainMenuScript()) {
errorstream << "No future without mainmenu" << std::endl;
@@ -217,10 +217,9 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev,
}
run();
- }
- catch(LuaError &e) {
+ } catch (LuaError &e) {
errorstream << "MAINMENU ERROR: " << e.what() << std::endl;
- m_data->errormessage = e.what();
+ m_data->script_data.errormessage = e.what();
}
m_menu->quitMenu();
@@ -304,7 +303,7 @@ void GUIEngine::run()
GUIEngine::~GUIEngine()
{
video::IVideoDriver* driver = m_device->getVideoDriver();
- assert(driver != 0);
+ FATAL_ERROR_IF(driver == 0, "Could not get video driver");
if(m_sound_manager != &dummySoundManager){
delete m_sound_manager;
@@ -408,7 +407,7 @@ void GUIEngine::drawBackground(video::IVideoDriver* driver)
{
for (unsigned int y = 0; y < screensize.Y; y += tilesize.Y )
{
- driver->draw2DImage(texture,
+ draw2DImageFilterScaled(driver, texture,
core::rect<s32>(x, y, x+tilesize.X, y+tilesize.Y),
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
NULL, NULL, true);
@@ -418,7 +417,7 @@ void GUIEngine::drawBackground(video::IVideoDriver* driver)
}
/* Draw background texture */
- driver->draw2DImage(texture,
+ draw2DImageFilterScaled(driver, texture,
core::rect<s32>(0, 0, screensize.X, screensize.Y),
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
NULL, NULL, true);
@@ -437,7 +436,7 @@ void GUIEngine::drawOverlay(video::IVideoDriver* driver)
/* Draw background texture */
v2u32 sourcesize = texture->getOriginalSize();
- driver->draw2DImage(texture,
+ draw2DImageFilterScaled(driver, texture,
core::rect<s32>(0, 0, screensize.X, screensize.Y),
core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
NULL, NULL, true);
@@ -470,7 +469,7 @@ void GUIEngine::drawHeader(video::IVideoDriver* driver)
video::SColor bgcolor(255,50,50,50);
- driver->draw2DImage(texture, splashrect,
+ draw2DImageFilterScaled(driver, texture, splashrect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL, NULL, true);
@@ -502,7 +501,7 @@ void GUIEngine::drawFooter(video::IVideoDriver* driver)
rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
rect -= v2s32(footersize.X/2, 0);
- driver->draw2DImage(texture, rect,
+ draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL, NULL, true);
@@ -514,7 +513,7 @@ bool GUIEngine::setTexture(texture_layer layer, std::string texturepath,
bool tile_image, unsigned int minsize)
{
video::IVideoDriver* driver = m_device->getVideoDriver();
- assert(driver != 0);
+ FATAL_ERROR_IF(driver == 0, "Could not get video driver");
if (m_textures[layer].texture != NULL)
{
@@ -570,13 +569,13 @@ bool GUIEngine::downloadFile(std::string url, std::string target)
/******************************************************************************/
void GUIEngine::setTopleftText(std::string append)
{
- std::wstring toset = narrow_to_wide( std::string("Minetest ") +
- minetest_version_hash);
+ std::wstring toset = utf8_to_wide(std::string(PROJECT_NAME_C " ") +
+ g_version_hash);
if (append != "")
{
toset += L" / ";
- toset += narrow_to_wide(append);
+ toset += utf8_to_wide(append);
}
m_irr_toplefttext->setText(toset.c_str());
diff --git a/src/guiEngine.h b/src/guiEngine.h
index 0be8634dd..d527f7222 100644
--- a/src/guiEngine.h
+++ b/src/guiEngine.h
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "modalMenu.h"
#include "guiFormSpecMenu.h"
#include "sound.h"
-#include "tile.h"
+#include "client/tile.h"
/******************************************************************************/
/* Typedefs and macros */
@@ -73,7 +73,7 @@ public:
* receive fields transmitted by guiFormSpecMenu
* @param fields map containing formspec field elements currently active
*/
- void gotText(std::map<std::string, std::string> fields);
+ void gotText(const StringMap &fields);
/**
* receive text/events transmitted by guiFormSpecMenu
diff --git a/src/guiFileSelectMenu.cpp b/src/guiFileSelectMenu.cpp
index e98b025c6..e02407427 100644
--- a/src/guiFileSelectMenu.cpp
+++ b/src/guiFileSelectMenu.cpp
@@ -26,7 +26,7 @@ GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
std::string title, std::string formname) :
GUIModalMenu(env, parent, id, menumgr)
{
- m_title = narrow_to_wide(title);
+ m_title = utf8_to_wide(title);
m_parent = parent;
m_formname = formname;
m_text_dst = 0;
@@ -84,10 +84,10 @@ void GUIFileSelectMenu::drawMenu()
void GUIFileSelectMenu::acceptInput() {
if ((m_text_dst != 0) && (this->m_formname != "")){
- std::map<std::string, std::string> fields;
+ StringMap fields;
if (m_accepted)
- fields[m_formname + "_accepted"] = wide_to_narrow(m_fileOpenDialog->getFileName());
+ fields[m_formname + "_accepted"] = wide_to_utf8(m_fileOpenDialog->getFileName());
else
fields[m_formname + "_canceled"] = m_formname;
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 3f285fa5e..62a84460f 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -37,21 +37,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IGUITabControl.h>
#include <IGUIComboBox.h>
#include "log.h"
-#include "tile.h" // ITextureSource
+#include "client/tile.h" // ITextureSource
#include "hud.h" // drawItemStack
-#include "hex.h"
-#include "util/string.h"
-#include "util/numeric.h"
#include "filesys.h"
#include "gettime.h"
#include "gettext.h"
#include "scripting_game.h"
#include "porting.h"
-#include "main.h"
#include "settings.h"
#include "client.h"
-#include "util/string.h" // for parseColorString()
#include "fontengine.h"
+#include "util/hex.h"
+#include "util/numeric.h"
+#include "util/string.h" // for parseColorString()
+#include "guiscalingfilter.h"
+
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+#include "intlGUIEditBox.h"
+#endif
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
@@ -78,7 +81,7 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
InventoryManager *invmgr, IGameDef *gamedef,
ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst,
- Client* client) :
+ Client* client, bool remap_dbl_click) :
GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
m_device(dev),
m_invmgr(invmgr),
@@ -97,10 +100,11 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
m_form_src(fsrc),
m_text_dst(tdst),
m_formspec_version(0),
- m_focused_element(L""),
- m_font(NULL)
+ m_focused_element(""),
+ m_font(NULL),
+ m_remap_dbl_click(remap_dbl_click)
#ifdef __ANDROID__
- ,m_JavaDialogFieldName(L"")
+ , m_JavaDialogFieldName("")
#endif
{
current_keys_pending.key_down = false;
@@ -227,7 +231,7 @@ void GUIFormSpecMenu::setInitialFocus()
Environment->setFocus(*(children.begin()));
}
-GUITable* GUIFormSpecMenu::getTable(std::wstring tablename)
+GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
{
for (u32 i = 0; i < m_tables.size(); ++i) {
if (tablename == m_tables[i].first.fname)
@@ -236,28 +240,27 @@ GUITable* GUIFormSpecMenu::getTable(std::wstring tablename)
return 0;
}
-std::vector<std::string> split(const std::string &s, char delim) {
+static std::vector<std::string> split(const std::string &s, char delim)
+{
std::vector<std::string> tokens;
std::string current = "";
bool last_was_escape = false;
- for(unsigned int i=0; i < s.size(); i++) {
+ for (unsigned int i = 0; i < s.size(); i++) {
+ char si = s.c_str()[i];
if (last_was_escape) {
current += '\\';
- current += s.c_str()[i];
+ current += si;
last_was_escape = false;
- }
- else {
- if (s.c_str()[i] == delim) {
+ } else {
+ if (si == delim) {
tokens.push_back(current);
current = "";
last_was_escape = false;
- }
- else if (s.c_str()[i] == '\\'){
+ } else if (si == '\\') {
last_was_escape = true;
- }
- else {
- current += s.c_str()[i];
+ } else {
+ current += si;
last_was_escape = false;
}
}
@@ -349,6 +352,41 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
}
+void GUIFormSpecMenu::parseListRing(parserData* data, std::string element)
+{
+ if (m_gamedef == 0) {
+ errorstream << "WARNING: invalid use of 'listring' with m_gamedef==0" << std::endl;
+ return;
+ }
+
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() == 2) {
+ std::string location = parts[0];
+ std::string listname = parts[1];
+
+ InventoryLocation loc;
+
+ if (location == "context" || location == "current_name")
+ loc = m_current_inventory_location;
+ else
+ loc.deSerialize(location);
+
+ m_inventory_rings.push_back(ListRingSpec(loc, listname));
+ return;
+ } else if ((element == "") && (m_inventorylists.size() > 1)) {
+ size_t siz = m_inventorylists.size();
+ // insert the last two inv list elements into the list ring
+ const ListDrawSpec &spa = m_inventorylists[siz - 2];
+ const ListDrawSpec &spb = m_inventorylists[siz - 1];
+ m_inventory_rings.push_back(ListRingSpec(spa.inventoryloc, spa.listname));
+ m_inventory_rings.push_back(ListRingSpec(spb.inventoryloc, spb.listname));
+ return;
+ }
+ errorstream<< "Invalid list ring element(" << parts.size() << ", "
+ << m_inventorylists.size() << "): '" << element << "'" << std::endl;
+}
+
void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
{
std::vector<std::string> parts = split(element,';');
@@ -375,7 +413,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
if (selected == "true")
fselected = true;
- std::wstring wlabel = narrow_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(label);
core::rect<s32> rect = core::rect<s32>(
pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
@@ -383,7 +421,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
pos.Y + ((imgsize.Y/2) + m_btn_height));
FieldSpec spec(
- narrow_to_wide(name),
+ name,
wlabel, //Needed for displaying text on MSVC
wlabel,
258+m_fields.size()
@@ -435,7 +473,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
FieldSpec spec(
- narrow_to_wide(name),
+ name,
L"",
L"",
258+m_fields.size()
@@ -573,10 +611,10 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
- narrow_to_wide(name),
+ name,
wlabel,
L"",
258+m_fields.size()
@@ -698,10 +736,8 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- std::wstring fname_w = narrow_to_wide(name);
-
FieldSpec spec(
- fname_w,
+ name,
L"",
L"",
258+m_fields.size()
@@ -723,8 +759,8 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
e->setTable(data->table_options, data->table_columns, items);
- if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
- e->setDynamicData(data->table_dyndata[fname_w]);
+ if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
+ e->setDynamicData(data->table_dyndata[name]);
}
if ((str_initial_selection != "") &&
@@ -772,10 +808,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- std::wstring fname_w = narrow_to_wide(name);
-
FieldSpec spec(
- fname_w,
+ name,
L"",
L"",
258+m_fields.size()
@@ -797,8 +831,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
e->setTextList(items, is_yes(str_transparent));
- if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
- e->setDynamicData(data->table_dyndata[fname_w]);
+ if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
+ e->setDynamicData(data->table_dyndata[name]);
}
if ((str_initial_selection != "") &&
@@ -837,10 +871,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
pos.X + width, pos.Y + (m_btn_height * 2));
- std::wstring fname_w = narrow_to_wide(name);
-
FieldSpec spec(
- fname_w,
+ name,
L"",
L"",
258+m_fields.size()
@@ -857,7 +889,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
}
for (unsigned int i=0; i < items.size(); i++) {
- e->addItem(narrow_to_wide(items[i]).c_str());
+ e->addItem(utf8_to_wide(items[i]).c_str());
}
if (str_initial_selection != "")
@@ -900,10 +932,10 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
- narrow_to_wide(name),
+ name,
wlabel,
L"",
258+m_fields.size()
@@ -966,12 +998,12 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
default_val = unescape_string(default_val);
label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
- narrow_to_wide(name),
+ name,
wlabel,
- narrow_to_wide(default_val),
+ utf8_to_wide(default_val),
258+m_fields.size()
);
@@ -983,9 +1015,18 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data,
else
{
spec.send = true;
- gui::IGUIEditBox *e =
- Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
-
+ gui::IGUIElement *e;
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+ if (g_settings->getBool("freetype")) {
+ e = (gui::IGUIElement *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
+ true, Environment, this, spec.fid, rect);
+ e->drop();
+ } else {
+#else
+ {
+#endif
+ e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+ }
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
@@ -1056,12 +1097,12 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
default_val = unescape_string(default_val);
label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
- narrow_to_wide(name),
+ name,
wlabel,
- narrow_to_wide(default_val),
+ utf8_to_wide(default_val),
258+m_fields.size()
);
@@ -1073,8 +1114,19 @@ void GUIFormSpecMenu::parseTextArea(parserData* data,
else
{
spec.send = true;
- gui::IGUIEditBox *e =
- Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+
+ gui::IGUIEditBox *e;
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+ if (g_settings->getBool("freetype")) {
+ e = (gui::IGUIEditBox *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
+ true, Environment, this, spec.fid, rect);
+ e->drop();
+ } else {
+#else
+ {
+#endif
+ e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+ }
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
@@ -1159,13 +1211,13 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
// in the integer cases: 0.4 is not exactly
// representable in binary floating point.
s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
- std::wstring wlabel = narrow_to_wide(lines[i]);
+ std::wstring wlabel = utf8_to_wide(lines[i]);
core::rect<s32> rect = core::rect<s32>(
pos.X, posy - m_btn_height,
pos.X + m_font->getDimension(wlabel.c_str()).Width,
posy + m_btn_height);
FieldSpec spec(
- L"",
+ "",
wlabel,
L"",
258+m_fields.size()
@@ -1191,7 +1243,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
- std::wstring text = narrow_to_wide(unescape_string(parts[1]));
+ std::wstring text = utf8_to_wide(unescape_string(parts[1]));
MY_CHECKPOS("vertlabel",1);
@@ -1218,7 +1270,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
}
FieldSpec spec(
- L"",
+ "",
label,
L"",
258+m_fields.size()
@@ -1280,12 +1332,12 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
pressed_image_name = unescape_string(pressed_image_name);
label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label);
+ std::wstring wlabel = utf8_to_wide(label);
FieldSpec spec(
- narrow_to_wide(name),
+ name,
wlabel,
- narrow_to_wide(image_name),
+ utf8_to_wide(image_name),
258+m_fields.size()
);
spec.ftype = f_Button;
@@ -1307,8 +1359,10 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
}
e->setUseAlphaChannel(true);
- e->setImage(texture);
- e->setPressedImage(pressed_texture);
+ e->setImage(guiScalingImageButton(
+ Environment->getVideoDriver(), texture, geom.X, geom.Y));
+ e->setPressedImage(guiScalingImageButton(
+ Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
e->setScaleImage(true);
e->setNotClipped(noclip);
e->setDrawBorder(drawborder);
@@ -1345,7 +1399,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
}
FieldSpec spec(
- narrow_to_wide(name),
+ name,
L"",
L"",
258+m_fields.size()
@@ -1375,8 +1429,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
e->setNotClipped(true);
- for (unsigned int i=0; i< buttons.size(); i++) {
- e->addTab(narrow_to_wide(buttons[i]).c_str(), -1);
+ for (unsigned int i = 0; i < buttons.size(); i++) {
+ e->addTab(utf8_to_wide(buttons[i]).c_str(), -1);
}
if ((tab_index >= 0) &&
@@ -1432,17 +1486,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
item.deSerialize(item_name, idef);
video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
- m_tooltips[narrow_to_wide(name)] =
- TooltipSpec (item.getDefinition(idef).description,
+ m_tooltips[name] =
+ TooltipSpec(item.getDefinition(idef).description,
m_default_tooltip_bgcolor,
m_default_tooltip_color);
label = unescape_string(label);
FieldSpec spec(
- narrow_to_wide(name),
- narrow_to_wide(label),
- narrow_to_wide(item_name),
- 258+m_fields.size()
+ name,
+ utf8_to_wide(label),
+ utf8_to_wide(item_name),
+ 258 + m_fields.size()
);
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
@@ -1452,8 +1506,8 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
}
e->setUseAlphaChannel(true);
- e->setImage(texture);
- e->setPressedImage(texture);
+ e->setImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
+ e->setPressedImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y));
e->setScaleImage(true);
spec.ftype = f_Button;
rect+=data->basepos-padding;
@@ -1551,13 +1605,15 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
std::vector<std::string> parts = split(element,';');
if (parts.size() == 2) {
std::string name = parts[0];
- m_tooltips[narrow_to_wide(name)] = TooltipSpec (parts[1], m_default_tooltip_bgcolor, m_default_tooltip_color);
+ m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
+ m_default_tooltip_bgcolor, m_default_tooltip_color);
return;
} else if (parts.size() == 4) {
std::string name = parts[0];
video::SColor tmp_color1, tmp_color2;
if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
- m_tooltips[narrow_to_wide(name)] = TooltipSpec (parts[1], tmp_color1, tmp_color2);
+ m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
+ tmp_color1, tmp_color2);
return;
}
}
@@ -1642,6 +1698,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
return;
}
+ if (type == "listring") {
+ parseListRing(data, description);
+ return;
+ }
+
if (type == "checkbox") {
parseCheckbox(data,description);
return;
@@ -1769,7 +1830,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
//preserve tables
for (u32 i = 0; i < m_tables.size(); ++i) {
- std::wstring tablename = m_tables[i].first.fname;
+ std::string tablename = m_tables[i].first.fname;
GUITable *table = m_tables[i].second;
mydata.table_dyndata[tablename] = table->getDynamicData();
}
@@ -1974,7 +2035,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_tooltip_element->setOverrideFont(m_font);
gui::IGUISkin* skin = Environment->getSkin();
- assert(skin != NULL);
+ sanity_check(skin != NULL);
gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font);
@@ -2024,7 +2085,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
bool GUIFormSpecMenu::getAndroidUIInput()
{
/* no dialog shown */
- if (m_JavaDialogFieldName == L"") {
+ if (m_JavaDialogFieldName == "") {
return false;
}
@@ -2033,8 +2094,8 @@ bool GUIFormSpecMenu::getAndroidUIInput()
return true;
}
- std::wstring fieldname = m_JavaDialogFieldName;
- m_JavaDialogFieldName = L"";
+ std::string fieldname = m_JavaDialogFieldName;
+ m_JavaDialogFieldName = "";
/* no value abort dialog processing */
if (porting::getInputDialogState() != 0) {
@@ -2060,7 +2121,7 @@ bool GUIFormSpecMenu::getAndroidUIInput()
std::string text = porting::getInputDialogValue();
((gui::IGUIEditBox*) tochange)->
- setText(narrow_to_wide(text).c_str());
+ setText(utf8_to_wide(text).c_str());
}
return false;
}
@@ -2184,7 +2245,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
m_tooltip_element->setOverrideColor(m_default_tooltip_color);
m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element);
- m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
+ m_tooltip_element->setText(utf8_to_wide(tooltip_text).c_str());
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
v2u32 screenSize = driver->getScreenSize();
@@ -2218,9 +2279,9 @@ void GUIFormSpecMenu::drawSelectedItem()
video::IVideoDriver* driver = Environment->getVideoDriver();
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
- assert(inv);
+ sanity_check(inv);
InventoryList *list = inv->getList(m_selected_item->listname);
- assert(list);
+ sanity_check(list);
ItemStack stack = list->getItem(m_selected_item->i);
stack.count = m_selected_amount;
@@ -2240,7 +2301,7 @@ void GUIFormSpecMenu::drawMenu()
}
gui::IGUISkin* skin = Environment->getSkin();
- assert(skin != NULL);
+ sanity_check(skin != NULL);
gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font);
@@ -2281,7 +2342,7 @@ void GUIFormSpecMenu::drawMenu()
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
- driver->draw2DImage(texture, rect,
+ draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
@@ -2331,7 +2392,7 @@ void GUIFormSpecMenu::drawMenu()
core::rect<s32> rect = imgrect + spec.pos;
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
- driver->draw2DImage(texture, rect,
+ draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
NULL/*&AbsoluteClippingRect*/, colors, true);
}
@@ -2360,7 +2421,7 @@ void GUIFormSpecMenu::drawMenu()
core::rect<s32> rect = imgrect + spec.pos;
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
- driver->draw2DImage(texture, rect,
+ draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
@@ -2416,7 +2477,7 @@ void GUIFormSpecMenu::drawMenu()
if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
m_old_tooltip = m_tooltips[iter->fname].tooltip;
- m_tooltip_element->setText(narrow_to_wide(m_tooltips[iter->fname].tooltip).c_str());
+ m_tooltip_element->setText(utf8_to_wide(m_tooltips[iter->fname].tooltip).c_str());
std::vector<std::string> tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n');
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
@@ -2575,7 +2636,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
{
if(m_text_dst)
{
- std::map<std::string, std::string> fields;
+ StringMap fields;
if (quitmode == quit_mode_accept) {
fields["quit"] = "true";
@@ -2610,11 +2671,10 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
for(unsigned int i=0; i<m_fields.size(); i++) {
const FieldSpec &s = m_fields[i];
if(s.send) {
- std::string name = wide_to_narrow(s.fname);
- if(s.ftype == f_Button) {
- fields[name] = wide_to_narrow(s.flabel);
- }
- else if(s.ftype == f_Table) {
+ std::string name = s.fname;
+ if (s.ftype == f_Button) {
+ fields[name] = wide_to_utf8(s.flabel);
+ } else if (s.ftype == f_Table) {
GUITable *table = getTable(s.fname);
if (table) {
fields[name] = table->checkEvent();
@@ -2631,7 +2691,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
s32 selected = e->getSelected();
if (selected >= 0) {
fields[name] =
- wide_to_narrow(e->getItem(selected));
+ wide_to_utf8(e->getItem(selected));
}
}
else if (s.ftype == f_TabHeader) {
@@ -2687,7 +2747,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
{
IGUIElement* e = getElementFromId(s.fid);
if(e != NULL) {
- fields[name] = wide_to_narrow(e->getText());
+ fields[name] = wide_to_utf8(e->getText());
}
}
}
@@ -2726,7 +2786,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
if (hovered && isMyChild(hovered) &&
hovered->getType() == gui::EGUIET_TAB_CONTROL) {
gui::IGUISkin* skin = Environment->getSkin();
- assert(skin != NULL);
+ sanity_check(skin != NULL);
gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font);
bool retval = hovered->OnEvent(event);
@@ -2795,7 +2855,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
}
m_JavaDialogFieldName = getNameByID(hovered->getID());
std::string message = gettext("Enter ");
- std::string label = wide_to_narrow(getLabelByID(hovered->getID()));
+ std::string label = wide_to_utf8(getLabelByID(hovered->getID()));
if (label == "") {
label = "text";
}
@@ -2815,7 +2875,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
}
porting::showInputDialog(gettext("ok"), "",
- wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()),
+ wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()),
type);
return retval;
}
@@ -2937,6 +2997,19 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
/******************************************************************************/
bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
{
+ /* The following code is for capturing double-clicks of the mouse button
+ * and translating the double-click into an EET_KEY_INPUT_EVENT event
+ * -- which closes the form -- under some circumstances.
+ *
+ * There have been many github issues reporting this as a bug even though it
+ * was an intended feature. For this reason, remapping the double-click as
+ * an ESC must be explicitly set when creating this class via the
+ * /p remap_dbl_click parameter of the constructor.
+ */
+
+ if (!m_remap_dbl_click)
+ return false;
+
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
@@ -2975,26 +3048,27 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
delete translated;
return true;
}
+
return false;
}
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
- if(event.EventType==EET_KEY_INPUT_EVENT) {
+ if (event.EventType==EET_KEY_INPUT_EVENT) {
KeyPress kp(event.KeyInput);
if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
- (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
+ (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
if (m_allowclose) {
doPause = false;
acceptInput(quit_mode_cancel);
quitMenu();
} else {
- m_text_dst->gotText(narrow_to_wide("MenuQuit"));
+ m_text_dst->gotText(L"MenuQuit");
}
return true;
} else if (m_client != NULL && event.KeyInput.PressedDown &&
- (kp == getKeySetting("keymap_screenshot"))) {
- m_client->makeScreenshot(m_device);
+ (kp == getKeySetting("keymap_screenshot"))) {
+ m_client->makeScreenshot(m_device);
}
if (event.KeyInput.PressedDown &&
(event.KeyInput.Key==KEY_RETURN ||
@@ -3014,7 +3088,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
break;
default:
//can't happen at all!
- assert("reached a source line that can't ever been reached" == 0);
+ FATAL_ERROR("Reached a source line that can't ever been reached");
break;
}
if (current_keys_pending.key_enter && m_allowclose) {
@@ -3045,44 +3119,45 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
Inventory *inv_selected = NULL;
Inventory *inv_s = NULL;
+ InventoryList *list_s = NULL;
- if(m_selected_item) {
+ if (m_selected_item) {
inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
- assert(inv_selected);
- assert(inv_selected->getList(m_selected_item->listname) != NULL);
+ sanity_check(inv_selected);
+ sanity_check(inv_selected->getList(m_selected_item->listname) != NULL);
}
u32 s_count = 0;
- if(s.isValid())
+ if (s.isValid())
do { // breakable
inv_s = m_invmgr->getInventory(s.inventoryloc);
- if(!inv_s) {
- errorstream<<"InventoryMenu: The selected inventory location "
- <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
- <<std::endl;
+ if (!inv_s) {
+ errorstream << "InventoryMenu: The selected inventory location "
+ << "\"" << s.inventoryloc.dump() << "\" doesn't exist"
+ << std::endl;
s.i = -1; // make it invalid again
break;
}
- InventoryList *list = inv_s->getList(s.listname);
- if(list == NULL) {
- verbosestream<<"InventoryMenu: The selected inventory list \""
- <<s.listname<<"\" does not exist"<<std::endl;
+ list_s = inv_s->getList(s.listname);
+ if (list_s == NULL) {
+ verbosestream << "InventoryMenu: The selected inventory list \""
+ << s.listname << "\" does not exist" << std::endl;
s.i = -1; // make it invalid again
break;
}
- if((u32)s.i >= list->getSize()) {
- infostream<<"InventoryMenu: The selected inventory list \""
- <<s.listname<<"\" is too small (i="<<s.i<<", size="
- <<list->getSize()<<")"<<std::endl;
+ if ((u32)s.i >= list_s->getSize()) {
+ infostream << "InventoryMenu: The selected inventory list \""
+ << s.listname << "\" is too small (i=" << s.i << ", size="
+ << list_s->getSize() << ")" << std::endl;
s.i = -1; // make it invalid again
break;
}
- s_count = list->getItem(s.i).count;
+ s_count = list_s->getItem(s.i).count;
} while(0);
bool identical = (m_selected_item != NULL) && s.isValid() &&
@@ -3094,25 +3169,29 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement
int button = 0;
int updown = 2;
- if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
+ if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
{ button = 0; updown = 0; }
- else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
+ else if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
{ button = 1; updown = 0; }
- else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
+ else if (event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
{ button = 2; updown = 0; }
- else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
+ else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
{ button = 0; updown = 1; }
- else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
+ else if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
{ button = 1; updown = 1; }
- else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
+ else if (event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
{ button = 2; updown = 1; }
- else if(event.MouseInput.Event == EMIE_MOUSE_MOVED)
+ else if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
{ updown = -1;}
// Set this number to a positive value to generate a move action
// from m_selected_item to s.
u32 move_amount = 0;
+ // Set this number to a positive value to generate a move action
+ // from s to the next inventory ring.
+ u32 shift_move_amount = 0;
+
// Set this number to a positive value to generate a drop action
// from m_selected_item.
u32 drop_amount = 0;
@@ -3120,7 +3199,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Set this number to a positive value to generate a craft action at s.
u32 craft_amount = 0;
- if(updown == 0) {
+ if (updown == 0) {
// Some mouse button has been pressed
//infostream<<"Mouse button "<<button<<" pressed at p=("
@@ -3128,40 +3207,49 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_dragging = false;
- if(s.isValid() && s.listname == "craftpreview") {
+ if (s.isValid() && s.listname == "craftpreview") {
// Craft preview has been clicked: craft
craft_amount = (button == 2 ? 10 : 1);
- }
- else if(m_selected_item == NULL) {
- if(s_count != 0) {
- // Non-empty stack has been clicked: select it
+ } else if (m_selected_item == NULL) {
+ if (s_count != 0) {
+ // Non-empty stack has been clicked: select or shift-move it
m_selected_item = new ItemSpec(s);
- if(button == 1) // right
- m_selected_amount = (s_count + 1) / 2;
- else if(button == 2) // middle
- m_selected_amount = MYMIN(s_count, 10);
+ u32 count;
+ if (button == 1) // right
+ count = (s_count + 1) / 2;
+ else if (button == 2) // middle
+ count = MYMIN(s_count, 10);
else // left
- m_selected_amount = s_count;
+ count = s_count;
- m_selected_dragging = true;
- m_rmouse_auto_place = false;
+ if (!event.MouseInput.Shift) {
+ // no shift: select item
+ m_selected_amount = count;
+ m_selected_dragging = true;
+ m_rmouse_auto_place = false;
+ } else {
+ // shift pressed: move item
+ if (button != 1)
+ shift_move_amount = count;
+ else // count of 1 at left click like after drag & drop
+ shift_move_amount = 1;
+ }
}
- }
- else { // m_selected_item != NULL
+ } else { // m_selected_item != NULL
assert(m_selected_amount >= 1);
- if(s.isValid()) {
+ if (s.isValid()) {
// Clicked a slot: move
- if(button == 1) // right
+ if (button == 1) // right
move_amount = 1;
- else if(button == 2) // middle
+ else if (button == 2) // middle
move_amount = MYMIN(m_selected_amount, 10);
else // left
move_amount = m_selected_amount;
- if(identical) {
- if(move_amount >= m_selected_amount)
+ if (identical) {
+ if (move_amount >= m_selected_amount)
m_selected_amount = 0;
else
m_selected_amount -= move_amount;
@@ -3170,29 +3258,28 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
// Clicked outside of the window: drop
- if(button == 1) // right
+ if (button == 1) // right
drop_amount = 1;
- else if(button == 2) // middle
+ else if (button == 2) // middle
drop_amount = MYMIN(m_selected_amount, 10);
else // left
drop_amount = m_selected_amount;
}
}
}
- else if(updown == 1) {
+ else if (updown == 1) {
// Some mouse button has been released
//infostream<<"Mouse button "<<button<<" released at p=("
// <<p.X<<","<<p.Y<<")"<<std::endl;
- if(m_selected_item != NULL && m_selected_dragging && s.isValid()) {
- if(!identical) {
+ if (m_selected_item != NULL && m_selected_dragging && s.isValid()) {
+ if (!identical) {
// Dragged to different slot: move all selected
move_amount = m_selected_amount;
}
- }
- else if(m_selected_item != NULL && m_selected_dragging &&
- !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
+ } else if (m_selected_item != NULL && m_selected_dragging &&
+ !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
// Dragged outside of window: drop all selected
drop_amount = m_selected_amount;
}
@@ -3201,14 +3288,13 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Keep count of how many times right mouse button has been
// clicked. One click is drag without dropping. Click + release
// + click changes to drop one item when moved mode
- if(button == 1 && m_selected_item != NULL)
+ if (button == 1 && m_selected_item != NULL)
m_rmouse_auto_place = !m_rmouse_auto_place;
- }
- else if(updown == -1) {
+ } else if (updown == -1) {
// Mouse has been moved and rmb is down and mouse pointer just
// entered a new inventory field (checked in the entry-if, this
// is the only action here that is generated by mouse movement)
- if(m_selected_item != NULL && s.isValid()){
+ if (m_selected_item != NULL && s.isValid()) {
// Move 1 item
// TODO: middle mouse to move 10 items might be handy
if (m_rmouse_auto_place) {
@@ -3216,7 +3302,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// or contains the same item type as what is going to be
// moved
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
- InventoryList *list_to = inv_s->getList(s.listname);
+ InventoryList *list_to = list_s;
assert(list_from && list_to);
ItemStack stack_from = list_from->getItem(m_selected_item->i);
ItemStack stack_to = list_to->getItem(s.i);
@@ -3227,8 +3313,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
// Possibly send inventory action to server
- if(move_amount > 0)
- {
+ if (move_amount > 0) {
// Send IACTION_MOVE
assert(m_selected_item && m_selected_item->isValid());
@@ -3236,7 +3321,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
assert(inv_selected && inv_s);
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
- InventoryList *list_to = inv_s->getList(s.listname);
+ InventoryList *list_to = list_s;
assert(list_from && list_to);
ItemStack stack_from = list_from->getItem(m_selected_item->i);
ItemStack stack_to = list_to->getItem(s.i);
@@ -3255,7 +3340,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_content_guess_inventory = s.inventoryloc;
}
// Source stack goes fully into destination stack
- else if(leftover.empty()) {
+ else if (leftover.empty()) {
m_selected_amount -= move_amount;
m_selected_content_guess = ItemStack(); // Clear
}
@@ -3266,7 +3351,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_content_guess = ItemStack(); // Clear
}
- infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
+ infostream << "Handing IACTION_MOVE to manager" << std::endl;
IMoveAction *a = new IMoveAction();
a->count = move_amount;
a->from_inv = m_selected_item->inventoryloc;
@@ -3276,8 +3361,68 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->to_list = s.listname;
a->to_i = s.i;
m_invmgr->inventoryAction(a);
- }
- else if(drop_amount > 0) {
+ } else if (shift_move_amount > 0) {
+ u32 mis = m_inventory_rings.size();
+ u32 i = 0;
+ for (; i < mis; i++) {
+ const ListRingSpec &sp = m_inventory_rings[i];
+ if (sp.inventoryloc == s.inventoryloc
+ && sp.listname == s.listname)
+ break;
+ }
+ do {
+ if (i >= mis) // if not found
+ break;
+ u32 to_inv_ind = (i + 1) % mis;
+ const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
+ InventoryList *list_from = list_s;
+ if (!s.isValid())
+ break;
+ Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
+ if (!inv_to)
+ break;
+ InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
+ if (!list_to)
+ break;
+ ItemStack stack_from = list_from->getItem(s.i);
+ assert(shift_move_amount <= stack_from.count);
+ if (m_client->getProtoVersion() >= 25) {
+ infostream << "Handing IACTION_MOVE to manager" << std::endl;
+ IMoveAction *a = new IMoveAction();
+ a->count = shift_move_amount;
+ a->from_inv = s.inventoryloc;
+ a->from_list = s.listname;
+ a->from_i = s.i;
+ a->to_inv = to_inv_sp.inventoryloc;
+ a->to_list = to_inv_sp.listname;
+ a->move_somewhere = true;
+ m_invmgr->inventoryAction(a);
+ } else {
+ // find a place (or more than one) to add the new item
+ u32 ilt_size = list_to->getSize();
+ ItemStack leftover;
+ for (u32 slot_to = 0; slot_to < ilt_size
+ && shift_move_amount > 0; slot_to++) {
+ list_to->itemFits(slot_to, stack_from, &leftover);
+ if (leftover.count < stack_from.count) {
+ infostream << "Handing IACTION_MOVE to manager" << std::endl;
+ IMoveAction *a = new IMoveAction();
+ a->count = MYMIN(shift_move_amount,
+ (u32) (stack_from.count - leftover.count));
+ shift_move_amount -= a->count;
+ a->from_inv = s.inventoryloc;
+ a->from_list = s.listname;
+ a->from_i = s.i;
+ a->to_inv = to_inv_sp.inventoryloc;
+ a->to_list = to_inv_sp.listname;
+ a->to_i = slot_to;
+ m_invmgr->inventoryAction(a);
+ stack_from = leftover;
+ }
+ }
+ }
+ } while (0);
+ } else if (drop_amount > 0) {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_DROP
@@ -3293,15 +3438,14 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
assert(drop_amount > 0 && drop_amount <= m_selected_amount);
m_selected_amount -= drop_amount;
- infostream<<"Handing IACTION_DROP to manager"<<std::endl;
+ infostream << "Handing IACTION_DROP to manager" << std::endl;
IDropAction *a = new IDropAction();
a->count = drop_amount;
a->from_inv = m_selected_item->inventoryloc;
a->from_list = m_selected_item->listname;
a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
- }
- else if(craft_amount > 0) {
+ } else if (craft_amount > 0) {
m_selected_content_guess = ItemStack(); // Clear
// Send IACTION_CRAFT
@@ -3309,7 +3453,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
assert(s.isValid());
assert(inv_s);
- infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
+ infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
@@ -3317,7 +3461,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
// If m_selected_amount has been decreased to zero, deselect
- if(m_selected_amount == 0) {
+ if (m_selected_amount == 0) {
delete m_selected_item;
m_selected_item = NULL;
m_selected_amount = 0;
@@ -3326,12 +3470,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
m_old_pointer = m_pointer;
}
- if(event.EventType==EET_GUI_EVENT) {
+ if (event.EventType == EET_GUI_EVENT) {
- if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED
+ if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
&& isVisible()) {
// find the element that was clicked
- for(unsigned int i=0; i<m_fields.size(); i++) {
+ for (unsigned int i=0; i<m_fields.size(); i++) {
FieldSpec &s = m_fields[i];
if ((s.ftype == f_TabHeader) &&
(s.fid == event.GUIEvent.Caller->getID())) {
@@ -3342,16 +3486,16 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}
}
- if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
+ if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible()) {
- if(!canTakeFocus(event.GUIEvent.Element)) {
+ if (!canTakeFocus(event.GUIEvent.Element)) {
infostream<<"GUIFormSpecMenu: Not allowing focus change."
<<std::endl;
// Returning true disables focus change
return true;
}
}
- if((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
+ if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
(event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
(event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
(event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
@@ -3363,26 +3507,26 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
quitMenu();
} else {
acceptInput();
- m_text_dst->gotText(narrow_to_wide("ExitButton"));
+ m_text_dst->gotText(L"ExitButton");
}
// quitMenu deallocates menu
return true;
}
// find the element that was clicked
- for(u32 i=0; i<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s = m_fields[i];
// if its a button, set the send field so
// lua knows which button was pressed
if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
(s.fid == event.GUIEvent.Caller->getID())) {
s.send = true;
- if(s.is_exit) {
+ if (s.is_exit) {
if (m_allowclose) {
acceptInput(quit_mode_accept);
quitMenu();
} else {
- m_text_dst->gotText(narrow_to_wide("ExitButton"));
+ m_text_dst->gotText(L"ExitButton");
}
return true;
} else {
@@ -3390,11 +3534,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
s.send = false;
return true;
}
- }
- else if ((s.ftype == f_DropDown) &&
+ } else if ((s.ftype == f_DropDown) &&
(s.fid == event.GUIEvent.Caller->getID())) {
// only send the changed dropdown
- for(u32 i=0; i<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s2 = m_fields[i];
if (s2.ftype == f_DropDown) {
s2.send = false;
@@ -3405,17 +3548,15 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// revert configuration to make sure dropdowns are sent on
// regular button click
- for(u32 i=0; i<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s2 = m_fields[i];
if (s2.ftype == f_DropDown) {
s2.send = true;
}
}
return true;
- }
- else if ((s.ftype == f_ScrollBar) &&
- (s.fid == event.GUIEvent.Caller->getID()))
- {
+ } else if ((s.ftype == f_ScrollBar) &&
+ (s.fid == event.GUIEvent.Caller->getID())) {
s.fdefault = L"Changed";
acceptInput(quit_mode_no);
s.fdefault = L"";
@@ -3423,8 +3564,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}
- if(event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
- if(event.GUIEvent.Caller->getID() > 257) {
+ if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
+ if (event.GUIEvent.Caller->getID() > 257) {
if (m_allowclose) {
acceptInput(quit_mode_accept);
@@ -3438,11 +3579,11 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}
- if(event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
+ if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
int current_id = event.GUIEvent.Caller->getID();
- if(current_id > 257) {
+ if (current_id > 257) {
// find the element that was clicked
- for(u32 i=0; i<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s = m_fields[i];
// if it's a table, set the send field
// so lua knows which table was changed
@@ -3465,7 +3606,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
* @param id of element
* @return name string or empty string
*/
-std::wstring GUIFormSpecMenu::getNameByID(s32 id)
+std::string GUIFormSpecMenu::getNameByID(s32 id)
{
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
iter != m_fields.end(); iter++) {
@@ -3473,7 +3614,7 @@ std::wstring GUIFormSpecMenu::getNameByID(s32 id)
return iter->fname;
}
}
- return L"";
+ return "";
}
/**
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index 48cb5c553..2ba47f7ff 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "inventorymanager.h"
#include "modalMenu.h"
#include "guiTable.h"
-#include "clientserver.h"
+#include "network/networkprotocol.h"
class IGameDef;
class InventoryManager;
@@ -56,7 +56,7 @@ struct TextDest
virtual ~TextDest() {};
// This is deprecated I guess? -celeron55
virtual void gotText(std::wstring text){}
- virtual void gotText(std::map<std::string, std::string> fields) = 0;
+ virtual void gotText(const StringMap &fields) = 0;
virtual void setFormName(std::string formname)
{ m_formname = formname;};
@@ -121,6 +121,22 @@ class GUIFormSpecMenu : public GUIModalMenu
s32 start_item_i;
};
+ struct ListRingSpec
+ {
+ ListRingSpec()
+ {
+ }
+ ListRingSpec(const InventoryLocation &a_inventoryloc,
+ const std::string &a_listname):
+ inventoryloc(a_inventoryloc),
+ listname(a_listname)
+ {
+ }
+
+ InventoryLocation inventoryloc;
+ std::string listname;
+ };
+
struct ImageDrawSpec
{
ImageDrawSpec()
@@ -152,7 +168,7 @@ class GUIFormSpecMenu : public GUIModalMenu
FieldSpec()
{
}
- FieldSpec(const std::wstring &name, const std::wstring &label,
+ FieldSpec(const std::string &name, const std::wstring &label,
const std::wstring &fdeflt, int id) :
fname(name),
flabel(label),
@@ -163,7 +179,7 @@ class GUIFormSpecMenu : public GUIModalMenu
ftype = f_Unknown;
is_exit = false;
}
- std::wstring fname;
+ std::string fname;
std::wstring flabel;
std::wstring fdefault;
int fid;
@@ -210,8 +226,8 @@ public:
ISimpleTextureSource *tsrc,
IFormSource* fs_src,
TextDest* txt_dst,
- Client* client
- );
+ Client* client,
+ bool remap_dbl_click = true);
~GUIFormSpecMenu();
@@ -255,7 +271,7 @@ public:
void removeChildren();
void setInitialFocus();
- void setFocus(std::wstring elementname)
+ void setFocus(std::string &elementname)
{
m_focused_element = elementname;
}
@@ -278,7 +294,7 @@ public:
bool doPause;
bool pausesGame() { return doPause; }
- GUITable* getTable(std::wstring tablename);
+ GUITable* getTable(const std::string &tablename);
#ifdef __ANDROID__
bool getAndroidUIInput();
@@ -306,6 +322,7 @@ protected:
std::vector<ListDrawSpec> m_inventorylists;
+ std::vector<ListRingSpec> m_inventory_rings;
std::vector<ImageDrawSpec> m_backgrounds;
std::vector<ImageDrawSpec> m_images;
std::vector<ImageDrawSpec> m_itemimages;
@@ -313,7 +330,7 @@ protected:
std::vector<FieldSpec> m_fields;
std::vector<std::pair<FieldSpec,GUITable*> > m_tables;
std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
- std::map<std::wstring, TooltipSpec> m_tooltips;
+ std::map<std::string, TooltipSpec> m_tooltips;
std::vector<std::pair<FieldSpec,gui::IGUIScrollBar*> > m_scrollbars;
ItemSpec *m_selected_item;
@@ -355,7 +372,7 @@ private:
IFormSource *m_form_src;
TextDest *m_text_dst;
unsigned int m_formspec_version;
- std::wstring m_focused_element;
+ std::string m_focused_element;
typedef struct {
bool explicit_size;
@@ -364,11 +381,11 @@ private:
core::rect<s32> rect;
v2s32 basepos;
v2u32 screensize;
- std::wstring focused_fieldname;
+ std::string focused_fieldname;
GUITable::TableOptions table_options;
GUITable::TableColumns table_columns;
// used to restore table selection/scroll/treeview state
- std::map<std::wstring,GUITable::DynamicData> table_dyndata;
+ std::map<std::string, GUITable::DynamicData> table_dyndata;
} parserData;
typedef struct {
@@ -384,6 +401,7 @@ private:
void parseSize(parserData* data,std::string element);
void parseList(parserData* data,std::string element);
+ void parseListRing(parserData* data,std::string element);
void parseCheckbox(parserData* data,std::string element);
void parseImage(parserData* data,std::string element);
void parseItemImage(parserData* data,std::string element);
@@ -430,12 +448,20 @@ private:
gui::IGUIFont *m_font;
std::wstring getLabelByID(s32 id);
- std::wstring getNameByID(s32 id);
+ std::string getNameByID(s32 id);
#ifdef __ANDROID__
v2s32 m_down_pos;
- std::wstring m_JavaDialogFieldName;
+ std::string m_JavaDialogFieldName;
#endif
+ /* If true, remap a double-click (or double-tap) action to ESC. This is so
+ * that, for example, Android users can double-tap to close a formspec.
+ *
+ * This value can (currently) only be set by the class constructor
+ * and the default value for the setting is true.
+ */
+ bool m_remap_dbl_click;
+
};
class FormspecFormSource: public IFormSource
diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp
index 4cd9f36d9..261592394 100644
--- a/src/guiKeyChangeMenu.cpp
+++ b/src/guiKeyChangeMenu.cpp
@@ -22,7 +22,6 @@
#include "guiKeyChangeMenu.h"
#include "debug.h"
#include "serialization.h"
-#include "main.h"
#include <string>
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
@@ -51,6 +50,7 @@ enum
GUI_ID_KEY_FAST_BUTTON,
GUI_ID_KEY_JUMP_BUTTON,
GUI_ID_KEY_NOCLIP_BUTTON,
+ GUI_ID_KEY_CINEMATIC_BUTTON,
GUI_ID_KEY_CHAT_BUTTON,
GUI_ID_KEY_CMD_BUTTON,
GUI_ID_KEY_CONSOLE_BUTTON,
@@ -137,20 +137,20 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
{
key_setting *k = key_settings.at(i);
{
- core::rect < s32 > rect(0, 0, 100, 20);
+ core::rect < s32 > rect(0, 0, 110, 20);
rect += topleft + v2s32(offset.X, offset.Y);
Environment->addStaticText(k->button_name, rect, false, true, this, -1);
}
{
core::rect < s32 > rect(0, 0, 100, 30);
- rect += topleft + v2s32(offset.X + 105, offset.Y - 5);
+ rect += topleft + v2s32(offset.X + 115, offset.Y - 5);
const wchar_t *text = wgettext(k->key.name());
k->button = Environment->addButton(rect, this, k->id, text);
delete[] text;
}
if(i + 1 == KMaxButtonPerColumns)
- offset = v2s32(250, 60);
+ offset = v2s32(260, 60);
else
offset += v2s32(0, 25);
}
@@ -269,8 +269,7 @@ bool GUIKeyChangeMenu::resetMenu()
bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
{
if (event.EventType == EET_KEY_INPUT_EVENT && activeKey >= 0
- && event.KeyInput.PressedDown)
- {
+ && event.KeyInput.PressedDown) {
bool prefer_character = shift_down;
KeyPress kp(event.KeyInput, prefer_character);
@@ -302,7 +301,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
// But go on
{
- key_setting *k=NULL;
+ key_setting *k = NULL;
for(size_t i = 0; i < key_settings.size(); i++)
{
if(key_settings.at(i)->id == activeKey)
@@ -311,7 +310,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
break;
}
}
- assert(k);
+ FATAL_ERROR_IF(k == NULL, "Key setting not found");
k->key = kp;
const wchar_t *text = wgettext(k->key.name());
k->button->setText(text);
@@ -328,9 +327,12 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
return true;
}
}
- }
- if (event.EventType == EET_GUI_EVENT)
- {
+ } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
+ && event.KeyInput.PressedDown
+ && event.KeyInput.Key == irr::KEY_ESCAPE) {
+ quitMenu();
+ return true;
+ } else if (event.EventType == EET_GUI_EVENT) {
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
&& isVisible())
{
@@ -363,7 +365,7 @@ bool GUIKeyChangeMenu::OnEvent(const SEvent& event)
break;
}
}
- assert(k);
+ FATAL_ERROR_IF(k == NULL, "Key setting not found");
resetMenu();
shift_down = false;
@@ -394,22 +396,23 @@ void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::st
void GUIKeyChangeMenu::init_keys()
{
- this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward");
- this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward");
- this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left");
- this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right");
- this->add_key(GUI_ID_KEY_USE_BUTTON, wgettext("Use"), "keymap_special1");
- this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump");
- this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak");
- this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop");
- this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory");
- this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat");
- this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd");
- this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console");
- this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove");
- this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove");
- this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip");
- this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect");
- this->add_key(GUI_ID_KEY_DUMP_BUTTON, wgettext("Print stacks"), "keymap_print_debug_stacks");
+ this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wgettext("Forward"), "keymap_forward");
+ this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward");
+ this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left");
+ this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right");
+ this->add_key(GUI_ID_KEY_USE_BUTTON, wgettext("Use"), "keymap_special1");
+ this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump");
+ this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak");
+ this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop");
+ this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"), "keymap_inventory");
+ this->add_key(GUI_ID_KEY_CHAT_BUTTON, wgettext("Chat"), "keymap_chat");
+ this->add_key(GUI_ID_KEY_CMD_BUTTON, wgettext("Command"), "keymap_cmd");
+ this->add_key(GUI_ID_KEY_CONSOLE_BUTTON, wgettext("Console"), "keymap_console");
+ this->add_key(GUI_ID_KEY_FLY_BUTTON, wgettext("Toggle fly"), "keymap_freemove");
+ this->add_key(GUI_ID_KEY_FAST_BUTTON, wgettext("Toggle fast"), "keymap_fastmove");
+ this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
+ this->add_key(GUI_ID_KEY_NOCLIP_BUTTON, wgettext("Toggle noclip"), "keymap_noclip");
+ this->add_key(GUI_ID_KEY_RANGE_BUTTON, wgettext("Range select"), "keymap_rangeselect");
+ this->add_key(GUI_ID_KEY_DUMP_BUTTON, wgettext("Print stacks"), "keymap_print_debug_stacks");
}
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index 34362dba6..711ad10f8 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -25,17 +25,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <list>
-enum
-{
- TAB_SINGLEPLAYER=0,
- TAB_MULTIPLAYER,
- TAB_ADVANCED,
- TAB_SETTINGS,
- TAB_CREDITS
+struct MainMenuDataForScript {
+
+ MainMenuDataForScript() :
+ reconnect_requested(false)
+ {}
+
+ // Whether the server has requested a reconnect
+ bool reconnect_requested;
+
+ std::string errormessage;
};
-struct MainMenuData
-{
+struct MainMenuData {
// Client options
std::string servername;
std::string serverdescription;
@@ -43,19 +45,22 @@ struct MainMenuData
std::string port;
std::string name;
std::string password;
+ // Whether to reconnect
+ bool do_reconnect;
// Server options
bool enable_public;
int selected_world;
bool simple_singleplayer_mode;
- //error handling
- std::string errormessage;
+ // Data to be passed to the script
+ MainMenuDataForScript script_data;
+
MainMenuData():
+ do_reconnect(false),
enable_public(false),
selected_world(0),
- simple_singleplayer_mode(false),
- errormessage("")
+ simple_singleplayer_mode(false)
{}
};
diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp
index 0e19f571d..e2f9994be 100644
--- a/src/guiPasswordChange.cpp
+++ b/src/guiPasswordChange.cpp
@@ -79,7 +79,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
Remove stuff
*/
removeChildren();
-
+
/*
Calculate new sizes and positions
*/
@@ -89,7 +89,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
screensize.X/2 + 580/2,
screensize.Y/2 + 300/2
);
-
+
DesiredRect = rect;
recalculateAbsolutePosition(false);
@@ -112,7 +112,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, ypos);
- gui::IGUIEditBox *e =
+ gui::IGUIEditBox *e =
Environment->addEditBox(L"", rect, true, this, ID_oldPassword);
Environment->setFocus(e);
e->setPasswordBox(true);
@@ -128,7 +128,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, ypos);
- gui::IGUIEditBox *e =
+ gui::IGUIEditBox *e =
Environment->addEditBox(L"", rect, true, this, ID_newPassword1);
e->setPasswordBox(true);
}
@@ -143,7 +143,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
{
core::rect<s32> rect(0, 0, 230, 30);
rect += topleft_client + v2s32(160, ypos);
- gui::IGUIEditBox *e =
+ gui::IGUIEditBox *e =
Environment->addEditBox(L"", rect, true, this, ID_newPassword2);
e->setPasswordBox(true);
}
@@ -162,7 +162,7 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
core::rect<s32> rect(0, 0, 300, 20);
rect += topleft_client + v2s32(35, ypos);
text = wgettext("Passwords do not match!");
- IGUIElement *e =
+ IGUIElement *e =
Environment->addStaticText(
text,
rect, false, true, this, ID_message);
@@ -177,7 +177,7 @@ void GUIPasswordChange::drawMenu()
if (!skin)
return;
video::IVideoDriver* driver = Environment->getVideoDriver();
-
+
video::SColor bgcolor(140,0,0,0);
driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
@@ -203,7 +203,8 @@ bool GUIPasswordChange::acceptInput()
e->setVisible(true);
return false;
}
- m_client->sendChangePassword(oldpass, newpass);
+ m_client->sendChangePassword(wide_to_utf8(oldpass),
+ wide_to_utf8(newpass));
return true;
}
diff --git a/src/guiTable.cpp b/src/guiTable.cpp
index 05db228da..ed5b0d87b 100644
--- a/src/guiTable.cpp
+++ b/src/guiTable.cpp
@@ -28,14 +28,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <IGUIScrollBar.h>
#include "debug.h"
#include "log.h"
-#include "tile.h"
+#include "client/tile.h"
#include "gettime.h"
#include "util/string.h"
#include "util/numeric.h"
#include "util/string.h" // for parseColorString()
-#include "main.h"
#include "settings.h" // for settings
#include "porting.h" // for dpi
+#include "guiscalingfilter.h"
/*
GUITable
@@ -929,7 +929,7 @@ s32 GUITable::allocString(const std::string &text)
std::map<std::string, s32>::iterator it = m_alloc_strings.find(text);
if (it == m_alloc_strings.end()) {
s32 id = m_strings.size();
- std::wstring wtext = narrow_to_wide(text);
+ std::wstring wtext = utf8_to_wide(text);
m_strings.push_back(core::stringw(wtext.c_str()));
m_alloc_strings.insert(std::make_pair(text, id));
return id;
diff --git a/src/guiVolumeChange.cpp b/src/guiVolumeChange.cpp
index b31b99a98..c8e257f7f 100644
--- a/src/guiVolumeChange.cpp
+++ b/src/guiVolumeChange.cpp
@@ -26,7 +26,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <IGUIScrollBar.h>
#include <IGUIStaticText.h>
#include <IGUIFont.h>
-#include "main.h"
#include "settings.h"
#include "gettext.h"
diff --git a/src/guiscalingfilter.cpp b/src/guiscalingfilter.cpp
new file mode 100644
index 000000000..26a2265a8
--- /dev/null
+++ b/src/guiscalingfilter.cpp
@@ -0,0 +1,169 @@
+/*
+Copyright (C) 2015 Aaron Suen <warr1024@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.
+*/
+
+#include "guiscalingfilter.h"
+#include "imagefilters.h"
+#include "settings.h"
+#include "util/numeric.h"
+#include <stdio.h>
+
+/* Maintain a static cache to store the images that correspond to textures
+ * in a format that's manipulable by code. Some platforms exhibit issues
+ * converting textures back into images repeatedly, and some don't even
+ * allow it at all.
+ */
+std::map<io::path, video::IImage *> g_imgCache;
+
+/* Maintain a static cache of all pre-scaled textures. These need to be
+ * cleared as well when the cached images.
+ */
+std::map<io::path, video::ITexture *> g_txrCache;
+
+/* Manually insert an image into the cache, useful to avoid texture-to-image
+ * conversion whenever we can intercept it.
+ */
+void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *value)
+{
+ if (!g_settings->getBool("gui_scaling_filter"))
+ return;
+ video::IImage *copied = driver->createImage(value->getColorFormat(),
+ value->getDimension());
+ value->copyTo(copied);
+ g_imgCache[key] = copied;
+}
+
+// Manually clear the cache, e.g. when switching to different worlds.
+void guiScalingCacheClear(video::IVideoDriver *driver)
+{
+ for (std::map<io::path, video::IImage *>::iterator it = g_imgCache.begin();
+ it != g_imgCache.end(); it++) {
+ if (it->second != NULL)
+ it->second->drop();
+ }
+ g_imgCache.clear();
+ for (std::map<io::path, video::ITexture *>::iterator it = g_txrCache.begin();
+ it != g_txrCache.end(); it++) {
+ if (it->second != NULL)
+ driver->removeTexture(it->second);
+ }
+ g_txrCache.clear();
+}
+
+/* Get a cached, high-quality pre-scaled texture for display purposes. If the
+ * texture is not already cached, attempt to create it. Returns a pre-scaled texture,
+ * or the original texture if unable to pre-scale it.
+ */
+video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver,
+ video::ITexture *src, const core::rect<s32> &srcrect,
+ const core::rect<s32> &destrect)
+{
+ if (src == NULL)
+ return src;
+ if (!g_settings->getBool("gui_scaling_filter"))
+ return src;
+
+ // Calculate scaled texture name.
+ char rectstr[200];
+ snprintf(rectstr, sizeof(rectstr), "%d:%d:%d:%d:%d:%d",
+ srcrect.UpperLeftCorner.X,
+ srcrect.UpperLeftCorner.Y,
+ srcrect.getWidth(),
+ srcrect.getHeight(),
+ destrect.getWidth(),
+ destrect.getHeight());
+ io::path origname = src->getName().getPath();
+ io::path scalename = origname + "@guiScalingFilter:" + rectstr;
+
+ // Search for existing scaled texture.
+ video::ITexture *scaled = g_txrCache[scalename];
+ if (scaled)
+ return scaled;
+
+ // Try to find the texture converted to an image in the cache.
+ // If the image was not found, try to extract it from the texture.
+ video::IImage* srcimg = g_imgCache[origname];
+ if (srcimg == NULL) {
+ if (!g_settings->getBool("gui_scaling_filter_txr2img"))
+ return src;
+ srcimg = driver->createImageFromData(src->getColorFormat(),
+ src->getSize(), src->lock(), false);
+ src->unlock();
+ g_imgCache[origname] = srcimg;
+ }
+
+ // Create a new destination image and scale the source into it.
+ imageCleanTransparent(srcimg, 0);
+ video::IImage *destimg = driver->createImage(src->getColorFormat(),
+ core::dimension2d<u32>((u32)destrect.getWidth(),
+ (u32)destrect.getHeight()));
+ imageScaleNNAA(srcimg, srcrect, destimg);
+
+#ifdef __ANDROID__
+ // Android is very picky about textures being powers of 2, so expand
+ // the image dimensions to the next power of 2, if necessary, for
+ // that platform.
+ video::IImage *po2img = driver->createImage(src->getColorFormat(),
+ core::dimension2d<u32>(npot2((u32)destrect.getWidth()),
+ npot2((u32)destrect.getHeight())));
+ po2img->fill(video::SColor(0, 0, 0, 0));
+ destimg->copyTo(po2img);
+ destimg->drop();
+ destimg = po2img;
+#endif
+
+ // Convert the scaled image back into a texture.
+ scaled = driver->addTexture(scalename, destimg, NULL);
+ destimg->drop();
+ g_txrCache[scalename] = scaled;
+
+ return scaled;
+}
+
+/* Convenience wrapper for guiScalingResizeCached that accepts parameters that
+ * are available at GUI imagebutton creation time.
+ */
+video::ITexture *guiScalingImageButton(video::IVideoDriver *driver,
+ video::ITexture *src, s32 width, s32 height)
+{
+ if (src == NULL)
+ return src;
+ return guiScalingResizeCached(driver, src,
+ core::rect<s32>(0, 0, src->getSize().Width, src->getSize().Height),
+ core::rect<s32>(0, 0, width, height));
+}
+
+/* Replacement for driver->draw2DImage() that uses the high-quality pre-scaled
+ * texture, if configured.
+ */
+void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
+ const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
+ const core::rect<s32> *cliprect, const video::SColor *const colors,
+ bool usealpha)
+{
+ // Attempt to pre-scale image in software in high quality.
+ video::ITexture *scaled = guiScalingResizeCached(driver, txr, srcrect, destrect);
+ if (scaled == NULL)
+ return;
+
+ // Correct source rect based on scaled image.
+ const core::rect<s32> mysrcrect = (scaled != txr)
+ ? core::rect<s32>(0, 0, destrect.getWidth(), destrect.getHeight())
+ : srcrect;
+
+ driver->draw2DImage(scaled, destrect, mysrcrect, cliprect, colors, usealpha);
+}
diff --git a/src/guiscalingfilter.h b/src/guiscalingfilter.h
new file mode 100644
index 000000000..768fe8d52
--- /dev/null
+++ b/src/guiscalingfilter.h
@@ -0,0 +1,52 @@
+/*
+Copyright (C) 2015 Aaron Suen <warr1024@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.
+*/
+#ifndef _GUI_SCALING_FILTER_H_
+#define _GUI_SCALING_FILTER_H_
+
+#include "irrlichttypes_extrabloated.h"
+
+/* Manually insert an image into the cache, useful to avoid texture-to-image
+ * conversion whenever we can intercept it.
+ */
+void guiScalingCache(io::path key, video::IVideoDriver *driver, video::IImage *value);
+
+// Manually clear the cache, e.g. when switching to different worlds.
+void guiScalingCacheClear(video::IVideoDriver *driver);
+
+/* Get a cached, high-quality pre-scaled texture for display purposes. If the
+ * texture is not already cached, attempt to create it. Returns a pre-scaled texture,
+ * or the original texture if unable to pre-scale it.
+ */
+video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver, video::ITexture *src,
+ const core::rect<s32> &srcrect, const core::rect<s32> &destrect);
+
+/* Convenience wrapper for guiScalingResizeCached that accepts parameters that
+ * are available at GUI imagebutton creation time.
+ */
+video::ITexture *guiScalingImageButton(video::IVideoDriver *driver, video::ITexture *src,
+ s32 width, s32 height);
+
+/* Replacement for driver->draw2DImage() that uses the high-quality pre-scaled
+ * texture, if configured.
+ */
+void draw2DImageFilterScaled(video::IVideoDriver *driver, video::ITexture *txr,
+ const core::rect<s32> &destrect, const core::rect<s32> &srcrect,
+ const core::rect<s32> *cliprect = 0, const video::SColor *const colors = 0,
+ bool usealpha = false);
+
+#endif
diff --git a/src/httpfetch.cpp b/src/httpfetch.cpp
index 47e33480b..56cdad2b1 100644
--- a/src/httpfetch.cpp
+++ b/src/httpfetch.cpp
@@ -33,11 +33,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/container.h"
#include "util/thread.h"
#include "version.h"
-#include "main.h"
#include "settings.h"
JMutex g_httpfetch_mutex;
-std::map<unsigned long, std::list<HTTPFetchResult> > g_httpfetch_results;
+std::map<unsigned long, std::queue<HTTPFetchResult> > g_httpfetch_results;
HTTPFetchRequest::HTTPFetchRequest()
{
@@ -48,7 +47,7 @@ HTTPFetchRequest::HTTPFetchRequest()
connect_timeout = timeout;
multipart = false;
- useragent = std::string("Minetest/") + minetest_version_hash + " (" + porting::get_sysinfo() + ")";
+ useragent = std::string(PROJECT_NAME_C "/") + g_version_hash + " (" + porting::get_sysinfo() + ")";
}
@@ -57,7 +56,7 @@ static void httpfetch_deliver_result(const HTTPFetchResult &fetch_result)
unsigned long caller = fetch_result.caller;
if (caller != HTTPFETCH_DISCARD) {
JMutexAutoLock lock(g_httpfetch_mutex);
- g_httpfetch_results[caller].push_back(fetch_result);
+ g_httpfetch_results[caller].push(fetch_result);
}
}
@@ -70,18 +69,18 @@ unsigned long httpfetch_caller_alloc()
// Check each caller ID except HTTPFETCH_DISCARD
const unsigned long discard = HTTPFETCH_DISCARD;
for (unsigned long caller = discard + 1; caller != discard; ++caller) {
- std::map<unsigned long, std::list<HTTPFetchResult> >::iterator
+ std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
it = g_httpfetch_results.find(caller);
if (it == g_httpfetch_results.end()) {
- verbosestream<<"httpfetch_caller_alloc: allocating "
- <<caller<<std::endl;
+ verbosestream << "httpfetch_caller_alloc: allocating "
+ << caller << std::endl;
// Access element to create it
g_httpfetch_results[caller];
return caller;
}
}
- assert("httpfetch_caller_alloc: ran out of caller IDs" == 0);
+ FATAL_ERROR("httpfetch_caller_alloc: ran out of caller IDs");
return discard;
}
@@ -102,19 +101,19 @@ bool httpfetch_async_get(unsigned long caller, HTTPFetchResult &fetch_result)
JMutexAutoLock lock(g_httpfetch_mutex);
// Check that caller exists
- std::map<unsigned long, std::list<HTTPFetchResult> >::iterator
+ std::map<unsigned long, std::queue<HTTPFetchResult> >::iterator
it = g_httpfetch_results.find(caller);
if (it == g_httpfetch_results.end())
return false;
// Check that result queue is nonempty
- std::list<HTTPFetchResult> &caller_results = it->second;
+ std::queue<HTTPFetchResult> &caller_results = it->second;
if (caller_results.empty())
return false;
// Pop first result
fetch_result = caller_results.front();
- caller_results.pop_front();
+ caller_results.pop();
return true;
}
@@ -194,7 +193,6 @@ private:
HTTPFetchRequest request;
HTTPFetchResult result;
std::ostringstream oss;
- char *post_fields;
struct curl_slist *http_header;
curl_httppost *post;
};
@@ -268,8 +266,7 @@ HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *po
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
} else if (request.multipart) {
curl_httppost *last = NULL;
- for (std::map<std::string, std::string>::iterator it =
- request.post_fields.begin();
+ for (StringMap::iterator it = request.post_fields.begin();
it != request.post_fields.end(); ++it) {
curl_formadd(&post, &last,
CURLFORM_NAMELENGTH, it->first.size(),
@@ -284,10 +281,8 @@ HTTPFetchOngoing::HTTPFetchOngoing(HTTPFetchRequest request_, CurlHandlePool *po
} else if (request.post_data.empty()) {
curl_easy_setopt(curl, CURLOPT_POST, 1);
std::string str;
- for (std::map<std::string, std::string>::iterator it =
- request.post_fields.begin();
- it != request.post_fields.end();
- ++it) {
+ for (StringMap::iterator it = request.post_fields.begin();
+ it != request.post_fields.end(); ++it) {
if (str != "")
str += "&";
str += urlencode(it->first);
@@ -634,7 +629,7 @@ protected:
return NULL;
}
- assert(m_all_ongoing.empty());
+ FATAL_ERROR_IF(!m_all_ongoing.empty(), "Expected empty");
while (!StopRequested()) {
BEGIN_DEBUG_EXCEPTION_HANDLER
@@ -715,7 +710,7 @@ void httpfetch_init(int parallel_limit)
<<std::endl;
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
- assert(res == CURLE_OK);
+ FATAL_ERROR_IF(res != CURLE_OK, "CURL init failed");
g_httpfetch_thread = new CurlFetchThread(parallel_limit);
}
diff --git a/src/httpfetch.h b/src/httpfetch.h
index 50a4c93d8..c44c8d2d3 100644
--- a/src/httpfetch.h
+++ b/src/httpfetch.h
@@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef HTTPFETCH_HEADER
#define HTTPFETCH_HEADER
-#include <string>
#include <vector>
-#include <map>
+#include "util/string.h"
#include "config.h"
// Can be used in place of "caller" in asynchronous transfers to discard result
@@ -54,7 +53,7 @@ struct HTTPFetchRequest
// POST fields. Fields are escaped properly.
// If this is empty a GET request is done instead.
- std::map<std::string, std::string> post_fields;
+ StringMap post_fields;
// Raw POST data, overrides post_fields.
std::string post_data;
diff --git a/src/hud.cpp b/src/hud.cpp
index 29ebb8103..dbc4a01a3 100644
--- a/src/hud.cpp
+++ b/src/hud.cpp
@@ -20,18 +20,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "hud.h"
-#include "main.h"
#include "settings.h"
#include "util/numeric.h"
#include "log.h"
#include "gamedef.h"
#include "itemdef.h"
#include "inventory.h"
-#include "tile.h"
+#include "client/tile.h"
#include "localplayer.h"
#include "camera.h"
#include "porting.h"
#include "fontengine.h"
+#include "guiscalingfilter.h"
#include <IGUIStaticText.h>
#ifdef HAVE_TOUCHSCREENGUI
@@ -94,7 +94,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect, bool sele
imgrect2.LowerRightCorner.Y += (m_padding*2);
video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
core::dimension2di imgsize(texture->getOriginalSize());
- driver->draw2DImage(texture, imgrect2,
+ draw2DImageFilterScaled(driver, texture, imgrect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
} else {
@@ -200,7 +200,7 @@ void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
core::rect<s32> rect2 = imgrect2 + pos;
video::ITexture *texture = tsrc->getTexture(hotbar_image);
core::dimension2di imgsize(texture->getOriginalSize());
- driver->draw2DImage(texture, rect2,
+ draw2DImageFilterScaled(driver, texture, rect2,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, hbar_colors, true);
}
@@ -266,7 +266,7 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
(e->align.Y - 1.0) * dstsize.Y / 2);
core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
- driver->draw2DImage(texture, rect,
+ draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0), imgsize),
NULL, colors, true);
break; }
@@ -275,7 +275,7 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
- std::wstring text = narrow_to_wide(e->text);
+ std::wstring text = utf8_to_wide(e->text);
core::dimension2d<u32> textsize = font->getDimension(text.c_str());
v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
(e->align.Y - 1.0) * (textsize.Height / 2));
@@ -310,11 +310,11 @@ void Hud::drawLuaElements(v3s16 camera_offset) {
(e->number >> 8) & 0xFF,
(e->number >> 0) & 0xFF);
core::rect<s32> size(0, 0, 200, 2 * text_height);
- std::wstring text = narrow_to_wide(e->name);
+ std::wstring text = utf8_to_wide(e->name);
font->draw(text.c_str(), size + pos, color);
std::ostringstream os;
- os<<distance<<e->text;
- text = narrow_to_wide(os.str());
+ os << distance << e->text;
+ text = utf8_to_wide(os.str());
pos.Y += text_height;
font->draw(text.c_str(), size + pos, color);
break; }
@@ -378,7 +378,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
dstrect += p;
- driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
+ draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
p += steppos;
}
@@ -388,7 +388,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
dstrect += p;
- driver->draw2DImage(stat_texture, dstrect, srcrect, NULL, colors, true);
+ draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
}
}
@@ -502,7 +502,7 @@ void drawItemStack(video::IVideoDriver *driver,
{
const video::SColor color(255,255,255,255);
const video::SColor colors[] = {color,color,color,color};
- driver->draw2DImage(texture, rect,
+ draw2DImageFilterScaled(driver, texture, rect,
core::rect<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
clip, colors, true);
@@ -552,7 +552,7 @@ void drawItemStack(video::IVideoDriver *driver,
{
// Get the item count as a string
std::string text = itos(item.count);
- v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
+ v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
v2s32 sdim(dim.X,dim.Y);
core::rect<s32> rect2(
diff --git a/src/hud.h b/src/hud.h
index 2e6838eb1..614e7c92d 100644
--- a/src/hud.h
+++ b/src/hud.h
@@ -32,11 +32,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define HUD_CORNER_LOWER 1
#define HUD_CORNER_CENTER 2
+// Note that these visibility flags do not determine if the hud items are
+// actually drawn, but rather, allows the item to be drawn should the rest of
+// the game state permit it.
#define HUD_FLAG_HOTBAR_VISIBLE (1 << 0)
#define HUD_FLAG_HEALTHBAR_VISIBLE (1 << 1)
#define HUD_FLAG_CROSSHAIR_VISIBLE (1 << 2)
#define HUD_FLAG_WIELDITEM_VISIBLE (1 << 3)
#define HUD_FLAG_BREATHBAR_VISIBLE (1 << 4)
+#define HUD_FLAG_MINIMAP_VISIBLE (1 << 5)
#define HUD_PARAM_HOTBAR_ITEMCOUNT 1
#define HUD_PARAM_HOTBAR_IMAGE 2
@@ -116,11 +120,11 @@ public:
std::string hotbar_selected_image;
bool use_hotbar_selected_image;
v3s16 camera_offset;
-
+
Hud(video::IVideoDriver *driver,scene::ISceneManager* smgr,
gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player,
Inventory *inventory);
-
+
void drawHotbar(u16 playeritem);
void resizeHotbar();
void drawCrosshair();
@@ -129,12 +133,12 @@ public:
private:
void drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
s32 count, v2s32 offset, v2s32 size=v2s32());
-
+
void drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
InventoryList *mainlist, u16 selectitem, u16 direction);
void drawItem(const ItemStack &item, const core::rect<s32>& rect, bool selected);
-
+
v2u32 m_screensize;
v2s32 m_displaycenter;
s32 m_hotbar_imagesize;
diff --git a/src/imagefilters.cpp b/src/imagefilters.cpp
new file mode 100644
index 000000000..b34027725
--- /dev/null
+++ b/src/imagefilters.cpp
@@ -0,0 +1,172 @@
+/*
+Copyright (C) 2015 Aaron Suen <warr1024@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.
+*/
+
+#include "imagefilters.h"
+#include "util/numeric.h"
+#include <math.h>
+
+/* Fill in RGB values for transparent pixels, to correct for odd colors
+ * appearing at borders when blending. This is because many PNG optimizers
+ * like to discard RGB values of transparent pixels, but when blending then
+ * with non-transparent neighbors, their RGB values will shpw up nonetheless.
+ *
+ * This function modifies the original image in-place.
+ *
+ * Parameter "threshold" is the alpha level below which pixels are considered
+ * transparent. Should be 127 for 3d where alpha is threshold, but 0 for
+ * 2d where alpha is blended.
+ */
+void imageCleanTransparent(video::IImage *src, u32 threshold)
+{
+ core::dimension2d<u32> dim = src->getDimension();
+
+ // Walk each pixel looking for fully transparent ones.
+ // Note: loop y around x for better cache locality.
+ for (u32 ctry = 0; ctry < dim.Height; ctry++)
+ for (u32 ctrx = 0; ctrx < dim.Width; ctrx++) {
+
+ // Ignore opaque pixels.
+ irr::video::SColor c = src->getPixel(ctrx, ctry);
+ if (c.getAlpha() > threshold)
+ continue;
+
+ // Sample size and total weighted r, g, b values.
+ u32 ss = 0, sr = 0, sg = 0, sb = 0;
+
+ // Walk each neighbor pixel (clipped to image bounds).
+ for (u32 sy = (ctry < 1) ? 0 : (ctry - 1);
+ sy <= (ctry + 1) && sy < dim.Height; sy++)
+ for (u32 sx = (ctrx < 1) ? 0 : (ctrx - 1);
+ sx <= (ctrx + 1) && sx < dim.Width; sx++) {
+
+ // Ignore transparent pixels.
+ irr::video::SColor d = src->getPixel(sx, sy);
+ if (d.getAlpha() <= threshold)
+ continue;
+
+ // Add RGB values weighted by alpha.
+ u32 a = d.getAlpha();
+ ss += a;
+ sr += a * d.getRed();
+ sg += a * d.getGreen();
+ sb += a * d.getBlue();
+ }
+
+ // If we found any neighbor RGB data, set pixel to average
+ // weighted by alpha.
+ if (ss > 0) {
+ c.setRed(sr / ss);
+ c.setGreen(sg / ss);
+ c.setBlue(sb / ss);
+ src->setPixel(ctrx, ctry, c);
+ }
+ }
+}
+
+/* Scale a region of an image into another image, using nearest-neighbor with
+ * anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
+ * to prevent non-integer scaling ratio artifacts. Note that this may cause
+ * some blending at the edges where pixels don't line up perfectly, but this
+ * filter is designed to produce the most accurate results for both upscaling
+ * and downscaling.
+ */
+void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest)
+{
+ double sx, sy, minsx, maxsx, minsy, maxsy, area, ra, ga, ba, aa, pw, ph, pa;
+ u32 dy, dx;
+ video::SColor pxl;
+
+ // Cache rectsngle boundaries.
+ double sox = srcrect.UpperLeftCorner.X * 1.0;
+ double soy = srcrect.UpperLeftCorner.Y * 1.0;
+ double sw = srcrect.getWidth() * 1.0;
+ double sh = srcrect.getHeight() * 1.0;
+
+ // Walk each destination image pixel.
+ // Note: loop y around x for better cache locality.
+ core::dimension2d<u32> dim = dest->getDimension();
+ for (dy = 0; dy < dim.Height; dy++)
+ for (dx = 0; dx < dim.Width; dx++) {
+
+ // Calculate floating-point source rectangle bounds.
+ // Do some basic clipping, and for mirrored/flipped rects,
+ // make sure min/max are in the right order.
+ minsx = sox + (dx * sw / dim.Width);
+ minsx = rangelim(minsx, 0, sw);
+ maxsx = minsx + sw / dim.Width;
+ maxsx = rangelim(maxsx, 0, sw);
+ if (minsx > maxsx)
+ SWAP(double, minsx, maxsx);
+ minsy = soy + (dy * sh / dim.Height);
+ minsy = rangelim(minsy, 0, sh);
+ maxsy = minsy + sh / dim.Height;
+ maxsy = rangelim(maxsy, 0, sh);
+ if (minsy > maxsy)
+ SWAP(double, minsy, maxsy);
+
+ // Total area, and integral of r, g, b values over that area,
+ // initialized to zero, to be summed up in next loops.
+ area = 0;
+ ra = 0;
+ ga = 0;
+ ba = 0;
+ aa = 0;
+
+ // Loop over the integral pixel positions described by those bounds.
+ for (sy = floor(minsy); sy < maxsy; sy++)
+ for (sx = floor(minsx); sx < maxsx; sx++) {
+
+ // Calculate width, height, then area of dest pixel
+ // that's covered by this source pixel.
+ pw = 1;
+ if (minsx > sx)
+ pw += sx - minsx;
+ if (maxsx < (sx + 1))
+ pw += maxsx - sx - 1;
+ ph = 1;
+ if (minsy > sy)
+ ph += sy - minsy;
+ if (maxsy < (sy + 1))
+ ph += maxsy - sy - 1;
+ pa = pw * ph;
+
+ // Get source pixel and add it to totals, weighted
+ // by covered area and alpha.
+ pxl = src->getPixel((u32)sx, (u32)sy);
+ area += pa;
+ ra += pa * pxl.getRed();
+ ga += pa * pxl.getGreen();
+ ba += pa * pxl.getBlue();
+ aa += pa * pxl.getAlpha();
+ }
+
+ // Set the destination image pixel to the average color.
+ if (area > 0) {
+ pxl.setRed(ra / area + 0.5);
+ pxl.setGreen(ga / area + 0.5);
+ pxl.setBlue(ba / area + 0.5);
+ pxl.setAlpha(aa / area + 0.5);
+ } else {
+ pxl.setRed(0);
+ pxl.setGreen(0);
+ pxl.setBlue(0);
+ pxl.setAlpha(0);
+ }
+ dest->setPixel(dx, dy, pxl);
+ }
+}
diff --git a/src/imagefilters.h b/src/imagefilters.h
new file mode 100644
index 000000000..28787027f
--- /dev/null
+++ b/src/imagefilters.h
@@ -0,0 +1,46 @@
+/*
+Copyright (C) 2015 Aaron Suen <warr1024@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.
+*/
+
+#ifndef _IMAGE_FILTERS_H_
+#define _IMAGE_FILTERS_H_
+
+#include "irrlichttypes_extrabloated.h"
+
+/* Fill in RGB values for transparent pixels, to correct for odd colors
+ * appearing at borders when blending. This is because many PNG optimizers
+ * like to discard RGB values of transparent pixels, but when blending then
+ * with non-transparent neighbors, their RGB values will shpw up nonetheless.
+ *
+ * This function modifies the original image in-place.
+ *
+ * Parameter "threshold" is the alpha level below which pixels are considered
+ * transparent. Should be 127 for 3d where alpha is threshold, but 0 for
+ * 2d where alpha is blended.
+ */
+void imageCleanTransparent(video::IImage *src, u32 threshold);
+
+/* Scale a region of an image into another image, using nearest-neighbor with
+ * anti-aliasing; treat pixels as crisp rectangles, but blend them at boundaries
+ * to prevent non-integer scaling ratio artifacts. Note that this may cause
+ * some blending at the edges where pixels don't line up perfectly, but this
+ * filter is designed to produce the most accurate results for both upscaling
+ * and downscaling.
+ */
+void imageScaleNNAA(video::IImage *src, const core::rect<s32> &srcrect, video::IImage *dest);
+
+#endif
diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp
new file mode 100644
index 000000000..33bf8a13c
--- /dev/null
+++ b/src/intlGUIEditBox.cpp
@@ -0,0 +1,1509 @@
+// 11.11.2011 11:11 ValkaTR
+//
+// This is a copy of intlGUIEditBox from the irrlicht, but with a
+// fix in the OnEvent function, which doesn't allowed input of
+// other keyboard layouts than latin-1
+//
+// Characters like: ä ö ü õ Ñ‹ й ÑŽ Ñ ÑŠ â„– € ° ...
+//
+// This fix is only needed for linux, because of a bug
+// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht
+//
+// Also locale in the programm should not be changed to
+// a "C", "POSIX" or whatever, it should be set to "",
+// or XLookupString will return nothing for the international
+// characters.
+//
+// From the "man setlocale":
+//
+// On startup of the main program, the portable "C" locale
+// is selected as default. A program may be made
+// portable to all locales by calling:
+//
+// setlocale(LC_ALL, "");
+//
+// after program initialization....
+//
+
+// Copyright (C) 2002-2013 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include "intlGUIEditBox.h"
+
+#if defined(_IRR_COMPILE_WITH_GUI_) && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+
+#include "IGUISkin.h"
+#include "IGUIEnvironment.h"
+#include "IGUIFont.h"
+#include "IVideoDriver.h"
+//#include "rect.h"
+//#include "irrlicht/os.cpp"
+#include "porting.h"
+//#include "Keycodes.h"
+#include "log.h"
+
+/*
+ todo:
+ optional scrollbars
+ ctrl+left/right to select word
+ double click/ctrl click: word select + drag to select whole words, triple click to select line
+ optional? dragging selected text
+ numerical
+*/
+
+namespace irr
+{
+namespace gui
+{
+
+//! constructor
+intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
+ IGUIEnvironment* environment, IGUIElement* parent, s32 id,
+ const core::rect<s32>& rectangle)
+ : IGUIEditBox(environment, parent, id, rectangle), MouseMarking(false),
+ Border(border), OverrideColorEnabled(false), MarkBegin(0), MarkEnd(0),
+ OverrideColor(video::SColor(101,255,255,255)), OverrideFont(0), LastBreakFont(0),
+ Operator(0), BlinkStartTime(0), CursorPos(0), HScrollPos(0), VScrollPos(0), Max(0),
+ WordWrap(false), MultiLine(false), AutoScroll(true), PasswordBox(false),
+ PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER),
+ CurrentTextRect(0,0,1,1), FrameRect(rectangle)
+{
+ #ifdef _DEBUG
+ setDebugName("intlintlGUIEditBox");
+ #endif
+
+ Text = text;
+
+ if (Environment)
+ Operator = Environment->getOSOperator();
+
+ if (Operator)
+ Operator->grab();
+
+ // this element can be tabbed to
+ setTabStop(true);
+ setTabOrder(-1);
+
+ IGUISkin *skin = 0;
+ if (Environment)
+ skin = Environment->getSkin();
+ if (Border && skin)
+ {
+ FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ }
+
+ breakText();
+
+ calculateScrollPos();
+}
+
+
+//! destructor
+intlGUIEditBox::~intlGUIEditBox()
+{
+ if (OverrideFont)
+ OverrideFont->drop();
+
+ if (Operator)
+ Operator->drop();
+}
+
+
+//! Sets another skin independent font.
+void intlGUIEditBox::setOverrideFont(IGUIFont* font)
+{
+ if (OverrideFont == font)
+ return;
+
+ if (OverrideFont)
+ OverrideFont->drop();
+
+ OverrideFont = font;
+
+ if (OverrideFont)
+ OverrideFont->grab();
+
+ breakText();
+}
+
+IGUIFont * intlGUIEditBox::getOverrideFont() const
+{
+ return OverrideFont;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* intlGUIEditBox::getActiveFont() const
+{
+ if ( OverrideFont )
+ return OverrideFont;
+ IGUISkin* skin = Environment->getSkin();
+ if (skin)
+ return skin->getFont();
+ return 0;
+}
+
+//! Sets another color for the text.
+void intlGUIEditBox::setOverrideColor(video::SColor color)
+{
+ OverrideColor = color;
+ OverrideColorEnabled = true;
+}
+
+video::SColor intlGUIEditBox::getOverrideColor() const
+{
+ return OverrideColor;
+}
+
+//! Turns the border on or off
+void intlGUIEditBox::setDrawBorder(bool border)
+{
+ Border = border;
+}
+
+//! Sets whether to draw the background
+void intlGUIEditBox::setDrawBackground(bool draw)
+{
+}
+
+//! Sets if the text should use the overide color or the color in the gui skin.
+void intlGUIEditBox::enableOverrideColor(bool enable)
+{
+ OverrideColorEnabled = enable;
+}
+
+bool intlGUIEditBox::isOverrideColorEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return OverrideColorEnabled;
+}
+
+//! Enables or disables word wrap
+void intlGUIEditBox::setWordWrap(bool enable)
+{
+ WordWrap = enable;
+ breakText();
+}
+
+
+void intlGUIEditBox::updateAbsolutePosition()
+{
+ core::rect<s32> oldAbsoluteRect(AbsoluteRect);
+ IGUIElement::updateAbsolutePosition();
+ if ( oldAbsoluteRect != AbsoluteRect )
+ {
+ breakText();
+ }
+}
+
+
+//! Checks if word wrap is enabled
+bool intlGUIEditBox::isWordWrapEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return WordWrap;
+}
+
+
+//! Enables or disables newlines.
+void intlGUIEditBox::setMultiLine(bool enable)
+{
+ MultiLine = enable;
+}
+
+
+//! Checks if multi line editing is enabled
+bool intlGUIEditBox::isMultiLineEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return MultiLine;
+}
+
+
+void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
+{
+ PasswordBox = passwordBox;
+ if (PasswordBox)
+ {
+ PasswordChar = passwordChar;
+ setMultiLine(false);
+ setWordWrap(false);
+ BrokenText.clear();
+ }
+}
+
+
+bool intlGUIEditBox::isPasswordBox() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return PasswordBox;
+}
+
+
+//! Sets text justification
+void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+ HAlign = horizontal;
+ VAlign = vertical;
+}
+
+
+//! called if an event happened.
+bool intlGUIEditBox::OnEvent(const SEvent& event)
+{
+ if (IsEnabled)
+ {
+
+ switch(event.EventType)
+ {
+ case EET_GUI_EVENT:
+ if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
+ {
+ if (event.GUIEvent.Caller == this)
+ {
+ MouseMarking = false;
+ setTextMarkers(0,0);
+ }
+ }
+ break;
+ case EET_KEY_INPUT_EVENT:
+ {
+#if (defined(linux) || defined(__linux) || defined(__FreeBSD__))
+ // ################################################################
+ // ValkaTR:
+ // This part is the difference from the original intlGUIEditBox
+ // It converts UTF-8 character into a UCS-2 (wchar_t)
+ wchar_t wc = L'_';
+ mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
+
+ //printf( "char: %lc (%u) \r\n", wc, wc );
+
+ SEvent irrevent(event);
+ irrevent.KeyInput.Char = wc;
+ // ################################################################
+
+ if (processKey(irrevent))
+ return true;
+#else
+ if (processKey(event))
+ return true;
+#endif // defined(linux)
+
+ break;
+ }
+ case EET_MOUSE_INPUT_EVENT:
+ if (processMouse(event))
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return IGUIElement::OnEvent(event);
+}
+
+
+bool intlGUIEditBox::processKey(const SEvent& event)
+{
+ if (!event.KeyInput.PressedDown)
+ return false;
+
+ bool textChanged = false;
+ s32 newMarkBegin = MarkBegin;
+ s32 newMarkEnd = MarkEnd;
+
+ // control shortcut handling
+
+ if (event.KeyInput.Control)
+ {
+ // german backlash '\' entered with control + '?'
+ if ( event.KeyInput.Char == '\\' )
+ {
+ inputChar(event.KeyInput.Char);
+ return true;
+ }
+
+ switch(event.KeyInput.Key)
+ {
+ case KEY_KEY_A:
+ // select all
+ newMarkBegin = 0;
+ newMarkEnd = Text.size();
+ break;
+ case KEY_KEY_C:
+ // copy to clipboard
+ if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+ {
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ core::stringc s;
+ s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ Operator->copyToClipboard(s.c_str());
+ }
+ break;
+ case KEY_KEY_X:
+ // cut to the clipboard
+ if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+ {
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ // copy
+ core::stringc sc;
+ sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
+ Operator->copyToClipboard(sc.c_str());
+
+ if (IsEnabled)
+ {
+ // delete
+ core::stringw s;
+ s = Text.subString(0, realmbgn);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+
+ CursorPos = realmbgn;
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ }
+ break;
+ case KEY_KEY_V:
+ if ( !IsEnabled )
+ break;
+
+ // paste from the clipboard
+ if (Operator)
+ {
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ // add new character
+ const c8* p = Operator->getTextFromClipboard();
+ if (p)
+ {
+ if (MarkBegin == MarkEnd)
+ {
+ // insert text
+ core::stringw s = Text.subString(0, CursorPos);
+ s.append(p);
+ s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+
+ if (!Max || s.size()<=Max) // thx to Fish FH for fix
+ {
+ Text = s;
+ s = p;
+ CursorPos += s.size();
+ }
+ }
+ else
+ {
+ // replace text
+
+ core::stringw s = Text.subString(0, realmbgn);
+ s.append(p);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+
+ if (!Max || s.size()<=Max) // thx to Fish FH for fix
+ {
+ Text = s;
+ s = p;
+ CursorPos = realmbgn + s.size();
+ }
+ }
+ }
+
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ break;
+ case KEY_HOME:
+ // move/highlight to start of text
+ if (event.KeyInput.Shift)
+ {
+ newMarkEnd = CursorPos;
+ newMarkBegin = 0;
+ CursorPos = 0;
+ }
+ else
+ {
+ CursorPos = 0;
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ break;
+ case KEY_END:
+ // move/highlight to end of text
+ if (event.KeyInput.Shift)
+ {
+ newMarkBegin = CursorPos;
+ newMarkEnd = Text.size();
+ CursorPos = 0;
+ }
+ else
+ {
+ CursorPos = Text.size();
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ // default keyboard handling
+ else
+ switch(event.KeyInput.Key)
+ {
+ case KEY_END:
+ {
+ s32 p = Text.size();
+ if (WordWrap || MultiLine)
+ {
+ p = getLineFromPos(CursorPos);
+ p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
+ if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
+ p-=1;
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+
+ newMarkEnd = p;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ CursorPos = p;
+ BlinkStartTime = porting::getTimeMs();
+ }
+ break;
+ case KEY_HOME:
+ {
+
+ s32 p = 0;
+ if (WordWrap || MultiLine)
+ {
+ p = getLineFromPos(CursorPos);
+ p = BrokenTextPositions[p];
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+ newMarkEnd = p;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+ CursorPos = p;
+ BlinkStartTime = porting::getTimeMs();
+ }
+ break;
+ case KEY_RETURN:
+ if (MultiLine)
+ {
+ inputChar(L'\n');
+ return true;
+ }
+ else
+ {
+ sendGuiEvent( EGET_EDITBOX_ENTER );
+ }
+ break;
+ case KEY_LEFT:
+
+ if (event.KeyInput.Shift)
+ {
+ if (CursorPos > 0)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+
+ newMarkEnd = CursorPos-1;
+ }
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ if (CursorPos > 0) CursorPos--;
+ BlinkStartTime = porting::getTimeMs();
+ break;
+
+ case KEY_RIGHT:
+ if (event.KeyInput.Shift)
+ {
+ if (Text.size() > (u32)CursorPos)
+ {
+ if (MarkBegin == MarkEnd)
+ newMarkBegin = CursorPos;
+
+ newMarkEnd = CursorPos+1;
+ }
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ if (Text.size() > (u32)CursorPos) CursorPos++;
+ BlinkStartTime = porting::getTimeMs();
+ break;
+ case KEY_UP:
+ if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+ {
+ s32 lineNo = getLineFromPos(CursorPos);
+ s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
+ if (lineNo > 0)
+ {
+ s32 cp = CursorPos - BrokenTextPositions[lineNo];
+ if ((s32)BrokenText[lineNo-1].size() < cp)
+ CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
+ else
+ CursorPos = BrokenTextPositions[lineNo-1] + cp;
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ newMarkBegin = mb;
+ newMarkEnd = CursorPos;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ }
+ else
+ {
+ return false;
+ }
+ break;
+ case KEY_DOWN:
+ if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+ {
+ s32 lineNo = getLineFromPos(CursorPos);
+ s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
+ if (lineNo < (s32)BrokenText.size()-1)
+ {
+ s32 cp = CursorPos - BrokenTextPositions[lineNo];
+ if ((s32)BrokenText[lineNo+1].size() < cp)
+ CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
+ else
+ CursorPos = BrokenTextPositions[lineNo+1] + cp;
+ }
+
+ if (event.KeyInput.Shift)
+ {
+ newMarkBegin = mb;
+ newMarkEnd = CursorPos;
+ }
+ else
+ {
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ }
+
+ }
+ else
+ {
+ return false;
+ }
+ break;
+
+ case KEY_BACK:
+ if ( !this->IsEnabled )
+ break;
+
+ if (Text.size())
+ {
+ core::stringw s;
+
+ if (MarkBegin != MarkEnd)
+ {
+ // delete marked text
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ s = Text.subString(0, realmbgn);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+
+ CursorPos = realmbgn;
+ }
+ else
+ {
+ // delete text behind cursor
+ if (CursorPos>0)
+ s = Text.subString(0, CursorPos-1);
+ else
+ s = L"";
+ s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+ Text = s;
+ --CursorPos;
+ }
+
+ if (CursorPos < 0)
+ CursorPos = 0;
+ BlinkStartTime = porting::getTimeMs();
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ break;
+ case KEY_DELETE:
+ if ( !this->IsEnabled )
+ break;
+
+ if (Text.size() != 0)
+ {
+ core::stringw s;
+
+ if (MarkBegin != MarkEnd)
+ {
+ // delete marked text
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ s = Text.subString(0, realmbgn);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+
+ CursorPos = realmbgn;
+ }
+ else
+ {
+ // delete text before cursor
+ s = Text.subString(0, CursorPos);
+ s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
+ Text = s;
+ }
+
+ if (CursorPos > (s32)Text.size())
+ CursorPos = (s32)Text.size();
+
+ BlinkStartTime = porting::getTimeMs();
+ newMarkBegin = 0;
+ newMarkEnd = 0;
+ textChanged = true;
+ }
+ break;
+
+ case KEY_ESCAPE:
+ case KEY_TAB:
+ case KEY_SHIFT:
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ case KEY_F13:
+ case KEY_F14:
+ case KEY_F15:
+ case KEY_F16:
+ case KEY_F17:
+ case KEY_F18:
+ case KEY_F19:
+ case KEY_F20:
+ case KEY_F21:
+ case KEY_F22:
+ case KEY_F23:
+ case KEY_F24:
+ // ignore these keys
+ return false;
+
+ default:
+ inputChar(event.KeyInput.Char);
+ return true;
+ }
+
+ // Set new text markers
+ setTextMarkers( newMarkBegin, newMarkEnd );
+
+ // break the text if it has changed
+ if (textChanged)
+ {
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ }
+
+ calculateScrollPos();
+
+ return true;
+}
+
+
+//! draws the element and its children
+void intlGUIEditBox::draw()
+{
+ if (!IsVisible)
+ return;
+
+ const bool focus = Environment->hasFocus(this);
+
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+
+ FrameRect = AbsoluteRect;
+
+ // draw the border
+
+ if (Border)
+ {
+ skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
+ false, true, FrameRect, &AbsoluteClippingRect);
+
+ FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+ FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+ }
+ core::rect<s32> localClipRect = FrameRect;
+ localClipRect.clipAgainst(AbsoluteClippingRect);
+
+ // draw the text
+
+ IGUIFont* font = OverrideFont;
+ if (!OverrideFont)
+ font = skin->getFont();
+
+ s32 cursorLine = 0;
+ s32 charcursorpos = 0;
+
+ if (font)
+ {
+ if (LastBreakFont != font)
+ {
+ breakText();
+ }
+
+ // calculate cursor pos
+
+ core::stringw *txtLine = &Text;
+ s32 startPos = 0;
+
+ core::stringw s, s2;
+
+ // get mark position
+ const bool ml = (!PasswordBox && (WordWrap || MultiLine));
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+ const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
+ const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
+ const s32 lineCount = ml ? BrokenText.size() : 1;
+
+ // Save the override color information.
+ // Then, alter it if the edit box is disabled.
+ const bool prevOver = OverrideColorEnabled;
+ const video::SColor prevColor = OverrideColor;
+
+ if (Text.size())
+ {
+ if (!IsEnabled && !OverrideColorEnabled)
+ {
+ OverrideColorEnabled = true;
+ OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+ }
+
+ for (s32 i=0; i < lineCount; ++i)
+ {
+ setTextRect(i);
+
+ // clipping test - don't draw anything outside the visible area
+ core::rect<s32> c = localClipRect;
+ c.clipAgainst(CurrentTextRect);
+ if (!c.isValid())
+ continue;
+
+ // get current line
+ if (PasswordBox)
+ {
+ if (BrokenText.size() != 1)
+ {
+ BrokenText.clear();
+ BrokenText.push_back(core::stringw());
+ }
+ if (BrokenText[0].size() != Text.size())
+ {
+ BrokenText[0] = Text;
+ for (u32 q = 0; q < Text.size(); ++q)
+ {
+ BrokenText[0] [q] = PasswordChar;
+ }
+ }
+ txtLine = &BrokenText[0];
+ startPos = 0;
+ }
+ else
+ {
+ txtLine = ml ? &BrokenText[i] : &Text;
+ startPos = ml ? BrokenTextPositions[i] : 0;
+ }
+
+
+ // draw normal text
+ font->draw(txtLine->c_str(), CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ false, true, &localClipRect);
+
+ // draw mark and marked text
+ if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
+ {
+
+ s32 mbegin = 0, mend = 0;
+ s32 lineStartPos = 0, lineEndPos = txtLine->size();
+
+ if (i == hlineStart)
+ {
+ // highlight start is on this line
+ s = txtLine->subString(0, realmbgn - startPos);
+ mbegin = font->getDimension(s.c_str()).Width;
+
+ // deal with kerning
+ mbegin += font->getKerningWidth(
+ &((*txtLine)[realmbgn - startPos]),
+ realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
+
+ lineStartPos = realmbgn - startPos;
+ }
+ if (i == hlineStart + hlineCount - 1)
+ {
+ // highlight end is on this line
+ s2 = txtLine->subString(0, realmend - startPos);
+ mend = font->getDimension(s2.c_str()).Width;
+ lineEndPos = (s32)s2.size();
+ }
+ else
+ mend = font->getDimension(txtLine->c_str()).Width;
+
+ CurrentTextRect.UpperLeftCorner.X += mbegin;
+ CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
+
+ // draw mark
+ skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
+
+ // draw marked text
+ s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
+
+ if (s.size())
+ font->draw(s.c_str(), CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
+ false, true, &localClipRect);
+
+ }
+ }
+
+ // Return the override color information to its previous settings.
+ OverrideColorEnabled = prevOver;
+ OverrideColor = prevColor;
+ }
+
+ // draw cursor
+
+ if (WordWrap || MultiLine)
+ {
+ cursorLine = getLineFromPos(CursorPos);
+ txtLine = &BrokenText[cursorLine];
+ startPos = BrokenTextPositions[cursorLine];
+ }
+ s = txtLine->subString(0,CursorPos-startPos);
+ charcursorpos = font->getDimension(s.c_str()).Width +
+ font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
+
+ if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350)
+ {
+ setTextRect(cursorLine);
+ CurrentTextRect.UpperLeftCorner.X += charcursorpos;
+
+ font->draw(L"_", CurrentTextRect,
+ OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+ false, true, &localClipRect);
+ }
+ }
+
+ // draw children
+ IGUIElement::draw();
+}
+
+
+//! Sets the new caption of this element.
+void intlGUIEditBox::setText(const wchar_t* text)
+{
+ Text = text;
+ if (u32(CursorPos) > Text.size())
+ CursorPos = Text.size();
+ HScrollPos = 0;
+ breakText();
+}
+
+
+//! Enables or disables automatic scrolling with cursor position
+//! \param enable: If set to true, the text will move around with the cursor position
+void intlGUIEditBox::setAutoScroll(bool enable)
+{
+ AutoScroll = enable;
+}
+
+
+//! Checks to see if automatic scrolling is enabled
+//! \return true if automatic scrolling is enabled, false if not
+bool intlGUIEditBox::isAutoScrollEnabled() const
+{
+ _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+ return AutoScroll;
+}
+
+
+//! Gets the area of the text in the edit box
+//! \return Returns the size in pixels of the text
+core::dimension2du intlGUIEditBox::getTextDimension()
+{
+ core::rect<s32> ret;
+
+ setTextRect(0);
+ ret = CurrentTextRect;
+
+ for (u32 i=1; i < BrokenText.size(); ++i)
+ {
+ setTextRect(i);
+ ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
+ ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
+ }
+
+ return core::dimension2du(ret.getSize());
+}
+
+
+//! Sets the maximum amount of characters which may be entered in the box.
+//! \param max: Maximum amount of characters. If 0, the character amount is
+//! infinity.
+void intlGUIEditBox::setMax(u32 max)
+{
+ Max = max;
+
+ if (Text.size() > Max && Max != 0)
+ Text = Text.subString(0, Max);
+}
+
+
+//! Returns maximum amount of characters, previously set by setMax();
+u32 intlGUIEditBox::getMax() const
+{
+ return Max;
+}
+
+
+bool intlGUIEditBox::processMouse(const SEvent& event)
+{
+ switch(event.MouseInput.Event)
+ {
+ case irr::EMIE_LMOUSE_LEFT_UP:
+ if (Environment->hasFocus(this))
+ {
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ if (MouseMarking)
+ {
+ setTextMarkers( MarkBegin, CursorPos );
+ }
+ MouseMarking = false;
+ calculateScrollPos();
+ return true;
+ }
+ break;
+ case irr::EMIE_MOUSE_MOVED:
+ {
+ if (MouseMarking)
+ {
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ setTextMarkers( MarkBegin, CursorPos );
+ calculateScrollPos();
+ return true;
+ }
+ }
+ break;
+ case EMIE_LMOUSE_PRESSED_DOWN:
+ if (!Environment->hasFocus(this))
+ {
+ BlinkStartTime = porting::getTimeMs();
+ MouseMarking = true;
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+ setTextMarkers(CursorPos, CursorPos );
+ calculateScrollPos();
+ return true;
+ }
+ else
+ {
+ if (!AbsoluteClippingRect.isPointInside(
+ core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y)))
+ {
+ return false;
+ }
+ else
+ {
+ // move cursor
+ CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+
+ s32 newMarkBegin = MarkBegin;
+ if (!MouseMarking)
+ newMarkBegin = CursorPos;
+
+ MouseMarking = true;
+ setTextMarkers( newMarkBegin, CursorPos);
+ calculateScrollPos();
+ return true;
+ }
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
+{
+ IGUIFont* font = OverrideFont;
+ IGUISkin* skin = Environment->getSkin();
+ if (!OverrideFont)
+ font = skin->getFont();
+
+ const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+
+ core::stringw *txtLine=0;
+ s32 startPos=0;
+ x+=3;
+
+ for (u32 i=0; i < lineCount; ++i)
+ {
+ setTextRect(i);
+ if (i == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
+ y = CurrentTextRect.UpperLeftCorner.Y;
+ if (i == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y )
+ y = CurrentTextRect.LowerRightCorner.Y;
+
+ // is it inside this region?
+ if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y)
+ {
+ // we've found the clicked line
+ txtLine = (WordWrap || MultiLine) ? &BrokenText[i] : &Text;
+ startPos = (WordWrap || MultiLine) ? BrokenTextPositions[i] : 0;
+ break;
+ }
+ }
+
+ if (x < CurrentTextRect.UpperLeftCorner.X)
+ x = CurrentTextRect.UpperLeftCorner.X;
+
+ s32 idx = font->getCharacterFromPos(Text.c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+
+ // click was on or left of the line
+ if (idx != -1)
+ return idx + startPos;
+
+ // click was off the right edge of the line, go to end.
+ return txtLine->size() + startPos;
+}
+
+
+//! Breaks the single text line.
+void intlGUIEditBox::breakText()
+{
+ IGUISkin* skin = Environment->getSkin();
+
+ if ((!WordWrap && !MultiLine) || !skin)
+ return;
+
+ BrokenText.clear(); // need to reallocate :/
+ BrokenTextPositions.set_used(0);
+
+ IGUIFont* font = OverrideFont;
+ if (!OverrideFont)
+ font = skin->getFont();
+
+ if (!font)
+ return;
+
+ LastBreakFont = font;
+
+ core::stringw line;
+ core::stringw word;
+ core::stringw whitespace;
+ s32 lastLineStart = 0;
+ s32 size = Text.size();
+ s32 length = 0;
+ s32 elWidth = RelativeRect.getWidth() - 6;
+ wchar_t c;
+
+ for (s32 i=0; i<size; ++i)
+ {
+ c = Text[i];
+ bool lineBreak = false;
+
+ if (c == L'\r') // Mac or Windows breaks
+ {
+ lineBreak = true;
+ c = ' ';
+ if (Text[i+1] == L'\n') // Windows breaks
+ {
+ Text.erase(i+1);
+ --size;
+ }
+ }
+ else if (c == L'\n') // Unix breaks
+ {
+ lineBreak = true;
+ c = ' ';
+ }
+
+ // don't break if we're not a multi-line edit box
+ if (!MultiLine)
+ lineBreak = false;
+
+ if (c == L' ' || c == 0 || i == (size-1))
+ {
+ if (word.size())
+ {
+ // here comes the next whitespace, look if
+ // we can break the last word to the next line.
+ s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+ s32 worldlgth = font->getDimension(word.c_str()).Width;
+
+ if (WordWrap && length + worldlgth + whitelgth > elWidth)
+ {
+ // break to next line
+ length = worldlgth;
+ BrokenText.push_back(line);
+ BrokenTextPositions.push_back(lastLineStart);
+ lastLineStart = i - (s32)word.size();
+ line = word;
+ }
+ else
+ {
+ // add word to line
+ line += whitespace;
+ line += word;
+ length += whitelgth + worldlgth;
+ }
+
+ word = L"";
+ whitespace = L"";
+ }
+
+ whitespace += c;
+
+ // compute line break
+ if (lineBreak)
+ {
+ line += whitespace;
+ line += word;
+ BrokenText.push_back(line);
+ BrokenTextPositions.push_back(lastLineStart);
+ lastLineStart = i+1;
+ line = L"";
+ word = L"";
+ whitespace = L"";
+ length = 0;
+ }
+ }
+ else
+ {
+ // yippee this is a word..
+ word += c;
+ }
+ }
+
+ line += whitespace;
+ line += word;
+ BrokenText.push_back(line);
+ BrokenTextPositions.push_back(lastLineStart);
+}
+
+
+void intlGUIEditBox::setTextRect(s32 line)
+{
+ core::dimension2du d;
+
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+
+ IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+
+ if (!font)
+ return;
+
+ // get text dimension
+ const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+ if (WordWrap || MultiLine)
+ {
+ d = font->getDimension(BrokenText[line].c_str());
+ }
+ else
+ {
+ d = font->getDimension(Text.c_str());
+ d.Height = AbsoluteRect.getHeight();
+ }
+ d.Height += font->getKerningHeight();
+
+ // justification
+ switch (HAlign)
+ {
+ case EGUIA_CENTER:
+ // align to h centre
+ CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
+ CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
+ break;
+ case EGUIA_LOWERRIGHT:
+ // align to right edge
+ CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
+ CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
+ break;
+ default:
+ // align to left edge
+ CurrentTextRect.UpperLeftCorner.X = 0;
+ CurrentTextRect.LowerRightCorner.X = d.Width;
+
+ }
+
+ switch (VAlign)
+ {
+ case EGUIA_CENTER:
+ // align to v centre
+ CurrentTextRect.UpperLeftCorner.Y =
+ (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
+ break;
+ case EGUIA_LOWERRIGHT:
+ // align to bottom edge
+ CurrentTextRect.UpperLeftCorner.Y =
+ FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
+ break;
+ default:
+ // align to top edge
+ CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
+ break;
+ }
+
+ CurrentTextRect.UpperLeftCorner.X -= HScrollPos;
+ CurrentTextRect.LowerRightCorner.X -= HScrollPos;
+ CurrentTextRect.UpperLeftCorner.Y -= VScrollPos;
+ CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
+
+ CurrentTextRect += FrameRect.UpperLeftCorner;
+
+}
+
+
+s32 intlGUIEditBox::getLineFromPos(s32 pos)
+{
+ if (!WordWrap && !MultiLine)
+ return 0;
+
+ s32 i=0;
+ while (i < (s32)BrokenTextPositions.size())
+ {
+ if (BrokenTextPositions[i] > pos)
+ return i-1;
+ ++i;
+ }
+ return (s32)BrokenTextPositions.size() - 1;
+}
+
+
+void intlGUIEditBox::inputChar(wchar_t c)
+{
+ if (!IsEnabled)
+ return;
+
+ if (c != 0)
+ {
+ if (Text.size() < Max || Max == 0)
+ {
+ core::stringw s;
+
+ if (MarkBegin != MarkEnd)
+ {
+ // replace marked text
+ const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+ const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+ s = Text.subString(0, realmbgn);
+ s.append(c);
+ s.append( Text.subString(realmend, Text.size()-realmend) );
+ Text = s;
+ CursorPos = realmbgn+1;
+ }
+ else
+ {
+ // add new character
+ s = Text.subString(0, CursorPos);
+ s.append(c);
+ s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+ Text = s;
+ ++CursorPos;
+ }
+
+ BlinkStartTime = porting::getTimeMs();
+ setTextMarkers(0, 0);
+ }
+ }
+ breakText();
+ sendGuiEvent(EGET_EDITBOX_CHANGED);
+ calculateScrollPos();
+}
+
+
+void intlGUIEditBox::calculateScrollPos()
+{
+ if (!AutoScroll)
+ return;
+
+ // calculate horizontal scroll position
+ s32 cursLine = getLineFromPos(CursorPos);
+ setTextRect(cursLine);
+
+ // don't do horizontal scrolling when wordwrap is enabled.
+ if (!WordWrap)
+ {
+ // get cursor position
+ IGUISkin* skin = Environment->getSkin();
+ if (!skin)
+ return;
+ IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+ if (!font)
+ return;
+
+ core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
+ s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
+
+ s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
+ font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
+
+ s32 cEnd = cStart + font->getDimension(L"_ ").Width;
+
+ if (FrameRect.LowerRightCorner.X < cEnd)
+ HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
+ else if (FrameRect.UpperLeftCorner.X > cStart)
+ HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
+ else
+ HScrollPos = 0;
+
+ // todo: adjust scrollbar
+ }
+
+ // vertical scroll position
+ if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
+ VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
+
+ else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
+ VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
+ else
+ VScrollPos = 0;
+
+ // todo: adjust scrollbar
+}
+
+//! set text markers
+void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
+{
+ if ( begin != MarkBegin || end != MarkEnd )
+ {
+ MarkBegin = begin;
+ MarkEnd = end;
+ sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
+ }
+}
+
+//! send some gui event to parent
+void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
+{
+ if ( Parent )
+ {
+ SEvent e;
+ e.EventType = EET_GUI_EVENT;
+ e.GUIEvent.Caller = this;
+ e.GUIEvent.Element = 0;
+ e.GUIEvent.EventType = type;
+
+ Parent->OnEvent(e);
+ }
+}
+
+//! Writes attributes of the element.
+void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
+{
+ // IGUIEditBox::serializeAttributes(out,options);
+
+ out->addBool ("OverrideColorEnabled",OverrideColorEnabled );
+ out->addColor ("OverrideColor", OverrideColor);
+ // out->addFont("OverrideFont",OverrideFont);
+ out->addInt ("MaxChars", Max);
+ out->addBool ("WordWrap", WordWrap);
+ out->addBool ("MultiLine", MultiLine);
+ out->addBool ("AutoScroll", AutoScroll);
+ out->addBool ("PasswordBox", PasswordBox);
+ core::stringw ch = L" ";
+ ch[0] = PasswordChar;
+ out->addString("PasswordChar", ch.c_str());
+ out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
+ out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
+
+ IGUIEditBox::serializeAttributes(out,options);
+}
+
+
+//! Reads attributes of the element
+void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
+{
+ IGUIEditBox::deserializeAttributes(in,options);
+
+ setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+ enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+ setMax(in->getAttributeAsInt("MaxChars"));
+ setWordWrap(in->getAttributeAsBool("WordWrap"));
+ setMultiLine(in->getAttributeAsBool("MultiLine"));
+ setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+ core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+ if (!ch.size())
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+ else
+ setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+ setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+ (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+ // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+}
+
+
+} // end namespace gui
+} // end namespace irr
+
+#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/intlGUIEditBox.h b/src/intlGUIEditBox.h
new file mode 100644
index 000000000..e3ee15a30
--- /dev/null
+++ b/src/intlGUIEditBox.h
@@ -0,0 +1,178 @@
+// Copyright (C) 2002-2013 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef __C_INTL_GUI_EDIT_BOX_H_INCLUDED__
+#define __C_INTL_GUI_EDIT_BOX_H_INCLUDED__
+
+#include "IrrCompileConfig.h"
+//#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUIEditBox.h"
+#include "irrArray.h"
+#include "IOSOperator.h"
+
+namespace irr
+{
+namespace gui
+{
+ class intlGUIEditBox : public IGUIEditBox
+ {
+ public:
+
+ //! constructor
+ intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
+ IGUIElement* parent, s32 id, const core::rect<s32>& rectangle);
+
+ //! destructor
+ virtual ~intlGUIEditBox();
+
+ //! Sets another skin independent font.
+ virtual void setOverrideFont(IGUIFont* font=0);
+
+ //! Gets the override font (if any)
+ /** \return The override font (may be 0) */
+ virtual IGUIFont* getOverrideFont() const;
+
+ //! Get the font which is used right now for drawing
+ /** Currently this is the override font when one is set and the
+ font of the active skin otherwise */
+ virtual IGUIFont* getActiveFont() const;
+
+ //! Sets another color for the text.
+ virtual void setOverrideColor(video::SColor color);
+
+ //! Gets the override color
+ virtual video::SColor getOverrideColor() const;
+
+ //! Sets if the text should use the overide color or the
+ //! color in the gui skin.
+ virtual void enableOverrideColor(bool enable);
+
+ //! Checks if an override color is enabled
+ /** \return true if the override color is enabled, false otherwise */
+ virtual bool isOverrideColorEnabled(void) const;
+
+ //! Sets whether to draw the background
+ virtual void setDrawBackground(bool draw);
+
+ //! Turns the border on or off
+ virtual void setDrawBorder(bool border);
+
+ //! Enables or disables word wrap for using the edit box as multiline text editor.
+ virtual void setWordWrap(bool enable);
+
+ //! Checks if word wrap is enabled
+ //! \return true if word wrap is enabled, false otherwise
+ virtual bool isWordWrapEnabled() const;
+
+ //! Enables or disables newlines.
+ /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
+ instead a newline character will be inserted. */
+ virtual void setMultiLine(bool enable);
+
+ //! Checks if multi line editing is enabled
+ //! \return true if mult-line is enabled, false otherwise
+ virtual bool isMultiLineEnabled() const;
+
+ //! Enables or disables automatic scrolling with cursor position
+ //! \param enable: If set to true, the text will move around with the cursor position
+ virtual void setAutoScroll(bool enable);
+
+ //! Checks to see if automatic scrolling is enabled
+ //! \return true if automatic scrolling is enabled, false if not
+ virtual bool isAutoScrollEnabled() const;
+
+ //! Gets the size area of the text in the edit box
+ //! \return Returns the size in pixels of the text
+ virtual core::dimension2du getTextDimension();
+
+ //! Sets text justification
+ virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+ //! called if an event happened.
+ virtual bool OnEvent(const SEvent& event);
+
+ //! draws the element and its children
+ virtual void draw();
+
+ //! Sets the new caption of this element.
+ virtual void setText(const wchar_t* text);
+
+ //! Sets the maximum amount of characters which may be entered in the box.
+ //! \param max: Maximum amount of characters. If 0, the character amount is
+ //! infinity.
+ virtual void setMax(u32 max);
+
+ //! Returns maximum amount of characters, previously set by setMax();
+ virtual u32 getMax() const;
+
+ //! Sets whether the edit box is a password box. Setting this to true will
+ /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
+ \param passwordBox: true to enable password, false to disable
+ \param passwordChar: the character that is displayed instead of letters */
+ virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
+
+ //! Returns true if the edit box is currently a password box.
+ virtual bool isPasswordBox() const;
+
+ //! Updates the absolute position, splits text if required
+ virtual void updateAbsolutePosition();
+
+ //! Writes attributes of the element.
+ virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
+
+ //! Reads attributes of the element
+ virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
+
+ protected:
+ //! Breaks the single text line.
+ void breakText();
+ //! sets the area of the given line
+ void setTextRect(s32 line);
+ //! returns the line number that the cursor is on
+ s32 getLineFromPos(s32 pos);
+ //! adds a letter to the edit box
+ void inputChar(wchar_t c);
+ //! calculates the current scroll position
+ void calculateScrollPos();
+ //! send some gui event to parent
+ void sendGuiEvent(EGUI_EVENT_TYPE type);
+ //! set text markers
+ void setTextMarkers(s32 begin, s32 end);
+
+ bool processKey(const SEvent& event);
+ bool processMouse(const SEvent& event);
+ s32 getCursorPos(s32 x, s32 y);
+
+ bool MouseMarking;
+ bool Border;
+ bool OverrideColorEnabled;
+ s32 MarkBegin;
+ s32 MarkEnd;
+
+ video::SColor OverrideColor;
+ gui::IGUIFont *OverrideFont, *LastBreakFont;
+ IOSOperator* Operator;
+
+ u32 BlinkStartTime;
+ s32 CursorPos;
+ s32 HScrollPos, VScrollPos; // scroll position in characters
+ u32 Max;
+
+ bool WordWrap, MultiLine, AutoScroll, PasswordBox;
+ wchar_t PasswordChar;
+ EGUI_ALIGNMENT HAlign, VAlign;
+
+ core::array< core::stringw > BrokenText;
+ core::array< s32 > BrokenTextPositions;
+
+ core::rect<s32> CurrentTextRect, FrameRect; // temporary values
+ };
+
+
+} // end namespace gui
+} // end namespace irr
+
+//#endif // _IRR_COMPILE_WITH_GUI_
+#endif // __C_GUI_EDIT_BOX_H_INCLUDED__
diff --git a/src/inventory.cpp b/src/inventory.cpp
index 3fa31965c..af8b1b301 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -163,7 +163,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
std::getline(is, tmp, ' ');
if(!tmp.empty())
throw SerializationError("Unexpected text after item name");
-
+
if(name == "MaterialItem")
{
// Obsoleted on 2011-07-30
@@ -478,7 +478,7 @@ void InventoryList::setName(const std::string &name)
void InventoryList::serialize(std::ostream &os) const
{
//os.imbue(std::locale("C"));
-
+
os<<"Width "<<m_width<<"\n";
for(u32 i=0; i<m_items.size(); i++)
@@ -620,13 +620,13 @@ u32 InventoryList::getFreeSlots() const
const ItemStack& InventoryList::getItem(u32 i) const
{
- assert(i < m_size);
+ assert(i < m_size); // Pre-condition
return m_items[i];
}
ItemStack& InventoryList::getItem(u32 i)
{
- assert(i < m_size);
+ assert(i < m_size); // Pre-condition
return m_items[i];
}
@@ -643,7 +643,7 @@ ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem)
void InventoryList::deleteItem(u32 i)
{
- assert(i < m_items.size());
+ assert(i < m_items.size()); // Pre-condition
m_items[i].clear();
}
@@ -653,7 +653,7 @@ ItemStack InventoryList::addItem(const ItemStack &newitem_)
if(newitem.empty())
return newitem;
-
+
/*
First try to find if it could be added to some existing items
*/
@@ -782,11 +782,47 @@ ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const
return m_items[i].peekItem(peekcount);
}
-void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
+void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count)
{
- if(this == dest && i == dest_i)
+ // Take item from source list
+ ItemStack item1;
+ if (count == 0)
+ item1 = changeItem(i, ItemStack());
+ else
+ item1 = takeItem(i, count);
+
+ if (item1.empty())
return;
+ // Try to add the item to destination list
+ u32 dest_size = dest->getSize();
+ // First try all the non-empty slots
+ for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
+ if (!m_items[dest_i].empty()) {
+ item1 = dest->addItem(dest_i, item1);
+ if (item1.empty()) return;
+ }
+ }
+
+ // Then try all the empty ones
+ for (u32 dest_i = 0; dest_i < dest_size; dest_i++) {
+ if (m_items[dest_i].empty()) {
+ item1 = dest->addItem(dest_i, item1);
+ if (item1.empty()) return;
+ }
+ }
+
+ // If we reach this, the item was not fully added
+ // Add the remaining part back to the source item
+ addItem(i, item1);
+}
+
+u32 InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i,
+ u32 count, bool swap_if_needed, bool *did_swap)
+{
+ if(this == dest && i == dest_i)
+ return count;
+
// Take item from source list
ItemStack item1;
if(count == 0)
@@ -795,7 +831,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
item1 = takeItem(i, count);
if(item1.empty())
- return;
+ return 0;
// Try to add the item to destination list
u32 oldcount = item1.count;
@@ -813,8 +849,11 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
// If olditem is returned, nothing was added.
// Swap the items
- if(nothing_added)
- {
+ if (nothing_added && swap_if_needed) {
+ // Tell that we swapped
+ if (did_swap != NULL) {
+ *did_swap = true;
+ }
// Take item from source list
item1 = changeItem(i, ItemStack());
// Adding was not possible, swap the items.
@@ -823,6 +862,7 @@ void InventoryList::moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count)
changeItem(i, item2);
}
}
+ return (oldcount - item1.count);
}
/*
diff --git a/src/inventory.h b/src/inventory.h
index e4a97e1d3..a690eb5ae 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -71,7 +71,7 @@ struct ItemStack
void remove(u16 n)
{
- assert(count >= n);
+ assert(count >= n); // Pre-condition
count -= n;
if(count == 0)
clear(); // reset name, wear and metadata too
@@ -244,7 +244,13 @@ public:
// Move an item to a different list (or a different stack in the same list)
// count is the maximum number of items to move (0 for everything)
- void moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count = 0);
+ // returns number of moved items
+ u32 moveItem(u32 i, InventoryList *dest, u32 dest_i,
+ u32 count = 0, bool swap_if_needed = true, bool *did_swap = NULL);
+
+ // like moveItem, but without a fixed destination index
+ // also with optional rollback recording
+ void moveItemSomewhere(u32 i, InventoryList *dest, u32 count);
private:
std::vector<ItemStack> m_items;
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index ed18126d0..476768b8c 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "environment.h"
#include "scripting_game.h"
#include "serverobject.h"
-#include "main.h" // for g_settings
#include "settings.h"
#include "craftdef.h"
#include "rollback_interface.h"
@@ -62,7 +61,7 @@ void InventoryLocation::serialize(std::ostream &os) const
os<<"detached:"<<name;
break;
default:
- assert(0);
+ FATAL_ERROR("Unhandled inventory location type");
}
}
@@ -122,16 +121,13 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is)
InventoryAction *a = NULL;
- if(type == "Move")
- {
- a = new IMoveAction(is);
- }
- else if(type == "Drop")
- {
+ if (type == "Move") {
+ a = new IMoveAction(is, false);
+ } else if (type == "MoveSomewhere") {
+ a = new IMoveAction(is, true);
+ } else if (type == "Drop") {
a = new IDropAction(is);
- }
- else if(type == "Craft")
- {
+ } else if(type == "Craft") {
a = new ICraftAction(is);
}
@@ -142,9 +138,12 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is)
IMoveAction
*/
-IMoveAction::IMoveAction(std::istream &is)
+IMoveAction::IMoveAction(std::istream &is, bool somewhere)
{
std::string ts;
+ move_somewhere = somewhere;
+ caused_by_move_somewhere = false;
+ move_count = 0;
std::getline(is, ts, ' ');
count = stoi(ts);
@@ -162,25 +161,27 @@ IMoveAction::IMoveAction(std::istream &is)
std::getline(is, to_list, ' ');
- std::getline(is, ts, ' ');
- to_i = stoi(ts);
+ if (!somewhere) {
+ std::getline(is, ts, ' ');
+ to_i = stoi(ts);
+ }
}
void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
{
Inventory *inv_from = mgr->getInventory(from_inv);
Inventory *inv_to = mgr->getInventory(to_inv);
-
- if(!inv_from){
- infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
- <<"from_inv=\""<<from_inv.dump()<<"\""
- <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
+
+ if (!inv_from) {
+ infostream << "IMoveAction::apply(): FAIL: source inventory not found: "
+ << "from_inv=\""<<from_inv.dump() << "\""
+ << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl;
return;
}
- if(!inv_to){
- infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
- <<"from_inv=\""<<from_inv.dump()<<"\""
- <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
+ if (!inv_to) {
+ infostream << "IMoveAction::apply(): FAIL: destination inventory not found: "
+ << "from_inv=\"" << from_inv.dump() << "\""
+ << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl;
return;
}
@@ -190,19 +191,68 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
/*
If a list doesn't exist or the source item doesn't exist
*/
- if(!list_from){
- infostream<<"IMoveAction::apply(): FAIL: source list not found: "
- <<"from_inv=\""<<from_inv.dump()<<"\""
- <<", from_list=\""<<from_list<<"\""<<std::endl;
+ if (!list_from) {
+ infostream << "IMoveAction::apply(): FAIL: source list not found: "
+ << "from_inv=\"" << from_inv.dump() << "\""
+ << ", from_list=\"" << from_list << "\"" << std::endl;
return;
}
- if(!list_to){
- infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
- <<"to_inv=\""<<to_inv.dump()<<"\""
- <<", to_list=\""<<to_list<<"\""<<std::endl;
+ if (!list_to) {
+ infostream << "IMoveAction::apply(): FAIL: destination list not found: "
+ << "to_inv=\""<<to_inv.dump() << "\""
+ << ", to_list=\"" << to_list << "\"" << std::endl;
return;
}
+ if (move_somewhere) {
+ s16 old_to_i = to_i;
+ u16 old_count = count;
+ caused_by_move_somewhere = true;
+ move_somewhere = false;
+
+ infostream << "IMoveAction::apply(): moving item somewhere"
+ << " msom=" << move_somewhere
+ << " count=" << count
+ << " from inv=\"" << from_inv.dump() << "\""
+ << " list=\"" << from_list << "\""
+ << " i=" << from_i
+ << " to inv=\"" << to_inv.dump() << "\""
+ << " list=\"" << to_list << "\""
+ << std::endl;
+
+ // Try to add the item to destination list
+ s16 dest_size = list_to->getSize();
+ // First try all the non-empty slots
+ for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) {
+ if (!list_to->getItem(dest_i).empty()) {
+ to_i = dest_i;
+ apply(mgr, player, gamedef);
+ count -= move_count;
+ }
+ }
+
+ // Then try all the empty ones
+ for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) {
+ if (list_to->getItem(dest_i).empty()) {
+ to_i = dest_i;
+ apply(mgr, player, gamedef);
+ count -= move_count;
+ }
+ }
+
+ to_i = old_to_i;
+ count = old_count;
+ caused_by_move_somewhere = false;
+ move_somewhere = true;
+ return;
+ }
+
+ if ((u16)to_i > list_to->getSize()) {
+ infostream << "IMoveAction::apply(): FAIL: destination index out of bounds: "
+ << "to_i=" << to_i
+ << ", size=" << list_to->getSize() << std::endl;
+ return;
+ }
/*
Do not handle rollback if both inventories are that of the same player
*/
@@ -221,7 +271,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
int src_can_take_count = 0xffff;
int dst_can_put_count = 0xffff;
-
+
/* Query detached inventories */
// Move occurs in the same detached inventory
@@ -288,7 +338,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
}
int old_count = count;
-
+
/* Modify count according to collected data */
count = try_take_count;
if(src_can_take_count != -1 && count > src_can_take_count)
@@ -298,7 +348,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
/* Limit according to source item count */
if(count > list_from->getItem(from_i).count)
count = list_from->getItem(from_i).count;
-
+
/* If no items will be moved, don't go further */
if(count == 0)
{
@@ -325,24 +375,33 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
If something is wrong (source item is empty, destination is the
same as source), nothing happens
*/
- list_from->moveItem(from_i, list_to, to_i, count);
+ bool did_swap = false;
+ move_count = list_from->moveItem(from_i,
+ list_to, to_i, count, !caused_by_move_somewhere, &did_swap);
// If source is infinite, reset it's stack
- if(src_can_take_count == -1){
- // If destination stack is of different type and there are leftover
- // items, attempt to put the leftover items to a different place in the
- // destination inventory.
- // The client-side GUI will try to guess if this happens.
- if(from_stack_was.name != to_stack_was.name){
- for(u32 i=0; i<list_to->getSize(); i++){
- if(list_to->getItem(i).empty()){
- list_to->changeItem(i, to_stack_was);
- break;
+ if (src_can_take_count == -1) {
+ // For the caused_by_move_somewhere == true case we didn't force-put the item,
+ // which guarantees there is no leftover, and code below would duplicate the
+ // (not replaced) to_stack_was item.
+ if (!caused_by_move_somewhere) {
+ // If destination stack is of different type and there are leftover
+ // items, attempt to put the leftover items to a different place in the
+ // destination inventory.
+ // The client-side GUI will try to guess if this happens.
+ if (from_stack_was.name != to_stack_was.name) {
+ for (u32 i = 0; i < list_to->getSize(); i++) {
+ if (list_to->getItem(i).empty()) {
+ list_to->changeItem(i, to_stack_was);
+ break;
+ }
}
}
}
- list_from->deleteItem(from_i);
- list_from->addItem(from_i, from_stack_was);
+ if (move_count > 0 || did_swap) {
+ list_from->deleteItem(from_i);
+ list_from->addItem(from_i, from_stack_was);
+ }
}
// If destination is infinite, reset it's stack and take count from source
if(dst_can_put_count == -1){
@@ -353,15 +412,24 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
list_from->takeItem(from_i, count);
}
- infostream<<"IMoveAction::apply(): moved"
- <<" count="<<count
- <<" from inv=\""<<from_inv.dump()<<"\""
- <<" list=\""<<from_list<<"\""
- <<" i="<<from_i
- <<" to inv=\""<<to_inv.dump()<<"\""
- <<" list=\""<<to_list<<"\""
- <<" i="<<to_i
- <<std::endl;
+ infostream << "IMoveAction::apply(): moved"
+ << " msom=" << move_somewhere
+ << " caused=" << caused_by_move_somewhere
+ << " count=" << count
+ << " from inv=\"" << from_inv.dump() << "\""
+ << " list=\"" << from_list << "\""
+ << " i=" << from_i
+ << " to inv=\"" << to_inv.dump() << "\""
+ << " list=\"" << to_list << "\""
+ << " i=" << to_i
+ << std::endl;
+
+ // If we are inside the move somewhere loop, we don't need to report
+ // anything if nothing happened (perhaps we don't need to report
+ // anything for caused_by_move_somewhere == true, but this way its safer)
+ if (caused_by_move_somewhere && move_count == 0) {
+ return;
+ }
/*
Record rollback information
@@ -401,7 +469,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
/*
Report move to endpoints
*/
-
+
/* Detached inventories */
// Both endpoints are same detached
@@ -454,10 +522,10 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
from_inv.p, from_list, from_i, src_item, player);
}
}
-
- mgr->setInventoryModified(from_inv);
+
+ mgr->setInventoryModified(from_inv, false);
if(inv_from != inv_to)
- mgr->setInventoryModified(to_inv);
+ mgr->setInventoryModified(to_inv, false);
}
void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
@@ -481,7 +549,10 @@ void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
if(!list_from || !list_to)
return;
- list_from->moveItem(from_i, list_to, to_i, count);
+ if (!move_somewhere)
+ list_from->moveItem(from_i, list_to, to_i, count);
+ else
+ list_from->moveItemSomewhere(from_i, list_to, count);
mgr->setInventoryModified(from_inv);
if(inv_from != inv_to)
@@ -511,7 +582,7 @@ IDropAction::IDropAction(std::istream &is)
void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
{
Inventory *inv_from = mgr->getInventory(from_inv);
-
+
if(!inv_from){
infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
<<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
@@ -571,7 +642,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
if(src_can_take_count != -1 && src_can_take_count < take_count)
take_count = src_can_take_count;
-
+
int actually_dropped_count = 0;
ItemStack src_item = list_from->getItem(from_i);
@@ -588,7 +659,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
infostream<<"Actually dropped no items"<<std::endl;
return;
}
-
+
// If source isn't infinite
if(src_can_take_count != -1){
// Take item from source list
@@ -597,7 +668,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
if(item2.count != actually_dropped_count)
errorstream<<"Could not take dropped count of items"<<std::endl;
- mgr->setInventoryModified(from_inv);
+ mgr->setInventoryModified(from_inv, false);
}
}
@@ -606,13 +677,13 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
<<" list=\""<<from_list<<"\""
<<" i="<<from_i
<<std::endl;
-
+
src_item.count = actually_dropped_count;
/*
Report drop to endpoints
*/
-
+
// Source is detached
if(from_inv.type == InventoryLocation::DETACHED)
{
@@ -692,72 +763,112 @@ ICraftAction::ICraftAction(std::istream &is)
craft_inv.deSerialize(ts);
}
-void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
+void ICraftAction::apply(InventoryManager *mgr,
+ ServerActiveObject *player, IGameDef *gamedef)
{
Inventory *inv_craft = mgr->getInventory(craft_inv);
-
- if(!inv_craft){
- infostream<<"ICraftAction::apply(): FAIL: inventory not found: "
- <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
+
+ if (!inv_craft) {
+ infostream << "ICraftAction::apply(): FAIL: inventory not found: "
+ << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
return;
}
InventoryList *list_craft = inv_craft->getList("craft");
InventoryList *list_craftresult = inv_craft->getList("craftresult");
+ InventoryList *list_main = inv_craft->getList("main");
/*
If a list doesn't exist or the source item doesn't exist
*/
- if(!list_craft){
- infostream<<"ICraftAction::apply(): FAIL: craft list not found: "
- <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
+ if (!list_craft) {
+ infostream << "ICraftAction::apply(): FAIL: craft list not found: "
+ << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
return;
}
- if(!list_craftresult){
- infostream<<"ICraftAction::apply(): FAIL: craftresult list not found: "
- <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
+ if (!list_craftresult) {
+ infostream << "ICraftAction::apply(): FAIL: craftresult list not found: "
+ << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
return;
}
- if(list_craftresult->getSize() < 1){
- infostream<<"ICraftAction::apply(): FAIL: craftresult list too short: "
- <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
+ if (list_craftresult->getSize() < 1) {
+ infostream << "ICraftAction::apply(): FAIL: craftresult list too short: "
+ << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
return;
}
ItemStack crafted;
ItemStack craftresultitem;
int count_remaining = count;
- getCraftingResult(inv_craft, crafted, false, gamedef);
+ std::vector<ItemStack> output_replacements;
+ getCraftingResult(inv_craft, crafted, output_replacements, false, gamedef);
PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
bool found = !crafted.empty();
- while(found && list_craftresult->itemFits(0, crafted))
- {
+ while (found && list_craftresult->itemFits(0, crafted)) {
InventoryList saved_craft_list = *list_craft;
-
+
+ std::vector<ItemStack> temp;
// Decrement input and add crafting output
- getCraftingResult(inv_craft, crafted, true, gamedef);
+ getCraftingResult(inv_craft, crafted, temp, true, gamedef);
PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv);
list_craftresult->addItem(0, crafted);
mgr->setInventoryModified(craft_inv);
- actionstream<<player->getDescription()
- <<" crafts "
- <<crafted.getItemString()
- <<std::endl;
+ // Add the new replacements to the list
+ IItemDefManager *itemdef = gamedef->getItemDefManager();
+ for (std::vector<ItemStack>::iterator it = temp.begin();
+ it != temp.end(); it++) {
+ for (std::vector<ItemStack>::iterator jt = output_replacements.begin();
+ jt != output_replacements.end(); jt++) {
+ if (it->name == jt->name) {
+ *it = jt->addItem(*it, itemdef);
+ if (it->empty())
+ continue;
+ }
+ }
+ output_replacements.push_back(*it);
+ }
+
+ actionstream << player->getDescription()
+ << " crafts "
+ << crafted.getItemString()
+ << std::endl;
// Decrement counter
- if(count_remaining == 1)
+ if (count_remaining == 1)
break;
- else if(count_remaining > 1)
+ else if (count_remaining > 1)
count_remaining--;
// Get next crafting result
- found = getCraftingResult(inv_craft, crafted, false, gamedef);
+ found = getCraftingResult(inv_craft, crafted, temp, false, gamedef);
PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
found = !crafted.empty();
}
+ // Put the replacements in the inventory or drop them on the floor, if
+ // the invenotry is full
+ for (std::vector<ItemStack>::iterator it = output_replacements.begin();
+ it != output_replacements.end(); it++) {
+ if (list_main)
+ *it = list_main->addItem(*it);
+ if (it->empty())
+ continue;
+ u16 count = it->count;
+ do {
+ PLAYER_TO_SA(player)->item_OnDrop(*it, player,
+ player->getBasePosition() + v3f(0,1,0));
+ if (count >= it->count) {
+ errorstream << "Couldn't drop replacement stack " <<
+ it->getItemString() << " because drop loop didn't "
+ "decrease count." << std::endl;
+
+ break;
+ }
+ } while (!it->empty());
+ }
+
infostream<<"ICraftAction::apply(): crafted "
<<" craft_inv=\""<<craft_inv.dump()<<"\""
<<std::endl;
@@ -772,10 +883,11 @@ void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
// Crafting helper
bool getCraftingResult(Inventory *inv, ItemStack& result,
+ std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef)
{
DSTACK(__FUNCTION_NAME);
-
+
result.clear();
// Get the InventoryList in which we will operate
@@ -793,7 +905,7 @@ bool getCraftingResult(Inventory *inv, ItemStack& result,
// Find out what is crafted and add it to result item slot
CraftOutput co;
bool found = gamedef->getCraftDefManager()->getCraftResult(
- ci, co, decrementInput, gamedef);
+ ci, co, output_replacements, decrementInput, gamedef);
if(found)
result.deSerialize(co.item, gamedef->getItemDefManager());
diff --git a/src/inventorymanager.h b/src/inventorymanager.h
index 8e2abc961..35fcf4b99 100644
--- a/src/inventorymanager.h
+++ b/src/inventorymanager.h
@@ -108,11 +108,11 @@ class InventoryManager
public:
InventoryManager(){}
virtual ~InventoryManager(){}
-
+
// Get an inventory (server and client)
virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;}
// Set modified (will be saved and sent over network; only on server)
- virtual void setInventoryModified(const InventoryLocation &loc){}
+ virtual void setInventoryModified(const InventoryLocation &loc, bool playerSend = true){}
// Send inventory action to server (only on client)
virtual void inventoryAction(InventoryAction *a){}
};
@@ -124,7 +124,7 @@ public:
struct InventoryAction
{
static InventoryAction * deSerialize(std::istream &is);
-
+
virtual u16 getType() const = 0;
virtual void serialize(std::ostream &os) const = 0;
virtual void apply(InventoryManager *mgr, ServerActiveObject *player,
@@ -143,15 +143,24 @@ struct IMoveAction : public InventoryAction
InventoryLocation to_inv;
std::string to_list;
s16 to_i;
-
+ bool move_somewhere;
+
+ // treat these as private
+ // related to movement to somewhere
+ bool caused_by_move_somewhere;
+ u32 move_count;
+
IMoveAction()
{
count = 0;
from_i = -1;
to_i = -1;
+ move_somewhere = false;
+ caused_by_move_somewhere = false;
+ move_count = 0;
}
-
- IMoveAction(std::istream &is);
+
+ IMoveAction(std::istream &is, bool somewhere);
u16 getType() const
{
@@ -160,14 +169,18 @@ struct IMoveAction : public InventoryAction
void serialize(std::ostream &os) const
{
- os<<"Move ";
- os<<count<<" ";
- os<<from_inv.dump()<<" ";
- os<<from_list<<" ";
- os<<from_i<<" ";
- os<<to_inv.dump()<<" ";
- os<<to_list<<" ";
- os<<to_i;
+ if (!move_somewhere)
+ os << "Move ";
+ else
+ os << "MoveSomewhere ";
+ os << count << " ";
+ os << from_inv.dump() << " ";
+ os << from_list << " ";
+ os << from_i << " ";
+ os << to_inv.dump() << " ";
+ os << to_list;
+ if (!move_somewhere)
+ os << " " << to_i;
}
void apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef);
@@ -182,13 +195,13 @@ struct IDropAction : public InventoryAction
InventoryLocation from_inv;
std::string from_list;
s16 from_i;
-
+
IDropAction()
{
count = 0;
from_i = -1;
}
-
+
IDropAction(std::istream &is);
u16 getType() const
@@ -215,12 +228,12 @@ struct ICraftAction : public InventoryAction
// count=0 means "everything"
u16 count;
InventoryLocation craft_inv;
-
+
ICraftAction()
{
count = 0;
}
-
+
ICraftAction(std::istream &is);
u16 getType() const
@@ -242,6 +255,7 @@ struct ICraftAction : public InventoryAction
// Crafting helper
bool getCraftingResult(Inventory *inv, ItemStack& result,
+ std::vector<ItemStack> &output_replacements,
bool decrementInput, IGameDef *gamedef);
#endif
diff --git a/src/irr_v3d.h b/src/irr_v3d.h
index 7bc73ad10..f74d601e8 100644
--- a/src/irr_v3d.h
+++ b/src/irr_v3d.h
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
typedef core::vector3df v3f;
typedef core::vector3d<s16> v3s16;
+typedef core::vector3d<u16> v3u16;
typedef core::vector3d<s32> v3s32;
#endif
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index ac67c5b27..0133b1b3f 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -28,10 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock_mesh.h"
#include "mesh.h"
#include "wieldmesh.h"
-#include "tile.h"
+#include "client/tile.h"
#endif
#include "log.h"
-#include "main.h" // g_settings
#include "settings.h"
#include "util/serialize.h"
#include "util/container.h"
@@ -249,8 +248,8 @@ public:
virtual ~CItemDefManager()
{
#ifndef SERVER
- const std::list<ClientCached*> &values = m_clientcached.getValues();
- for(std::list<ClientCached*>::const_iterator
+ const std::vector<ClientCached*> &values = m_clientcached.getValues();
+ for(std::vector<ClientCached*>::const_iterator
i = values.begin(); i != values.end(); ++i)
{
ClientCached *cc = *i;
@@ -281,26 +280,23 @@ public:
}
virtual std::string getAlias(const std::string &name) const
{
- std::map<std::string, std::string>::const_iterator i;
- i = m_aliases.find(name);
- if(i != m_aliases.end())
- return i->second;
+ StringMap::const_iterator it = m_aliases.find(name);
+ if (it != m_aliases.end())
+ return it->second;
return name;
}
virtual std::set<std::string> getAll() const
{
std::set<std::string> result;
- for(std::map<std::string, ItemDefinition*>::const_iterator
- i = m_item_definitions.begin();
- i != m_item_definitions.end(); i++)
- {
- result.insert(i->first);
+ for(std::map<std::string, ItemDefinition *>::const_iterator
+ it = m_item_definitions.begin();
+ it != m_item_definitions.end(); ++it) {
+ result.insert(it->first);
}
- for(std::map<std::string, std::string>::const_iterator
- i = m_aliases.begin();
- i != m_aliases.end(); i++)
- {
- result.insert(i->first);
+ for (StringMap::const_iterator
+ it = m_aliases.begin();
+ it != m_aliases.end(); ++it) {
+ result.insert(it->first);
}
return result;
}
@@ -321,7 +317,7 @@ public:
<<name<<"\""<<std::endl;
// This is not thread-safe
- assert(get_current_thread_id() == m_main_thread);
+ sanity_check(get_current_thread_id() == m_main_thread);
// Skip if already in cache
ClientCached *cc = NULL;
@@ -362,8 +358,6 @@ public:
scene::IMesh *node_mesh = NULL;
- bool reenable_shaders = false;
-
if (need_rtt_mesh || need_wield_mesh) {
u8 param1 = 0;
if (f.param_type == CPT_LIGHT)
@@ -372,11 +366,7 @@ public:
/*
Make a mesh from the node
*/
- if (g_settings->getBool("enable_shaders")) {
- reenable_shaders = true;
- g_settings->setBool("enable_shaders", false);
- }
- MeshMakeData mesh_make_data(gamedef);
+ MeshMakeData mesh_make_data(gamedef, false);
u8 param2 = 0;
if (f.param_type_2 == CPT2_WALLMOUNTED)
param2 = 1;
@@ -443,9 +433,6 @@ public:
if (node_mesh)
node_mesh->drop();
-
- if (reenable_shaders)
- g_settings->setBool("enable_shaders",true);
}
// Put in cache
@@ -553,7 +540,7 @@ public:
verbosestream<<"ItemDefManager: registering \""<<def.name<<"\""<<std::endl;
// Ensure that the "" item (the hand) always has ToolCapabilities
if(def.name == "")
- assert(def.tool_capabilities != NULL);
+ FATAL_ERROR_IF(!def.tool_capabilities, "Hand does not have ToolCapabilities");
if(m_item_definitions.count(def.name) == 0)
m_item_definitions[def.name] = new ItemDefinition(def);
@@ -581,22 +568,24 @@ public:
writeU8(os, 0); // version
u16 count = m_item_definitions.size();
writeU16(os, count);
- for(std::map<std::string, ItemDefinition*>::const_iterator
- i = m_item_definitions.begin();
- i != m_item_definitions.end(); i++)
- {
- ItemDefinition *def = i->second;
+
+ for (std::map<std::string, ItemDefinition *>::const_iterator
+ it = m_item_definitions.begin();
+ it != m_item_definitions.end(); ++it) {
+ ItemDefinition *def = it->second;
// Serialize ItemDefinition and write wrapped in a string
std::ostringstream tmp_os(std::ios::binary);
def->serialize(tmp_os, protocol_version);
- os<<serializeString(tmp_os.str());
+ os << serializeString(tmp_os.str());
}
+
writeU16(os, m_aliases.size());
- for(std::map<std::string, std::string>::const_iterator
- i = m_aliases.begin(); i != m_aliases.end(); i++)
- {
- os<<serializeString(i->first);
- os<<serializeString(i->second);
+
+ for (StringMap::const_iterator
+ it = m_aliases.begin();
+ it != m_aliases.end(); ++it) {
+ os << serializeString(it->first);
+ os << serializeString(it->second);
}
}
void deSerialize(std::istream &is)
@@ -643,7 +632,7 @@ private:
// Key is name
std::map<std::string, ItemDefinition*> m_item_definitions;
// Aliases
- std::map<std::string, std::string> m_aliases;
+ StringMap m_aliases;
#ifndef SERVER
// The id of the thread that is allowed to use irrlicht directly
threadid_t m_main_thread;
diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt
index 5887d523a..9056e4b6d 100644
--- a/src/json/CMakeLists.txt
+++ b/src/json/CMakeLists.txt
@@ -1,14 +1,7 @@
-if( UNIX )
- set(json_SRCS jsoncpp.cpp)
- set(json_platform_LIBS "")
-else( UNIX )
- set(json_SRCS jsoncpp.cpp)
- set(json_platform_LIBS "")
-endif( UNIX )
+if(MSVC)
+ set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Ob2 /D NDEBUG")
+endif()
-add_library(jsoncpp ${json_SRCS})
+add_library(jsoncpp jsoncpp.cpp)
+target_link_libraries(jsoncpp)
-target_link_libraries(
- jsoncpp
- ${json_platform_LIBS}
-)
diff --git a/src/jthread/CMakeLists.txt b/src/jthread/CMakeLists.txt
index a581a3b02..cebb35caa 100644
--- a/src/jthread/CMakeLists.txt
+++ b/src/jthread/CMakeLists.txt
@@ -1,15 +1,14 @@
-if( UNIX )
- set(JTHREAD_SRCS
- ${CMAKE_CURRENT_SOURCE_DIR}/pthread/jmutex.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/pthread/jthread.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/pthread/jsemaphore.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/pthread/jevent.cpp
- PARENT_SCOPE)
-else( UNIX )
- set(JTHREAD_SRCS
- ${CMAKE_CURRENT_SOURCE_DIR}/win32/jmutex.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/win32/jthread.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/win32/jsemaphore.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/win32/jevent.cpp
- PARENT_SCOPE)
-endif( UNIX )
+if(UNIX)
+ set(THREAD_SYS_DIR pthread)
+else()
+ set(THREAD_SYS_DIR win32)
+endif()
+
+set(SRC_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/${THREAD_SYS_DIR})
+set(JTHREAD_SRCS
+ ${SRC_PREFIX}/jmutex.cpp
+ ${SRC_PREFIX}/jthread.cpp
+ ${SRC_PREFIX}/jsemaphore.cpp
+ ${SRC_PREFIX}/jevent.cpp
+ PARENT_SCOPE)
+
diff --git a/src/keycode.cpp b/src/keycode.cpp
index c5f102b44..990dee339 100644
--- a/src/keycode.cpp
+++ b/src/keycode.cpp
@@ -18,12 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "keycode.h"
-#include "main.h" // For g_settings
#include "exceptions.h"
#include "settings.h"
#include "log.h"
-#include "hex.h"
#include "debug.h"
+#include "util/hex.h"
class UnknownKeycode : public BaseException
{
@@ -257,13 +256,18 @@ KeyPress::KeyPress() :
KeyPress::KeyPress(const char *name)
{
- if (strlen(name) > 4) {
+ if (name[0] == 0) {
+ Key = irr::KEY_KEY_CODES_COUNT;
+ Char = L'\0';
+ return;
+ } else if (strlen(name) > 4) {
try {
Key = keyname_to_keycode(name);
m_name = name;
if (strlen(name) > 8 && strncmp(name, "KEY_KEY_", 8) == 0) {
int chars_read = mbtowc(&Char, name + 8, 1);
- assert (chars_read == 1 && "unexpected multibyte character");
+
+ FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
} else
Char = L'\0';
return;
@@ -275,7 +279,8 @@ KeyPress::KeyPress(const char *name)
try {
Key = keyname_to_keycode(m_name.c_str());
int chars_read = mbtowc(&Char, name, 1);
- assert (chars_read == 1 && "unexpected multibyte character");
+
+ FATAL_ERROR_IF(chars_read != 1, "Unexpected multibyte character");
return;
} catch (UnknownKeycode &e) {};
}
@@ -285,7 +290,7 @@ KeyPress::KeyPress(const char *name)
Key = irr::KEY_KEY_CODES_COUNT;
int mbtowc_ret = mbtowc(&Char, name, 1);
- assert (mbtowc_ret == 1 && "unexpected multibyte character");
+ FATAL_ERROR_IF(mbtowc_ret != 1, "Unexpected multibyte character");
m_name = name[0];
}
diff --git a/src/light.cpp b/src/light.cpp
index 08380a180..5dc01fcf0 100644
--- a/src/light.cpp
+++ b/src/light.cpp
@@ -88,7 +88,7 @@ void set_light_table(float gamma)
0
};
- gamma = rangelim(gamma, 1.1, 3.0);
+ gamma = rangelim(gamma, 1.0, 3.0);
float brightness = brightness_step;
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index 69d4ec7ef..77e7a9e16 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "localplayer.h"
-#include "main.h" // For g_settings
#include "event.h"
#include "collision.h"
#include "gamedef.h"
@@ -43,23 +42,24 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
last_pitch(0),
last_yaw(0),
last_keyPressed(0),
- eye_offset_first(v3f(0,0,0)),
- eye_offset_third(v3f(0,0,0)),
last_animation(NO_ANIM),
hotbar_image(""),
hotbar_selected_image(""),
light_color(255,255,255,255),
m_sneak_node(32767,32767,32767),
m_sneak_node_exists(false),
+ m_need_to_get_new_sneak_node(true),
+ m_sneak_node_bb_ymax(0),
m_old_node_below(32767,32767,32767),
m_old_node_below_type("air"),
- m_need_to_get_new_sneak_node(true),
m_can_jump(false),
m_cao(NULL)
{
// Initialize hp to 0, so that no hearts will be shown if server
// doesn't support health points
hp = 0;
+ eye_offset_first = v3f(0,0,0);
+ eye_offset_third = v3f(0,0,0);
}
LocalPlayer::~LocalPlayer()
@@ -67,7 +67,7 @@ LocalPlayer::~LocalPlayer()
}
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
- std::list<CollisionInfo> *collision_info)
+ std::vector<CollisionInfo> *collision_info)
{
Map *map = &env->getMap();
INodeDefManager *nodemgr = m_gamedef->ndef();
@@ -87,9 +87,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
bool noclip = m_gamedef->checkLocalPrivilege("noclip") &&
g_settings->getBool("noclip");
bool free_move = noclip && fly_allowed && g_settings->getBool("free_move");
- if(free_move)
- {
- position += m_speed * dtime;
+ if (free_move) {
+ position += m_speed * dtime;
setPosition(position);
m_sneak_node_exists = false;
return;
@@ -172,7 +171,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
f32 d = 0.15*BS;
// This should always apply, otherwise there are glitches
- assert(d > pos_max_d);
+ sanity_check(d > pos_max_d);
// Maximum distance over border for sneaking
f32 sneak_max = BS*0.4;
@@ -181,25 +180,26 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
If sneaking, keep in range from the last walked node and don't
fall off from it
*/
- if(control.sneak && m_sneak_node_exists &&
+ if (control.sneak && m_sneak_node_exists &&
!(fly_allowed && g_settings->getBool("free_move")) && !in_liquid &&
- physics_override_sneak)
- {
- f32 maxd = 0.5*BS + sneak_max;
+ physics_override_sneak) {
+ f32 maxd = 0.5 * BS + sneak_max;
v3f lwn_f = intToFloat(m_sneak_node, BS);
position.X = rangelim(position.X, lwn_f.X-maxd, lwn_f.X+maxd);
position.Z = rangelim(position.Z, lwn_f.Z-maxd, lwn_f.Z+maxd);
- if(!is_climbing)
- {
- f32 min_y = lwn_f.Y + 0.5*BS;
- if(position.Y < min_y)
- {
- position.Y = min_y;
-
- if(m_speed.Y < 0)
- m_speed.Y = 0;
- }
+ if (!is_climbing) {
+ // Move up if necessary
+ f32 new_y = (lwn_f.Y - 0.5 * BS) + m_sneak_node_bb_ymax;
+ if (position.Y < new_y)
+ position.Y = new_y;
+ /*
+ Collision seems broken, since player is sinking when
+ sneaking over the edges of current sneaking_node.
+ TODO (when fixed): Set Y-speed only to 0 when position.Y < new_y.
+ */
+ if (m_speed.Y < 0)
+ m_speed.Y = 0;
}
}
@@ -232,27 +232,28 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
player is sneaking from, if any. If the node from under
the player has been removed, the player falls.
*/
- v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS);
- if(m_sneak_node_exists &&
- nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
- m_old_node_below_type != "air")
- {
+ f32 position_y_mod = 0.05 * BS;
+ if (m_sneak_node_bb_ymax > 0)
+ position_y_mod = m_sneak_node_bb_ymax - position_y_mod;
+ v3s16 current_node = floatToInt(position - v3f(0, position_y_mod, 0), BS);
+ if (m_sneak_node_exists &&
+ nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" &&
+ m_old_node_below_type != "air") {
// Old node appears to have been removed; that is,
// it wasn't air before but now it is
m_need_to_get_new_sneak_node = false;
m_sneak_node_exists = false;
- }
- else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air")
- {
+ } else if (nodemgr->get(map->getNodeNoEx(current_node)).name != "air") {
// We are on something, so make sure to recalculate the sneak
// node.
m_need_to_get_new_sneak_node = true;
}
- if(m_need_to_get_new_sneak_node && physics_override_sneak)
- {
- v3s16 pos_i_bottom = floatToInt(position - v3f(0,BS/2,0), BS);
+
+ if (m_need_to_get_new_sneak_node && physics_override_sneak) {
+ m_sneak_node_bb_ymax = 0;
+ v3s16 pos_i_bottom = floatToInt(position - v3f(0, position_y_mod, 0), BS);
v2f player_p2df(position.X, position.Z);
- f32 min_distance_f = 100000.0*BS;
+ f32 min_distance_f = 100000.0 * BS;
// If already seeking from some node, compare to it.
/*if(m_sneak_node_exists)
{
@@ -300,11 +301,24 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
new_sneak_node = p;
}
- bool sneak_node_found = (min_distance_f < 100000.0*BS*0.9);
+ bool sneak_node_found = (min_distance_f < 100000.0 * BS * 0.9);
m_sneak_node = new_sneak_node;
m_sneak_node_exists = sneak_node_found;
+ if (sneak_node_found) {
+ f32 cb_max = 0;
+ MapNode n = map->getNodeNoEx(m_sneak_node);
+ std::vector<aabb3f> nodeboxes = n.getCollisionBoxes(nodemgr);
+ for (std::vector<aabb3f>::iterator it = nodeboxes.begin();
+ it != nodeboxes.end(); ++it) {
+ aabb3f box = *it;
+ if (box.MaxEdge.Y > cb_max)
+ cb_max = box.MaxEdge.Y;
+ }
+ m_sneak_node_bb_ymax = cb_max;
+ }
+
/*
If sneaking, the player's collision box can be in air, so
this has to be set explicitly
@@ -321,26 +335,15 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
/*
Report collisions
*/
- bool bouncy_jump = false;
+
// Dont report if flying
- if(collision_info && !(g_settings->getBool("free_move") && fly_allowed))
- {
- for(size_t i=0; i<result.collisions.size(); i++){
+ if(collision_info && !(g_settings->getBool("free_move") && fly_allowed)) {
+ for(size_t i=0; i<result.collisions.size(); i++) {
const CollisionInfo &info = result.collisions[i];
collision_info->push_back(info);
- if(info.new_speed.Y - info.old_speed.Y > 0.1*BS &&
- info.bouncy)
- bouncy_jump = true;
}
}
- if(bouncy_jump && control.jump){
- m_speed.Y += movement_speed_jump*BS;
- touching_ground = false;
- MtEvent *e = new SimpleTriggerEvent("PlayerJump");
- m_gamedef->event()->put(e);
- }
-
if(!touching_ground_was && touching_ground){
MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround");
m_gamedef->event()->put(e);
@@ -374,6 +377,19 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d,
m_can_jump = touching_ground && !in_liquid;
if(itemgroup_get(f.groups, "disable_jump"))
m_can_jump = false;
+ // Jump key pressed while jumping off from a bouncy block
+ if (m_can_jump && control.jump && itemgroup_get(f.groups, "bouncy") &&
+ m_speed.Y >= -0.5 * BS) {
+ float jumpspeed = movement_speed_jump * physics_override_jump;
+ if (m_speed.Y > 1) {
+ // Reduce boost when speed already is high
+ m_speed.Y += jumpspeed / (1 + (m_speed.Y / 16 ));
+ } else {
+ m_speed.Y += jumpspeed;
+ }
+ setSpeed(m_speed);
+ m_can_jump = false;
+ }
}
void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d)
@@ -410,11 +426,12 @@ void LocalPlayer::applyControl(float dtime)
// When aux1_descends is enabled the fast key is used to go down, so fast isn't possible
bool fast_climb = fast_move && control.aux1 && !g_settings->getBool("aux1_descends");
bool continuous_forward = g_settings->getBool("continuous_forward");
+ bool always_fly_fast = g_settings->getBool("always_fly_fast");
// Whether superspeed mode is used or not
bool superspeed = false;
- if(g_settings->getBool("always_fly_fast") && free_move && fast_move)
+ if (always_fly_fast && free_move && fast_move)
superspeed = true;
// Old descend control
@@ -472,7 +489,7 @@ void LocalPlayer::applyControl(float dtime)
if(free_move)
{
// In free movement mode, sneak descends
- if(fast_move && (control.aux1 || g_settings->getBool("always_fly_fast")))
+ if (fast_move && (control.aux1 || always_fly_fast))
speedV.Y = -movement_speed_fast;
else
speedV.Y = -movement_speed_walk;
@@ -519,11 +536,9 @@ void LocalPlayer::applyControl(float dtime)
}
if(control.jump)
{
- if(free_move)
- {
- if(g_settings->getBool("aux1_descends") || g_settings->getBool("always_fly_fast"))
- {
- if(fast_move)
+ if (free_move) {
+ if (g_settings->getBool("aux1_descends") || always_fly_fast) {
+ if (fast_move)
speedV.Y = movement_speed_fast;
else
speedV.Y = movement_speed_walk;
diff --git a/src/localplayer.h b/src/localplayer.h
index 16b66716d..40a7f089e 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -48,7 +48,7 @@ public:
void move(f32 dtime, Environment *env, f32 pos_max_d);
void move(f32 dtime, Environment *env, f32 pos_max_d,
- std::list<CollisionInfo> *collision_info);
+ std::vector<CollisionInfo> *collision_info);
void applyControl(float dtime);
@@ -62,8 +62,6 @@ public:
unsigned int last_keyPressed;
float camera_impact;
- v3f eye_offset_first;
- v3f eye_offset_third;
int last_animation;
float last_animation_speed;
@@ -78,7 +76,7 @@ public:
}
void setCAO(GenericCAO* toset) {
- assert( m_cao == NULL );
+ assert( m_cao == NULL ); // Pre-condition
m_cao = toset;
}
@@ -87,12 +85,15 @@ private:
v3s16 m_sneak_node;
// Whether the player is allowed to sneak
bool m_sneak_node_exists;
+ // Whether recalculation of the sneak node is needed
+ bool m_need_to_get_new_sneak_node;
+ // Stores the max player uplift by m_sneak_node and is updated
+ // when m_need_to_get_new_sneak_node == true
+ f32 m_sneak_node_bb_ymax;
// Node below player, used to determine whether it has been removed,
// and its old type
v3s16 m_old_node_below;
std::string m_old_node_below_type;
- // Whether recalculation of the sneak node is needed
- bool m_need_to_get_new_sneak_node;
bool m_can_jump;
GenericCAO* m_cao;
diff --git a/src/log.cpp b/src/log.cpp
index b3b3f3f1b..e6d80db34 100644
--- a/src/log.cpp
+++ b/src/log.cpp
@@ -24,13 +24,28 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
#include <algorithm>
#include "threads.h"
+#include "jthread/jmutexautolock.h"
#include "debug.h"
#include "gettime.h"
#include "porting.h"
#include "config.h"
+// Connection
+std::ostream *dout_con_ptr = &dummyout;
+std::ostream *derr_con_ptr = &verbosestream;
+
+// Server
+std::ostream *dout_server_ptr = &infostream;
+std::ostream *derr_server_ptr = &errorstream;
+
+#ifndef SERVER
+// Client
+std::ostream *dout_client_ptr = &infostream;
+std::ostream *derr_client_ptr = &errorstream;
+#endif
+
#ifdef __ANDROID__
-unsigned int android_log_level_mapping[] {
+unsigned int android_log_level_mapping[] = {
/* LMT_ERROR */ ANDROID_LOG_ERROR,
/* LMT_ACTION */ ANDROID_LOG_WARN,
/* LMT_INFO */ ANDROID_LOG_INFO,
@@ -38,7 +53,7 @@ unsigned int android_log_level_mapping[] {
};
#endif
-std::list<ILogOutput*> log_outputs[LMT_NUM_VALUES];
+std::vector<ILogOutput*> log_outputs[LMT_NUM_VALUES];
std::map<threadid_t, std::string> log_threadnames;
JMutex log_threadnamemutex;
@@ -62,7 +77,7 @@ void log_add_output_all_levs(ILogOutput *out)
void log_remove_output(ILogOutput *out)
{
for(int i=0; i<LMT_NUM_VALUES; i++){
- std::list<ILogOutput*>::iterator it =
+ std::vector<ILogOutput*>::iterator it =
std::find(log_outputs[i].begin(), log_outputs[i].end(), out);
if(it != log_outputs[i].end())
log_outputs[i].erase(it);
@@ -71,33 +86,29 @@ void log_remove_output(ILogOutput *out)
void log_set_lev_silence(enum LogMessageLevel lev, bool silence)
{
- log_threadnamemutex.Lock();
+ JMutexAutoLock lock(log_threadnamemutex);
- for (std::list<ILogOutput *>::iterator
- it = log_outputs[lev].begin();
- it != log_outputs[lev].end();
- ++it) {
+ for (std::vector<ILogOutput *>::iterator it = log_outputs[lev].begin();
+ it != log_outputs[lev].end(); ++it) {
ILogOutput *out = *it;
out->silence = silence;
}
-
- log_threadnamemutex.Unlock();
}
void log_register_thread(const std::string &name)
{
threadid_t id = get_current_thread_id();
- log_threadnamemutex.Lock();
+ JMutexAutoLock lock(log_threadnamemutex);
+
log_threadnames[id] = name;
- log_threadnamemutex.Unlock();
}
void log_deregister_thread()
{
threadid_t id = get_current_thread_id();
- log_threadnamemutex.Lock();
+ JMutexAutoLock lock(log_threadnamemutex);
+
log_threadnames.erase(id);
- log_threadnamemutex.Unlock();
}
static std::string get_lev_string(enum LogMessageLevel lev)
@@ -119,7 +130,7 @@ static std::string get_lev_string(enum LogMessageLevel lev)
void log_printline(enum LogMessageLevel lev, const std::string &text)
{
- log_threadnamemutex.Lock();
+ JMutexAutoLock lock(log_threadnamemutex);
std::string threadname = "(unknown thread)";
std::map<threadid_t, std::string>::const_iterator i;
i = log_threadnames.find(get_current_thread_id());
@@ -127,9 +138,10 @@ void log_printline(enum LogMessageLevel lev, const std::string &text)
threadname = i->second;
std::string levelname = get_lev_string(lev);
std::ostringstream os(std::ios_base::binary);
- os<<getTimestamp()<<": "<<levelname<<"["<<threadname<<"]: "<<text;
- for(std::list<ILogOutput*>::iterator i = log_outputs[lev].begin();
- i != log_outputs[lev].end(); i++){
+ os << getTimestamp() << ": " << levelname << "["<<threadname<<"]: " << text;
+
+ for(std::vector<ILogOutput*>::iterator i = log_outputs[lev].begin();
+ i != log_outputs[lev].end(); i++) {
ILogOutput *out = *i;
if (out->silence)
continue;
@@ -138,7 +150,6 @@ void log_printline(enum LogMessageLevel lev, const std::string &text)
out->printLog(os.str(), lev);
out->printLog(lev, text);
}
- log_threadnamemutex.Unlock();
}
class Logbuf : public std::streambuf
diff --git a/src/log.h b/src/log.h
index 6240e34ed..bd223927a 100644
--- a/src/log.h
+++ b/src/log.h
@@ -81,5 +81,22 @@ extern bool log_trace_level_enabled;
#define TRACESTREAM(x){ if(log_trace_level_enabled) verbosestream x; }
#define TRACEDO(x){ if(log_trace_level_enabled){ x ;} }
+extern std::ostream *dout_con_ptr;
+extern std::ostream *derr_con_ptr;
+extern std::ostream *dout_server_ptr;
+extern std::ostream *derr_server_ptr;
+#define dout_con (*dout_con_ptr)
+#define derr_con (*derr_con_ptr)
+#define dout_server (*dout_server_ptr)
+#define derr_server (*derr_server_ptr)
+
+#ifndef SERVER
+extern std::ostream *dout_client_ptr;
+extern std::ostream *derr_client_ptr;
+#define dout_client (*dout_client_ptr)
+#define derr_client (*derr_client_ptr)
+
+#endif
+
#endif
diff --git a/src/lua/CMakeLists.txt b/src/lua/CMakeLists.txt
index 36e271889..119dd6302 100644
--- a/src/lua/CMakeLists.txt
+++ b/src/lua/CMakeLists.txt
@@ -1,10 +1,12 @@
-#
-# Lua 5.1.x
-#
cmake_minimum_required(VERSION 2.4 FATAL_ERROR)
project(lua C)
+set(LUA_VERSION_MAJOR 5)
+set(LUA_VERSION_MINOR 1)
+set(LUA_VERSION_PATCH 4)
+set(LUA_VERSION "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
+
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
set(COMMON_CFLAGS)
@@ -16,9 +18,7 @@ if(APPLE)
set(DEFAULT_DLOPEN ON)
# use this on Mac OS X 10.3-
option(LUA_USE_MACOSX "Mac OS X 10.3-" OFF)
-elseif(CYGWIN)
- set(DEFAULT_POSIX TRUE)
-elseif(UNIX)
+elseif(UNIX OR CYGWIN)
set(DEFAULT_POSIX TRUE)
elseif(WIN32)
set(LUA_WIN TRUE)
@@ -32,54 +32,18 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(DEFAULT_DLOPEN ON)
endif()
-if(WIN32)
- #set(BUILD_STATIC OFF)
- set(BUILD_STATIC ON)
-else()
- #option(BUILD_STATIC "build static library" ON)
- set(BUILD_STATIC ON)
-endif()
-
-if(DEFAULT_DLOPEN)
- option(LUA_USE_DLOPEN "Enable dlopen support." ON)
-else()
- option(LUA_USE_DLOPEN "Enable dlopen support." OFF)
-endif()
-mark_as_advanced(LUA_USE_DLOPEN)
-
-if(DEFAULT_POSIX)
-else()
-endif()
-
-if(DEFAULT_ANSI)
- option(LUA_ANSI "Disable non-ansi features." ON)
-else()
- option(LUA_ANSI "Disable non-ansi features." OFF)
-endif()
-mark_as_advanced(LUA_ANSI)
-
-#
-# Lua version
-#
-set(LUA_VERSION_MAJOR 5)
-set(LUA_VERSION_MINOR 1)
-set(LUA_VERSION_PATCH 4)
-set(LUA_VERSION
- "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}")
-set(LUA_SOVERSION
- "${LUA_VERSION_MAJOR}")
-
-#
-# libs & cflags
-#
-set(COMMON_LDFLAGS "${COMMON_LDFLAGS}")
-
# For "Mac OS X 10.3-"
if(LUA_USE_MACOSX)
set(COMMON_CFLAGS "${COMMON_CFLAGS} -DLUA_USE_MACOSX")
set(LUA_USE_DLOPEN FALSE)
endif(LUA_USE_MACOSX)
+option(LUA_USE_DLOPEN "Enable dlopen support." ${DEFAULT_DLOPEN})
+mark_as_advanced(LUA_USE_DLOPEN)
+
+option(LUA_ANSI "Disable non-ANSI features." ${DEFAULT_ANSI})
+mark_as_advanced(LUA_ANSI)
+
if(LUA_USE_DLOPEN)
set(COMMON_CFLAGS "${COMMON_CFLAGS} -DLUA_USE_DLOPEN")
if(NOT APPLE)
@@ -87,18 +51,19 @@ if(LUA_USE_DLOPEN)
endif(NOT APPLE)
endif(LUA_USE_DLOPEN)
+if(DEFAULT_POSIX)
+ set(COMMON_CFLAGS "${COMMON_CFLAGS} -DLUA_USE_POSIX")
+endif(DEFAULT_POSIX)
+
if(LUA_ANSI)
set(COMMON_CFLAGS "${COMMON_CFLAGS} -DLUA_ANSI")
endif(LUA_ANSI)
-#
# COMMON_CFLAGS has no effect without this line
-#
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_CFLAGS}")
-#
-# standard flags to use for each build type.
-#
+
+# Standard flags to use for each build type.
if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -Wall -Wextra -Wshadow -W -pedantic -std=gnu99")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2")
@@ -107,8 +72,6 @@ if(CMAKE_COMPILER_IS_GNUCC)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_WITHDEBINFO} -O2 -g")
endif(CMAKE_COMPILER_IS_GNUCC)
-#
-# sub-folders
-#
-ADD_SUBDIRECTORY(src build)
+
+add_subdirectory(src build)
diff --git a/src/lua/src/CMakeLists.txt b/src/lua/src/CMakeLists.txt
index 8fdc7e58b..8f6cc1213 100644
--- a/src/lua/src/CMakeLists.txt
+++ b/src/lua/src/CMakeLists.txt
@@ -39,11 +39,9 @@ set(LUA_LIB_HEADERS
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_BINARY_DIR})
+ ${CMAKE_CURRENT_BINARY_DIR})
-#
# Lua library.
-#
add_library(lua STATIC ${LUA_CORE_SRC})
target_link_libraries(lua ${LIBS})
set(LUA_STATIC_LIB lua)
@@ -51,13 +49,6 @@ set(LUA_LIBS lua)
set_target_properties(${LUA_LIBS} PROPERTIES
VERSION ${LUA_VERSION}
- SOVERSION ${LUA_SOVERSION}
CLEAN_DIRECT_OUTPUT 1
)
-# Install library
-#install(TARGETS ${LUA_LIBS}
-# RUNTIME DESTINATION bin
-# LIBRARY DESTINATION lib
-# ARCHIVE DESTINATION lib)
-
diff --git a/src/lua/src/lauxlib.c b/src/lua/src/lauxlib.c
index 10f14e2c0..be41ebcd3 100644
--- a/src/lua/src/lauxlib.c
+++ b/src/lua/src/lauxlib.c
@@ -574,7 +574,8 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
- while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
+ while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0])
+ {}
lf.extraline = 0;
}
ungetc(c, lf.f);
diff --git a/src/main.cpp b/src/main.cpp
index bbf88e80d..1d73b4025 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -17,19 +17,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifdef NDEBUG
- /*#ifdef _WIN32
- #pragma message ("Disabling unit tests")
- #else
- #warning "Disabling unit tests"
- #endif*/
- // Disable unit tests
- #define ENABLE_TESTS 0
-#else
- // Enable unit tests
- #define ENABLE_TESTS 1
-#endif
-
#ifdef _MSC_VER
#ifndef SERVER // Dedicated server isn't linked with Irrlicht
#pragma comment(lib, "Irrlicht.lib")
@@ -42,101 +29,39 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlicht.h" // createDevice
-#include "main.h"
#include "mainmenumanager.h"
-#include <iostream>
-#include <fstream>
-#include <locale.h>
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
-#include "test.h"
-#include "clouds.h"
+#include "unittest/test.h"
#include "server.h"
-#include "constants.h"
-#include "porting.h"
-#include "gettime.h"
#include "filesys.h"
-#include "config.h"
#include "version.h"
#include "guiMainMenu.h"
#include "game.h"
-#include "keycode.h"
-#include "tile.h"
-#include "chat.h"
#include "defaultsettings.h"
#include "gettext.h"
-#include "settings.h"
-#include "profiler.h"
#include "log.h"
-#include "mods.h"
-#include "util/string.h"
-#include "subgame.h"
#include "quicktune.h"
-#include "serverlist.h"
#include "httpfetch.h"
#include "guiEngine.h"
+#include "map.h"
#include "mapsector.h"
-#include "player.h"
#include "fontengine.h"
-
-#include "database-sqlite3.h"
-#ifdef USE_LEVELDB
-#include "database-leveldb.h"
-#endif
-
-#if USE_REDIS
-#include "database-redis.h"
+#include "gameparams.h"
+#include "database.h"
+#ifndef SERVER
+#include "client/clientlauncher.h"
#endif
#ifdef HAVE_TOUCHSCREENGUI
#include "touchscreengui.h"
#endif
-/*
- Settings.
- These are loaded from the config file.
-*/
-static Settings main_settings;
-Settings *g_settings = &main_settings;
-std::string g_settings_path;
-
-// Global profiler
-Profiler main_profiler;
-Profiler *g_profiler = &main_profiler;
-
-// Menu clouds are created later
-Clouds *g_menuclouds = 0;
-irr::scene::ISceneManager *g_menucloudsmgr = 0;
-
-/*
- Debug streams
-*/
-
-// Connection
-std::ostream *dout_con_ptr = &dummyout;
-std::ostream *derr_con_ptr = &verbosestream;
-
-// Server
-std::ostream *dout_server_ptr = &infostream;
-std::ostream *derr_server_ptr = &errorstream;
-
-// Client
-std::ostream *dout_client_ptr = &infostream;
-std::ostream *derr_client_ptr = &errorstream;
-
#define DEBUGFILE "debug.txt"
#define DEFAULT_SERVER_PORT 30000
typedef std::map<std::string, ValueSpec> OptionList;
-struct GameParams {
- u16 socket_port;
- std::string world_path;
- SubgameSpec game_spec;
- bool is_dedicated_server;
- int log_level;
-};
-
/**********************************************************************
* Private functions
**********************************************************************/
@@ -174,36 +99,10 @@ static bool get_game_from_cmdline(GameParams *game_params, const Settings &cmd_a
static bool determine_subgame(GameParams *game_params);
static bool run_dedicated_server(const GameParams &game_params, const Settings &cmd_args);
-static bool migrate_database(const GameParams &game_params, const Settings &cmd_args,
- Server *server);
-
-#ifndef SERVER
-static bool print_video_modes();
-static void speed_tests();
-#endif
+static bool migrate_database(const GameParams &game_params, const Settings &cmd_args);
/**********************************************************************/
-#ifndef SERVER
-/*
- Random stuff
-*/
-
-/* mainmenumanager.h */
-
-gui::IGUIEnvironment* guienv = NULL;
-gui::IGUIStaticText *guiroot = NULL;
-MainMenuManager g_menumgr;
-
-bool noMenuActive()
-{
- return (g_menumgr.menuCount() == 0);
-}
-
-// Passed to menus to allow disconnecting and exiting
-MainGameCallback *g_gamecallback = NULL;
-#endif
-
/*
gettime.h implementation
*/
@@ -221,61 +120,6 @@ u32 getTime(TimePrecision prec)
return porting::getTime(prec);
}
-#else
-
-// A small helper class
-class TimeGetter
-{
-public:
- virtual u32 getTime(TimePrecision prec) = 0;
-};
-
-// A precise irrlicht one
-class IrrlichtTimeGetter: public TimeGetter
-{
-public:
- IrrlichtTimeGetter(IrrlichtDevice *device):
- m_device(device)
- {}
- u32 getTime(TimePrecision prec)
- {
- if (prec == PRECISION_MILLI) {
- if (m_device == NULL)
- return 0;
- return m_device->getTimer()->getRealTime();
- } else {
- return porting::getTime(prec);
- }
- }
-private:
- IrrlichtDevice *m_device;
-};
-// Not so precise one which works without irrlicht
-class SimpleTimeGetter: public TimeGetter
-{
-public:
- u32 getTime(TimePrecision prec)
- {
- return porting::getTime(prec);
- }
-};
-
-// A pointer to a global instance of the time getter
-// TODO: why?
-TimeGetter *g_timegetter = NULL;
-
-u32 getTimeMs()
-{
- if (g_timegetter == NULL)
- return 0;
- return g_timegetter->getTime(PRECISION_MILLI);
-}
-
-u32 getTime(TimePrecision prec) {
- if (g_timegetter == NULL)
- return 0;
- return g_timegetter->getTime(prec);
-}
#endif
class StderrLogOutput: public ILogOutput
@@ -298,482 +142,6 @@ public:
}
} main_dstream_no_stderr_log_out;
-#ifndef SERVER
-
-/*
- Event handler for Irrlicht
-
- NOTE: Everything possible should be moved out from here,
- probably to InputHandler and the_game
-*/
-
-class MyEventReceiver : public IEventReceiver
-{
-public:
- // This is the one method that we have to implement
- virtual bool OnEvent(const SEvent& event)
- {
- /*
- React to nothing here if a menu is active
- */
- if (noMenuActive() == false) {
-#ifdef HAVE_TOUCHSCREENGUI
- if (m_touchscreengui != 0) {
- m_touchscreengui->Toggle(false);
- }
-#endif
- return g_menumgr.preprocessEvent(event);
- }
-
- // Remember whether each key is down or up
- if (event.EventType == irr::EET_KEY_INPUT_EVENT) {
- if (event.KeyInput.PressedDown) {
- keyIsDown.set(event.KeyInput);
- keyWasDown.set(event.KeyInput);
- } else {
- keyIsDown.unset(event.KeyInput);
- }
- }
-
-#ifdef HAVE_TOUCHSCREENGUI
- // case of touchscreengui we have to handle different events
- if ((m_touchscreengui != 0) &&
- (event.EventType == irr::EET_TOUCH_INPUT_EVENT)) {
- m_touchscreengui->translateEvent(event);
- return true;
- }
-#endif
- // handle mouse events
- if (event.EventType == irr::EET_MOUSE_INPUT_EVENT) {
- if (noMenuActive() == false) {
- left_active = false;
- middle_active = false;
- right_active = false;
- } else {
- left_active = event.MouseInput.isLeftPressed();
- middle_active = event.MouseInput.isMiddlePressed();
- right_active = event.MouseInput.isRightPressed();
-
- if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
- leftclicked = true;
- }
- if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) {
- rightclicked = true;
- }
- if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
- leftreleased = true;
- }
- if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) {
- rightreleased = true;
- }
- if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
- mouse_wheel += event.MouseInput.Wheel;
- }
- }
- }
- if (event.EventType == irr::EET_LOG_TEXT_EVENT) {
- dstream << std::string("Irrlicht log: ") + std::string(event.LogEvent.Text)
- << std::endl;
- return true;
- }
- /* always return false in order to continue processing events */
- return false;
- }
-
- bool IsKeyDown(const KeyPress &keyCode) const
- {
- return keyIsDown[keyCode];
- }
-
- // Checks whether a key was down and resets the state
- bool WasKeyDown(const KeyPress &keyCode)
- {
- bool b = keyWasDown[keyCode];
- if (b)
- keyWasDown.unset(keyCode);
- return b;
- }
-
- s32 getMouseWheel()
- {
- s32 a = mouse_wheel;
- mouse_wheel = 0;
- return a;
- }
-
- void clearInput()
- {
- keyIsDown.clear();
- keyWasDown.clear();
-
- leftclicked = false;
- rightclicked = false;
- leftreleased = false;
- rightreleased = false;
-
- left_active = false;
- middle_active = false;
- right_active = false;
-
- mouse_wheel = 0;
- }
-
- MyEventReceiver()
- {
- clearInput();
-#ifdef HAVE_TOUCHSCREENGUI
- m_touchscreengui = NULL;
-#endif
- }
-
- bool leftclicked;
- bool rightclicked;
- bool leftreleased;
- bool rightreleased;
-
- bool left_active;
- bool middle_active;
- bool right_active;
-
- s32 mouse_wheel;
-
-#ifdef HAVE_TOUCHSCREENGUI
- TouchScreenGUI* m_touchscreengui;
-#endif
-
-private:
- // The current state of keys
- KeyList keyIsDown;
- // Whether a key has been pressed or not
- KeyList keyWasDown;
-};
-
-/*
- Separated input handler
-*/
-
-class RealInputHandler : public InputHandler
-{
-public:
- RealInputHandler(IrrlichtDevice *device, MyEventReceiver *receiver):
- m_device(device),
- m_receiver(receiver),
- m_mousepos(0,0)
- {
- }
- virtual bool isKeyDown(const KeyPress &keyCode)
- {
- return m_receiver->IsKeyDown(keyCode);
- }
- virtual bool wasKeyDown(const KeyPress &keyCode)
- {
- return m_receiver->WasKeyDown(keyCode);
- }
- virtual v2s32 getMousePos()
- {
- if (m_device->getCursorControl()) {
- return m_device->getCursorControl()->getPosition();
- }
- else {
- return m_mousepos;
- }
- }
- virtual void setMousePos(s32 x, s32 y)
- {
- if (m_device->getCursorControl()) {
- m_device->getCursorControl()->setPosition(x, y);
- }
- else {
- m_mousepos = v2s32(x,y);
- }
- }
-
- virtual bool getLeftState()
- {
- return m_receiver->left_active;
- }
- virtual bool getRightState()
- {
- return m_receiver->right_active;
- }
-
- virtual bool getLeftClicked()
- {
- return m_receiver->leftclicked;
- }
- virtual bool getRightClicked()
- {
- return m_receiver->rightclicked;
- }
- virtual void resetLeftClicked()
- {
- m_receiver->leftclicked = false;
- }
- virtual void resetRightClicked()
- {
- m_receiver->rightclicked = false;
- }
-
- virtual bool getLeftReleased()
- {
- return m_receiver->leftreleased;
- }
- virtual bool getRightReleased()
- {
- return m_receiver->rightreleased;
- }
- virtual void resetLeftReleased()
- {
- m_receiver->leftreleased = false;
- }
- virtual void resetRightReleased()
- {
- m_receiver->rightreleased = false;
- }
-
- virtual s32 getMouseWheel()
- {
- return m_receiver->getMouseWheel();
- }
-
- void clear()
- {
- m_receiver->clearInput();
- }
-private:
- IrrlichtDevice *m_device;
- MyEventReceiver *m_receiver;
- v2s32 m_mousepos;
-};
-
-class RandomInputHandler : public InputHandler
-{
-public:
- RandomInputHandler()
- {
- leftdown = false;
- rightdown = false;
- leftclicked = false;
- rightclicked = false;
- leftreleased = false;
- rightreleased = false;
- keydown.clear();
- }
- virtual bool isKeyDown(const KeyPress &keyCode)
- {
- return keydown[keyCode];
- }
- virtual bool wasKeyDown(const KeyPress &keyCode)
- {
- return false;
- }
- virtual v2s32 getMousePos()
- {
- return mousepos;
- }
- virtual void setMousePos(s32 x, s32 y)
- {
- mousepos = v2s32(x, y);
- }
-
- virtual bool getLeftState()
- {
- return leftdown;
- }
- virtual bool getRightState()
- {
- return rightdown;
- }
-
- virtual bool getLeftClicked()
- {
- return leftclicked;
- }
- virtual bool getRightClicked()
- {
- return rightclicked;
- }
- virtual void resetLeftClicked()
- {
- leftclicked = false;
- }
- virtual void resetRightClicked()
- {
- rightclicked = false;
- }
-
- virtual bool getLeftReleased()
- {
- return leftreleased;
- }
- virtual bool getRightReleased()
- {
- return rightreleased;
- }
- virtual void resetLeftReleased()
- {
- leftreleased = false;
- }
- virtual void resetRightReleased()
- {
- rightreleased = false;
- }
-
- virtual s32 getMouseWheel()
- {
- return 0;
- }
-
- virtual void step(float dtime)
- {
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_jump"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_special1"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_forward"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 40);
- keydown.toggle(getKeySetting("keymap_left"));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 20);
- mousespeed = v2s32(Rand(-20, 20), Rand(-15, 20));
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 30);
- leftdown = !leftdown;
- if (leftdown)
- leftclicked = true;
- if (!leftdown)
- leftreleased = true;
- }
- }
- {
- static float counter1 = 0;
- counter1 -= dtime;
- if (counter1 < 0.0) {
- counter1 = 0.1 * Rand(1, 15);
- rightdown = !rightdown;
- if (rightdown)
- rightclicked = true;
- if (!rightdown)
- rightreleased = true;
- }
- }
- mousepos += mousespeed;
- }
-
- s32 Rand(s32 min, s32 max)
- {
- return (myrand()%(max-min+1))+min;
- }
-private:
- KeyList keydown;
- v2s32 mousepos;
- v2s32 mousespeed;
- bool leftdown;
- bool rightdown;
- bool leftclicked;
- bool rightclicked;
- bool leftreleased;
- bool rightreleased;
-};
-
-
-class ClientLauncher
-{
-public:
- ClientLauncher() :
- list_video_modes(false),
- skip_main_menu(false),
- use_freetype(false),
- random_input(false),
- address(""),
- playername(""),
- password(""),
- device(NULL),
- input(NULL),
- receiver(NULL),
- skin(NULL),
- font(NULL),
- simple_singleplayer_mode(false),
- current_playername("inv£lid"),
- current_password(""),
- current_address("does-not-exist"),
- current_port(0)
- {}
-
- ~ClientLauncher();
-
- bool run(GameParams &game_params, const Settings &cmd_args);
-
-protected:
- void init_args(GameParams &game_params, const Settings &cmd_args);
- bool init_engine(int log_level);
-
- bool launch_game(std::wstring *error_message, GameParams &game_params,
- const Settings &cmd_args);
-
- void main_menu(MainMenuData *menudata);
- bool create_engine_device(int log_level);
-
- bool list_video_modes;
- bool skip_main_menu;
- bool use_freetype;
- bool random_input;
- std::string address;
- std::string playername;
- std::string password;
- IrrlichtDevice *device;
- InputHandler *input;
- MyEventReceiver *receiver;
- gui::IGUISkin *skin;
- gui::IGUIFont *font;
- scene::ISceneManager *smgr;
- SubgameSpec gamespec;
- WorldSpec worldspec;
- bool simple_singleplayer_mode;
-
- // These are set up based on the menu and other things
- // TODO: Are these required since there's already playername, password, etc
- std::string current_playername;
- std::string current_password;
- std::string current_address;
- int current_port;
-};
-
-#endif // !SERVER
-
static OptionList allowed_options;
int main(int argc, char *argv[])
@@ -836,9 +204,9 @@ int main(int argc, char *argv[])
#ifndef __ANDROID__
// Run unit tests
- if ((ENABLE_TESTS && cmd_args.getFlag("disable-unittests") == false)
- || cmd_args.getFlag("enable-unittests") == true) {
+ if (cmd_args.getFlag("run-unittests")) {
run_tests();
+ return 0;
}
#endif
@@ -851,7 +219,7 @@ int main(int argc, char *argv[])
if (!game_configure(&game_params, cmd_args))
return 1;
- assert(game_params.world_path != "");
+ sanity_check(game_params.world_path != "");
infostream << "Using commanded world path ["
<< game_params.world_path << "]" << std::endl;
@@ -909,10 +277,8 @@ static void set_allowed_options(OptionList *allowed_options)
_("Load configuration from specified file"))));
allowed_options->insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING,
_("Set network port (UDP)"))));
- allowed_options->insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG,
- _("Disable unit tests"))));
- allowed_options->insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG,
- _("Enable unit tests"))));
+ allowed_options->insert(std::make_pair("run-unittests", ValueSpec(VALUETYPE_FLAG,
+ _("Run the unit tests and exit"))));
allowed_options->insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING,
_("Same as --world (deprecated)"))));
allowed_options->insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING,
@@ -980,13 +346,11 @@ static void print_allowed_options(const OptionList &allowed_options)
static void print_version()
{
-#ifdef SERVER
- dstream << "minetestserver " << minetest_version_hash << std::endl;
-#else
- dstream << "Minetest " << minetest_version_hash << std::endl;
+ dstream << PROJECT_NAME_C " " << g_version_hash << std::endl;
+#ifndef SERVER
dstream << "Using Irrlicht " << IRRLICHT_SDK_VERSION << std::endl;
#endif
- dstream << "Build info: " << minetest_build_info << std::endl;
+ dstream << "Build info: " << g_build_info << std::endl;
}
static void list_game_ids()
@@ -1123,13 +487,13 @@ static void startup_message()
infostream << PROJECT_NAME << " " << _("with")
<< " SER_FMT_VER_HIGHEST_READ="
<< (int)SER_FMT_VER_HIGHEST_READ << ", "
- << minetest_build_info << std::endl;
+ << g_build_info << std::endl;
}
static bool read_config_file(const Settings &cmd_args)
{
// Path of configuration file in use
- assert(g_settings_path == ""); // Sanity check
+ sanity_check(g_settings_path == ""); // Sanity check
if (cmd_args.exists("config")) {
bool r = g_settings->readConfigFile(cmd_args.get("config").c_str());
@@ -1328,7 +692,7 @@ static bool auto_select_world(GameParams *game_params)
<< world_path << "]" << std::endl;
}
- assert(world_path != "");
+ assert(world_path != ""); // Post-condition
game_params->world_path = world_path;
return true;
}
@@ -1384,7 +748,7 @@ static bool determine_subgame(GameParams *game_params)
{
SubgameSpec gamespec;
- assert(game_params->world_path != ""); // pre-condition
+ assert(game_params->world_path != ""); // Pre-condition
verbosestream << _("Determining gameid/gamespec") << std::endl;
// If world doesn't exist
@@ -1466,14 +830,14 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
return false;
}
+ // Database migration
+ if (cmd_args.exists("migrate"))
+ return migrate_database(game_params, cmd_args);
+
// Create server
Server server(game_params.world_path,
game_params.game_spec, false, bind_addr.isIPv6());
- // Database migration
- if (cmd_args.exists("migrate"))
- return migrate_database(game_params, cmd_args, &server);
-
server.start(bind_addr);
// Run server
@@ -1483,79 +847,63 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
return true;
}
-static bool migrate_database(const GameParams &game_params, const Settings &cmd_args,
- Server *server)
+static bool migrate_database(const GameParams &game_params, const Settings &cmd_args)
{
+ std::string migrate_to = cmd_args.get("migrate");
Settings world_mt;
- bool success = world_mt.readConfigFile((game_params.world_path
- + DIR_DELIM + "world.mt").c_str());
- if (!success) {
- errorstream << "Cannot read world.mt" << std::endl;
+ std::string world_mt_path = game_params.world_path + DIR_DELIM + "world.mt";
+ if (!world_mt.readConfigFile(world_mt_path.c_str())) {
+ errorstream << "Cannot read world.mt!" << std::endl;
return false;
}
-
if (!world_mt.exists("backend")) {
- errorstream << "Please specify your current backend in world.mt file:"
- << std::endl << " backend = {sqlite3|leveldb|redis|dummy}"
- << std::endl;
+ errorstream << "Please specify your current backend in world.mt:"
+ << std::endl
+ << " backend = {sqlite3|leveldb|redis|dummy}"
+ << std::endl;
return false;
}
-
std::string backend = world_mt.get("backend");
- Database *new_db;
- std::string migrate_to = cmd_args.get("migrate");
-
if (backend == migrate_to) {
- errorstream << "Cannot migrate: new backend is same as the old one"
- << std::endl;
+ errorstream << "Cannot migrate: new backend is same"
+ << " as the old one" << std::endl;
return false;
}
+ Database *old_db = ServerMap::createDatabase(backend, game_params.world_path, world_mt),
+ *new_db = ServerMap::createDatabase(migrate_to, game_params.world_path, world_mt);
- if (migrate_to == "sqlite3")
- new_db = new Database_SQLite3(&(ServerMap&)server->getMap(),
- game_params.world_path);
-#if USE_LEVELDB
- else if (migrate_to == "leveldb")
- new_db = new Database_LevelDB(&(ServerMap&)server->getMap(),
- game_params.world_path);
-#endif
-#if USE_REDIS
- else if (migrate_to == "redis")
- new_db = new Database_Redis(&(ServerMap&)server->getMap(),
- game_params.world_path);
-#endif
- else {
- errorstream << "Migration to " << migrate_to << " is not supported"
- << std::endl;
- return false;
- }
+ u32 count = 0;
+ time_t last_update_time = 0;
+ bool &kill = *porting::signal_handler_killstatus();
- std::list<v3s16> blocks;
- ServerMap &old_map = ((ServerMap&)server->getMap());
- old_map.listAllLoadableBlocks(blocks);
- int count = 0;
+ std::vector<v3s16> blocks;
+ old_db->listAllLoadableBlocks(blocks);
new_db->beginSave();
- for (std::list<v3s16>::iterator i = blocks.begin(); i != blocks.end(); i++) {
- MapBlock *block = old_map.loadBlock(*i);
- if (!block) {
- errorstream << "Failed to load block " << PP(*i) << ", skipping it.";
+ for (std::vector<v3s16>::const_iterator it = blocks.begin(); it != blocks.end(); ++it) {
+ if (kill) return false;
+
+ const std::string &data = old_db->loadBlock(*it);
+ if (!data.empty()) {
+ new_db->saveBlock(*it, data);
} else {
- old_map.saveBlock(block, new_db);
- MapSector *sector = old_map.getSectorNoGenerate(v2s16(i->X, i->Z));
- sector->deleteBlock(block);
+ errorstream << "Failed to load block " << PP(*it) << ", skipping it." << std::endl;
+ }
+ if (++count % 0xFF == 0 && time(NULL) - last_update_time >= 1) {
+ std::cerr << " Migrated " << count << " blocks, "
+ << (100.0 * count / blocks.size()) << "% completed.\r";
+ new_db->endSave();
+ new_db->beginSave();
+ last_update_time = time(NULL);
}
- ++count;
- if (count % 500 == 0)
- actionstream << "Migrated " << count << " blocks "
- << (100.0 * count / blocks.size()) << "% completed" << std::endl;
}
+ std::cerr << std::endl;
new_db->endSave();
+ delete old_db;
delete new_db;
actionstream << "Successfully migrated " << count << " blocks" << std::endl;
world_mt.set("backend", migrate_to);
- if (!world_mt.updateConfigFile(
- (game_params.world_path+ DIR_DELIM + "world.mt").c_str()))
+ if (!world_mt.updateConfigFile(world_mt_path.c_str()))
errorstream << "Failed to update world.mt!" << std::endl;
else
actionstream << "world.mt updated" << std::endl;
@@ -1563,671 +911,3 @@ static bool migrate_database(const GameParams &game_params, const Settings &cmd_
return true;
}
-
-/*****************************************************************************
- * Client
- *****************************************************************************/
-#ifndef SERVER
-
-ClientLauncher::~ClientLauncher()
-{
- if (receiver)
- delete receiver;
-
- if (input)
- delete input;
-
- if (g_fontengine)
- delete g_fontengine;
-
- if (device)
- device->drop();
-}
-
-
-bool ClientLauncher::run(GameParams &game_params, const Settings &cmd_args)
-{
- init_args(game_params, cmd_args);
-
- // List video modes if requested
- if (list_video_modes)
- return print_video_modes();
-
- if (!init_engine(game_params.log_level)) {
- errorstream << "Could not initialize game engine." << std::endl;
- return false;
- }
-
- // Speed tests (done after irrlicht is loaded to get timer)
- if (cmd_args.getFlag("speedtests")) {
- dstream << "Running speed tests" << std::endl;
- speed_tests();
- return true;
- }
-
- video::IVideoDriver *video_driver = device->getVideoDriver();
- if (video_driver == NULL) {
- errorstream << "Could not initialize video driver." << std::endl;
- return false;
- }
-
- porting::setXorgClassHint(video_driver->getExposedVideoData(), "Minetest");
-
- /*
- This changes the minimum allowed number of vertices in a VBO.
- Default is 500.
- */
- //driver->setMinHardwareBufferVertexCount(50);
-
- // Create time getter
- g_timegetter = new IrrlichtTimeGetter(device);
-
- // Create game callback for menus
- g_gamecallback = new MainGameCallback(device);
-
- device->setResizable(true);
-
- if (random_input)
- input = new RandomInputHandler();
- else
- input = new RealInputHandler(device, receiver);
-
- smgr = device->getSceneManager();
-
- guienv = device->getGUIEnvironment();
- skin = guienv->getSkin();
- skin->setColor(gui::EGDC_BUTTON_TEXT, video::SColor(255, 255, 255, 255));
- skin->setColor(gui::EGDC_3D_HIGH_LIGHT, video::SColor(255, 0, 0, 0));
- skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255, 0, 0, 0));
- skin->setColor(gui::EGDC_HIGH_LIGHT, video::SColor(255, 70, 100, 50));
- skin->setColor(gui::EGDC_HIGH_LIGHT_TEXT, video::SColor(255, 255, 255, 255));
-
- g_fontengine = new FontEngine(g_settings, guienv);
- assert(g_fontengine != NULL);
-
-#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
- // Irrlicht 1.8 input colours
- skin->setColor(gui::EGDC_EDITABLE, video::SColor(255, 128, 128, 128));
- skin->setColor(gui::EGDC_FOCUSED_EDITABLE, video::SColor(255, 96, 134, 49));
-#endif
-
- // Create the menu clouds
- if (!g_menucloudsmgr)
- g_menucloudsmgr = smgr->createNewSceneManager();
- if (!g_menuclouds)
- g_menuclouds = new Clouds(g_menucloudsmgr->getRootSceneNode(),
- g_menucloudsmgr, -1, rand(), 100);
- g_menuclouds->update(v2f(0, 0), video::SColor(255, 200, 200, 255));
- scene::ICameraSceneNode* camera;
- camera = g_menucloudsmgr->addCameraSceneNode(0,
- v3f(0, 0, 0), v3f(0, 60, 100));
- camera->setFarValue(10000);
-
- /*
- GUI stuff
- */
-
- ChatBackend chat_backend;
-
- // If an error occurs, this is set to something by menu().
- // It is then displayed before the menu shows on the next call to menu()
- std::wstring error_message = L"";
-
- bool first_loop = true;
-
- /*
- Menu-game loop
- */
- bool retval = true;
- bool *kill = porting::signal_handler_killstatus();
-
- while (device->run() && !*kill && !g_gamecallback->shutdown_requested)
- {
- // Set the window caption
- const wchar_t *text = wgettext("Main Menu");
- device->setWindowCaption((std::wstring(L"Minetest [") + text + L"]").c_str());
- delete[] text;
-
- try { // This is used for catching disconnects
-
- guienv->clear();
-
- /*
- We need some kind of a root node to be able to add
- custom gui elements directly on the screen.
- Otherwise they won't be automatically drawn.
- */
- guiroot = guienv->addStaticText(L"", core::rect<s32>(0, 0, 10000, 10000));
-
- bool game_has_run = launch_game(&error_message, game_params, cmd_args);
-
- // If skip_main_menu, we only want to startup once
- if (skip_main_menu && !first_loop)
- break;
-
- first_loop = false;
-
- if (!game_has_run) {
- if (skip_main_menu)
- break;
- else
- continue;
- }
-
- // Break out of menu-game loop to shut down cleanly
- if (!device->run() || *kill) {
- if (g_settings_path != "")
- g_settings->updateConfigFile(g_settings_path.c_str());
- break;
- }
-
- if (current_playername.length() > PLAYERNAME_SIZE-1) {
- error_message = wgettext("Player name too long.");
- playername = current_playername.substr(0, PLAYERNAME_SIZE-1);
- g_settings->set("name", playername);
- continue;
- }
-
- device->getVideoDriver()->setTextureCreationFlag(
- video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
-
-#ifdef HAVE_TOUCHSCREENGUI
- receiver->m_touchscreengui = new TouchScreenGUI(device, receiver);
- g_touchscreengui = receiver->m_touchscreengui;
-#endif
- the_game(
- kill,
- random_input,
- input,
- device,
- worldspec.path,
- current_playername,
- current_password,
- current_address,
- current_port,
- error_message,
- chat_backend,
- gamespec,
- simple_singleplayer_mode
- );
- smgr->clear();
-
-#ifdef HAVE_TOUCHSCREENGUI
- delete g_touchscreengui;
- g_touchscreengui = NULL;
- receiver->m_touchscreengui = NULL;
-#endif
-
- } //try
- catch (con::PeerNotFoundException &e) {
- error_message = wgettext("Connection error (timed out?)");
- errorstream << wide_to_narrow(error_message) << std::endl;
- }
-
-#ifdef NDEBUG
- catch (std::exception &e) {
- std::string narrow_message = "Some exception: \"";
- narrow_message += e.what();
- narrow_message += "\"";
- errorstream << narrow_message << std::endl;
- error_message = narrow_to_wide(narrow_message);
- }
-#endif
-
- // If no main menu, show error and exit
- if (skip_main_menu) {
- if (error_message != L"") {
- verbosestream << "error_message = "
- << wide_to_narrow(error_message) << std::endl;
- retval = false;
- }
- break;
- }
- } // Menu-game loop
-
- g_menuclouds->drop();
- g_menucloudsmgr->drop();
-
- return retval;
-}
-
-void ClientLauncher::init_args(GameParams &game_params, const Settings &cmd_args)
-{
-
- skip_main_menu = cmd_args.getFlag("go");
-
- // FIXME: This is confusing (but correct)
-
- /* If world_path is set then override it unless skipping the main menu using
- * the --go command line param. Else, give preference to the address
- * supplied on the command line
- */
- address = g_settings->get("address");
- if (game_params.world_path != "" && !skip_main_menu)
- address = "";
- else if (cmd_args.exists("address"))
- address = cmd_args.get("address");
-
- playername = g_settings->get("name");
- if (cmd_args.exists("name"))
- playername = cmd_args.get("name");
-
- list_video_modes = cmd_args.getFlag("videomodes");
-
- use_freetype = g_settings->getBool("freetype");
-
- random_input = g_settings->getBool("random_input")
- || cmd_args.getFlag("random-input");
-}
-
-bool ClientLauncher::init_engine(int log_level)
-{
- receiver = new MyEventReceiver();
- create_engine_device(log_level);
- return device != NULL;
-}
-
-bool ClientLauncher::launch_game(std::wstring *error_message,
- GameParams &game_params, const Settings &cmd_args)
-{
- // Initialize menu data
- MainMenuData menudata;
- menudata.address = address;
- menudata.name = playername;
- menudata.port = itos(game_params.socket_port);
- menudata.errormessage = wide_to_narrow(*error_message);
-
- *error_message = L"";
-
- if (cmd_args.exists("password"))
- menudata.password = cmd_args.get("password");
-
- menudata.enable_public = g_settings->getBool("server_announce");
-
- // If a world was commanded, append and select it
- if (game_params.world_path != "") {
- worldspec.gameid = getWorldGameId(game_params.world_path, true);
- worldspec.name = _("[--world parameter]");
-
- if (worldspec.gameid == "") { // Create new
- worldspec.gameid = g_settings->get("default_game");
- worldspec.name += " [new]";
- }
- worldspec.path = game_params.world_path;
- }
-
- /* Show the GUI menu
- */
- if (!skip_main_menu) {
- main_menu(&menudata);
-
- // Skip further loading if there was an exit signal.
- if (*porting::signal_handler_killstatus())
- return false;
-
- address = menudata.address;
- int newport = stoi(menudata.port);
- if (newport != 0)
- game_params.socket_port = newport;
-
- simple_singleplayer_mode = menudata.simple_singleplayer_mode;
-
- std::vector<WorldSpec> worldspecs = getAvailableWorlds();
-
- if (menudata.selected_world >= 0
- && menudata.selected_world < (int)worldspecs.size()) {
- g_settings->set("selected_world_path",
- worldspecs[menudata.selected_world].path);
- worldspec = worldspecs[menudata.selected_world];
- }
- }
-
- if (menudata.errormessage != "") {
- /* The calling function will pass this back into this function upon the
- * next iteration (if any) causing it to be displayed by the GUI
- */
- *error_message = narrow_to_wide(menudata.errormessage);
- return false;
- }
-
- if (menudata.name == "")
- menudata.name = std::string("Guest") + itos(myrand_range(1000, 9999));
- else
- playername = menudata.name;
-
- password = translatePassword(playername, narrow_to_wide(menudata.password));
-
- g_settings->set("name", playername);
-
- current_playername = playername;
- current_password = password;
- current_address = address;
- current_port = game_params.socket_port;
-
- // If using simple singleplayer mode, override
- if (simple_singleplayer_mode) {
- assert(skip_main_menu == false);
- current_playername = "singleplayer";
- current_password = "";
- current_address = "";
- current_port = myrand_range(49152, 65535);
- } else if (address != "") {
- ServerListSpec server;
- server["name"] = menudata.servername;
- server["address"] = menudata.address;
- server["port"] = menudata.port;
- server["description"] = menudata.serverdescription;
- ServerList::insert(server);
- }
-
- infostream << "Selected world: " << worldspec.name
- << " [" << worldspec.path << "]" << std::endl;
-
- if (current_address == "") { // If local game
- if (worldspec.path == "") {
- *error_message = wgettext("No world selected and no address "
- "provided. Nothing to do.");
- errorstream << wide_to_narrow(*error_message) << std::endl;
- return false;
- }
-
- if (!fs::PathExists(worldspec.path)) {
- *error_message = wgettext("Provided world path doesn't exist: ")
- + narrow_to_wide(worldspec.path);
- errorstream << wide_to_narrow(*error_message) << std::endl;
- return false;
- }
-
- // Load gamespec for required game
- gamespec = findWorldSubgame(worldspec.path);
- if (!gamespec.isValid() && !game_params.game_spec.isValid()) {
- *error_message = wgettext("Could not find or load game \"")
- + narrow_to_wide(worldspec.gameid) + L"\"";
- errorstream << wide_to_narrow(*error_message) << std::endl;
- return false;
- }
-
- if (porting::signal_handler_killstatus())
- return true;
-
- if (game_params.game_spec.isValid() &&
- game_params.game_spec.id != worldspec.gameid) {
- errorstream << "WARNING: Overriding gamespec from \""
- << worldspec.gameid << "\" to \""
- << game_params.game_spec.id << "\"" << std::endl;
- gamespec = game_params.game_spec;
- }
-
- if (!gamespec.isValid()) {
- *error_message = wgettext("Invalid gamespec.");
- *error_message += L" (world_gameid="
- + narrow_to_wide(worldspec.gameid) + L")";
- errorstream << wide_to_narrow(*error_message) << std::endl;
- return false;
- }
- }
-
- return true;
-}
-
-void ClientLauncher::main_menu(MainMenuData *menudata)
-{
- bool *kill = porting::signal_handler_killstatus();
- video::IVideoDriver *driver = device->getVideoDriver();
-
- infostream << "Waiting for other menus" << std::endl;
- while (device->run() && *kill == false) {
- if (noMenuActive())
- break;
- driver->beginScene(true, true, video::SColor(255, 128, 128, 128));
- guienv->drawAll();
- driver->endScene();
- // On some computers framerate doesn't seem to be automatically limited
- sleep_ms(25);
- }
- infostream << "Waited for other menus" << std::endl;
-
- // Cursor can be non-visible when coming from the game
-#ifndef ANDROID
- device->getCursorControl()->setVisible(true);
-#endif
-
- /* show main menu */
- GUIEngine mymenu(device, guiroot, &g_menumgr, smgr, menudata, *kill);
-
- smgr->clear(); /* leave scene manager in a clean state */
-}
-
-bool ClientLauncher::create_engine_device(int log_level)
-{
- static const irr::ELOG_LEVEL irr_log_level[5] = {
- ELL_NONE,
- ELL_ERROR,
- ELL_WARNING,
- ELL_INFORMATION,
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
- ELL_INFORMATION
-#else
- ELL_DEBUG
-#endif
- };
-
- // Resolution selection
- bool fullscreen = g_settings->getBool("fullscreen");
- u16 screenW = g_settings->getU16("screenW");
- u16 screenH = g_settings->getU16("screenH");
-
- // bpp, fsaa, vsync
- bool vsync = g_settings->getBool("vsync");
- u16 bits = g_settings->getU16("fullscreen_bpp");
- u16 fsaa = g_settings->getU16("fsaa");
-
- // Determine driver
- video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
- std::string driverstring = g_settings->get("video_driver");
- std::vector<video::E_DRIVER_TYPE> drivers
- = porting::getSupportedVideoDrivers();
- u32 i;
- for (i = 0; i != drivers.size(); i++) {
- if (!strcasecmp(driverstring.c_str(),
- porting::getVideoDriverName(drivers[i]))) {
- driverType = drivers[i];
- break;
- }
- }
- if (i == drivers.size()) {
- errorstream << "Invalid video_driver specified; "
- "defaulting to opengl" << std::endl;
- }
-
- SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
- params.DriverType = driverType;
- params.WindowSize = core::dimension2d<u32>(screenW, screenH);
- params.Bits = bits;
- params.AntiAlias = fsaa;
- params.Fullscreen = fullscreen;
- params.Stencilbuffer = false;
- params.Vsync = vsync;
- params.EventReceiver = receiver;
- params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
-#ifdef __ANDROID__
- params.PrivateData = porting::app_global;
- params.OGLES2ShaderPath = std::string(porting::path_user + DIR_DELIM +
- "media" + DIR_DELIM + "Shaders" + DIR_DELIM).c_str();
-#endif
-
- device = createDeviceEx(params);
-
- if (device) {
- // Map our log level to irrlicht engine one.
- ILogger* irr_logger = device->getLogger();
- irr_logger->setLogLevel(irr_log_level[log_level]);
-
- porting::initIrrlicht(device);
- }
-
- return device != NULL;
-}
-
-// Misc functions
-
-static bool print_video_modes()
-{
- IrrlichtDevice *nulldevice;
-
- bool vsync = g_settings->getBool("vsync");
- u16 fsaa = g_settings->getU16("fsaa");
- MyEventReceiver* receiver = new MyEventReceiver();
-
- SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
- params.DriverType = video::EDT_NULL;
- params.WindowSize = core::dimension2d<u32>(640, 480);
- params.Bits = 24;
- params.AntiAlias = fsaa;
- params.Fullscreen = false;
- params.Stencilbuffer = false;
- params.Vsync = vsync;
- params.EventReceiver = receiver;
- params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
-
- nulldevice = createDeviceEx(params);
-
- if (nulldevice == NULL) {
- delete receiver;
- return false;
- }
-
- dstream << _("Available video modes (WxHxD):") << std::endl;
-
- video::IVideoModeList *videomode_list = nulldevice->getVideoModeList();
-
- if (videomode_list != NULL) {
- s32 videomode_count = videomode_list->getVideoModeCount();
- core::dimension2d<u32> videomode_res;
- s32 videomode_depth;
- for (s32 i = 0; i < videomode_count; ++i) {
- videomode_res = videomode_list->getVideoModeResolution(i);
- videomode_depth = videomode_list->getVideoModeDepth(i);
- dstream << videomode_res.Width << "x" << videomode_res.Height
- << "x" << videomode_depth << std::endl;
- }
-
- dstream << _("Active video mode (WxHxD):") << std::endl;
- videomode_res = videomode_list->getDesktopResolution();
- videomode_depth = videomode_list->getDesktopDepth();
- dstream << videomode_res.Width << "x" << videomode_res.Height
- << "x" << videomode_depth << std::endl;
-
- }
-
- nulldevice->drop();
- delete receiver;
-
- return videomode_list != NULL;
-}
-
-#endif // !SERVER
-
-/*****************************************************************************
- * Performance tests
- *****************************************************************************/
-#ifndef SERVER
-static void speed_tests()
-{
- // volatile to avoid some potential compiler optimisations
- volatile static s16 temp16;
- volatile static f32 tempf;
- static v3f tempv3f1;
- static v3f tempv3f2;
- static std::string tempstring;
- static std::string tempstring2;
-
- tempv3f1 = v3f();
- tempv3f2 = v3f();
- tempstring = std::string();
- tempstring2 = std::string();
-
- {
- infostream << "The following test should take around 20ms." << std::endl;
- TimeTaker timer("Testing std::string speed");
- const u32 jj = 10000;
- for (u32 j = 0; j < jj; j++) {
- tempstring = "";
- tempstring2 = "";
- const u32 ii = 10;
- for (u32 i = 0; i < ii; i++) {
- tempstring2 += "asd";
- }
- for (u32 i = 0; i < ii+1; i++) {
- tempstring += "asd";
- if (tempstring == tempstring2)
- break;
- }
- }
- }
-
- infostream << "All of the following tests should take around 100ms each."
- << std::endl;
-
- {
- TimeTaker timer("Testing floating-point conversion speed");
- tempf = 0.001;
- for (u32 i = 0; i < 4000000; i++) {
- temp16 += tempf;
- tempf += 0.001;
- }
- }
-
- {
- TimeTaker timer("Testing floating-point vector speed");
-
- tempv3f1 = v3f(1, 2, 3);
- tempv3f2 = v3f(4, 5, 6);
- for (u32 i = 0; i < 10000000; i++) {
- tempf += tempv3f1.dotProduct(tempv3f2);
- tempv3f2 += v3f(7, 8, 9);
- }
- }
-
- {
- TimeTaker timer("Testing std::map speed");
-
- std::map<v2s16, f32> map1;
- tempf = -324;
- const s16 ii = 300;
- for (s16 y = 0; y < ii; y++) {
- for (s16 x = 0; x < ii; x++) {
- map1[v2s16(x, y)] = tempf;
- tempf += 1;
- }
- }
- for (s16 y = ii - 1; y >= 0; y--) {
- for (s16 x = 0; x < ii; x++) {
- tempf = map1[v2s16(x, y)];
- }
- }
- }
-
- {
- infostream << "Around 5000/ms should do well here." << std::endl;
- TimeTaker timer("Testing mutex speed");
-
- JMutex m;
- u32 n = 0;
- u32 i = 0;
- do {
- n += 10000;
- for (; i < n; i++) {
- m.Lock();
- m.Unlock();
- }
- }
- // Do at least 10ms
- while(timer.getTimerTime() < 10);
-
- u32 dtime = timer.stop();
- u32 per_ms = n / dtime;
- infostream << "Done. " << dtime << "ms, " << per_ms << "/ms" << std::endl;
- }
-}
-#endif
diff --git a/src/main.h b/src/main.h
deleted file mode 100644
index 191b41887..000000000
--- a/src/main.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
-*/
-
-#ifndef MAIN_HEADER
-#define MAIN_HEADER
-
-#include <string>
-
-// Settings
-class Settings;
-extern Settings *g_settings;
-extern std::string g_settings_path;
-
-// Global profiler
-class Profiler;
-extern Profiler *g_profiler;
-
-// Menu clouds
-class Clouds;
-extern Clouds *g_menuclouds;
-
-// Scene manager used for menu clouds
-namespace irr{namespace scene{class ISceneManager;}}
-extern irr::scene::ISceneManager *g_menucloudsmgr;
-
-// Debug streams
-
-#include <fstream>
-
-extern std::ostream *dout_con_ptr;
-extern std::ostream *derr_con_ptr;
-extern std::ostream *dout_client_ptr;
-extern std::ostream *derr_client_ptr;
-extern std::ostream *dout_server_ptr;
-extern std::ostream *derr_server_ptr;
-
-#define dout_con (*dout_con_ptr)
-#define derr_con (*derr_con_ptr)
-#define dout_client (*dout_client_ptr)
-#define derr_client (*derr_client_ptr)
-#define dout_server (*dout_server_ptr)
-#define derr_server (*derr_server_ptr)
-
-#endif
-
diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h
index b14ca56e5..6f8aa9137 100644
--- a/src/mainmenumanager.h
+++ b/src/mainmenumanager.h
@@ -39,7 +39,7 @@ public:
virtual void signalKeyConfigChange() = 0;
};
-extern gui::IGUIEnvironment* guienv;
+extern gui::IGUIEnvironment *guienv;
extern gui::IGUIStaticText *guiroot;
// Handler for the modal menus
diff --git a/src/map.cpp b/src/map.cpp
index efa53ca08..76a558d43 100644
--- a/src/map.cpp
+++ b/src/map.cpp
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "mapsector.h"
#include "mapblock.h"
-#include "main.h"
#include "filesys.h"
#include "voxel.h"
#include "porting.h"
@@ -44,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "database-dummy.h"
#include "database-sqlite3.h"
#include <deque>
+#include <queue>
#if USE_LEVELDB
#include "database-leveldb.h"
#endif
@@ -53,22 +53,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
-/*
- SQLite format specification:
- - Initially only replaces sectors/ and sectors2/
-
- If map.sqlite does not exist in the save dir
- or the block was not found in the database
- the map will try to load from sectors folder.
- In either case, map.sqlite will be created
- and all future saves will save there.
-
- Structure of map.sqlite:
- Tables:
- blocks
- (PK) INT pos
- BLOB data
-*/
/*
Map
@@ -783,8 +767,7 @@ void Map::updateLighting(enum LightBank bank,
}
else
{
- // Invalid lighting bank
- assert(0);
+ assert("Invalid lighting bank" == NULL);
}
/*infostream<<"Bottom for sunlight-propagated block ("
@@ -799,7 +782,7 @@ void Map::updateLighting(enum LightBank bank,
}
catch(InvalidPositionException &e)
{
- assert(0);
+ FATAL_ERROR("Invalid position");
}
}
@@ -1236,7 +1219,7 @@ void Map::removeNodeAndUpdate(v3s16 p,
n.setLight(LIGHTBANK_DAY, 0, ndef);
setNode(p, n);
} else {
- assert(0);
+ FATAL_ERROR("Invalid position");
}
}
@@ -1417,71 +1400,140 @@ bool Map::getDayNightDiff(v3s16 blockpos)
return false;
}
+struct TimeOrderedMapBlock {
+ MapSector *sect;
+ MapBlock *block;
+
+ TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
+ sect(sect),
+ block(block)
+ {}
+
+ bool operator<(const TimeOrderedMapBlock &b) const
+ {
+ return block->getUsageTimer() < b.block->getUsageTimer();
+ };
+};
+
/*
Updates usage timers
*/
-void Map::timerUpdate(float dtime, float unload_timeout,
- std::list<v3s16> *unloaded_blocks)
+void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
+ std::vector<v3s16> *unloaded_blocks)
{
bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
// Profile modified reasons
Profiler modprofiler;
- std::list<v2s16> sector_deletion_queue;
+ std::vector<v2s16> sector_deletion_queue;
u32 deleted_blocks_count = 0;
u32 saved_blocks_count = 0;
u32 block_count_all = 0;
beginSave();
- for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
- si != m_sectors.end(); ++si)
- {
- MapSector *sector = si->second;
- bool all_blocks_deleted = true;
+ // If there is no practical limit, we spare creation of mapblock_queue
+ if (max_loaded_blocks == (u32)-1) {
+ for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+ si != m_sectors.end(); ++si) {
+ MapSector *sector = si->second;
- std::list<MapBlock*> blocks;
- sector->getBlocks(blocks);
+ bool all_blocks_deleted = true;
- for(std::list<MapBlock*>::iterator i = blocks.begin();
- i != blocks.end(); ++i)
- {
- MapBlock *block = (*i);
+ MapBlockVect blocks;
+ sector->getBlocks(blocks);
- block->incrementUsageTimer(dtime);
+ for (MapBlockVect::iterator i = blocks.begin();
+ i != blocks.end(); ++i) {
+ MapBlock *block = (*i);
- if(block->refGet() == 0 && block->getUsageTimer() > unload_timeout)
- {
- v3s16 p = block->getPos();
+ block->incrementUsageTimer(dtime);
- // Save if modified
- if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading)
- {
- modprofiler.add(block->getModifiedReason(), 1);
- if (!saveBlock(block))
- continue;
- saved_blocks_count++;
- }
+ if (block->refGet() == 0
+ && block->getUsageTimer() > unload_timeout) {
+ v3s16 p = block->getPos();
- // Delete from memory
- sector->deleteBlock(block);
+ // Save if modified
+ if (block->getModified() != MOD_STATE_CLEAN
+ && save_before_unloading) {
+ modprofiler.add(block->getModifiedReasonString(), 1);
+ if (!saveBlock(block))
+ continue;
+ saved_blocks_count++;
+ }
+
+ // Delete from memory
+ sector->deleteBlock(block);
- if(unloaded_blocks)
- unloaded_blocks->push_back(p);
+ if (unloaded_blocks)
+ unloaded_blocks->push_back(p);
- deleted_blocks_count++;
+ deleted_blocks_count++;
+ } else {
+ all_blocks_deleted = false;
+ block_count_all++;
+ }
}
- else
- {
- all_blocks_deleted = false;
- block_count_all++;
+
+ if (all_blocks_deleted) {
+ sector_deletion_queue.push_back(si->first);
}
}
+ } else {
+ std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
+ for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+ si != m_sectors.end(); ++si) {
+ MapSector *sector = si->second;
- if(all_blocks_deleted)
- {
- sector_deletion_queue.push_back(si->first);
+ MapBlockVect blocks;
+ sector->getBlocks(blocks);
+
+ for(MapBlockVect::iterator i = blocks.begin();
+ i != blocks.end(); ++i) {
+ MapBlock *block = (*i);
+
+ block->incrementUsageTimer(dtime);
+ mapblock_queue.push(TimeOrderedMapBlock(sector, block));
+ }
+ }
+ block_count_all = mapblock_queue.size();
+ // Delete old blocks, and blocks over the limit from the memory
+ while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
+ || mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
+ TimeOrderedMapBlock b = mapblock_queue.top();
+ mapblock_queue.pop();
+
+ MapBlock *block = b.block;
+
+ if (block->refGet() != 0)
+ continue;
+
+ v3s16 p = block->getPos();
+
+ // Save if modified
+ if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
+ modprofiler.add(block->getModifiedReasonString(), 1);
+ if (!saveBlock(block))
+ continue;
+ saved_blocks_count++;
+ }
+
+ // Delete from memory
+ b.sect->deleteBlock(block);
+
+ if (unloaded_blocks)
+ unloaded_blocks->push_back(p);
+
+ deleted_blocks_count++;
+ block_count_all--;
+ }
+ // Delete empty sectors
+ for (std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
+ si != m_sectors.end(); ++si) {
+ if (si->second->empty()) {
+ sector_deletion_queue.push_back(si->first);
+ }
}
}
endSave();
@@ -1506,16 +1558,15 @@ void Map::timerUpdate(float dtime, float unload_timeout,
}
}
-void Map::unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks)
+void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
{
- timerUpdate(0.0, -1.0, unloaded_blocks);
+ timerUpdate(0.0, -1.0, 0, unloaded_blocks);
}
-void Map::deleteSectors(std::list<v2s16> &list)
+void Map::deleteSectors(std::vector<v2s16> &sectorList)
{
- for(std::list<v2s16>::iterator j = list.begin();
- j != list.end(); ++j)
- {
+ for(std::vector<v2s16>::iterator j = sectorList.begin();
+ j != sectorList.end(); ++j) {
MapSector *sector = m_sectors[*j];
// If sector is in sector cache, remove it from there
if(m_sector_cache == sector)
@@ -1526,63 +1577,6 @@ void Map::deleteSectors(std::list<v2s16> &list)
}
}
-#if 0
-void Map::unloadUnusedData(float timeout,
- core::list<v3s16> *deleted_blocks)
-{
- core::list<v2s16> sector_deletion_queue;
- u32 deleted_blocks_count = 0;
- u32 saved_blocks_count = 0;
-
- core::map<v2s16, MapSector*>::Iterator si = m_sectors.getIterator();
- for(; si.atEnd() == false; si++)
- {
- MapSector *sector = si.getNode()->getValue();
-
- bool all_blocks_deleted = true;
-
- core::list<MapBlock*> blocks;
- sector->getBlocks(blocks);
- for(core::list<MapBlock*>::Iterator i = blocks.begin();
- i != blocks.end(); i++)
- {
- MapBlock *block = (*i);
-
- if(block->getUsageTimer() > timeout)
- {
- // Save if modified
- if(block->getModified() != MOD_STATE_CLEAN)
- {
- saveBlock(block);
- saved_blocks_count++;
- }
- // Delete from memory
- sector->deleteBlock(block);
- deleted_blocks_count++;
- }
- else
- {
- all_blocks_deleted = false;
- }
- }
-
- if(all_blocks_deleted)
- {
- sector_deletion_queue.push_back(si.getNode()->getKey());
- }
- }
-
- deleteSectors(sector_deletion_queue);
-
- infostream<<"Map: Unloaded "<<deleted_blocks_count<<" blocks from memory"
- <<", of which "<<saved_blocks_count<<" were wr."
- <<std::endl;
-
- //return sector_deletion_queue.getSize();
- //return deleted_blocks_count;
-}
-#endif
-
void Map::PrintInfo(std::ostream &out)
{
out<<"Map: ";
@@ -1972,6 +1966,47 @@ void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks)
}
}
+std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
+{
+ std::vector<v3s16> positions_with_meta;
+
+ sortBoxVerticies(p1, p2);
+ v3s16 bpmin = getNodeBlockPos(p1);
+ v3s16 bpmax = getNodeBlockPos(p2);
+
+ VoxelArea area(p1, p2);
+
+ for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
+ for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
+ for (s16 x = bpmin.X; x <= bpmax.X; x++) {
+ v3s16 blockpos(x, y, z);
+
+ MapBlock *block = getBlockNoCreateNoEx(blockpos);
+ if (!block) {
+ verbosestream << "Map::getNodeMetadata(): Need to emerge "
+ << PP(blockpos) << std::endl;
+ block = emergeBlock(blockpos, false);
+ }
+ if (!block) {
+ infostream << "WARNING: Map::getNodeMetadata(): Block not found"
+ << std::endl;
+ continue;
+ }
+
+ v3s16 p_base = blockpos * MAP_BLOCKSIZE;
+ std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
+ for (size_t i = 0; i != keys.size(); i++) {
+ v3s16 p(keys[i] + p_base);
+ if (!area.contains(p))
+ continue;
+
+ positions_with_meta.push_back(p);
+ }
+ }
+
+ return positions_with_meta;
+}
+
NodeMetadata *Map::getNodeMetadata(v3s16 p)
{
v3s16 blockpos = getNodeBlockPos(p);
@@ -2095,25 +2130,13 @@ ServerMap::ServerMap(std::string savedir, IGameDef *gamedef, EmergeManager *emer
bool succeeded = conf.readConfigFile(conf_path.c_str());
if (!succeeded || !conf.exists("backend")) {
// fall back to sqlite3
- dbase = new Database_SQLite3(this, savedir);
conf.set("backend", "sqlite3");
- } else {
- std::string backend = conf.get("backend");
- if (backend == "dummy")
- dbase = new Database_Dummy(this);
- else if (backend == "sqlite3")
- dbase = new Database_SQLite3(this, savedir);
- #if USE_LEVELDB
- else if (backend == "leveldb")
- dbase = new Database_LevelDB(this, savedir);
- #endif
- #if USE_REDIS
- else if (backend == "redis")
- dbase = new Database_Redis(this, savedir);
- #endif
- else
- throw BaseException("Unknown map backend");
}
+ std::string backend = conf.get("backend");
+ dbase = createDatabase(backend, savedir, conf);
+
+ if (!conf.updateConfigFile(conf_path.c_str()))
+ errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
m_savedir = savedir;
m_map_saving_enabled = false;
@@ -2272,7 +2295,7 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos)
v2s16 sectorpos(x, z);
// Sector metadata is loaded from disk if not already loaded.
ServerMapSector *sector = createSector(sectorpos);
- assert(sector);
+ FATAL_ERROR_IF(sector == NULL, "createSector() failed");
(void) sector;
for(s16 y=blockpos_min.Y-extra_borders.Y;
@@ -2465,7 +2488,7 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
Set block as modified
*/
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "finishBlockMake expireDayNightDiff");
+ MOD_REASON_EXPIRE_DAYNIGHTDIFF);
}
/*
@@ -2555,10 +2578,12 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d)
/*
Do not create over-limit
*/
- if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
+ const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
+ g_settings->getU16("map_generation_limit"));
+ if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE
+ || p2d.X > map_gen_limit / MAP_BLOCKSIZE
+ || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE
+ || p2d.Y > map_gen_limit / MAP_BLOCKSIZE)
throw InvalidPositionException("createSector(): pos. over limit");
/*
@@ -2702,12 +2727,7 @@ MapBlock * ServerMap::createBlock(v3s16 p)
/*
Do not create over-limit
*/
- if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
+ if (blockpos_over_limit(p))
throw InvalidPositionException("createBlock(): pos. over limit");
v2s16 p2d(p.X, p.Z);
@@ -2720,7 +2740,7 @@ MapBlock * ServerMap::createBlock(v3s16 p)
lighting on blocks for them.
*/
ServerMapSector *sector;
- try{
+ try {
sector = (ServerMapSector*)createSector(p2d);
assert(sector->getId() == MAPSECTOR_SERVER);
}
@@ -2892,7 +2912,8 @@ plan_b:
}
bool ServerMap::loadFromFolders() {
- if(!dbase->Initialized() && !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite")) // ?
+ if (!dbase->initialized() &&
+ !fs::PathExists(m_savedir + DIR_DELIM + "map.sqlite"))
return true;
return false;
}
@@ -2914,14 +2935,14 @@ std::string ServerMap::getSectorDir(v2s16 pos, int layout)
{
case 1:
snprintf(cc, 9, "%.4x%.4x",
- (unsigned int)pos.X&0xffff,
- (unsigned int)pos.Y&0xffff);
+ (unsigned int) pos.X & 0xffff,
+ (unsigned int) pos.Y & 0xffff);
return m_savedir + DIR_DELIM + "sectors" + DIR_DELIM + cc;
case 2:
- snprintf(cc, 9, "%.3x" DIR_DELIM "%.3x",
- (unsigned int)pos.X&0xfff,
- (unsigned int)pos.Y&0xfff);
+ snprintf(cc, 9, (std::string("%.3x") + DIR_DELIM + "%.3x").c_str(),
+ (unsigned int) pos.X & 0xfff,
+ (unsigned int) pos.Y & 0xfff);
return m_savedir + DIR_DELIM + "sectors2" + DIR_DELIM + cc;
default:
@@ -2945,16 +2966,17 @@ v2s16 ServerMap::getSectorPos(std::string dirname)
{
// New layout
fs::RemoveLastPathComponent(dirname, &component, 2);
- r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y);
+ r = sscanf(component.c_str(), (std::string("%3x") + DIR_DELIM + "%3x").c_str(), &x, &y);
// Sign-extend the 12 bit values up to 16 bits...
- if(x&0x800) x|=0xF000;
- if(y&0x800) y|=0xF000;
+ if(x & 0x800) x |= 0xF000;
+ if(y & 0x800) y |= 0xF000;
}
else
{
- assert(false);
+ r = -1;
}
- assert(r == 2);
+
+ FATAL_ERROR_IF(r != 2, "getSectorPos()");
v2s16 pos((s16)x, (s16)y);
return pos;
}
@@ -2983,8 +3005,7 @@ std::string ServerMap::getBlockFilename(v3s16 p)
void ServerMap::save(ModifiedState save_level)
{
DSTACK(__FUNCTION_NAME);
- if(m_map_saving_enabled == false)
- {
+ if(m_map_saving_enabled == false) {
infostream<<"WARNING: Not saving map, saving disabled."<<std::endl;
return;
}
@@ -2993,8 +3014,7 @@ void ServerMap::save(ModifiedState save_level)
infostream<<"ServerMap: Saving whole map, this can take time."
<<std::endl;
- if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN)
- {
+ if(m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
saveMapMeta();
}
@@ -3009,35 +3029,32 @@ void ServerMap::save(ModifiedState save_level)
bool save_started = false;
for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin();
- i != m_sectors.end(); ++i)
- {
+ i != m_sectors.end(); ++i) {
ServerMapSector *sector = (ServerMapSector*)i->second;
assert(sector->getId() == MAPSECTOR_SERVER);
- if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN)
- {
+ if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) {
saveSectorMeta(sector);
sector_meta_count++;
}
- std::list<MapBlock*> blocks;
+
+ MapBlockVect blocks;
sector->getBlocks(blocks);
- for(std::list<MapBlock*>::iterator j = blocks.begin();
- j != blocks.end(); ++j)
- {
+ for(MapBlockVect::iterator j = blocks.begin();
+ j != blocks.end(); ++j) {
MapBlock *block = *j;
block_count_all++;
- if(block->getModified() >= (u32)save_level)
- {
+ if(block->getModified() >= (u32)save_level) {
// Lazy beginSave()
- if(!save_started){
+ if(!save_started) {
beginSave();
save_started = true;
}
- modprofiler.add(block->getModifiedReason(), 1);
+ modprofiler.add(block->getModifiedReasonString(), 1);
saveBlock(block);
block_count++;
@@ -3050,6 +3067,7 @@ void ServerMap::save(ModifiedState save_level)
}
}
}
+
if(save_started)
endSave();
@@ -3057,8 +3075,7 @@ void ServerMap::save(ModifiedState save_level)
Only print if something happened or saved whole map
*/
if(save_level == MOD_STATE_CLEAN || sector_meta_count != 0
- || block_count != 0)
- {
+ || block_count != 0) {
infostream<<"ServerMap: Written: "
<<sector_meta_count<<" sector metadata files, "
<<block_count<<" block files"
@@ -3070,30 +3087,28 @@ void ServerMap::save(ModifiedState save_level)
}
}
-void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst)
+void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
{
- if(loadFromFolders()){
- errorstream<<"Map::listAllLoadableBlocks(): Result will be missing "
- <<"all blocks that are stored in flat files"<<std::endl;
+ if (loadFromFolders()) {
+ errorstream << "Map::listAllLoadableBlocks(): Result will be missing "
+ << "all blocks that are stored in flat files." << std::endl;
}
dbase->listAllLoadableBlocks(dst);
}
-void ServerMap::listAllLoadedBlocks(std::list<v3s16> &dst)
+void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
{
for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin();
si != m_sectors.end(); ++si)
{
MapSector *sector = si->second;
- std::list<MapBlock*> blocks;
+ MapBlockVect blocks;
sector->getBlocks(blocks);
- for(std::list<MapBlock*>::iterator i = blocks.begin();
- i != blocks.end(); ++i)
- {
- MapBlock *block = (*i);
- v3s16 p = block->getPos();
+ for(MapBlockVect::iterator i = blocks.begin();
+ i != blocks.end(); ++i) {
+ v3s16 p = (*i)->getPos();
dst.push_back(p);
}
}
@@ -3103,26 +3118,20 @@ void ServerMap::saveMapMeta()
{
DSTACK(__FUNCTION_NAME);
- /*infostream<<"ServerMap::saveMapMeta(): "
- <<"seed="<<m_seed
- <<std::endl;*/
-
createDirs(m_savedir);
- std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
- std::ostringstream ss(std::ios_base::binary);
-
- Settings params;
+ std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
+ std::ostringstream oss(std::ios_base::binary);
+ Settings conf;
- m_emerge->saveParamsToSettings(&params);
- params.writeLines(ss);
+ m_emerge->params.save(conf);
+ conf.writeLines(oss);
- ss<<"[end_of_params]\n";
+ oss << "[end_of_params]\n";
- if(!fs::safeWriteToFile(fullpath, ss.str()))
- {
- infostream<<"ERROR: ServerMap::saveMapMeta(): "
- <<"could not write "<<fullpath<<std::endl;
+ if(!fs::safeWriteToFile(fullpath, oss.str())) {
+ errorstream << "ServerMap::saveMapMeta(): "
+ << "could not write " << fullpath << std::endl;
throw FileNotGoodException("Cannot save chunk metadata");
}
@@ -3133,24 +3142,22 @@ void ServerMap::loadMapMeta()
{
DSTACK(__FUNCTION_NAME);
- Settings params;
- std::string fullpath = m_savedir + DIR_DELIM "map_meta.txt";
+ Settings conf;
+ std::string fullpath = m_savedir + DIR_DELIM + "map_meta.txt";
- if (fs::PathExists(fullpath)) {
- std::ifstream is(fullpath.c_str(), std::ios_base::binary);
- if (!is.good()) {
- errorstream << "ServerMap::loadMapMeta(): "
- "could not open " << fullpath << std::endl;
- throw FileNotGoodException("Cannot open map metadata");
- }
+ std::ifstream is(fullpath.c_str(), std::ios_base::binary);
+ if (!is.good()) {
+ errorstream << "ServerMap::loadMapMeta(): "
+ "could not open " << fullpath << std::endl;
+ throw FileNotGoodException("Cannot open map metadata");
+ }
- if (!params.parseConfigLines(is, "[end_of_params]")) {
- throw SerializationError("ServerMap::loadMapMeta(): "
+ if (!conf.parseConfigLines(is, "[end_of_params]")) {
+ throw SerializationError("ServerMap::loadMapMeta(): "
"[end_of_params] not found!");
- }
}
- m_emerge->loadParamsFromSettings(&params);
+ m_emerge->params.load(conf);
verbosestream << "ServerMap::loadMapMeta(): seed="
<< m_emerge->params.seed << std::endl;
@@ -3331,6 +3338,24 @@ bool ServerMap::loadSectorFull(v2s16 p2d)
}
#endif
+Database *ServerMap::createDatabase(const std::string &name, const std::string &savedir, Settings &conf)
+{
+ if (name == "sqlite3")
+ return new Database_SQLite3(savedir);
+ if (name == "dummy")
+ return new Database_Dummy();
+ #if USE_LEVELDB
+ else if (name == "leveldb")
+ return new Database_LevelDB(savedir);
+ #endif
+ #if USE_REDIS
+ else if (name == "redis")
+ return new Database_Redis(conf);
+ #endif
+ else
+ throw BaseException(std::string("Database backend ") + name + " not supported.");
+}
+
void ServerMap::beginSave()
{
dbase->beginSave();
@@ -3370,7 +3395,7 @@ bool ServerMap::saveBlock(MapBlock *block, Database *db)
std::string data = o.str();
bool ret = db->saveBlock(p3d, data);
- if(ret) {
+ if (ret) {
// We just wrote it to the disk so clear modified flag
block->resetModified();
}
@@ -3382,7 +3407,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
{
DSTACK(__FUNCTION_NAME);
- std::string fullpath = sectordir+DIR_DELIM+blockfile;
+ std::string fullpath = sectordir + DIR_DELIM + blockfile;
try {
std::ifstream is(fullpath.c_str(), std::ios_base::binary);
@@ -3448,7 +3473,7 @@ void ServerMap::loadBlock(std::string sectordir, std::string blockfile,
<<"what()="<<e.what()
<<std::endl;
// Ignoring. A new one will be generated.
- assert(0);
+ abort();
// TODO: Backup file; name is in fullpath.
}
@@ -3518,7 +3543,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool
<<"(ignore_world_load_errors)"<<std::endl;
} else {
throw SerializationError("Invalid block data in database");
- //assert(0);
}
}
}
@@ -3584,7 +3608,7 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos)
*/
std::string blockfilename = getBlockFilename(blockpos);
- if(fs::PathExists(sectordir+DIR_DELIM+blockfilename) == false)
+ if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false)
return NULL;
/*
diff --git a/src/map.h b/src/map.h
index 1089e6d34..2afd09639 100644
--- a/src/map.h
+++ b/src/map.h
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/container.h"
#include "nodetimer.h"
+class Settings;
class Database;
class ClientMap;
class MapSector;
@@ -262,43 +263,33 @@ public:
//bool updateChangedVisibleArea();
// Call these before and after saving of many blocks
- virtual void beginSave() {return;};
- virtual void endSave() {return;};
+ virtual void beginSave() { return; }
+ virtual void endSave() { return; }
- virtual void save(ModifiedState save_level){assert(0);};
+ virtual void save(ModifiedState save_level) { FATAL_ERROR("FIXME"); }
// Server implements these.
// Client leaves them as no-op.
- virtual bool saveBlock(MapBlock *block) { return false; };
- virtual bool deleteBlock(v3s16 blockpos) { return false; };
+ virtual bool saveBlock(MapBlock *block) { return false; }
+ virtual bool deleteBlock(v3s16 blockpos) { return false; }
/*
Updates usage timers and unloads unused blocks and sectors.
Saves modified blocks before unloading on MAPTYPE_SERVER.
*/
- void timerUpdate(float dtime, float unload_timeout,
- std::list<v3s16> *unloaded_blocks=NULL);
+ void timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
+ std::vector<v3s16> *unloaded_blocks=NULL);
/*
Unloads all blocks with a zero refCount().
Saves modified blocks before unloading on MAPTYPE_SERVER.
*/
- void unloadUnreferencedBlocks(std::list<v3s16> *unloaded_blocks=NULL);
+ void unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks=NULL);
// Deletes sectors and their blocks from memory
// Takes cache into account
// If deleted sector is in sector cache, clears cache
- void deleteSectors(std::list<v2s16> &list);
-
-#if 0
- /*
- Unload unused data
- = flush changed to disk and delete from memory, if usage timer of
- block is more than timeout
- */
- void unloadUnusedData(float timeout,
- core::list<v3s16> *deleted_blocks=NULL);
-#endif
+ void deleteSectors(std::vector<v2s16> &list);
// For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: "
virtual void PrintInfo(std::ostream &out);
@@ -310,7 +301,8 @@ public:
These are basically coordinate wrappers to MapBlock
*/
- NodeMetadata* getNodeMetadata(v3s16 p);
+ std::vector<v3s16> findNodesWithMetadata(v3s16 p1, v3s16 p2);
+ NodeMetadata *getNodeMetadata(v3s16 p);
/**
* Sets metadata for a node.
@@ -455,6 +447,7 @@ public:
/*
Database functions
*/
+ static Database *createDatabase(const std::string &name, const std::string &savedir, Settings &conf);
// Verify we can read/write to the database
void verifyDatabase();
@@ -466,8 +459,8 @@ public:
void endSave();
void save(ModifiedState save_level);
- void listAllLoadableBlocks(std::list<v3s16> &dst);
- void listAllLoadedBlocks(std::list<v3s16> &dst);
+ void listAllLoadableBlocks(std::vector<v3s16> &dst);
+ void listAllLoadedBlocks(std::vector<v3s16> &dst);
// Saves map seed and possibly other stuff
void saveMapMeta();
void loadMapMeta();
@@ -491,8 +484,8 @@ public:
// Returns true if sector now resides in memory
//bool deFlushSector(v2s16 p2d);
- bool saveBlock(MapBlock *block, Database *db);
bool saveBlock(MapBlock *block);
+ static bool saveBlock(MapBlock *block, Database *db);
// This will generate a sector with getSector if not found.
void loadBlock(std::string sectordir, std::string blockfile, MapSector *sector, bool save_after_load=false);
MapBlock* loadBlock(v3s16 p);
diff --git a/src/mapblock.cpp b/src/mapblock.cpp
index ecd9a016b..43057f3a5 100644
--- a/src/mapblock.cpp
+++ b/src/mapblock.cpp
@@ -38,6 +38,30 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
+static const char *modified_reason_strings[] = {
+ "initial",
+ "reallocate",
+ "setIsUnderground",
+ "setLightingExpired",
+ "setGenerated",
+ "setNode",
+ "setNodeNoCheck",
+ "setTimestamp",
+ "NodeMetaRef::reportMetadataChange",
+ "clearAllObjects",
+ "Timestamp expired (step)",
+ "addActiveObjectRaw",
+ "removeRemovedObjects/remove",
+ "removeRemovedObjects/deactivate",
+ "Stored list cleared in activateObjects due to overflow",
+ "deactivateFarObjects: Static data moved in",
+ "deactivateFarObjects: Static data moved out",
+ "deactivateFarObjects: Static data changed considerably",
+ "finishBlockMake: expireDayNightDiff",
+ "unknown",
+};
+
+
/*
MapBlock
*/
@@ -45,10 +69,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
m_parent(parent),
m_pos(pos),
+ m_pos_relative(pos * MAP_BLOCKSIZE),
m_gamedef(gamedef),
m_modified(MOD_STATE_WRITE_NEEDED),
- m_modified_reason("initial"),
- m_modified_reason_too_long(false),
+ m_modified_reason(MOD_REASON_INITIAL),
is_underground(false),
m_lighting_expired(true),
m_day_night_differs(false),
@@ -109,7 +133,28 @@ MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
}
if (is_valid_position)
*is_valid_position = true;
- return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
+ return data[p.Z * zstride + p.Y * ystride + p.X];
+}
+
+std::string MapBlock::getModifiedReasonString()
+{
+ std::string reason;
+
+ const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT,
+ ARRLEN(modified_reason_strings));
+
+ for (u32 i = 0; i != ubound; i++) {
+ if ((m_modified_reason & (1 << i)) == 0)
+ continue;
+
+ reason += modified_reason_strings[i];
+ reason += ", ";
+ }
+
+ if (reason.length() > 2)
+ reason.resize(reason.length() - 2);
+
+ return reason;
}
/*
@@ -330,47 +375,42 @@ void MapBlock::copyFrom(VoxelManipulator &dst)
void MapBlock::actuallyUpdateDayNightDiff()
{
INodeDefManager *nodemgr = m_gamedef->ndef();
+
// Running this function un-expires m_day_night_differs
m_day_night_differs_expired = false;
- if(data == NULL)
- {
+ if (data == NULL) {
m_day_night_differs = false;
return;
}
- bool differs = false;
+ bool differs;
/*
Check if any lighting value differs
*/
- for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
- {
+ for (u32 i = 0; i < nodecount; i++) {
MapNode &n = data[i];
- if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
- {
- differs = true;
+
+ differs = !n.isLightDayNightEq(nodemgr);
+ if (differs)
break;
- }
}
/*
If some lighting values differ, check if the whole thing is
- just air. If it is, differ = false
+ just air. If it is just air, differs = false
*/
- if(differs)
- {
+ if (differs) {
bool only_air = true;
- for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
- {
+ for (u32 i = 0; i < nodecount; i++) {
MapNode &n = data[i];
- if(n.getContent() != CONTENT_AIR)
- {
+ if (n.getContent() != CONTENT_AIR) {
only_air = false;
break;
}
}
- if(only_air)
+ if (only_air)
differs = false;
}
@@ -426,16 +466,15 @@ s16 MapBlock::getGroundLevel(v2s16 p2d)
// sure we can handle all content ids. But it's absolutely worth it as it's
// a speedup of 4 for one of the major time consuming functions on storing
// mapblocks.
-static content_t getBlockNodeIdMapping_mapping[USHRT_MAX];
+static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
INodeDefManager *nodedef)
{
- memset(getBlockNodeIdMapping_mapping, 0xFF, USHRT_MAX * sizeof(content_t));
+ memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
std::set<content_t> unknown_contents;
content_t id_counter = 0;
- for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
- {
+ for (u32 i = 0; i < MapBlock::nodecount; i++) {
content_t global_id = nodes[i].getContent();
content_t id = CONTENT_IGNORE;
@@ -480,8 +519,7 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
// correct ids.
std::set<content_t> unnamed_contents;
std::set<std::string> unallocatable_contents;
- for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
- {
+ for (u32 i = 0; i < MapBlock::nodecount; i++) {
content_t local_id = nodes[i].getContent();
std::string name;
bool found = nimap->getName(local_id, name);
@@ -526,7 +564,7 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
throw SerializationError("ERROR: Not writing dummy block.");
}
- assert(version >= SER_FMT_CLIENT_VER_LOWEST);
+ FATAL_ERROR_IF(version < SER_FMT_CLIENT_VER_LOWEST, "Serialize version error");
// First byte
u8 flags = 0;
@@ -544,7 +582,6 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
Bulk node data
*/
NameIdMapping nimap;
- u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
if(disk)
{
MapNode *tmp_nodes = new MapNode[nodecount];
@@ -644,7 +681,6 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
*/
TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
<<": Bulk node data"<<std::endl);
- u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
u8 content_width = readU8(is);
u8 params_width = readU8(is);
if(content_width != 1 && content_width != 2)
@@ -747,8 +783,6 @@ void MapBlock::deSerializeNetworkSpecific(std::istream &is)
void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
{
- u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
-
// Initialize default flags
is_underground = false;
m_day_night_differs = false;
diff --git a/src/mapblock.h b/src/mapblock.h
index e7d7798b8..334136b92 100644
--- a/src/mapblock.h
+++ b/src/mapblock.h
@@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodetimer.h"
#include "modifiedstate.h"
#include "util/numeric.h" // getContainerPos
+#include "settings.h"
class Map;
class NodeMetadataList;
@@ -97,21 +98,46 @@ public:
};
#endif
-/*
- MapBlock itself
-*/
+////
+//// MapBlock modified reason flags
+////
+
+#define MOD_REASON_INITIAL (1 << 0)
+#define MOD_REASON_REALLOCATE (1 << 1)
+#define MOD_REASON_SET_IS_UNDERGROUND (1 << 2)
+#define MOD_REASON_SET_LIGHTING_EXPIRED (1 << 3)
+#define MOD_REASON_SET_GENERATED (1 << 4)
+#define MOD_REASON_SET_NODE (1 << 5)
+#define MOD_REASON_SET_NODE_NO_CHECK (1 << 6)
+#define MOD_REASON_SET_TIMESTAMP (1 << 7)
+#define MOD_REASON_REPORT_META_CHANGE (1 << 8)
+#define MOD_REASON_CLEAR_ALL_OBJECTS (1 << 9)
+#define MOD_REASON_BLOCK_EXPIRED (1 << 10)
+#define MOD_REASON_ADD_ACTIVE_OBJECT_RAW (1 << 11)
+#define MOD_REASON_REMOVE_OBJECTS_REMOVE (1 << 12)
+#define MOD_REASON_REMOVE_OBJECTS_DEACTIVATE (1 << 13)
+#define MOD_REASON_TOO_MANY_OBJECTS (1 << 14)
+#define MOD_REASON_STATIC_DATA_ADDED (1 << 15)
+#define MOD_REASON_STATIC_DATA_REMOVED (1 << 16)
+#define MOD_REASON_STATIC_DATA_CHANGED (1 << 17)
+#define MOD_REASON_EXPIRE_DAYNIGHTDIFF (1 << 18)
+#define MOD_REASON_UNKNOWN (1 << 19)
+
+////
+//// MapBlock itself
+////
class MapBlock /*: public NodeContainer*/
{
public:
MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy=false);
~MapBlock();
-
+
/*virtual u16 nodeContainerId() const
{
return NODECONTAINER_ID_MAPBLOCK;
}*/
-
+
Map * getParent()
{
return m_parent;
@@ -119,150 +145,124 @@ public:
void reallocate()
{
- if(data != NULL)
- delete[] data;
- u32 l = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
- data = new MapNode[l];
- for(u32 i=0; i<l; i++){
- //data[i] = MapNode();
+ delete[] data;
+ data = new MapNode[nodecount];
+ for (u32 i = 0; i < nodecount; i++)
data[i] = MapNode(CONTENT_IGNORE);
- }
- raiseModified(MOD_STATE_WRITE_NEEDED, "reallocate");
- }
- /*
- Flags
- */
-
- bool isDummy()
- {
- return (data == NULL);
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE);
}
- void unDummify()
- {
- assert(isDummy());
- reallocate();
- }
-
- // m_modified methods
- void raiseModified(u32 mod, const std::string &reason="unknown")
- {
- if(mod > m_modified){
- m_modified = mod;
- m_modified_reason = reason;
- m_modified_reason_too_long = false;
- if(m_modified >= MOD_STATE_WRITE_AT_UNLOAD){
- m_disk_timestamp = m_timestamp;
- }
- } else if(mod == m_modified){
- if(!m_modified_reason_too_long){
- if(m_modified_reason.size() < 40)
- m_modified_reason += ", " + reason;
- else{
- m_modified_reason += "...";
- m_modified_reason_too_long = true;
- }
- }
- }
- }
- void raiseModified(u32 mod, const char *reason)
+ ////
+ //// Modification tracking methods
+ ////
+ void raiseModified(u32 mod, u32 reason=MOD_REASON_UNKNOWN)
{
- if (mod > m_modified){
+ if (mod > m_modified) {
m_modified = mod;
m_modified_reason = reason;
- m_modified_reason_too_long = false;
-
- if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD){
+ if (m_modified >= MOD_STATE_WRITE_AT_UNLOAD)
m_disk_timestamp = m_timestamp;
- }
- }
- else if (mod == m_modified){
- if (!m_modified_reason_too_long){
- if (m_modified_reason.size() < 40)
- m_modified_reason += ", " + std::string(reason);
- else{
- m_modified_reason += "...";
- m_modified_reason_too_long = true;
- }
- }
+ } else if (mod == m_modified) {
+ m_modified_reason |= reason;
}
}
- u32 getModified()
+ inline u32 getModified()
{
return m_modified;
}
- std::string getModifiedReason()
+
+ inline u32 getModifiedReason()
{
return m_modified_reason;
}
- void resetModified()
+
+ std::string getModifiedReasonString();
+
+ inline void resetModified()
{
m_modified = MOD_STATE_CLEAN;
- m_modified_reason = "none";
- m_modified_reason_too_long = false;
+ m_modified_reason = 0;
}
-
+
+ ////
+ //// Flags
+ ////
+
+ inline bool isDummy()
+ {
+ return (data == NULL);
+ }
+
+ inline void unDummify()
+ {
+ assert(isDummy()); // Pre-condition
+ reallocate();
+ }
+
// is_underground getter/setter
- bool getIsUnderground()
+ inline bool getIsUnderground()
{
return is_underground;
}
- void setIsUnderground(bool a_is_underground)
+
+ inline void setIsUnderground(bool a_is_underground)
{
is_underground = a_is_underground;
- raiseModified(MOD_STATE_WRITE_NEEDED, "setIsUnderground");
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND);
}
- void setLightingExpired(bool expired)
+ inline void setLightingExpired(bool expired)
{
- if(expired != m_lighting_expired){
+ if (expired != m_lighting_expired){
m_lighting_expired = expired;
- raiseModified(MOD_STATE_WRITE_NEEDED, "setLightingExpired");
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_EXPIRED);
}
}
- bool getLightingExpired()
+
+ inline bool getLightingExpired()
{
return m_lighting_expired;
}
- bool isGenerated()
+ inline bool isGenerated()
{
return m_generated;
}
- void setGenerated(bool b)
+
+ inline void setGenerated(bool b)
{
- if(b != m_generated){
- raiseModified(MOD_STATE_WRITE_NEEDED, "setGenerated");
+ if (b != m_generated) {
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_GENERATED);
m_generated = b;
}
}
- bool isValid()
+ inline bool isValid()
{
- if(m_lighting_expired)
+ if (m_lighting_expired)
return false;
- if(data == NULL)
+ if (data == NULL)
return false;
return true;
}
- /*
- Position stuff
- */
+ ////
+ //// Position stuff
+ ////
- v3s16 getPos()
+ inline v3s16 getPos()
{
return m_pos;
}
-
- v3s16 getPosRelative()
+
+ inline v3s16 getPosRelative()
{
- return m_pos * MAP_BLOCKSIZE;
+ return m_pos_relative;
}
-
- core::aabbox3d<s16> getBox()
+
+ inline core::aabbox3d<s16> getBox()
{
return core::aabbox3d<s16>(getPosRelative(),
getPosRelative()
@@ -270,140 +270,135 @@ public:
- v3s16(1,1,1));
}
- /*
- Regular MapNode get-setters
- */
-
- bool isValidPosition(s16 x, s16 y, s16 z)
+ ////
+ //// Regular MapNode get-setters
+ ////
+
+ inline bool isValidPosition(s16 x, s16 y, s16 z)
{
return data != NULL
- && x >= 0 && x < MAP_BLOCKSIZE
- && y >= 0 && y < MAP_BLOCKSIZE
- && z >= 0 && z < MAP_BLOCKSIZE;
+ && x >= 0 && x < MAP_BLOCKSIZE
+ && y >= 0 && y < MAP_BLOCKSIZE
+ && z >= 0 && z < MAP_BLOCKSIZE;
}
- bool isValidPosition(v3s16 p)
+ inline bool isValidPosition(v3s16 p)
{
return isValidPosition(p.X, p.Y, p.Z);
}
- MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
+ inline MapNode getNode(s16 x, s16 y, s16 z, bool *valid_position)
{
*valid_position = isValidPosition(x, y, z);
if (!*valid_position)
return MapNode(CONTENT_IGNORE);
- return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
+ return data[z * zstride + y * ystride + x];
}
-
- MapNode getNode(v3s16 p, bool *valid_position)
+
+ inline MapNode getNode(v3s16 p, bool *valid_position)
{
return getNode(p.X, p.Y, p.Z, valid_position);
}
-
- MapNode getNodeNoEx(v3s16 p)
+
+ inline MapNode getNodeNoEx(v3s16 p)
{
bool is_valid;
MapNode node = getNode(p.X, p.Y, p.Z, &is_valid);
return is_valid ? node : MapNode(CONTENT_IGNORE);
}
-
- void setNode(s16 x, s16 y, s16 z, MapNode & n)
+
+ inline void setNode(s16 x, s16 y, s16 z, MapNode & n)
{
- if(data == NULL)
+ if (!isValidPosition(x, y, z))
throw InvalidPositionException();
- if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
- if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
- if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
- data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
- raiseModified(MOD_STATE_WRITE_NEEDED, "setNode");
+
+ data[z * zstride + y * ystride + x] = n;
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
}
-
- void setNode(v3s16 p, MapNode & n)
+
+ inline void setNode(v3s16 p, MapNode & n)
{
setNode(p.X, p.Y, p.Z, n);
}
- /*
- Non-checking variants of the above
- */
+ ////
+ //// Non-checking variants of the above
+ ////
- MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
+ inline MapNode getNodeNoCheck(s16 x, s16 y, s16 z, bool *valid_position)
{
*valid_position = data != NULL;
- if(!valid_position)
+ if (!valid_position)
return MapNode(CONTENT_IGNORE);
- return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
+ return data[z * zstride + y * ystride + x];
}
-
- MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
+
+ inline MapNode getNodeNoCheck(v3s16 p, bool *valid_position)
{
return getNodeNoCheck(p.X, p.Y, p.Z, valid_position);
}
-
- void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
+
+ inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n)
{
- if(data == NULL)
+ if (data == NULL)
throw InvalidPositionException();
- data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x] = n;
- raiseModified(MOD_STATE_WRITE_NEEDED, "setNodeNoCheck");
+
+ data[z * zstride + y * ystride + x] = n;
+ raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
}
-
- void setNodeNoCheck(v3s16 p, MapNode & n)
+
+ inline void setNodeNoCheck(v3s16 p, MapNode & n)
{
setNodeNoCheck(p.X, p.Y, p.Z, n);
}
- /*
- These functions consult the parent container if the position
- is not valid on this MapBlock.
- */
+ // These functions consult the parent container if the position
+ // is not valid on this MapBlock.
bool isValidPositionParent(v3s16 p);
MapNode getNodeParent(v3s16 p, bool *is_valid_position = NULL);
void setNodeParent(v3s16 p, MapNode & n);
- void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
+ inline void drawbox(s16 x0, s16 y0, s16 z0, s16 w, s16 h, s16 d, MapNode node)
{
- for(u16 z=0; z<d; z++)
- for(u16 y=0; y<h; y++)
- for(u16 x=0; x<w; x++)
- setNode(x0+x, y0+y, z0+z, node);
+ for (u16 z = 0; z < d; z++)
+ for (u16 y = 0; y < h; y++)
+ for (u16 x = 0; x < w; x++)
+ setNode(x0 + x, y0 + y, z0 + z, node);
}
// See comments in mapblock.cpp
- bool propagateSunlight(std::set<v3s16> & light_sources,
- bool remove_light=false, bool *black_air_left=NULL);
-
+ bool propagateSunlight(std::set<v3s16> &light_sources,
+ bool remove_light=false, bool *black_air_left=NULL);
+
// Copies data to VoxelManipulator to getPosRelative()
void copyTo(VoxelManipulator &dst);
+
// Copies data from VoxelManipulator getPosRelative()
void copyFrom(VoxelManipulator &dst);
- /*
- Update day-night lighting difference flag.
- Sets m_day_night_differs to appropriate value.
- These methods don't care about neighboring blocks.
- */
+ // Update day-night lighting difference flag.
+ // Sets m_day_night_differs to appropriate value.
+ // These methods don't care about neighboring blocks.
void actuallyUpdateDayNightDiff();
- /*
- Call this to schedule what the previous function does to be done
- when the value is actually needed.
- */
+
+ // Call this to schedule what the previous function does to be done
+ // when the value is actually needed.
void expireDayNightDiff();
- bool getDayNightDiff()
+ inline bool getDayNightDiff()
{
- if(m_day_night_differs_expired)
+ if (m_day_night_differs_expired)
actuallyUpdateDayNightDiff();
return m_day_night_differs;
}
- /*
- Miscellaneous stuff
- */
-
+ ////
+ //// Miscellaneous stuff
+ ////
+
/*
Tries to measure ground level.
Return value:
@@ -414,84 +409,99 @@ public:
*/
s16 getGroundLevel(v2s16 p2d);
- /*
- Timestamp (see m_timestamp)
- NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
- */
- void setTimestamp(u32 time)
+ ////
+ //// Timestamp (see m_timestamp)
+ ////
+
+ // NOTE: BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
+
+ inline void setTimestamp(u32 time)
{
m_timestamp = time;
- raiseModified(MOD_STATE_WRITE_AT_UNLOAD, "setTimestamp");
+ raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_TIMESTAMP);
}
- void setTimestampNoChangedFlag(u32 time)
+
+ inline void setTimestampNoChangedFlag(u32 time)
{
m_timestamp = time;
}
- u32 getTimestamp()
+
+ inline u32 getTimestamp()
{
return m_timestamp;
}
- u32 getDiskTimestamp()
+
+ inline u32 getDiskTimestamp()
{
return m_disk_timestamp;
}
-
- /*
- See m_usage_timer
- */
- void resetUsageTimer()
+
+ ////
+ //// Usage timer (see m_usage_timer)
+ ////
+
+ inline void resetUsageTimer()
{
m_usage_timer = 0;
}
- void incrementUsageTimer(float dtime)
+
+ inline void incrementUsageTimer(float dtime)
{
m_usage_timer += dtime;
}
- float getUsageTimer()
+
+ inline float getUsageTimer()
{
return m_usage_timer;
}
- /*
- See m_refcount
- */
- void refGrab()
+ ////
+ //// Reference counting (see m_refcount)
+ ////
+
+ inline void refGrab()
{
m_refcount++;
}
- void refDrop()
+
+ inline void refDrop()
{
m_refcount--;
}
- int refGet()
+
+ inline int refGet()
{
return m_refcount;
}
-
- /*
- Node Timers
- */
- // Get timer
- NodeTimer getNodeTimer(v3s16 p){
+
+ ////
+ //// Node Timers
+ ////
+
+ inline NodeTimer getNodeTimer(v3s16 p)
+ {
return m_node_timers.get(p);
}
- // Deletes timer
- void removeNodeTimer(v3s16 p){
+
+ inline void removeNodeTimer(v3s16 p)
+ {
m_node_timers.remove(p);
}
- // Deletes old timer and sets a new one
- void setNodeTimer(v3s16 p, NodeTimer t){
+
+ inline void setNodeTimer(v3s16 p, NodeTimer t)
+ {
m_node_timers.set(p,t);
}
- // Deletes all timers
- void clearNodeTimers(){
+
+ inline void clearNodeTimers()
+ {
m_node_timers.clear();
}
- /*
- Serialization
- */
-
+ ////
+ //// Serialization
+ ///
+
// These don't write or read version by itself
// Set disk to true for on-disk format, false for over-the-network format
// Precondition: version >= SER_FMT_CLIENT_VER_LOWEST
@@ -514,16 +524,15 @@ private:
Used only internally, because changes can't be tracked
*/
- MapNode & getNodeRef(s16 x, s16 y, s16 z)
+ inline MapNode &getNodeRef(s16 x, s16 y, s16 z)
{
- if(data == NULL)
+ if (!isValidPosition(x, y, z))
throw InvalidPositionException();
- if(x < 0 || x >= MAP_BLOCKSIZE) throw InvalidPositionException();
- if(y < 0 || y >= MAP_BLOCKSIZE) throw InvalidPositionException();
- if(z < 0 || z >= MAP_BLOCKSIZE) throw InvalidPositionException();
- return data[z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + y*MAP_BLOCKSIZE + x];
+
+ return data[z * zstride + y * ystride + x];
}
- MapNode & getNodeRef(v3s16 &p)
+
+ inline MapNode &getNodeRef(v3s16 &p)
{
return getNodeRef(p.X, p.Y, p.Z);
}
@@ -536,11 +545,16 @@ public:
#ifndef SERVER // Only on client
MapBlockMesh *mesh;
#endif
-
+
NodeMetadataList m_node_metadata;
NodeTimerList m_node_timers;
StaticObjectList m_static_objects;
+ static const u32 ystride = MAP_BLOCKSIZE;
+ static const u32 zstride = MAP_BLOCKSIZE * MAP_BLOCKSIZE;
+
+ static const u32 nodecount = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
+
private:
/*
Private member variables
@@ -551,13 +565,21 @@ private:
// Position in blocks on parent
v3s16 m_pos;
+ /* This is the precalculated m_pos_relative value
+ * This caches the value, improving performance by removing 3 s16 multiplications
+ * at runtime on each getPosRelative call
+ * For a 5 minutes runtime with valgrind this removes 3 * 19M s16 multiplications
+ * The gain can be estimated in Release Build to 3 * 100M multiply operations for 5 mins
+ */
+ v3s16 m_pos_relative;
+
IGameDef *m_gamedef;
-
+
/*
If NULL, block is a dummy block.
Dummy blocks are used for caching not-found-on-disk blocks.
*/
- MapNode * data;
+ MapNode *data;
/*
- On the server, this is used for telling whether the
@@ -565,8 +587,7 @@ private:
- On the client, this is used for nothing.
*/
u32 m_modified;
- std::string m_modified_reason;
- bool m_modified_reason_too_long;
+ u32 m_modified_reason;
/*
When propagating sunlight and the above block doesn't exist,
@@ -586,13 +607,13 @@ private:
If this is true, lighting might be wrong or right.
*/
bool m_lighting_expired;
-
+
// Whether day and night lighting differs
bool m_day_night_differs;
bool m_day_night_differs_expired;
bool m_generated;
-
+
/*
When block is removed from active blocks, this is set to gametime.
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.
@@ -614,15 +635,18 @@ private:
int m_refcount;
};
+typedef std::vector<MapBlock*> MapBlockVect;
+
inline bool blockpos_over_limit(v3s16 p)
{
- return
- (p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
- || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE);
+ const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT,
+ g_settings->getU16("map_generation_limit"));
+ return (p.X < -map_gen_limit / MAP_BLOCKSIZE
+ || p.X > map_gen_limit / MAP_BLOCKSIZE
+ || p.Y < -map_gen_limit / MAP_BLOCKSIZE
+ || p.Y > map_gen_limit / MAP_BLOCKSIZE
+ || p.Z < -map_gen_limit / MAP_BLOCKSIZE
+ || p.Z > map_gen_limit / MAP_BLOCKSIZE);
}
/*
@@ -659,4 +683,3 @@ inline void getNodeSectorPosWithOffset(const v2s16 &p, v2s16 &block, v2s16 &offs
std::string analyze_block(MapBlock *block);
#endif
-
diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp
index cf311acba..33597b2fc 100644
--- a/src/mapblock_mesh.cpp
+++ b/src/mapblock_mesh.cpp
@@ -21,16 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "light.h"
#include "mapblock.h"
#include "map.h"
-#include "main.h" // for g_profiler
#include "profiler.h"
#include "nodedef.h"
#include "gamedef.h"
#include "mesh.h"
+#include "minimap.h"
#include "content_mapblock.h"
#include "noise.h"
#include "shader.h"
#include "settings.h"
#include "util/directiontables.h"
+#include <IMeshManipulator.h>
static void applyFacesShading(video::SColor& color, float factor)
{
@@ -42,7 +43,7 @@ static void applyFacesShading(video::SColor& color, float factor)
MeshMakeData
*/
-MeshMakeData::MeshMakeData(IGameDef *gamedef):
+MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders):
m_vmanip(),
m_blockpos(-1337,-1337,-1337),
m_crack_pos_relative(-1337, -1337, -1337),
@@ -50,7 +51,8 @@ MeshMakeData::MeshMakeData(IGameDef *gamedef):
m_smooth_lighting(false),
m_show_hud(false),
m_highlight_mesh_color(255, 255, 255, 255),
- m_gamedef(gamedef)
+ m_gamedef(gamedef),
+ m_use_shaders(use_shaders)
{}
void MeshMakeData::fill(MapBlock *block)
@@ -247,7 +249,7 @@ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
for (u32 i = 0; i < 8; i++)
{
- MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
+ const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]);
// if it's CONTENT_IGNORE we can't do any light calculations
if (n.getContent() == CONTENT_IGNORE) {
@@ -437,8 +439,6 @@ struct FastFace
static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
{
- FastFace face;
-
// Position is at the center of the cube.
v3f pos = p * BS;
@@ -589,6 +589,10 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
u8 alpha = tile.alpha;
+ dest.push_back(FastFace());
+
+ FastFace& face = *dest.rbegin();
+
face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
MapBlock_LightColor(alpha, li0, light_source),
core::vector2d<f32>(x0+w*abs_scale, y0+h));
@@ -603,7 +607,6 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
core::vector2d<f32>(x0+w*abs_scale, y0));
face.tile = tile;
- dest.push_back(face);
}
/*
@@ -744,8 +747,8 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
static void getTileInfo(
// Input:
MeshMakeData *data,
- v3s16 p,
- v3s16 face_dir,
+ const v3s16 &p,
+ const v3s16 &face_dir,
// Output:
bool &makes_face,
v3s16 &p_corrected,
@@ -759,14 +762,20 @@ static void getTileInfo(
INodeDefManager *ndef = data->m_gamedef->ndef();
v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
- MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
+ MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
// Don't even try to get n1 if n0 is already CONTENT_IGNORE
- if (n0.getContent() == CONTENT_IGNORE ) {
+ if (n0.getContent() == CONTENT_IGNORE) {
+ makes_face = false;
+ return;
+ }
+
+ const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
+
+ if (n1.getContent() == CONTENT_IGNORE) {
makes_face = false;
return;
}
- MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
// This is hackish
bool equivalent = false;
@@ -1020,7 +1029,10 @@ static void updateAllFastFaceRows(MeshMakeData *data,
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_mesh(new scene::SMesh()),
+ m_minimap_mapblock(NULL),
m_gamedef(data->m_gamedef),
+ m_tsrc(m_gamedef->getTextureSource()),
+ m_shdrsrc(m_gamedef->getShaderSource()),
m_animation_force_timer(0), // force initial animation
m_last_crack(-1),
m_crack_materials(),
@@ -1028,14 +1040,21 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
m_last_daynight_ratio((u32) -1),
m_daynight_diffs()
{
- m_enable_shaders = g_settings->getBool("enable_shaders");
+ m_enable_shaders = data->m_use_shaders;
m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
+ if (g_settings->getBool("enable_minimap")) {
+ m_minimap_mapblock = new MinimapMapblock;
+ m_minimap_mapblock->getMinimapNodes(
+ &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
+ }
+
// 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
// 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
//TimeTaker timer1("MapBlockMesh()");
std::vector<FastFace> fastfaces_new;
+ fastfaces_new.reserve(512);
/*
We are including the faces of the trailing edges of the block.
@@ -1102,8 +1121,6 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
/*
Convert MeshCollector to SMesh
*/
- ITextureSource *tsrc = m_gamedef->tsrc();
- IShaderSource *shdrsrc = m_gamedef->getShaderSource();
for(u32 i = 0; i < collector.prebuffers.size(); i++)
{
@@ -1115,13 +1132,13 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
{
// Find the texture name plus ^[crack:N:
std::ostringstream os(std::ios::binary);
- os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
+ os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
os<<"o"; // use ^[cracko
os<<":"<<(u32)p.tile.animation_frame_count<<":";
m_crack_materials.insert(std::make_pair(i, os.str()));
// Replace tile texture with the cracked one
- p.tile.texture = tsrc->getTexture(
+ p.tile.texture = m_tsrc->getTextureForMesh(
os.str()+"0",
&p.tile.texture_id);
}
@@ -1146,24 +1163,25 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
}
if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
- m_highlighted_materials.push_back(i);
+ m_highlighted_materials.push_back(i);
for(u32 j = 0; j < p.vertices.size(); j++)
{
+ video::S3DVertexTangents *vertex = &p.vertices[j];
// Note applyFacesShading second parameter is precalculated sqrt
// value for speed improvement
// Skip it for lightsources and top faces.
- video::SColor &vc = p.vertices[j].Color;
+ video::SColor &vc = vertex->Color;
if (!vc.getBlue()) {
- if (p.vertices[j].Normal.Y < -0.5) {
+ if (vertex->Normal.Y < -0.5) {
applyFacesShading (vc, 0.447213);
- } else if (p.vertices[j].Normal.X > 0.5) {
+ } else if (vertex->Normal.X > 0.5) {
applyFacesShading (vc, 0.670820);
- } else if (p.vertices[j].Normal.X < -0.5) {
+ } else if (vertex->Normal.X < -0.5) {
applyFacesShading (vc, 0.670820);
- } else if (p.vertices[j].Normal.Z > 0.5) {
+ } else if (vertex->Normal.Z > 0.5) {
applyFacesShading (vc, 0.836660);
- } else if (p.vertices[j].Normal.Z < -0.5) {
+ } else if (vertex->Normal.Z < -0.5) {
applyFacesShading (vc, 0.836660);
}
}
@@ -1191,33 +1209,28 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
} else {
if (m_enable_shaders) {
- material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
+ material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
p.tile.applyMaterialOptionsWithShaders(material);
if (p.tile.normal_texture) {
material.setTexture(1, p.tile.normal_texture);
- material.setTexture(2, tsrc->getTexture("enable_img.png"));
- } else {
- material.setTexture(2, tsrc->getTexture("disable_img.png"));
}
+ material.setTexture(2, p.tile.flags_texture);
} else {
p.tile.applyMaterialOptions(material);
}
}
- // Create meshbuffer
- // This is a "Standard MeshBuffer",
- // it's a typedeffed CMeshBuffer<video::S3DVertex>
- scene::SMeshBuffer *buf = new scene::SMeshBuffer();
- // Set material
- buf->Material = material;
- // Add to mesh
- m_mesh->addMeshBuffer(buf);
- // Mesh grabbed it
- buf->drop();
- buf->append(&p.vertices[0], p.vertices.size(),
- &p.indices[0], p.indices.size());
- }
-
+ // Create meshbuffer
+ scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
+ // Set material
+ buf->Material = material;
+ // Add to mesh
+ m_mesh->addMeshBuffer(buf);
+ // Mesh grabbed it
+ buf->drop();
+ buf->append(&p.vertices[0], p.vertices.size(),
+ &p.indices[0], p.indices.size());
+}
m_camera_offset = camera_offset;
/*
@@ -1226,6 +1239,11 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
+ if (m_enable_shaders) {
+ scene::IMeshManipulator* meshmanip = m_gamedef->getSceneManager()->getMeshManipulator();
+ meshmanip->recalculateTangents(m_mesh, true, false, false);
+ }
+
if(m_mesh)
{
#if 0
@@ -1260,11 +1278,11 @@ MapBlockMesh::~MapBlockMesh()
{
m_mesh->drop();
m_mesh = NULL;
+ delete m_minimap_mapblock;
}
bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
{
-
if(!m_has_animation)
{
m_animation_force_timer = 100000;
@@ -1284,12 +1302,11 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
std::string basename = i->second;
// Create new texture name from original
- ITextureSource *tsrc = m_gamedef->getTextureSource();
std::ostringstream os;
os<<basename<<crack;
u32 new_texture_id = 0;
video::ITexture *new_texture =
- tsrc->getTexture(os.str(), &new_texture_id);
+ m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
buf->getMaterial().setTexture(0, new_texture);
// If the current material is also animated,
@@ -1325,17 +1342,14 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
m_animation_frames[i->first] = frame;
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
- ITextureSource *tsrc = m_gamedef->getTextureSource();
FrameSpec animation_frame = tile.frames[frame];
buf->getMaterial().setTexture(0, animation_frame.texture);
if (m_enable_shaders) {
if (animation_frame.normal_texture) {
buf->getMaterial().setTexture(1, animation_frame.normal_texture);
- buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
- } else {
- buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
}
+ buf->getMaterial().setTexture(2, animation_frame.flags_texture);
}
}
@@ -1347,16 +1361,14 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
i != m_daynight_diffs.end(); i++)
{
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+ video::S3DVertexTangents *vertices = (video::S3DVertexTangents *)buf->getVertices();
for(std::map<u32, std::pair<u8, u8 > >::iterator
j = i->second.begin();
j != i->second.end(); j++)
{
- u32 vertexIndex = j->first;
u8 day = j->second.first;
u8 night = j->second.second;
- finalColorBlend(vertices[vertexIndex].Color,
- day, night, daynight_ratio);
+ finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
}
}
m_last_daynight_ratio = daynight_ratio;
@@ -1365,7 +1377,7 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
// Node highlighting
if (m_enable_highlighting) {
u8 day = m_highlight_mesh_color.getRed();
- u8 night = m_highlight_mesh_color.getGreen();
+ u8 night = m_highlight_mesh_color.getGreen();
video::SColor hc;
finalColorBlend(hc, day, night, daynight_ratio);
float sin_r = 0.07 * sin(1.5 * time);
@@ -1380,7 +1392,7 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat
i != m_highlighted_materials.end(); i++)
{
scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+ video::S3DVertexTangents *vertices = (video::S3DVertexTangents*)buf->getVertices();
for (u32 j = 0; j < buf->getVertexCount() ;j++)
vertices[j].Color = hc;
}
@@ -1405,42 +1417,40 @@ void MeshCollector::append(const TileSpec &tile,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices)
{
- if(numIndices > 65535)
- {
+ if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
PreMeshBuffer *p = NULL;
- for(u32 i=0; i<prebuffers.size(); i++)
- {
+ for (u32 i = 0; i < prebuffers.size(); i++) {
PreMeshBuffer &pp = prebuffers[i];
- if(pp.tile != tile)
+ if (pp.tile != tile)
continue;
- if(pp.indices.size() + numIndices > 65535)
+ if (pp.indices.size() + numIndices > 65535)
continue;
p = &pp;
break;
}
- if(p == NULL)
- {
+ if (p == NULL) {
PreMeshBuffer pp;
pp.tile = tile;
prebuffers.push_back(pp);
- p = &prebuffers[prebuffers.size()-1];
+ p = &prebuffers[prebuffers.size() - 1];
}
u32 vertex_count = p->vertices.size();
- for(u32 i=0; i<numIndices; i++)
- {
+ for (u32 i = 0; i < numIndices; i++) {
u32 j = indices[i] + vertex_count;
p->indices.push_back(j);
}
- for(u32 i=0; i<numVertices; i++)
- {
- p->vertices.push_back(vertices[i]);
+
+ for (u32 i = 0; i < numVertices; i++) {
+ video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
+ vertices[i].Color, vertices[i].TCoords);
+ p->vertices.push_back(vert);
}
}
@@ -1453,15 +1463,13 @@ void MeshCollector::append(const TileSpec &tile,
const u16 *indices, u32 numIndices,
v3f pos, video::SColor c)
{
- if(numIndices > 65535)
- {
+ if (numIndices > 65535) {
dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
return;
}
PreMeshBuffer *p = NULL;
- for(u32 i=0; i<prebuffers.size(); i++)
- {
+ for (u32 i = 0; i < prebuffers.size(); i++) {
PreMeshBuffer &pp = prebuffers[i];
if(pp.tile != tile)
continue;
@@ -1472,25 +1480,22 @@ void MeshCollector::append(const TileSpec &tile,
break;
}
- if(p == NULL)
- {
+ if (p == NULL) {
PreMeshBuffer pp;
pp.tile = tile;
prebuffers.push_back(pp);
- p = &prebuffers[prebuffers.size()-1];
+ p = &prebuffers[prebuffers.size() - 1];
}
u32 vertex_count = p->vertices.size();
- for(u32 i=0; i<numIndices; i++)
- {
+ for (u32 i = 0; i < numIndices; i++) {
u32 j = indices[i] + vertex_count;
p->indices.push_back(j);
}
- for(u32 i=0; i<numVertices; i++)
- {
- video::S3DVertex vert = vertices[i];
- vert.Pos += pos;
- vert.Color = c;
+
+ for (u32 i = 0; i < numVertices; i++) {
+ video::S3DVertexTangents vert(vertices[i].Pos + pos, vertices[i].Normal,
+ c, vertices[i].TCoords);
p->vertices.push_back(vert);
}
}
diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h
index be56d4c58..8e994ec6b 100644
--- a/src/mapblock_mesh.h
+++ b/src/mapblock_mesh.h
@@ -21,11 +21,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MAPBLOCK_MESH_HEADER
#include "irrlichttypes_extrabloated.h"
-#include "tile.h"
+#include "client/tile.h"
#include "voxel.h"
#include <map>
class IGameDef;
+class IShaderSource;
/*
Mesh making stuff
@@ -33,6 +34,7 @@ class IGameDef;
class MapBlock;
+struct MinimapMapblock;
struct MeshMakeData
{
@@ -45,8 +47,9 @@ struct MeshMakeData
video::SColor m_highlight_mesh_color;
IGameDef *m_gamedef;
+ bool m_use_shaders;
- MeshMakeData(IGameDef *gamedef);
+ MeshMakeData(IGameDef *gamedef, bool use_shaders);
/*
Copy central data directly from block, and other data from
@@ -101,11 +104,18 @@ public:
// Returns true if anything has been changed.
bool animate(bool faraway, float time, int crack, u32 daynight_ratio);
- scene::SMesh* getMesh()
+ scene::SMesh *getMesh()
{
return m_mesh;
}
+ MinimapMapblock *moveMinimapMapblock()
+ {
+ MinimapMapblock *p = m_minimap_mapblock;
+ m_minimap_mapblock = NULL;
+ return p;
+ }
+
bool isAnimationForced() const
{
return m_animation_force_timer == 0;
@@ -121,7 +131,10 @@ public:
private:
scene::SMesh *m_mesh;
+ MinimapMapblock *m_minimap_mapblock;
IGameDef *m_gamedef;
+ ITextureSource *m_tsrc;
+ IShaderSource *m_shdrsrc;
bool m_enable_shaders;
bool m_enable_highlighting;
@@ -164,13 +177,12 @@ struct PreMeshBuffer
{
TileSpec tile;
std::vector<u16> indices;
- std::vector<video::S3DVertex> vertices;
+ std::vector<video::S3DVertexTangents> vertices;
};
struct MeshCollector
{
std::vector<PreMeshBuffer> prebuffers;
-
void append(const TileSpec &material,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices);
diff --git a/src/mapchunk.h b/src/mapchunk.h
deleted file mode 100644
index a70de8711..000000000
--- a/src/mapchunk.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
-*/
-
-#ifndef MAPCHUNK_HEADER
-#define MAPCHUNK_HEADER
-
-/*
- TODO: Remove
-*/
-
-#if 0
-/*
- MapChunk contains map-generation-time metadata for an area of
- some MapSectors. (something like 16x16)
-*/
-
-// These should fit in 8 bits, as they are saved as such.
-enum{
- GENERATED_FULLY = 0,
- GENERATED_PARTLY = 10,
- GENERATED_NOT = 20
-};
-
-class MapChunk
-{
-public:
- MapChunk():
- m_generation_level(GENERATED_NOT),
- m_modified(true)
- {
- }
-
- /*
- Generation level. Possible values:
- GENERATED_FULLY = 0 = fully generated
- GENERATED_PARTLY = partly generated
- GENERATED_NOT = not generated
- */
- u16 getGenLevel(){ return m_generation_level; }
- void setGenLevel(u16 lev){ m_generation_level = lev; }
-
- void serialize(std::ostream &os, u8 version)
- {
- os.write((char*)&m_generation_level, 1);
- }
- void deSerialize(std::istream &is, u8 version)
- {
- is.read((char*)&m_generation_level, 1);
- }
-
- bool isModified(){ return m_modified; }
- void setModified(bool modified){ m_modified = modified; }
-
-private:
- u8 m_generation_level;
- bool m_modified;
-};
-#endif
-
-#endif
-
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index ba1b16d6a..f8e9477c5 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -1,6 +1,7 @@
/*
Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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
@@ -30,17 +31,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "emerge.h"
#include "content_mapnode.h" // For content_mapnode_get_new_name
#include "voxelalgorithms.h"
+#include "porting.h"
#include "profiler.h"
-#include "settings.h" // For g_settings
-#include "main.h" // For g_profiler
+#include "settings.h"
#include "treegen.h"
#include "serialization.h"
#include "util/serialize.h"
+#include "util/numeric.h"
#include "filesys.h"
#include "log.h"
-const char *GenElementManager::ELEMENT_TITLE = "element";
-
FlagDesc flagdesc_mapgen[] = {
{"trees", MG_TREES},
{"caves", MG_CAVES},
@@ -64,35 +64,40 @@ FlagDesc flagdesc_gennotify[] = {
///////////////////////////////////////////////////////////////////////////////
+
Mapgen::Mapgen()
{
- generating = false;
- id = -1;
- seed = 0;
- water_level = 0;
- flags = 0;
-
- vm = NULL;
- ndef = NULL;
- heightmap = NULL;
- biomemap = NULL;
+ generating = false;
+ id = -1;
+ seed = 0;
+ water_level = 0;
+ flags = 0;
+
+ vm = NULL;
+ ndef = NULL;
+ heightmap = NULL;
+ biomemap = NULL;
+ heatmap = NULL;
+ humidmap = NULL;
}
Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
{
- generating = false;
- id = mapgenid;
- seed = (int)params->seed;
- water_level = params->water_level;
- flags = params->flags;
- csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
+ generating = false;
+ id = mapgenid;
+ seed = (int)params->seed;
+ water_level = params->water_level;
+ flags = params->flags;
+ csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
vm = NULL;
ndef = NULL;
heightmap = NULL;
biomemap = NULL;
+ heatmap = NULL;
+ humidmap = NULL;
}
@@ -138,6 +143,7 @@ s16 Mapgen::findGroundLevelFull(v2s16 p2d)
}
+// Returns -MAX_MAP_GENERATION_LIMIT if not found
s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
{
v3s16 em = vm->m_area.getExtent();
@@ -151,7 +157,7 @@ s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
vm->m_area.add_y(em, i, -1);
}
- return y;
+ return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
}
@@ -166,12 +172,6 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
- // if the values found are out of range, trust the old heightmap
- if (y == nmax.Y && heightmap[index] > nmax.Y)
- continue;
- if (y == nmin.Y - 1 && heightmap[index] < nmin.Y)
- continue;
-
heightmap[index] = y;
}
}
@@ -342,30 +342,6 @@ void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
-void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax)
-{
- enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT};
- VoxelArea a(nmin, nmax);
- bool block_is_underground = (water_level > nmax.Y);
- bool sunlight = !block_is_underground;
-
- ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
-
- for (int i = 0; i < 2; i++) {
- enum LightBank bank = banks[i];
- std::set<v3s16> light_sources;
- std::map<v3s16, u8> unlight_from;
-
- voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef,
- light_sources, unlight_from);
- voxalgo::propagateSunlight(*vm, a, sunlight, light_sources, ndef);
-
- vm->unspreadLight(bank, unlight_from, light_sources, ndef);
- vm->spreadLight(bank, light_sources, ndef);
- }
-}
-
-
///////////////////////////////////////////////////////////////////////////////
GenerateNotifier::GenerateNotifier()
@@ -432,84 +408,46 @@ void GenerateNotifier::getEvents(
m_notify_events.clear();
}
-
///////////////////////////////////////////////////////////////////////////////
-
-GenElementManager::GenElementManager(IGameDef *gamedef)
-{
- m_ndef = gamedef->getNodeDefManager();
-}
-
-
-GenElementManager::~GenElementManager()
-{
- for (size_t i = 0; i != m_elements.size(); i++)
- delete m_elements[i];
-}
-
-
-u32 GenElementManager::add(GenElement *elem)
-{
- size_t nelem = m_elements.size();
-
- for (size_t i = 0; i != nelem; i++) {
- if (m_elements[i] == NULL) {
- elem->id = i;
- m_elements[i] = elem;
- return i;
- }
- }
-
- if (nelem >= this->ELEMENT_LIMIT)
- return -1;
-
- elem->id = nelem;
- m_elements.push_back(elem);
-
- verbosestream << "GenElementManager: added " << this->ELEMENT_TITLE
- << " element '" << elem->name << "'" << std::endl;
-
- return nelem;
-}
-
-
-GenElement *GenElementManager::get(u32 id)
-{
- return (id < m_elements.size()) ? m_elements[id] : NULL;
-}
-
-
-GenElement *GenElementManager::getByName(const std::string &name)
-{
- for (size_t i = 0; i != m_elements.size(); i++) {
- GenElement *elem = m_elements[i];
- if (elem && name == elem->name)
- return elem;
- }
-
- return NULL;
-}
-
-
-GenElement *GenElementManager::update(u32 id, GenElement *elem)
-{
- if (id >= m_elements.size())
- return NULL;
-
- GenElement *old_elem = m_elements[id];
- m_elements[id] = elem;
- return old_elem;
-}
-
-
-GenElement *GenElementManager::remove(u32 id)
+void MapgenParams::load(const Settings &settings)
{
- return update(id, NULL);
+ std::string seed_str;
+ const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
+
+ if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty())
+ seed = read_seed(seed_str.c_str());
+ else
+ myrand_bytes(&seed, sizeof(seed));
+
+ settings.getNoEx("mg_name", mg_name);
+ settings.getS16NoEx("water_level", water_level);
+ settings.getS16NoEx("chunksize", chunksize);
+ settings.getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
+ settings.getNoiseParams("mg_biome_np_heat", np_biome_heat);
+ settings.getNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend);
+ settings.getNoiseParams("mg_biome_np_humidity", np_biome_humidity);
+ settings.getNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend);
+
+ delete sparams;
+ sparams = EmergeManager::createMapgenParams(mg_name);
+ if (sparams)
+ sparams->readParams(&settings);
}
-void GenElementManager::clear()
+void MapgenParams::save(Settings &settings) const
{
- m_elements.clear();
+ settings.set("mg_name", mg_name);
+ settings.setU64("seed", seed);
+ settings.setS16("water_level", water_level);
+ settings.setS16("chunksize", chunksize);
+ settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, (u32)-1);
+ settings.setNoiseParams("mg_biome_np_heat", np_biome_heat);
+ settings.setNoiseParams("mg_biome_np_heat_blend", np_biome_heat_blend);
+ settings.setNoiseParams("mg_biome_np_humidity", np_biome_humidity);
+ settings.setNoiseParams("mg_biome_np_humidity_blend", np_biome_humidity_blend);
+
+ if (sparams)
+ sparams->writeParams(&settings);
}
diff --git a/src/mapgen.h b/src/mapgen.h
index 5bbdd724d..46328ba92 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -70,6 +70,13 @@ enum GenNotifyType {
NUM_GENNOTIFY_TYPES
};
+// TODO(hmmmm/paramat): make stone type selection dynamic
+enum MgStoneType {
+ STONE,
+ DESERT_STONE,
+ SANDSTONE,
+};
+
struct GenNotifyEvent {
GenNotifyType type;
v3s16 pos;
@@ -95,8 +102,8 @@ private:
};
struct MapgenSpecificParams {
- virtual void readParams(Settings *settings) = 0;
- virtual void writeParams(Settings *settings) = 0;
+ virtual void readParams(const Settings *settings) = 0;
+ virtual void writeParams(Settings *settings) const = 0;
virtual ~MapgenSpecificParams() {}
};
@@ -108,21 +115,27 @@ struct MapgenParams {
u32 flags;
NoiseParams np_biome_heat;
+ NoiseParams np_biome_heat_blend;
NoiseParams np_biome_humidity;
+ NoiseParams np_biome_humidity_blend;
MapgenSpecificParams *sparams;
- MapgenParams()
- {
- mg_name = DEFAULT_MAPGEN;
- seed = 0;
- water_level = 1;
- chunksize = 5;
- flags = MG_TREES | MG_CAVES | MG_LIGHT;
- sparams = NULL;
- np_biome_heat = NoiseParams(50, 50, v3f(500.0, 500.0, 500.0), 5349, 3, 0.5, 2.0);
- np_biome_humidity = NoiseParams(50, 50, v3f(500.0, 500.0, 500.0), 842, 3, 0.5, 2.0);
- }
+ MapgenParams() :
+ mg_name(DEFAULT_MAPGEN),
+ chunksize(5),
+ seed(0),
+ water_level(1),
+ flags(MG_TREES | MG_CAVES | MG_LIGHT),
+ np_biome_heat(NoiseParams(50, 50, v3f(1000.0, 1000.0, 1000.0), 5349, 3, 0.5, 2.0)),
+ np_biome_heat_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0)),
+ np_biome_humidity(NoiseParams(50, 50, v3f(1000.0, 1000.0, 1000.0), 842, 3, 0.5, 2.0)),
+ np_biome_humidity_blend(NoiseParams(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)),
+ sparams(NULL)
+ {}
+
+ void load(const Settings &settings);
+ void save(Settings &settings) const;
};
class Mapgen {
@@ -139,6 +152,8 @@ public:
u32 blockseed;
s16 *heightmap;
u8 *biomemap;
+ float *heatmap;
+ float *humidmap;
v3s16 csize;
GenerateNotifier gennotify;
@@ -164,8 +179,6 @@ public:
void propagateSunlight(v3s16 nmin, v3s16 nmax);
void spreadLight(v3s16 nmin, v3s16 nmax);
- void calcLightingOld(v3s16 nmin, v3s16 nmax);
-
virtual void makeChunk(BlockMakeData *data) {}
virtual int getGroundLevelAtPoint(v2s16 p) { return 0; }
};
@@ -177,34 +190,4 @@ struct MapgenFactory {
virtual ~MapgenFactory() {}
};
-class GenElement {
-public:
- virtual ~GenElement() {}
- u32 id;
- std::string name;
-};
-
-class GenElementManager {
-public:
- static const char *ELEMENT_TITLE;
- static const size_t ELEMENT_LIMIT = -1;
-
- GenElementManager(IGameDef *gamedef);
- virtual ~GenElementManager();
-
- virtual GenElement *create(int type) = 0;
-
- virtual u32 add(GenElement *elem);
- virtual GenElement *get(u32 id);
- virtual GenElement *update(u32 id, GenElement *elem);
- virtual GenElement *remove(u32 id);
- virtual void clear();
-
- virtual GenElement *getByName(const std::string &name);
-
-protected:
- INodeDefManager *m_ndef;
- std::vector<GenElement *> m_elements;
-};
-
#endif
diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp
index 5f81aba98..a8b84e849 100644
--- a/src/mapgen_singlenode.cpp
+++ b/src/mapgen_singlenode.cpp
@@ -24,21 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "nodedef.h"
#include "voxelalgorithms.h"
-#include "profiler.h"
#include "emerge.h"
-//////////////////////// Mapgen Singlenode parameter read/write
-
-void MapgenSinglenodeParams::readParams(Settings *settings)
-{
-}
-
-
-void MapgenSinglenodeParams::writeParams(Settings *settings)
-{
-}
-
-///////////////////////////////////////////////////////////////////////////////
MapgenSinglenode::MapgenSinglenode(int mapgenid,
MapgenParams *params, EmergeManager *emerge)
@@ -62,6 +49,7 @@ MapgenSinglenode::~MapgenSinglenode()
void MapgenSinglenode::makeChunk(BlockMakeData *data)
{
+ // Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h
index 9fd1d75b3..35f2ba385 100644
--- a/src/mapgen_singlenode.h
+++ b/src/mapgen_singlenode.h
@@ -27,8 +27,8 @@ struct MapgenSinglenodeParams : public MapgenSpecificParams {
MapgenSinglenodeParams() {}
~MapgenSinglenodeParams() {}
- void readParams(Settings *settings);
- void writeParams(Settings *settings);
+ void readParams(const Settings *settings) {}
+ void writeParams(Settings *settings) const {}
};
class MapgenSinglenode : public Mapgen {
diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp
index f7efc4e18..5b842a99e 100644
--- a/src/mapgen_v5.cpp
+++ b/src/mapgen_v5.cpp
@@ -27,9 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "nodedef.h"
#include "voxelalgorithms.h"
-#include "profiler.h"
#include "settings.h" // For g_settings
-#include "main.h" // For g_profiler
#include "emerge.h"
#include "dungeongen.h"
#include "cavegen.h"
@@ -42,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
FlagDesc flagdesc_mapgen_v5[] = {
- {"blobs", MGV5_BLOBS},
{NULL, 0}
};
@@ -60,6 +57,8 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
this->biomemap = new u8[csize.X * csize.Z];
this->heightmap = new s16[csize.X * csize.Z];
+ this->heatmap = NULL;
+ this->humidmap = NULL;
MapgenV5Params *sp = (MapgenV5Params *)params->sparams;
this->spflags = sp->spflags;
@@ -70,42 +69,42 @@ MapgenV5::MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_height = new Noise(&sp->np_height, seed, csize.X, csize.Z);
// 3D terrain noise
- noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z);
- noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z);
- noise_ground = new Noise(&sp->np_ground, seed, csize.X, csize.Y + 2, csize.Z);
- noise_crumble = new Noise(&sp->np_crumble, seed, csize.X, csize.Y + 2, csize.Z);
- noise_wetness = new Noise(&sp->np_wetness, seed, csize.X, csize.Y + 2, csize.Z);
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z);
+ noise_ground = new Noise(&sp->np_ground, seed, csize.X, csize.Y + 2, csize.Z);
// Biome noise
- noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
- noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+ noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
+ noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+ noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
+ noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
- c_stone = ndef->getId("mapgen_stone");
- c_dirt = ndef->getId("mapgen_dirt");
- c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
- c_sand = ndef->getId("mapgen_sand");
- c_water_source = ndef->getId("mapgen_water_source");
- c_lava_source = ndef->getId("mapgen_lava_source");
- c_gravel = ndef->getId("mapgen_gravel");
- c_cobble = ndef->getId("mapgen_cobble");
- c_ice = ndef->getId("default:ice");
- c_mossycobble = ndef->getId("mapgen_mossycobble");
- c_sandbrick = ndef->getId("mapgen_sandstonebrick");
- c_stair_cobble = ndef->getId("mapgen_stair_cobble");
- c_stair_sandstone = ndef->getId("mapgen_stair_sandstone");
+ c_stone = ndef->getId("mapgen_stone");
+ c_water_source = ndef->getId("mapgen_water_source");
+ c_lava_source = ndef->getId("mapgen_lava_source");
+ c_desert_stone = ndef->getId("mapgen_desert_stone");
+ c_ice = ndef->getId("mapgen_ice");
+ c_sandstone = ndef->getId("mapgen_sandstone");
+
+ c_cobble = ndef->getId("mapgen_cobble");
+ c_stair_cobble = ndef->getId("mapgen_stair_cobble");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+ c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
+ c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
+
if (c_ice == CONTENT_IGNORE)
c_ice = CONTENT_AIR;
if (c_mossycobble == CONTENT_IGNORE)
c_mossycobble = c_cobble;
- if (c_sandbrick == CONTENT_IGNORE)
- c_sandbrick = c_desert_stone;
if (c_stair_cobble == CONTENT_IGNORE)
c_stair_cobble = c_cobble;
- if (c_stair_sandstone == CONTENT_IGNORE)
- c_stair_sandstone = c_sandbrick;
+ if (c_sandstonebrick == CONTENT_IGNORE)
+ c_sandstonebrick = c_sandstone;
+ if (c_stair_sandstonebrick == CONTENT_IGNORE)
+ c_stair_sandstonebrick = c_sandstone;
}
@@ -117,11 +116,11 @@ MapgenV5::~MapgenV5()
delete noise_cave1;
delete noise_cave2;
delete noise_ground;
- delete noise_crumble;
- delete noise_wetness;
delete noise_heat;
delete noise_humidity;
+ delete noise_heat_blend;
+ delete noise_humidity_blend;
delete[] heightmap;
delete[] biomemap;
@@ -130,7 +129,7 @@ MapgenV5::~MapgenV5()
MapgenV5Params::MapgenV5Params()
{
- spflags = MGV5_BLOBS;
+ spflags = 0;
np_filler_depth = NoiseParams(0, 1, v3f(150, 150, 150), 261, 4, 0.7, 2.0);
np_factor = NoiseParams(0, 1, v3f(250, 250, 250), 920381, 3, 0.45, 2.0);
@@ -138,21 +137,14 @@ MapgenV5Params::MapgenV5Params()
np_cave1 = NoiseParams(0, 12, v3f(50, 50, 50), 52534, 4, 0.5, 2.0);
np_cave2 = NoiseParams(0, 12, v3f(50, 50, 50), 10325, 4, 0.5, 2.0);
np_ground = NoiseParams(0, 40, v3f(80, 80, 80), 983240, 4, 0.55, 2.0, NOISE_FLAG_EASED);
- np_crumble = NoiseParams(0, 1, v3f(20, 20, 20), 34413, 3, 1.3, 2.0, NOISE_FLAG_EASED);
- np_wetness = NoiseParams(0, 1, v3f(40, 40, 40), 32474, 4, 1.1, 2.0);
}
-// Scaling the output of the noise function affects the overdrive of the
-// contour function, which affects the shape of the output considerably.
-
-// Two original MT 0.3 parameters for non-eased noise:
-
//#define CAVE_NOISE_SCALE 12.0
-//#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE)
+//#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE) = 0.125
-void MapgenV5Params::readParams(Settings *settings)
+void MapgenV5Params::readParams(const Settings *settings)
{
settings->getFlagStrNoEx("mgv5_spflags", spflags, flagdesc_mapgen_v5);
@@ -162,12 +154,10 @@ void MapgenV5Params::readParams(Settings *settings)
settings->getNoiseParams("mgv5_np_cave1", np_cave1);
settings->getNoiseParams("mgv5_np_cave2", np_cave2);
settings->getNoiseParams("mgv5_np_ground", np_ground);
- settings->getNoiseParams("mgv5_np_crumble", np_crumble);
- settings->getNoiseParams("mgv5_np_wetness", np_wetness);
}
-void MapgenV5Params::writeParams(Settings *settings)
+void MapgenV5Params::writeParams(Settings *settings) const
{
settings->setFlagStr("mgv5_spflags", spflags, flagdesc_mapgen_v5, (u32)-1);
@@ -177,8 +167,6 @@ void MapgenV5Params::writeParams(Settings *settings)
settings->setNoiseParams("mgv5_np_cave1", np_cave1);
settings->setNoiseParams("mgv5_np_cave2", np_cave2);
settings->setNoiseParams("mgv5_np_ground", np_ground);
- settings->setNoiseParams("mgv5_np_crumble", np_crumble);
- settings->setNoiseParams("mgv5_np_wetness", np_wetness);
}
@@ -187,23 +175,20 @@ int MapgenV5::getGroundLevelAtPoint(v2s16 p)
//TimeTaker t("getGroundLevelAtPoint", NULL, PRECISION_MICRO);
float f = 0.55 + NoisePerlin2D(&noise_factor->np, p.X, p.Y, seed);
- if(f < 0.01)
+ if (f < 0.01)
f = 0.01;
- else if(f >= 1.0)
+ else if (f >= 1.0)
f *= 1.6;
- float h = water_level + NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
+ float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
s16 search_top = water_level + 15;
s16 search_base = water_level;
- // Use these 2 lines instead for a slower search returning highest ground level:
- //s16 search_top = h + f * noise_ground->np->octaves * noise_ground->np->scale;
- //s16 search_base = h - f * noise_ground->np->octaves * noise_ground->np->scale;
s16 level = -31000;
for (s16 y = search_top; y >= search_base; y--) {
float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed);
- if(n_ground * f > y - h) {
- if(y >= search_top - 7)
+ if (n_ground * f > y - h) {
+ if (y >= search_top - 7)
break;
else
level = y;
@@ -218,6 +203,7 @@ int MapgenV5::getGroundLevelAtPoint(v2s16 p)
void MapgenV5::makeChunk(BlockMakeData *data)
{
+ // Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
@@ -247,33 +233,68 @@ void MapgenV5::makeChunk(BlockMakeData *data)
// Generate base terrain
s16 stone_surface_max_y = generateBaseTerrain();
+
+ // Create heightmap
updateHeightmap(node_min, node_max);
- // Calculate biomes
+ // Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
noise_humidity->result, heightmap, biomemap);
// Actually place the biome-specific nodes
- generateBiomes();
+ MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
// Generate caves
if ((flags & MG_CAVES) && (stone_surface_max_y >= node_min.Y))
- generateCaves();
+ generateCaves(stone_surface_max_y);
// Generate dungeons and desert temples
if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
- DungeonGen dgen(this, NULL);
+ DungeonParams dp;
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_density = nparams_dungeon_density;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.c_water = c_water_source;
+ if (stone_type == STONE) {
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ } else if (stone_type == DESERT_STONE) {
+ dp.c_cobble = c_desert_stone;
+ dp.c_moss = c_desert_stone;
+ dp.c_stair = c_desert_stone;
+
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ dp.notifytype = GENNOTIFY_TEMPLE;
+ } else if (stone_type == SANDSTONE) {
+ dp.c_cobble = c_sandstonebrick;
+ dp.c_moss = c_sandstonebrick;
+ dp.c_stair = c_sandstonebrick;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 2, 2);
+ dp.roomsize = v3s16(2, 0, 2);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ }
+
+ DungeonGen dgen(this, &dp);
dgen.generate(blockseed, full_node_min, full_node_max);
}
// Generate the registered decorations
m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
- // Generate underground dirt, sand, gravel and lava blobs
- if (spflags & MGV5_BLOBS) {
- generateBlobs();
- }
-
// Generate the registered ores
m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
@@ -311,23 +332,23 @@ void MapgenV5::calculateNoise()
noise_cave2->perlinMap3D(x, y, z);
}
- if (spflags & MGV5_BLOBS) {
- noise_crumble->perlinMap3D(x, y, z);
- noise_wetness->perlinMap3D(x, y, z);
- }
+ noise_filler_depth->perlinMap2D(x, z);
+ noise_heat->perlinMap2D(x, z);
+ noise_humidity->perlinMap2D(x, z);
+ noise_heat_blend->perlinMap2D(x, z);
+ noise_humidity_blend->perlinMap2D(x, z);
- if (node_max.Y >= water_level) {
- noise_filler_depth->perlinMap2D(x, z);
- noise_heat->perlinMap2D(x, z);
- noise_humidity->perlinMap2D(x, z);
+ for (s32 i = 0; i < csize.X * csize.Z; i++) {
+ noise_heat->result[i] += noise_heat_blend->result[i];
+ noise_humidity->result[i] += noise_humidity_blend->result[i];
}
+ heatmap = noise_heat->result;
+ humidmap = noise_humidity->result;
//printf("calculateNoise: %dus\n", t.stop());
}
-// Two original MT 0.3 functions:
-
//bool is_cave(u32 index) {
// double d1 = contour(noise_cave1->result[index]);
// double d2 = contour(noise_cave2->result[index]);
@@ -349,24 +370,24 @@ int MapgenV5::generateBaseTerrain()
{
u32 index = 0;
u32 index2d = 0;
- int stone_surface_max_y = -MAP_GENERATION_LIMIT;
+ int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
- for(s16 z=node_min.Z; z<=node_max.Z; z++) {
- for(s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
+ for (s16 z=node_min.Z; z<=node_max.Z; z++) {
+ for (s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
u32 i = vm->m_area.index(node_min.X, y, z);
- for(s16 x=node_min.X; x<=node_max.X; x++, i++, index++, index2d++) {
- if(vm->m_data[i].getContent() != CONTENT_IGNORE)
+ for (s16 x=node_min.X; x<=node_max.X; x++, i++, index++, index2d++) {
+ if (vm->m_data[i].getContent() != CONTENT_IGNORE)
continue;
float f = 0.55 + noise_factor->result[index2d];
- if(f < 0.01)
+ if (f < 0.01)
f = 0.01;
- else if(f >= 1.0)
+ else if (f >= 1.0)
f *= 1.6;
- float h = water_level + noise_height->result[index2d];
+ float h = noise_height->result[index2d];
- if(noise_ground->result[index] * f < y - h) {
- if(y <= water_level)
+ if (noise_ground->result[index] * f < y - h) {
+ if (y <= water_level)
vm->m_data[i] = MapNode(c_water_source);
else
vm->m_data[i] = MapNode(CONTENT_AIR);
@@ -376,156 +397,142 @@ int MapgenV5::generateBaseTerrain()
stone_surface_max_y = y;
}
}
- index2d = index2d - ystride;
+ index2d -= ystride;
}
- index2d = index2d + ystride;
+ index2d += ystride;
}
return stone_surface_max_y;
}
-void MapgenV5::generateBiomes()
+MgStoneType MapgenV5::generateBiomes(float *heat_map, float *humidity_map)
{
- if (node_max.Y < water_level)
- return;
-
- MapNode n_air(CONTENT_AIR);
- MapNode n_stone(c_stone);
- MapNode n_water(c_water_source);
-
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
+ MgStoneType stone_type = STONE;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
- Biome *biome = (Biome *)bmgr->get(biomemap[index]);
- s16 dfiller = biome->depth_filler + noise_filler_depth->result[index];
- s16 y0_top = biome->depth_top;
- s16 y0_filler = biome->depth_top + dfiller;
- s16 shore_max = water_level + biome->height_shore;
- s16 depth_water_top = biome->depth_water_top;
-
- s16 nplaced = 0;
- u32 i = vm->m_area.index(x, node_max.Y, z);
-
- content_t c_above = vm->m_data[i + em.X].getContent();
- bool have_air = c_above == CONTENT_AIR;
+ Biome *biome = NULL;
+ u16 depth_top = 0;
+ u16 base_filler = 0;
+ u16 depth_water_top = 0;
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+ // Check node at base of mapchunk above, either a node of a previously
+ // generated mapchunk or if not, a node of overgenerated base terrain.
+ content_t c_above = vm->m_data[vi + em.X].getContent();
+ bool air_above = c_above == CONTENT_AIR;
+ bool water_above = c_above == c_water_source;
+
+ // If there is air or water above enable top/filler placement, otherwise force
+ // nplaced to stone level by setting a number exceeding any possible filler depth.
+ u16 nplaced = (air_above || water_above) ? 0 : (u16)-1;
for (s16 y = node_max.Y; y >= node_min.Y; y--) {
- content_t c = vm->m_data[i].getContent();
-
- if (c == c_stone && have_air) {
- content_t c_below = vm->m_data[i - em.X].getContent();
-
- if (c_below != CONTENT_AIR) {
- if (nplaced < y0_top) {
- if(y < water_level)
- vm->m_data[i] = MapNode(biome->c_underwater);
- else if(y <= shore_max)
- vm->m_data[i] = MapNode(biome->c_shore_top);
- else
- vm->m_data[i] = MapNode(biome->c_top);
- nplaced++;
- } else if (nplaced < y0_filler && nplaced >= y0_top) {
- if(y < water_level)
- vm->m_data[i] = MapNode(biome->c_underwater);
- else if(y <= shore_max)
- vm->m_data[i] = MapNode(biome->c_shore_filler);
- else
- vm->m_data[i] = MapNode(biome->c_filler);
- nplaced++;
- } else if (c == c_stone) {
- have_air = false;
- nplaced = 0;
- vm->m_data[i] = MapNode(biome->c_stone);
- } else {
- have_air = false;
- nplaced = 0;
- }
- } else if (c == c_stone) {
- have_air = false;
- nplaced = 0;
- vm->m_data[i] = MapNode(biome->c_stone);
+ content_t c = vm->m_data[vi].getContent();
+
+ // Biome is recalculated each time an upper surface is detected while
+ // working down a column. The selected biome then remains in effect for
+ // all nodes below until the next surface and biome recalculation.
+ // Biome is recalculated:
+ // 1. At the surface of stone below air or water.
+ // 2. At the surface of water below air.
+ // 3. When stone or water is detected but biome has not yet been calculated.
+ if ((c == c_stone && (air_above || water_above || !biome)) ||
+ (c == c_water_source && (air_above || !biome))) {
+ biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
+ depth_top = biome->depth_top;
+ base_filler = MYMAX(depth_top + biome->depth_filler
+ + noise_filler_depth->result[index], 0);
+ depth_water_top = biome->depth_water_top;
+
+ // Detect stone type for dungeons during every biome calculation.
+ // This is more efficient than detecting per-node and will not
+ // miss any desert stone or sandstone biomes.
+ if (biome->c_stone == c_desert_stone)
+ stone_type = DESERT_STONE;
+ else if (biome->c_stone == c_sandstone)
+ stone_type = SANDSTONE;
+ }
+
+ if (c == c_stone) {
+ content_t c_below = vm->m_data[vi - em.X].getContent();
+
+ // If the node below isn't solid, make this node stone, so that
+ // any top/filler nodes above are structurally supported.
+ // This is done by aborting the cycle of top/filler placement
+ // immediately by forcing nplaced to stone level.
+ if (c_below == CONTENT_AIR || c_below == c_water_source)
+ nplaced = (u16)-1;
+
+ if (nplaced < depth_top) {
+ vm->m_data[vi] = MapNode(biome->c_top);
+ nplaced++;
+ } else if (nplaced < base_filler) {
+ vm->m_data[vi] = MapNode(biome->c_filler);
+ nplaced++;
+ } else {
+ vm->m_data[vi] = MapNode(biome->c_stone);
}
- } else if (c == c_stone) {
- have_air = false;
- nplaced = 0;
- vm->m_data[i] = MapNode(biome->c_stone);
+
+ air_above = false;
+ water_above = false;
} else if (c == c_water_source) {
- have_air = true;
- nplaced = 0;
- if(y > water_level - depth_water_top)
- vm->m_data[i] = MapNode(biome->c_water_top);
- else
- vm->m_data[i] = MapNode(biome->c_water);
+ vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ?
+ biome->c_water_top : biome->c_water);
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = false;
+ water_above = true;
} else if (c == CONTENT_AIR) {
- have_air = true;
- nplaced = 0;
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = true;
+ water_above = false;
+ } else { // Possible various nodes overgenerated from neighbouring mapchunks
+ nplaced = (u16)-1; // Disable top/filler placement
+ air_above = false;
+ water_above = false;
}
- vm->m_area.add_y(em, i, -1);
+ vm->m_area.add_y(em, vi, -1);
}
}
+
+ return stone_type;
}
-void MapgenV5::generateCaves()
+void MapgenV5::generateCaves(int max_stone_y)
{
- u32 index = 0;
- u32 index2d = 0;
+ if (max_stone_y >= node_min.Y) {
+ u32 index = 0;
- for(s16 z=node_min.Z; z<=node_max.Z; z++) {
- for(s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
u32 i = vm->m_area.index(node_min.X, y, z);
- for(s16 x=node_min.X; x<=node_max.X; x++, i++, index++, index2d++) {
- Biome *biome = (Biome *)bmgr->get(biomemap[index2d]);
- content_t c = vm->m_data[i].getContent();
- if(c == CONTENT_AIR
- || (y <= water_level
- && c != biome->c_stone
- && c != c_stone))
- continue;
-
+ for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++) {
float d1 = contour(noise_cave1->result[index]);
float d2 = contour(noise_cave2->result[index]);
- if(d1*d2 > 0.125)
+ if (d1*d2 > 0.125) {
+ content_t c = vm->m_data[i].getContent();
+ if (!ndef->get(c).is_ground_content || c == CONTENT_AIR)
+ continue;
+
vm->m_data[i] = MapNode(CONTENT_AIR);
+ }
}
- index2d = index2d - ystride;
}
- index2d = index2d + ystride;
}
-}
+ if (node_max.Y > LARGE_CAVE_DEPTH)
+ return;
-void MapgenV5::generateBlobs()
-{
- u32 index = 0;
-
- for(s16 z=node_min.Z; z<=node_max.Z; z++) {
- for(s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
- u32 i = vm->m_area.index(node_min.X, y, z);
- for(s16 x=node_min.X; x<=node_max.X; x++, i++, index++) {
- content_t c = vm->m_data[i].getContent();
- if(c != c_stone)
- continue;
-
- if(noise_crumble->result[index] > 1.3) {
- if(noise_wetness->result[index] > 0.0)
- vm->m_data[i] = MapNode(c_dirt);
- else
- vm->m_data[i] = MapNode(c_sand);
- } else if(noise_crumble->result[index] > 0.7) {
- if(noise_wetness->result[index] < -0.6)
- vm->m_data[i] = MapNode(c_gravel);
- } else if(noise_crumble->result[index] < -3.5 +
- MYMIN(0.1 *
- sqrt((float)MYMAX(0, -y)), 1.5)) {
- vm->m_data[i] = MapNode(c_lava_source);
- }
- }
- }
+ PseudoRandom ps(blockseed + 21343);
+ u32 bruises_count = (ps.range(1, 4) == 1) ? ps.range(1, 2) : 0;
+ for (u32 i = 0; i < bruises_count; i++) {
+ CaveV5 cave(this, &ps);
+ cave.makeCave(node_min, node_max, max_stone_y);
}
}
@@ -540,14 +547,31 @@ void MapgenV5::dustTopNodes()
for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
- Biome *biome = (Biome *)bmgr->get(biomemap[index]);
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]);
if (biome->c_dust == CONTENT_IGNORE)
continue;
- s16 y = node_max.Y + 1;
- u32 vi = vm->m_area.index(x, y, z);
- for (; y >= node_min.Y; y--) {
+ u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+ content_t c_full_max = vm->m_data[vi].getContent();
+ s16 y_start;
+
+ if (c_full_max == CONTENT_AIR) {
+ y_start = full_node_max.Y - 1;
+ } else if (c_full_max == CONTENT_IGNORE) {
+ vi = vm->m_area.index(x, node_max.Y + 1, z);
+ content_t c_max = vm->m_data[vi].getContent();
+
+ if (c_max == CONTENT_AIR)
+ y_start = node_max.Y;
+ else
+ continue;
+ } else {
+ continue;
+ }
+
+ vi = vm->m_area.index(x, y_start, z);
+ for (s16 y = y_start; y >= node_min.Y - 1; y--) {
if (vm->m_data[vi].getContent() != CONTENT_AIR)
break;
@@ -555,14 +579,9 @@ void MapgenV5::dustTopNodes()
}
content_t c = vm->m_data[vi].getContent();
- if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE
- && c != biome->c_dust) {
- if (y == node_max.Y + 1)
- continue;
-
+ if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
vm->m_area.add_y(em, vi, 1);
vm->m_data[vi] = MapNode(biome->c_dust);
}
}
}
-
diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h
index 1949bf5db..a6fdc2b2b 100644
--- a/src/mapgen_v5.h
+++ b/src/mapgen_v5.h
@@ -22,8 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapgen.h"
-/////////////////// Mapgen V5 flags
-#define MGV5_BLOBS 0x01
+#define LARGE_CAVE_DEPTH -256
+
+class BiomeManager;
extern FlagDesc flagdesc_mapgen_v5[];
@@ -36,14 +37,12 @@ struct MapgenV5Params : public MapgenSpecificParams {
NoiseParams np_cave1;
NoiseParams np_cave2;
NoiseParams np_ground;
- NoiseParams np_crumble;
- NoiseParams np_wetness;
MapgenV5Params();
~MapgenV5Params() {}
- void readParams(Settings *settings);
- void writeParams(Settings *settings);
+ void readParams(const Settings *settings);
+ void writeParams(Settings *settings) const;
};
@@ -67,26 +66,24 @@ public:
Noise *noise_cave1;
Noise *noise_cave2;
Noise *noise_ground;
- Noise *noise_crumble;
- Noise *noise_wetness;
+
Noise *noise_heat;
Noise *noise_humidity;
+ Noise *noise_heat_blend;
+ Noise *noise_humidity_blend;
content_t c_stone;
- content_t c_dirt;
- content_t c_dirt_with_grass;
- content_t c_sand;
content_t c_water_source;
content_t c_lava_source;
+ content_t c_desert_stone;
content_t c_ice;
- content_t c_gravel;
+ content_t c_sandstone;
+
content_t c_cobble;
- content_t c_desert_sand;
- content_t c_desert_stone;
- content_t c_mossycobble;
- content_t c_sandbrick;
content_t c_stair_cobble;
- content_t c_stair_sandstone;
+ content_t c_mossycobble;
+ content_t c_sandstonebrick;
+ content_t c_stair_sandstonebrick;
MapgenV5(int mapgenid, MapgenParams *params, EmergeManager *emerge);
~MapgenV5();
@@ -95,9 +92,8 @@ public:
int getGroundLevelAtPoint(v2s16 p);
void calculateNoise();
int generateBaseTerrain();
- void generateBiomes();
- void generateCaves();
- void generateBlobs();
+ MgStoneType generateBiomes(float *heat_map, float *humidity_map);
+ void generateCaves(int max_stone_y);
void dustTopNodes();
};
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
index 95cdbd279..9e34aac2d 100644
--- a/src/mapgen_v6.cpp
+++ b/src/mapgen_v6.cpp
@@ -28,9 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "content_mapnode.h" // For content_mapnode_get_new_name
#include "voxelalgorithms.h"
-#include "profiler.h"
#include "settings.h" // For g_settings
-#include "main.h" // For g_profiler
#include "emerge.h"
#include "dungeongen.h"
#include "cavegen.h"
@@ -43,6 +41,7 @@ FlagDesc flagdesc_mapgen_v6[] = {
{"jungles", MGV6_JUNGLES},
{"biomeblend", MGV6_BIOMEBLEND},
{"mudflow", MGV6_MUDFLOW},
+ {"snowbiomes", MGV6_SNOWBIOMES},
{NULL, 0}
};
@@ -55,6 +54,8 @@ MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge)
this->m_emerge = emerge;
this->ystride = csize.X; //////fix this
+ this->heightmap = new s16[csize.X * csize.Z];
+
MapgenV6Params *sp = (MapgenV6Params *)params->sparams;
this->spflags = sp->spflags;
this->freq_desert = sp->freq_desert;
@@ -72,7 +73,10 @@ MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_height_select = new Noise(&sp->np_height_select, seed, csize.X, csize.Y);
noise_mud = new Noise(&sp->np_mud, seed, csize.X, csize.Y);
noise_beach = new Noise(&sp->np_beach, seed, csize.X, csize.Y);
- noise_biome = new Noise(&sp->np_biome, seed, csize.X, csize.Y);
+ noise_biome = new Noise(&sp->np_biome, seed,
+ csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
+ noise_humidity = new Noise(&sp->np_humidity, seed,
+ csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
@@ -84,25 +88,33 @@ MapgenV6::MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge)
c_water_source = ndef->getId("mapgen_water_source");
c_lava_source = ndef->getId("mapgen_lava_source");
c_gravel = ndef->getId("mapgen_gravel");
- c_cobble = ndef->getId("mapgen_cobble");
- c_desert_sand = ndef->getId("mapgen_desert_sand");
c_desert_stone = ndef->getId("mapgen_desert_stone");
- c_mossycobble = ndef->getId("mapgen_mossycobble");
- c_sandbrick = ndef->getId("mapgen_sandstonebrick");
+ c_desert_sand = ndef->getId("mapgen_desert_sand");
+ c_dirt_with_snow = ndef->getId("mapgen_dirt_with_snow");
+ c_snow = ndef->getId("mapgen_snow");
+ c_snowblock = ndef->getId("mapgen_snowblock");
+ c_ice = ndef->getId("mapgen_ice");
+
+ c_cobble = ndef->getId("mapgen_cobble");
c_stair_cobble = ndef->getId("mapgen_stair_cobble");
- c_stair_sandstone = ndef->getId("mapgen_stair_sandstone");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+
if (c_desert_sand == CONTENT_IGNORE)
c_desert_sand = c_sand;
if (c_desert_stone == CONTENT_IGNORE)
c_desert_stone = c_stone;
if (c_mossycobble == CONTENT_IGNORE)
c_mossycobble = c_cobble;
- if (c_sandbrick == CONTENT_IGNORE)
- c_sandbrick = c_desert_stone;
if (c_stair_cobble == CONTENT_IGNORE)
c_stair_cobble = c_cobble;
- if (c_stair_sandstone == CONTENT_IGNORE)
- c_stair_sandstone = c_sandbrick;
+ if (c_dirt_with_snow == CONTENT_IGNORE)
+ c_dirt_with_snow = c_dirt_with_grass;
+ if (c_snow == CONTENT_IGNORE)
+ c_snow = CONTENT_AIR;
+ if (c_snowblock == CONTENT_IGNORE)
+ c_snowblock = c_dirt_with_grass;
+ if (c_ice == CONTENT_IGNORE)
+ c_ice = c_water_source;
}
@@ -115,6 +127,9 @@ MapgenV6::~MapgenV6()
delete noise_mud;
delete noise_beach;
delete noise_biome;
+ delete noise_humidity;
+
+ delete[] heightmap;
}
@@ -124,21 +139,21 @@ MapgenV6Params::MapgenV6Params()
freq_desert = 0.45;
freq_beach = 0.15;
- np_terrain_base = NoiseParams(-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0);
- np_terrain_higher = NoiseParams(20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0);
- np_steepness = NoiseParams(0.85,0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0);
- np_height_select = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0);
- np_mud = NoiseParams(4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0);
- np_beach = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0);
- np_biome = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 9130, 3, 0.50, 2.0);
- np_cave = NoiseParams(6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0);
- np_humidity = NoiseParams(0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 4, 0.66, 2.0);
- np_trees = NoiseParams(0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0);
- np_apple_trees = NoiseParams(0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0);
+ np_terrain_base = NoiseParams(-4, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 2.0);
+ np_terrain_higher = NoiseParams(20, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 2.0);
+ np_steepness = NoiseParams(0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 2.0);
+ np_height_select = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69, 2.0);
+ np_mud = NoiseParams(4, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 2.0);
+ np_beach = NoiseParams(0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50, 2.0);
+ np_biome = NoiseParams(0, 1.0, v3f(500.0, 500.0, 500.0), 9130, 3, 0.50, 2.0);
+ np_cave = NoiseParams(6, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50, 2.0);
+ np_humidity = NoiseParams(0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 3, 0.50, 2.0);
+ np_trees = NoiseParams(0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66, 2.0);
+ np_apple_trees = NoiseParams(0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0);
}
-void MapgenV6Params::readParams(Settings *settings)
+void MapgenV6Params::readParams(const Settings *settings)
{
settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
@@ -158,7 +173,7 @@ void MapgenV6Params::readParams(Settings *settings)
}
-void MapgenV6Params::writeParams(Settings *settings)
+void MapgenV6Params::writeParams(Settings *settings) const
{
settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, (u32)-1);
settings->setFloat("mgv6_freq_desert", freq_desert);
@@ -191,10 +206,8 @@ s16 MapgenV6::find_stone_level(v2s16 p2d)
s16 y;
for (y = y_nodes_max; y >= y_nodes_min; y--) {
- MapNode &n = vm->m_data[i];
- content_t c = n.getContent();
- if (c != CONTENT_IGNORE && (
- c == c_stone || c == c_desert_stone))
+ content_t c = vm->m_data[i].getContent();
+ if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
break;
vm->m_area.add_y(em, i, -1);
@@ -211,7 +224,7 @@ bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
// Nah, this is just a heuristic, just return something
s16 minimum_groundlevel = water_level;
- if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
+ if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
return true;
else
return false;
@@ -263,7 +276,7 @@ float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
p.X, 0.5, p.Y, 0.5, seed);
return baseTerrainLevel(terrain_base, terrain_higher,
- steepness, height_select);
+ steepness, height_select);
}
@@ -285,7 +298,7 @@ float MapgenV6::baseTerrainLevelFromMap(int index)
float height_select = noise_height_select->result[index];
return baseTerrainLevel(terrain_base, terrain_higher,
- steepness, height_select);
+ steepness, height_select);
}
@@ -319,7 +332,8 @@ bool MapgenV6::getHaveBeach(v2s16 p)
BiomeV6Type MapgenV6::getBiome(v2s16 p)
{
- int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+ int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
+ + (p.X - full_node_min.X);
return getBiome(index, p);
}
@@ -331,7 +345,9 @@ float MapgenV6::getHumidity(v2s16 p)
seed+72384, 4, 0.66);
noise = (noise + 1.0)/2.0;*/
- float noise = NoisePerlin2D(np_humidity, p.X, p.Y, seed);
+ int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
+ + (p.X - full_node_min.X);
+ float noise = noise_humidity->result[index];
if (noise < 0.0)
noise = 0.0;
@@ -352,7 +368,7 @@ float MapgenV6::getTreeAmount(v2s16 p)
if (noise < zeroval)
return 0;
else
- return 0.04 * (noise-zeroval) / (1.0-zeroval);
+ return 0.04 * (noise - zeroval) / (1.0 - zeroval);
}
@@ -401,22 +417,44 @@ BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
seed+9130, 3, 0.50);*/
float d = noise_biome->result[index];
- if (d > freq_desert)
- return BT_DESERT;
-
- if ((spflags & MGV6_BIOMEBLEND) &&
- (d > freq_desert - 0.10) &&
- ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
- return BT_DESERT;
-
- return BT_NORMAL;
+ float h = noise_humidity->result[index];
+
+ if (spflags & MGV6_SNOWBIOMES) {
+ float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
+
+ if (d > FREQ_HOT + blend) {
+ if (h > FREQ_JUNGLE + blend)
+ return BT_JUNGLE;
+ else
+ return BT_DESERT;
+ } else if (d < FREQ_SNOW + blend) {
+ if (h > FREQ_TAIGA + blend)
+ return BT_TAIGA;
+ else
+ return BT_TUNDRA;
+ } else {
+ return BT_NORMAL;
+ }
+ } else {
+ if (d > freq_desert)
+ return BT_DESERT;
+
+ if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
+ ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
+ return BT_DESERT;
+
+ if ((spflags & MGV6_JUNGLES) && h > 0.75)
+ return BT_JUNGLE;
+ else
+ return BT_NORMAL;
+ }
}
u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
{
- s32 x=p.X, y=p.Y, z=p.Z;
- return (u32)(seed%0x100000000ULL) + z*38134234 + y*42123 + x*23;
+ s32 x = p.X, y = p.Y, z = p.Z;
+ return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
}
@@ -424,6 +462,7 @@ u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
void MapgenV6::makeChunk(BlockMakeData *data)
{
+ // Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
@@ -442,14 +481,14 @@ void MapgenV6::makeChunk(BlockMakeData *data)
v3s16 blockpos_max = data->blockpos_max;
// Area of central chunk
- node_min = blockpos_min*MAP_BLOCKSIZE;
- node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1);
+ node_min = blockpos_min * MAP_BLOCKSIZE;
+ node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
// Full allocated area
- full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE;
- full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1);
+ full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+ full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
- central_area_size = node_max - node_min + v3s16(1,1,1);
+ central_area_size = node_max - node_min + v3s16(1, 1, 1);
assert(central_area_size.X == central_area_size.Z);
int volume_blocks = (blockpos_max.X - blockpos_min.X + 1)
@@ -472,7 +511,8 @@ void MapgenV6::makeChunk(BlockMakeData *data)
// Generate general ground level to full area
stone_surface_max_y = generateGround();
- generateExperimental();
+ // Create initial heightmap to limit caves
+ updateHeightmap(node_min, node_max);
const s16 max_spread_amount = MAP_BLOCKSIZE;
// Limit dirt flow area by 1 because mud is flown into neighbors.
@@ -489,15 +529,15 @@ void MapgenV6::makeChunk(BlockMakeData *data)
// Add mud to the central chunk
addMud();
- // Add blobs of dirt and gravel underground
- addDirtGravelBlobs();
-
// Flow mud away from steep edges
if (spflags & MGV6_MUDFLOW)
flowMud(mudflow_minpos, mudflow_maxpos);
}
+ // Update heightmap after mudflow
+ updateHeightmap(node_min, node_max);
+
// Add dungeons
if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
DungeonParams dp;
@@ -505,27 +545,27 @@ void MapgenV6::makeChunk(BlockMakeData *data)
dp.np_rarity = nparams_dungeon_rarity;
dp.np_density = nparams_dungeon_density;
dp.np_wetness = nparams_dungeon_wetness;
- dp.c_water = c_water_source;
- if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_NORMAL) {
- dp.c_cobble = c_cobble;
- dp.c_moss = c_mossycobble;
- dp.c_stair = c_stair_cobble;
+ dp.c_water = c_water_source;
+ if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
+ dp.c_cobble = c_desert_stone;
+ dp.c_moss = c_desert_stone;
+ dp.c_stair = c_desert_stone;
- dp.diagonal_dirs = false;
- dp.mossratio = 3.0;
- dp.holesize = v3s16(1, 2, 1);
- dp.roomsize = v3s16(0, 0, 0);
- dp.notifytype = GENNOTIFY_DUNGEON;
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ dp.notifytype = GENNOTIFY_TEMPLE;
} else {
- dp.c_cobble = c_sandbrick;
- dp.c_moss = c_sandbrick; // should make this 'cracked sandstone' later
- dp.c_stair = c_stair_sandstone;
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
- dp.diagonal_dirs = true;
- dp.mossratio = 0.0;
- dp.holesize = v3s16(2, 3, 2);
- dp.roomsize = v3s16(2, 5, 2);
- dp.notifytype = GENNOTIFY_TEMPLE;
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
}
DungeonGen dgen(this, &dp);
@@ -535,7 +575,7 @@ void MapgenV6::makeChunk(BlockMakeData *data)
// Add top and bottom side of water to transforming_liquid queue
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
- // Grow grass
+ // Add surface nodes
growGrass();
// Generate some trees, and add grass, if a jungle
@@ -560,6 +600,8 @@ void MapgenV6::calculateNoise()
{
int x = node_min.X;
int z = node_min.Z;
+ int fx = full_node_min.X;
+ int fz = full_node_min.Z;
if (!(flags & MG_FLAT)) {
noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
@@ -570,7 +612,11 @@ void MapgenV6::calculateNoise()
}
noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
- noise_biome->perlinMap2D_PO(x, 0.6, z, 0.2);
+
+ noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
+ noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
+ // Humidity map does not need range limiting 0 to 1,
+ // only humidity at point does
}
@@ -579,9 +625,10 @@ int MapgenV6::generateGround()
//TimeTaker timer1("Generating ground level");
MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
- int stone_surface_max_y = -MAP_GENERATION_LIMIT;
- u32 index = 0;
+ MapNode n_ice(c_ice);
+ int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+ u32 index = 0;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
// Surface height
@@ -591,7 +638,7 @@ int MapgenV6::generateGround()
if (surface_y > stone_surface_max_y)
stone_surface_max_y = surface_y;
- BiomeV6Type bt = getBiome(index, v2s16(x, z));
+ BiomeV6Type bt = getBiome(v2s16(x, z));
// Fill ground with stone
v3s16 em = vm->m_area.getExtent();
@@ -599,10 +646,13 @@ int MapgenV6::generateGround()
for (s16 y = node_min.Y; y <= node_max.Y; y++) {
if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
if (y <= surface_y) {
- vm->m_data[i] = (y > water_level && bt == BT_DESERT) ?
+ vm->m_data[i] = (y >= DESERT_STONE_BASE
+ && bt == BT_DESERT) ?
n_desert_stone : n_stone;
} else if (y <= water_level) {
- vm->m_data[i] = n_water_source;
+ vm->m_data[i] = (y >= ICE_BASE
+ && bt == BT_TUNDRA) ?
+ n_ice : n_water_source;
} else {
vm->m_data[i] = n_air;
}
@@ -636,7 +686,7 @@ void MapgenV6::addMud()
if (surface_y == vm->m_area.MinEdge.Y - 1)
continue;
- BiomeV6Type bt = getBiome(index, v2s16(x, z));
+ BiomeV6Type bt = getBiome(v2s16(x, z));
addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
@@ -644,25 +694,25 @@ void MapgenV6::addMud()
} else if (mud_add_amount <= 0) {
mud_add_amount = 1 - mud_add_amount;
addnode = n_gravel;
- } else if (bt == BT_NORMAL && getHaveBeach(index) &&
+ } else if (bt != BT_DESERT && getHaveBeach(index) &&
surface_y + mud_add_amount <= water_level + 2) {
addnode = n_sand;
}
- if (bt == BT_DESERT && surface_y > 20)
+ if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
- // If topmost node is grass, change it to mud. It might be if it was
+ /* If topmost node is grass, change it to mud. It might be if it was
// flown to there from a neighboring chunk and then converted.
u32 i = vm->m_area.index(x, surface_y, z);
if (vm->m_data[i].getContent() == c_dirt_with_grass)
- vm->m_data[i] = n_dirt;
+ vm->m_data[i] = n_dirt;*/
// Add mud on ground
s16 mudcount = 0;
v3s16 em = vm->m_area.getExtent();
s16 y_start = surface_y + 1;
- i = vm->m_area.index(x, y_start, z);
+ u32 i = vm->m_area.index(x, y_start, z);
for (s16 y = y_start; y <= node_max.Y; y++) {
if (mudcount >= mud_add_amount)
break;
@@ -679,10 +729,10 @@ void MapgenV6::addMud()
void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
{
// 340ms @cs=8
- TimeTaker timer1("flow mud");
+ //TimeTaker timer1("flow mud");
// Iterate a few times
- for(s16 k = 0; k < 3; k++) {
+ for (s16 k = 0; k < 3; k++) {
for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
// Invert coordinates every 2nd iteration
@@ -698,18 +748,16 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
s16 y = node_max.Y;
- while(y >= node_min.Y)
- {
+ while (y >= node_min.Y) {
- for(;; y--)
- {
+ for (;; y--) {
MapNode *n = NULL;
// Find mud
- for(; y >= node_min.Y; y--) {
+ for (; y >= node_min.Y; y--) {
n = &vm->m_data[i];
if (n->getContent() == c_dirt ||
- n->getContent() == c_dirt_with_grass ||
- n->getContent() == c_gravel)
+ n->getContent() == c_dirt_with_grass ||
+ n->getContent() == c_gravel)
break;
vm->m_area.add_y(em, i, -1);
@@ -721,8 +769,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
break;
if (n->getContent() == c_dirt ||
- n->getContent() == c_dirt_with_grass)
- {
+ n->getContent() == c_dirt_with_grass) {
// Make it exactly mud
n->setContent(c_dirt);
@@ -731,20 +778,20 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
u32 i2 = i;
vm->m_area.add_y(em, i2, -1);
// Cancel if out of area
- if(vm->m_area.contains(i2) == false)
+ if (vm->m_area.contains(i2) == false)
continue;
MapNode *n2 = &vm->m_data[i2];
if (n2->getContent() != c_dirt &&
- n2->getContent() != c_dirt_with_grass)
+ n2->getContent() != c_dirt_with_grass)
continue;
}
}
v3s16 dirs4[4] = {
- v3s16(0,0,1), // back
- v3s16(1,0,0), // right
- v3s16(0,0,-1), // front
- v3s16(-1,0,0), // left
+ v3s16(0, 0, 1), // back
+ v3s16(1, 0, 0), // right
+ v3s16(0, 0, -1), // front
+ v3s16(-1, 0, 0), // left
};
// Check that upper is air or doesn't exist.
@@ -752,11 +799,11 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
u32 i3 = i;
vm->m_area.add_y(em, i3, 1);
if (vm->m_area.contains(i3) == true &&
- ndef->get(vm->m_data[i3]).walkable)
+ ndef->get(vm->m_data[i3]).walkable)
continue;
// Drop mud on side
- for(u32 di=0; di<4; di++) {
+ for(u32 di = 0; di < 4; di++) {
v3s16 dirp = dirs4[di];
u32 i2 = i;
// Move to side
@@ -782,7 +829,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
n2 = &vm->m_data[i2];
// if out of known area
if(vm->m_area.contains(i2) == false ||
- n2->getContent() == CONTENT_IGNORE) {
+ n2->getContent() == CONTENT_IGNORE) {
dropped_to_unknown = true;
break;
}
@@ -812,45 +859,6 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
}
-void MapgenV6::addDirtGravelBlobs()
-{
- if (getBiome(v2s16(node_min.X, node_min.Z)) != BT_NORMAL)
- return;
-
- PseudoRandom pr(blockseed + 983);
- for (int i = 0; i < volume_nodes/10/10/10; i++) {
- bool only_fill_cave = (myrand_range(0,1) != 0);
- v3s16 size(
- pr.range(1, 8),
- pr.range(1, 8),
- pr.range(1, 8)
- );
- v3s16 p0(
- pr.range(node_min.X, node_max.X) - size.X / 2,
- pr.range(node_min.Y, node_max.Y) - size.Y / 2,
- pr.range(node_min.Z, node_max.Z) - size.Z / 2
- );
-
- MapNode n1((p0.Y > -32 && !pr.range(0, 1)) ? c_dirt : c_gravel);
- for (int z1 = 0; z1 < size.Z; z1++)
- for (int y1 = 0; y1 < size.Y; y1++)
- for (int x1 = 0; x1 < size.X; x1++) {
- v3s16 p = p0 + v3s16(x1, y1, z1);
- u32 i = vm->m_area.index(p);
- if (!vm->m_area.contains(i))
- continue;
- // Cancel if not stone and not cave air
- if (vm->m_data[i].getContent() != c_stone &&
- !(vm->m_flags[i] & VMANIP_FLAG_CAVE))
- continue;
- if (only_fill_cave && !(vm->m_flags[i] & VMANIP_FLAG_CAVE))
- continue;
- vm->m_data[i] = n1;
- }
- }
-}
-
-
void MapgenV6::placeTreesAndJungleGrass()
{
//TimeTaker t("placeTrees");
@@ -890,28 +898,30 @@ void MapgenV6::placeTreesAndJungleGrass()
node_min.Z + sidelen + sidelen * z0 - 1
);
- // Amount of trees, jungle area
- u32 tree_count = area * getTreeAmount(p2d_center);
+ // Get biome at center position of part of division
+ BiomeV6Type bt = getBiome(p2d_center);
- float humidity;
- bool is_jungle = false;
- if (spflags & MGV6_JUNGLES) {
- humidity = getHumidity(p2d_center);
- if (humidity > 0.75) {
- is_jungle = true;
+ // Amount of trees
+ u32 tree_count;
+ if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
+ tree_count = area * getTreeAmount(p2d_center);
+ if (bt == BT_JUNGLE)
tree_count *= 4;
- }
+ } else {
+ tree_count = 0;
}
// Add jungle grass
- if (is_jungle) {
+ if (bt == BT_JUNGLE) {
+ float humidity = getHumidity(p2d_center);
u32 grass_count = 5 * humidity * tree_count;
for (u32 i = 0; i < grass_count; i++) {
s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
-
- s16 y = findGroundLevelFull(v2s16(x, z)); ////////////////optimize this!
- if (y < water_level || y < node_min.Y || y > node_max.Y)
+ int mapindex = central_area_size.X * (z - node_min.Z)
+ + (x - node_min.X);
+ s16 y = heightmap[mapindex];
+ if (y < water_level)
continue;
u32 vi = vm->m_area.index(x, y, z);
@@ -927,29 +937,35 @@ void MapgenV6::placeTreesAndJungleGrass()
for (u32 i = 0; i < tree_count; i++) {
s16 x = myrand_range(p2d_min.X, p2d_max.X);
s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
- s16 y = findGroundLevelFull(v2s16(x, z)); ////////////////////optimize this!
+ int mapindex = central_area_size.X * (z - node_min.Z)
+ + (x - node_min.X);
+ s16 y = heightmap[mapindex];
// Don't make a tree under water level
// Don't make a tree so high that it doesn't fit
- if(y < water_level || y > node_max.Y - 6)
+ if (y < water_level || y > node_max.Y - 6)
continue;
- v3s16 p(x,y,z);
- // Trees grow only on mud and grass
+ v3s16 p(x, y, z);
+ // Trees grow only on mud and grass and snowblock
{
u32 i = vm->m_area.index(p);
- MapNode *n = &vm->m_data[i];
- if (n->getContent() != c_dirt &&
- n->getContent() != c_dirt_with_grass)
+ content_t c = vm->m_data[i].getContent();
+ if (c != c_dirt &&
+ c != c_dirt_with_grass &&
+ c != c_dirt_with_snow &&
+ c != c_snowblock)
continue;
}
p.Y++;
// Make a tree
- if (is_jungle) {
+ if (bt == BT_JUNGLE) {
treegen::make_jungletree(*vm, p, ndef, myrand());
- } else {
+ } else if (bt == BT_TAIGA) {
+ treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
+ } else if (bt == BT_NORMAL) {
bool is_apple_tree = (myrand_range(0, 3) == 0) &&
- getHaveAppleTree(v2s16(x, z));
+ getHaveAppleTree(v2s16(x, z));
treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
}
}
@@ -958,32 +974,54 @@ void MapgenV6::placeTreesAndJungleGrass()
}
-void MapgenV6::growGrass()
+void MapgenV6::growGrass() // Add surface nodes
{
+ MapNode n_dirt_with_grass(c_dirt_with_grass);
+ MapNode n_dirt_with_snow(c_dirt_with_snow);
+ MapNode n_snowblock(c_snowblock);
+ MapNode n_snow(c_snow);
+ v3s16 em = vm->m_area.getExtent();
+
+ u32 index = 0;
for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
- for (s16 x = full_node_min.X; x <= full_node_max.X; x++) {
+ for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
// Find the lowest surface to which enough light ends up to make
// grass grow. Basically just wait until not air and not leaves.
s16 surface_y = 0;
{
- v3s16 em = vm->m_area.getExtent();
u32 i = vm->m_area.index(x, node_max.Y, z);
s16 y;
// Go to ground level
for (y = node_max.Y; y >= full_node_min.Y; y--) {
MapNode &n = vm->m_data[i];
if (ndef->get(n).param_type != CPT_LIGHT ||
- ndef->get(n).liquid_type != LIQUID_NONE)
+ ndef->get(n).liquid_type != LIQUID_NONE ||
+ n.getContent() == c_ice)
break;
vm->m_area.add_y(em, i, -1);
}
surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
}
+ BiomeV6Type bt = getBiome(index, v2s16(x, z));
u32 i = vm->m_area.index(x, surface_y, z);
- MapNode *n = &vm->m_data[i];
- if (n->getContent() == c_dirt && surface_y >= water_level - 20)
- n->setContent(c_dirt_with_grass);
+ content_t c = vm->m_data[i].getContent();
+ if (surface_y >= water_level - 20) {
+ if (bt == BT_TAIGA && c == c_dirt) {
+ vm->m_data[i] = n_snowblock;
+ vm->m_area.add_y(em, i, -1);
+ vm->m_data[i] = n_dirt_with_snow;
+ } else if (bt == BT_TUNDRA) {
+ if (c == c_dirt) {
+ vm->m_data[i] = n_dirt_with_snow;
+ } else if (c == c_stone && surface_y < node_max.Y) {
+ vm->m_area.add_y(em, i, 1);
+ vm->m_data[i] = n_snow;
+ }
+ } else if (c == c_dirt) {
+ vm->m_data[i] = n_dirt_with_grass;
+ }
+ }
}
}
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
index 64aa2d87a..c71cf3c53 100644
--- a/src/mapgen_v6.h
+++ b/src/mapgen_v6.h
@@ -24,11 +24,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "noise.h"
#define AVERAGE_MUD_AMOUNT 4
-
-/////////////////// Mapgen V6 flags
+#define DESERT_STONE_BASE -32
+#define ICE_BASE 0
+#define FREQ_HOT 0.4
+#define FREQ_SNOW -0.4
+#define FREQ_TAIGA 0.5
+#define FREQ_JUNGLE 0.5
+
+//////////// Mapgen V6 flags
#define MGV6_JUNGLES 0x01
#define MGV6_BIOMEBLEND 0x02
#define MGV6_MUDFLOW 0x04
+#define MGV6_SNOWBIOMES 0x08
extern FlagDesc flagdesc_mapgen_v6[];
@@ -37,9 +44,13 @@ extern FlagDesc flagdesc_mapgen_v6[];
enum BiomeV6Type
{
BT_NORMAL,
- BT_DESERT
+ BT_DESERT,
+ BT_JUNGLE,
+ BT_TUNDRA,
+ BT_TAIGA,
};
+
struct MapgenV6Params : public MapgenSpecificParams {
u32 spflags;
float freq_desert;
@@ -59,10 +70,11 @@ struct MapgenV6Params : public MapgenSpecificParams {
MapgenV6Params();
~MapgenV6Params() {}
- void readParams(Settings *settings);
- void writeParams(Settings *settings);
+ void readParams(const Settings *settings);
+ void writeParams(Settings *settings) const;
};
+
class MapgenV6 : public Mapgen {
public:
EmergeManager *m_emerge;
@@ -84,6 +96,7 @@ public:
Noise *noise_mud;
Noise *noise_beach;
Noise *noise_biome;
+ Noise *noise_humidity;
NoiseParams *np_cave;
NoiseParams *np_humidity;
NoiseParams *np_trees;
@@ -98,14 +111,16 @@ public:
content_t c_water_source;
content_t c_lava_source;
content_t c_gravel;
- content_t c_cobble;
- content_t c_desert_sand;
content_t c_desert_stone;
+ content_t c_desert_sand;
+ content_t c_dirt_with_snow;
+ content_t c_snow;
+ content_t c_snowblock;
+ content_t c_ice;
+ content_t c_cobble;
content_t c_mossycobble;
- content_t c_sandbrick;
content_t c_stair_cobble;
- content_t c_stair_sandstone;
MapgenV6(int mapgenid, MapgenParams *params, EmergeManager *emerge);
~MapgenV6();
@@ -139,13 +154,12 @@ public:
int generateGround();
void addMud();
void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos);
- void addDirtGravelBlobs();
void growGrass();
void placeTreesAndJungleGrass();
virtual void generateCaves(int max_stone_y);
- virtual void generateExperimental() {}
};
+
struct MapgenFactoryV6 : public MapgenFactory {
Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge)
{
@@ -158,4 +172,5 @@ struct MapgenFactoryV6 : public MapgenFactory {
};
};
+
#endif
diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp
index a7b9076b3..9f612de81 100644
--- a/src/mapgen_v7.cpp
+++ b/src/mapgen_v7.cpp
@@ -27,9 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_sao.h"
#include "nodedef.h"
#include "voxelalgorithms.h"
-#include "profiler.h"
#include "settings.h" // For g_settings
-#include "main.h" // For g_profiler
#include "emerge.h"
#include "dungeongen.h"
#include "cavegen.h"
@@ -58,10 +56,12 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
//// amount of elements to skip for the next index
//// for noise/height/biome maps (not vmanip)
this->ystride = csize.X;
- this->zstride = csize.X * csize.Y;
+ this->zstride = csize.X * (csize.Y + 2);
- this->biomemap = new u8[csize.X * csize.Z];
- this->heightmap = new s16[csize.X * csize.Z];
+ this->biomemap = new u8[csize.X * csize.Z];
+ this->heightmap = new s16[csize.X * csize.Z];
+ this->heatmap = NULL;
+ this->humidmap = NULL;
this->ridge_heightmap = new s16[csize.X * csize.Z];
MapgenV7Params *sp = (MapgenV7Params *)params->sparams;
@@ -77,27 +77,43 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge)
noise_ridge_uwater = new Noise(&sp->np_ridge_uwater, seed, csize.X, csize.Z);
//// 3d terrain noise
- noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y, csize.Z);
- noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y, csize.Z);
- noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y, csize.Z);
- noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y, csize.Z);
+ noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
+ noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
+ noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z);
+ noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z);
//// Biome noise
- noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
- noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+ noise_heat = new Noise(&params->np_biome_heat, seed, csize.X, csize.Z);
+ noise_humidity = new Noise(&params->np_biome_humidity, seed, csize.X, csize.Z);
+ noise_heat_blend = new Noise(&params->np_biome_heat_blend, seed, csize.X, csize.Z);
+ noise_humidity_blend = new Noise(&params->np_biome_humidity_blend, seed, csize.X, csize.Z);
//// Resolve nodes to be used
INodeDefManager *ndef = emerge->ndef;
- c_stone = ndef->getId("mapgen_stone");
- c_dirt = ndef->getId("mapgen_dirt");
- c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
- c_sand = ndef->getId("mapgen_sand");
- c_water_source = ndef->getId("mapgen_water_source");
- c_lava_source = ndef->getId("mapgen_lava_source");
- c_ice = ndef->getId("default:ice");
+ c_stone = ndef->getId("mapgen_stone");
+ c_water_source = ndef->getId("mapgen_water_source");
+ c_lava_source = ndef->getId("mapgen_lava_source");
+ c_desert_stone = ndef->getId("mapgen_desert_stone");
+ c_ice = ndef->getId("mapgen_ice");
+ c_sandstone = ndef->getId("mapgen_sandstone");
+
+ c_cobble = ndef->getId("mapgen_cobble");
+ c_stair_cobble = ndef->getId("mapgen_stair_cobble");
+ c_mossycobble = ndef->getId("mapgen_mossycobble");
+ c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
+ c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
+
if (c_ice == CONTENT_IGNORE)
c_ice = CONTENT_AIR;
+ if (c_mossycobble == CONTENT_IGNORE)
+ c_mossycobble = c_cobble;
+ if (c_stair_cobble == CONTENT_IGNORE)
+ c_stair_cobble = c_cobble;
+ if (c_sandstonebrick == CONTENT_IGNORE)
+ c_sandstonebrick = c_sandstone;
+ if (c_stair_sandstonebrick == CONTENT_IGNORE)
+ c_stair_sandstonebrick = c_sandstone;
}
@@ -117,6 +133,8 @@ MapgenV7::~MapgenV7()
delete noise_heat;
delete noise_humidity;
+ delete noise_heat_blend;
+ delete noise_humidity_blend;
delete[] ridge_heightmap;
delete[] heightmap;
@@ -128,21 +146,21 @@ MapgenV7Params::MapgenV7Params()
{
spflags = MGV7_MOUNTAINS | MGV7_RIDGES;
- np_terrain_base = NoiseParams(4, 70, v3f(300, 300, 300), 82341, 6, 0.7, 2.0);
- np_terrain_alt = NoiseParams(4, 25, v3f(600, 600, 600), 5934, 5, 0.6, 2.0);
- np_terrain_persist = NoiseParams(0.6, 0.1, v3f(500, 500, 500), 539, 3, 0.6, 2.0);
- np_height_select = NoiseParams(-0.5, 1, v3f(250, 250, 250), 4213, 5, 0.69, 2.0);
- np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 4, 0.7, 2.0);
- np_mount_height = NoiseParams(100, 30, v3f(500, 500, 500), 72449, 4, 0.6, 2.0);
- np_ridge_uwater = NoiseParams(0, 1, v3f(500, 500, 500), 85039, 4, 0.6, 2.0);
- np_mountain = NoiseParams(-0.6, 1, v3f(250, 350, 250), 5333, 5, 0.68, 2.0);
- np_ridge = NoiseParams(0, 1, v3f(100, 100, 100), 6467, 4, 0.75, 2.0);
- np_cave1 = NoiseParams(0, 12, v3f(100, 100, 100), 52534, 4, 0.5, 2.0);
- np_cave2 = NoiseParams(0, 12, v3f(100, 100, 100), 10325, 4, 0.5, 2.0);
+ np_terrain_base = NoiseParams(4, 70, v3f(600, 600, 600), 82341, 5, 0.6, 2.0);
+ np_terrain_alt = NoiseParams(4, 25, v3f(600, 600, 600), 5934, 5, 0.6, 2.0);
+ np_terrain_persist = NoiseParams(0.6, 0.1, v3f(2000, 2000, 2000), 539, 3, 0.6, 2.0);
+ np_height_select = NoiseParams(-12, 24, v3f(500, 500, 500), 4213, 6, 0.7, 2.0);
+ np_filler_depth = NoiseParams(0, 1.2, v3f(150, 150, 150), 261, 3, 0.7, 2.0);
+ np_mount_height = NoiseParams(256, 112, v3f(1000, 1000, 1000), 72449, 3, 0.6, 2.0);
+ np_ridge_uwater = NoiseParams(0, 1, v3f(1000, 1000, 1000), 85039, 5, 0.6, 2.0);
+ np_mountain = NoiseParams(-0.6, 1, v3f(250, 350, 250), 5333, 5, 0.63, 2.0);
+ np_ridge = NoiseParams(0, 1, v3f(100, 100, 100), 6467, 4, 0.75, 2.0);
+ np_cave1 = NoiseParams(0, 12, v3f(100, 100, 100), 52534, 4, 0.5, 2.0);
+ np_cave2 = NoiseParams(0, 12, v3f(100, 100, 100), 10325, 4, 0.5, 2.0);
}
-void MapgenV7Params::readParams(Settings *settings)
+void MapgenV7Params::readParams(const Settings *settings)
{
settings->getFlagStrNoEx("mgv7_spflags", spflags, flagdesc_mapgen_v7);
@@ -160,7 +178,7 @@ void MapgenV7Params::readParams(Settings *settings)
}
-void MapgenV7Params::writeParams(Settings *settings)
+void MapgenV7Params::writeParams(Settings *settings) const
{
settings->setFlagStr("mgv7_spflags", spflags, flagdesc_mapgen_v7, (u32)-1);
@@ -210,14 +228,15 @@ int MapgenV7::getGroundLevelAtPoint(v2s16 p)
void MapgenV7::makeChunk(BlockMakeData *data)
{
+ // Pre-conditions
assert(data->vmanip);
assert(data->nodedef);
assert(data->blockpos_requested.X >= data->blockpos_min.X &&
- data->blockpos_requested.Y >= data->blockpos_min.Y &&
- data->blockpos_requested.Z >= data->blockpos_min.Z);
+ data->blockpos_requested.Y >= data->blockpos_min.Y &&
+ data->blockpos_requested.Z >= data->blockpos_min.Z);
assert(data->blockpos_requested.X <= data->blockpos_max.X &&
- data->blockpos_requested.Y <= data->blockpos_max.Y &&
- data->blockpos_requested.Z <= data->blockpos_max.Z);
+ data->blockpos_requested.Y <= data->blockpos_max.Y &&
+ data->blockpos_requested.Z <= data->blockpos_max.Z);
this->generating = true;
this->vm = data->vmanip;
@@ -239,20 +258,59 @@ void MapgenV7::makeChunk(BlockMakeData *data)
// Generate base terrain, mountains, and ridges with initial heightmaps
s16 stone_surface_max_y = generateTerrain();
+ // Create heightmap
updateHeightmap(node_min, node_max);
- // Calculate biomes
+ // Create biomemap at heightmap surface
bmgr->calcBiomes(csize.X, csize.Z, noise_heat->result,
noise_humidity->result, heightmap, biomemap);
- // Actually place the biome-specific nodes and what not
- generateBiomes();
+ // Actually place the biome-specific nodes
+ MgStoneType stone_type = generateBiomes(noise_heat->result, noise_humidity->result);
if (flags & MG_CAVES)
generateCaves(stone_surface_max_y);
if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
- DungeonGen dgen(this, NULL);
+ DungeonParams dp;
+
+ dp.np_rarity = nparams_dungeon_rarity;
+ dp.np_density = nparams_dungeon_density;
+ dp.np_wetness = nparams_dungeon_wetness;
+ dp.c_water = c_water_source;
+ if (stone_type == STONE) {
+ dp.c_cobble = c_cobble;
+ dp.c_moss = c_mossycobble;
+ dp.c_stair = c_stair_cobble;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 3.0;
+ dp.holesize = v3s16(1, 2, 1);
+ dp.roomsize = v3s16(0, 0, 0);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ } else if (stone_type == DESERT_STONE) {
+ dp.c_cobble = c_desert_stone;
+ dp.c_moss = c_desert_stone;
+ dp.c_stair = c_desert_stone;
+
+ dp.diagonal_dirs = true;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 3, 2);
+ dp.roomsize = v3s16(2, 5, 2);
+ dp.notifytype = GENNOTIFY_TEMPLE;
+ } else if (stone_type == SANDSTONE) {
+ dp.c_cobble = c_sandstonebrick;
+ dp.c_moss = c_sandstonebrick;
+ dp.c_stair = c_sandstonebrick;
+
+ dp.diagonal_dirs = false;
+ dp.mossratio = 0.0;
+ dp.holesize = v3s16(2, 2, 2);
+ dp.roomsize = v3s16(2, 0, 2);
+ dp.notifytype = GENNOTIFY_DUNGEON;
+ }
+
+ DungeonGen dgen(this, &dp);
dgen.generate(blockseed, full_node_min, full_node_max);
}
@@ -270,7 +328,9 @@ void MapgenV7::makeChunk(BlockMakeData *data)
updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
if (flags & MG_LIGHT)
- calcLighting(node_min, node_max);
+ calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+ full_node_min, full_node_max);
+
//setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
// node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
@@ -282,7 +342,7 @@ void MapgenV7::calculateNoise()
{
//TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
int x = node_min.X;
- int y = node_min.Y;
+ int y = node_min.Y - 1;
int z = node_min.Z;
noise_terrain_persist->perlinMap2D(x, z);
@@ -302,31 +362,38 @@ void MapgenV7::calculateNoise()
noise_ridge_uwater->perlinMap2D(x, z);
}
- if ((spflags & MGV7_MOUNTAINS) && node_max.Y >= 0) {
- noise_mountain->perlinMap3D(x, y, z);
- noise_mount_height->perlinMap2D(x, z);
- }
+ // Mountain noises are calculated in generateMountainTerrain()
- if (node_max.Y >= water_level) {
- noise_filler_depth->perlinMap2D(x, z);
- noise_heat->perlinMap2D(x, z);
- noise_humidity->perlinMap2D(x, z);
+ noise_filler_depth->perlinMap2D(x, z);
+ noise_heat->perlinMap2D(x, z);
+ noise_humidity->perlinMap2D(x, z);
+ noise_heat_blend->perlinMap2D(x, z);
+ noise_humidity_blend->perlinMap2D(x, z);
+
+ for (s32 i = 0; i < csize.X * csize.Z; i++) {
+ noise_heat->result[i] += noise_heat_blend->result[i];
+ noise_humidity->result[i] += noise_humidity_blend->result[i];
}
+
+ heatmap = noise_heat->result;
+ humidmap = noise_humidity->result;
//printf("calculateNoise: %dus\n", t.stop());
}
Biome *MapgenV7::getBiomeAtPoint(v3s16 p)
{
- float heat = NoisePerlin2D(&noise_heat->np, p.X, p.Z, seed);
- float humidity = NoisePerlin2D(&noise_humidity->np, p.X, p.Z, seed);
+ float heat = NoisePerlin2D(&noise_heat->np, p.X, p.Z, seed) +
+ NoisePerlin2D(&noise_heat_blend->np, p.X, p.Z, seed);
+ float humidity = NoisePerlin2D(&noise_humidity->np, p.X, p.Z, seed) +
+ NoisePerlin2D(&noise_humidity_blend->np, p.X, p.Z, seed);
s16 groundlevel = baseTerrainLevelAtPoint(p.X, p.Z);
return bmgr->getBiome(heat, humidity, groundlevel);
}
//needs to be updated
-float MapgenV7::baseTerrainLevelAtPoint(int x, int z)
+float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z)
{
float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed);
hselect = rangelim(hselect, 0.0, 1.0);
@@ -359,19 +426,23 @@ float MapgenV7::baseTerrainLevelFromMap(int index)
}
-bool MapgenV7::getMountainTerrainAtPoint(int x, int y, int z)
+bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
{
float mnt_h_n = NoisePerlin2D(&noise_mount_height->np, x, z, seed);
+ float density_gradient = -((float)y / mnt_h_n);
float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed);
- return mnt_n * mnt_h_n >= (float)y;
+
+ return mnt_n + density_gradient >= 0.0;
}
-bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, int y)
+bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
{
float mounthn = noise_mount_height->result[idx_xz];
+ float density_gradient = -((float)y / mounthn);
float mountn = noise_mountain->result[idx_xyz];
- return mountn * mounthn >= (float)y;
+
+ return mountn + density_gradient >= 0.0;
}
@@ -412,26 +483,30 @@ void MapgenV7::carveRivers() {
int MapgenV7::generateTerrain()
{
- int ymax = generateBaseTerrain();
+ s16 stone_surface_min_y;
+ s16 stone_surface_max_y;
+
+ generateBaseTerrain(&stone_surface_min_y, &stone_surface_max_y);
- if (spflags & MGV7_MOUNTAINS)
- ymax = generateMountainTerrain(ymax);
+ if ((spflags & MGV7_MOUNTAINS) && stone_surface_min_y < node_max.Y)
+ stone_surface_max_y = generateMountainTerrain(stone_surface_max_y);
if (spflags & MGV7_RIDGES)
generateRidgeTerrain();
- return ymax;
+ return stone_surface_max_y;
}
-int MapgenV7::generateBaseTerrain()
+void MapgenV7::generateBaseTerrain(s16 *stone_surface_min_y, s16 *stone_surface_max_y)
{
MapNode n_air(CONTENT_AIR);
MapNode n_stone(c_stone);
MapNode n_water(c_water_source);
- int stone_surface_max_y = -MAP_GENERATION_LIMIT;
v3s16 em = vm->m_area.getExtent();
+ s16 surface_min_y = MAX_MAP_GENERATION_LIMIT;
+ s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
u32 index = 0;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
@@ -442,11 +517,14 @@ int MapgenV7::generateBaseTerrain()
heightmap[index] = surface_y;
ridge_heightmap[index] = surface_y;
- if (surface_y > stone_surface_max_y)
- stone_surface_max_y = surface_y;
+ if (surface_y < surface_min_y)
+ surface_min_y = surface_y;
+
+ if (surface_y > surface_max_y)
+ surface_max_y = surface_y;
- u32 i = vm->m_area.index(x, node_min.Y, z);
- for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+ u32 i = vm->m_area.index(x, node_min.Y - 1, z);
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
if (y <= surface_y)
vm->m_data[i] = n_stone;
@@ -459,20 +537,21 @@ int MapgenV7::generateBaseTerrain()
}
}
- return stone_surface_max_y;
+ *stone_surface_min_y = surface_min_y;
+ *stone_surface_max_y = surface_max_y;
}
-int MapgenV7::generateMountainTerrain(int ymax)
+int MapgenV7::generateMountainTerrain(s16 ymax)
{
- if (node_max.Y < 0)
- return ymax;
+ noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+ noise_mount_height->perlinMap2D(node_min.X, node_min.Z);
MapNode n_stone(c_stone);
u32 j = 0;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
- for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
u32 vi = vm->m_area.index(node_min.X, y, z);
for (s16 x = node_min.X; x <= node_max.X; x++) {
int index = (z - node_min.Z) * csize.X + (x - node_min.X);
@@ -505,7 +584,7 @@ void MapgenV7::generateRidgeTerrain()
float width = 0.2; // TODO: figure out acceptable perlin noise values
for (s16 z = node_min.Z; z <= node_max.Z; z++)
- for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
u32 vi = vm->m_area.index(node_min.X, y, z);
for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) {
int j = (z - node_min.Z) * csize.X + (x - node_min.X);
@@ -534,98 +613,100 @@ void MapgenV7::generateRidgeTerrain()
}
-void MapgenV7::generateBiomes()
+MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map)
{
- if (node_max.Y < water_level)
- return;
-
- MapNode n_air(CONTENT_AIR);
- MapNode n_stone(c_stone);
- MapNode n_water(c_water_source);
-
v3s16 em = vm->m_area.getExtent();
u32 index = 0;
+ MgStoneType stone_type = STONE;
for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
- Biome *biome = (Biome *)bmgr->get(biomemap[index]);
- s16 dfiller = biome->depth_filler + noise_filler_depth->result[index];
- s16 y0_top = biome->depth_top;
- s16 y0_filler = biome->depth_top + dfiller;
- s16 shore_max = water_level + biome->height_shore;
- s16 depth_water_top = biome->depth_water_top;
-
- s16 nplaced = 0;
- u32 i = vm->m_area.index(x, node_max.Y, z);
-
- content_t c_above = vm->m_data[i + em.X].getContent();
- bool have_air = c_above == CONTENT_AIR;
+ Biome *biome = NULL;
+ u16 depth_top = 0;
+ u16 base_filler = 0;
+ u16 depth_water_top = 0;
+ u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+ // Check node at base of mapchunk above, either a node of a previously
+ // generated mapchunk or if not, a node of overgenerated base terrain.
+ content_t c_above = vm->m_data[vi + em.X].getContent();
+ bool air_above = c_above == CONTENT_AIR;
+ bool water_above = c_above == c_water_source;
+
+ // If there is air or water above enable top/filler placement, otherwise force
+ // nplaced to stone level by setting a number exceeding any possible filler depth.
+ u16 nplaced = (air_above || water_above) ? 0 : (u16)-1;
for (s16 y = node_max.Y; y >= node_min.Y; y--) {
- content_t c = vm->m_data[i].getContent();
+ content_t c = vm->m_data[vi].getContent();
- // It could be the case that the elevation is equal to the chunk
- // boundary, but the chunk above has not been generated yet
- if (y == node_max.Y && c_above == CONTENT_IGNORE &&
- y == heightmap[index] && c == c_stone) {
- int j = (z - node_min.Z) * zstride +
- (y - node_min.Y) * ystride +
- (x - node_min.X);
- have_air = !getMountainTerrainFromMap(j, index, y);
+ // Biome is recalculated each time an upper surface is detected while
+ // working down a column. The selected biome then remains in effect for
+ // all nodes below until the next surface and biome recalculation.
+ // Biome is recalculated:
+ // 1. At the surface of stone below air or water.
+ // 2. At the surface of water below air.
+ // 3. When stone or water is detected but biome has not yet been calculated.
+ if ((c == c_stone && (air_above || water_above || !biome)) ||
+ (c == c_water_source && (air_above || !biome))) {
+ biome = bmgr->getBiome(heat_map[index], humidity_map[index], y);
+ depth_top = biome->depth_top;
+ base_filler = MYMAX(depth_top + biome->depth_filler
+ + noise_filler_depth->result[index], 0);
+ depth_water_top = biome->depth_water_top;
+
+ // Detect stone type for dungeons during every biome calculation.
+ // This is more efficient than detecting per-node and will not
+ // miss any desert stone or sandstone biomes.
+ if (biome->c_stone == c_desert_stone)
+ stone_type = DESERT_STONE;
+ else if (biome->c_stone == c_sandstone)
+ stone_type = SANDSTONE;
}
- if (c == c_stone && have_air) {
- content_t c_below = vm->m_data[i - em.X].getContent();
-
- if (c_below != CONTENT_AIR) {
- if (nplaced < y0_top) {
- if(y < water_level)
- vm->m_data[i] = MapNode(biome->c_underwater);
- else if(y <= shore_max)
- vm->m_data[i] = MapNode(biome->c_shore_top);
- else
- vm->m_data[i] = MapNode(biome->c_top);
- nplaced++;
- } else if (nplaced < y0_filler && nplaced >= y0_top) {
- if(y < water_level)
- vm->m_data[i] = MapNode(biome->c_underwater);
- else if(y <= shore_max)
- vm->m_data[i] = MapNode(biome->c_shore_filler);
- else
- vm->m_data[i] = MapNode(biome->c_filler);
- nplaced++;
- } else if (c == c_stone) {
- have_air = false;
- nplaced = 0;
- vm->m_data[i] = MapNode(biome->c_stone);
- } else {
- have_air = false;
- nplaced = 0;
- }
- } else if (c == c_stone) {
- have_air = false;
- nplaced = 0;
- vm->m_data[i] = MapNode(biome->c_stone);
+ if (c == c_stone) {
+ content_t c_below = vm->m_data[vi - em.X].getContent();
+
+ // If the node below isn't solid, make this node stone, so that
+ // any top/filler nodes above are structurally supported.
+ // This is done by aborting the cycle of top/filler placement
+ // immediately by forcing nplaced to stone level.
+ if (c_below == CONTENT_AIR || c_below == c_water_source)
+ nplaced = (u16)-1;
+
+ if (nplaced < depth_top) {
+ vm->m_data[vi] = MapNode(biome->c_top);
+ nplaced++;
+ } else if (nplaced < base_filler) {
+ vm->m_data[vi] = MapNode(biome->c_filler);
+ nplaced++;
+ } else {
+ vm->m_data[vi] = MapNode(biome->c_stone);
}
- } else if (c == c_stone) {
- have_air = false;
- nplaced = 0;
- vm->m_data[i] = MapNode(biome->c_stone);
+
+ air_above = false;
+ water_above = false;
} else if (c == c_water_source) {
- have_air = true;
- nplaced = 0;
- if(y > water_level - depth_water_top)
- vm->m_data[i] = MapNode(biome->c_water_top);
- else
- vm->m_data[i] = MapNode(biome->c_water);
+ vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ?
+ biome->c_water_top : biome->c_water);
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = false;
+ water_above = true;
} else if (c == CONTENT_AIR) {
- have_air = true;
- nplaced = 0;
+ nplaced = 0; // Enable top/filler placement for next surface
+ air_above = true;
+ water_above = false;
+ } else { // Possible various nodes overgenerated from neighbouring mapchunks
+ nplaced = (u16)-1; // Disable top/filler placement
+ air_above = false;
+ water_above = false;
}
- vm->m_area.add_y(em, i, -1);
+ vm->m_area.add_y(em, vi, -1);
}
}
+
+ return stone_type;
}
@@ -639,14 +720,31 @@ void MapgenV7::dustTopNodes()
for (s16 z = node_min.Z; z <= node_max.Z; z++)
for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
- Biome *biome = (Biome *)bmgr->get(biomemap[index]);
+ Biome *biome = (Biome *)bmgr->getRaw(biomemap[index]);
if (biome->c_dust == CONTENT_IGNORE)
continue;
- s16 y = node_max.Y;
- u32 vi = vm->m_area.index(x, y, z);
- for (; y >= node_min.Y; y--) {
+ u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+ content_t c_full_max = vm->m_data[vi].getContent();
+ s16 y_start;
+
+ if (c_full_max == CONTENT_AIR) {
+ y_start = full_node_max.Y - 1;
+ } else if (c_full_max == CONTENT_IGNORE) {
+ vi = vm->m_area.index(x, node_max.Y + 1, z);
+ content_t c_max = vm->m_data[vi].getContent();
+
+ if (c_max == CONTENT_AIR)
+ y_start = node_max.Y;
+ else
+ continue;
+ } else {
+ continue;
+ }
+
+ vi = vm->m_area.index(x, y_start, z);
+ for (s16 y = y_start; y >= node_min.Y - 1; y--) {
if (vm->m_data[vi].getContent() != CONTENT_AIR)
break;
@@ -654,10 +752,7 @@ void MapgenV7::dustTopNodes()
}
content_t c = vm->m_data[vi].getContent();
- if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE) {
- if (y == node_max.Y)
- continue;
-
+ if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
vm->m_area.add_y(em, vi, 1);
vm->m_data[vi] = MapNode(biome->c_dust);
}
@@ -761,39 +856,32 @@ void MapgenV7::addTopNodes()
#endif
-void MapgenV7::generateCaves(int max_stone_y)
+void MapgenV7::generateCaves(s16 max_stone_y)
{
if (max_stone_y >= node_min.Y) {
u32 index = 0;
- u32 index2d = 0;
-
- for (s16 z = node_min.Z; z <= node_max.Z; z++) {
- for (s16 y = node_min.Y; y <= node_max.Y; y++) {
- u32 i = vm->m_area.index(node_min.X, y, z);
- for (s16 x = node_min.X; x <= node_max.X;
- x++, i++, index++, index2d++) {
- Biome *biome = (Biome *)bmgr->get(biomemap[index2d]);
+
+ for (s16 z = node_min.Z; z <= node_max.Z; z++)
+ for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+ u32 i = vm->m_area.index(node_min.X, y, z);
+ for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++) {
+ float d1 = contour(noise_cave1->result[index]);
+ float d2 = contour(noise_cave2->result[index]);
+ if (d1 * d2 > 0.3) {
content_t c = vm->m_data[i].getContent();
- if (c == CONTENT_AIR || (y <= water_level &&
- c != biome->c_stone && c != c_stone))
+ if (!ndef->get(c).is_ground_content || c == CONTENT_AIR)
continue;
- float d1 = contour(noise_cave1->result[index]);
- float d2 = contour(noise_cave2->result[index]);
- if (d1 * d2 > 0.3)
- vm->m_data[i] = MapNode(CONTENT_AIR);
+ vm->m_data[i] = MapNode(CONTENT_AIR);
}
- index2d -= ystride;
}
- index2d += ystride;
}
}
PseudoRandom ps(blockseed + 21343);
- u32 bruises_count = (ps.range(1, 5) == 1) ? ps.range(1, 2) : 0;
+ u32 bruises_count = (ps.range(1, 4) == 1) ? ps.range(1, 2) : 0;
for (u32 i = 0; i < bruises_count; i++) {
- CaveV7 cave(this, &ps, true);
+ CaveV7 cave(this, &ps);
cave.makeCave(node_min, node_max, max_stone_y);
}
}
-
diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h
index bcf362ac9..c0cfa8c77 100644
--- a/src/mapgen_v7.h
+++ b/src/mapgen_v7.h
@@ -48,8 +48,8 @@ struct MapgenV7Params : public MapgenSpecificParams {
MapgenV7Params();
~MapgenV7Params() {}
- void readParams(Settings *settings);
- void writeParams(Settings *settings);
+ void readParams(const Settings *settings);
+ void writeParams(Settings *settings) const;
};
class MapgenV7 : public Mapgen {
@@ -82,18 +82,21 @@ public:
Noise *noise_heat;
Noise *noise_humidity;
+ Noise *noise_heat_blend;
+ Noise *noise_humidity_blend;
content_t c_stone;
- content_t c_dirt;
- content_t c_dirt_with_grass;
- content_t c_sand;
content_t c_water_source;
content_t c_lava_source;
+ content_t c_desert_stone;
content_t c_ice;
- content_t c_gravel;
+ content_t c_sandstone;
+
content_t c_cobble;
- content_t c_desert_sand;
- content_t c_desert_stone;
+ content_t c_stair_cobble;
+ content_t c_mossycobble;
+ content_t c_sandstonebrick;
+ content_t c_stair_sandstonebrick;
MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge);
~MapgenV7();
@@ -102,24 +105,24 @@ public:
int getGroundLevelAtPoint(v2s16 p);
Biome *getBiomeAtPoint(v3s16 p);
- float baseTerrainLevelAtPoint(int x, int z);
+ float baseTerrainLevelAtPoint(s16 x, s16 z);
float baseTerrainLevelFromMap(int index);
- bool getMountainTerrainAtPoint(int x, int y, int z);
- bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, int y);
+ bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z);
+ bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y);
void calculateNoise();
virtual int generateTerrain();
- int generateBaseTerrain();
- int generateMountainTerrain(int ymax);
+ void generateBaseTerrain(s16 *stone_surface_min_y, s16 *stone_surface_max_y);
+ int generateMountainTerrain(s16 ymax);
void generateRidgeTerrain();
- void generateBiomes();
+ MgStoneType generateBiomes(float *heat_map, float *humidity_map);
void dustTopNodes();
//void addTopNodes();
- void generateCaves(int max_stone_y);
+ void generateCaves(s16 max_stone_y);
};
struct MapgenFactoryV7 : public MapgenFactory {
diff --git a/src/mapnode.cpp b/src/mapnode.cpp
index 056b94054..fe9686f0d 100644
--- a/src/mapnode.cpp
+++ b/src/mapnode.cpp
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h"
#include "mapnode.h"
#include "porting.h"
-#include "main.h" // For g_settings
#include "nodedef.h"
#include "content_mapnode.h" // For mapnode_translate_*_internal
#include "serialization.h" // For ser_ver_supported
@@ -71,7 +70,23 @@ void MapNode::setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr
param1 |= (a_light & 0x0f)<<4;
}
else
- assert(0);
+ assert("Invalid light bank" == NULL);
+}
+
+bool MapNode::isLightDayNightEq(INodeDefManager *nodemgr) const
+{
+ const ContentFeatures &f = nodemgr->get(*this);
+ bool isEqual;
+
+ if (f.param_type == CPT_LIGHT) {
+ u8 day = MYMAX(f.light_source, param1 & 0x0f);
+ u8 night = MYMAX(f.light_source, (param1 >> 4) & 0x0f);
+ isEqual = day == night;
+ } else {
+ isEqual = true;
+ }
+
+ return isEqual;
}
u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const
@@ -88,7 +103,7 @@ u8 MapNode::getLight(enum LightBank bank, INodeDefManager *nodemgr) const
return MYMAX(f.light_source, light);
}
-u8 MapNode::getLightNoChecks(enum LightBank bank, const ContentFeatures *f)
+u8 MapNode::getLightNoChecks(enum LightBank bank, const ContentFeatures *f) const
{
return MYMAX(f->light_source,
bank == LIGHTBANK_DAY ? param1 & 0x0f : (param1 >> 4) & 0x0f);
@@ -148,6 +163,9 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) {
ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2;
if (cpt2 == CPT2_FACEDIR) {
+ if (param2 >= 4)
+ return;
+
u8 newrot = param2 & 3;
param2 &= ~3;
param2 |= (newrot + rot) & 3;
@@ -500,8 +518,8 @@ void MapNode::serializeBulk(std::ostream &os, int version,
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
- assert(content_width == 2);
- assert(params_width == 2);
+ sanity_check(content_width == 2);
+ sanity_check(params_width == 2);
// Can't do this anymore; we have 16-bit dynamically allocated node IDs
// in memory; conversion just won't work in this direction.
@@ -547,9 +565,10 @@ void MapNode::deSerializeBulk(std::istream &is, int version,
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapNode format not supported");
- assert(version >= 22);
- assert(content_width == 1 || content_width == 2);
- assert(params_width == 2);
+ if (version < 22
+ || (content_width != 1 && content_width != 2)
+ || params_width != 2)
+ FATAL_ERROR("Deserialize bulk node data error");
// Uncompress or read data
u32 len = nodecount * (content_width + params_width);
diff --git a/src/mapnode.h b/src/mapnode.h
index 82c53e7d4..7cc25c60c 100644
--- a/src/mapnode.h
+++ b/src/mapnode.h
@@ -192,6 +192,14 @@ struct MapNode
}
void setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr);
+
+ /**
+ * Check if the light value for night differs from the light value for day.
+ *
+ * @return If the light values are equal, returns true; otherwise false
+ */
+ bool isLightDayNightEq(INodeDefManager *nodemgr) const;
+
u8 getLight(enum LightBank bank, INodeDefManager *nodemgr) const;
/**
@@ -209,7 +217,7 @@ struct MapNode
* @pre f != NULL
* @pre f->param_type == CPT_LIGHT
*/
- u8 getLightNoChecks(LightBank bank, const ContentFeatures *f);
+ u8 getLightNoChecks(LightBank bank, const ContentFeatures *f) const;
bool getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodemgr) const;
diff --git a/src/mapsector.cpp b/src/mapsector.cpp
index 0d40a659d..9ce3c8eb3 100644
--- a/src/mapsector.cpp
+++ b/src/mapsector.cpp
@@ -59,7 +59,7 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
if(m_block_cache != NULL && y == m_block_cache_y){
return m_block_cache;
}
-
+
// If block doesn't exist, return NULL
std::map<s16, MapBlock*>::iterator n = m_blocks.find(y);
if(n == m_blocks.end())
@@ -70,11 +70,11 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
else{
block = n->second;
}
-
+
// Cache the last result
m_block_cache_y = y;
m_block_cache = block;
-
+
return block;
}
@@ -85,19 +85,19 @@ MapBlock * MapSector::getBlockNoCreateNoEx(s16 y)
MapBlock * MapSector::createBlankBlockNoInsert(s16 y)
{
- assert(getBlockBuffered(y) == NULL);
+ assert(getBlockBuffered(y) == NULL); // Pre-condition
v3s16 blockpos_map(m_pos.X, y, m_pos.Y);
-
+
MapBlock *block = new MapBlock(m_parent, blockpos_map, m_gamedef);
-
+
return block;
}
MapBlock * MapSector::createBlankBlock(s16 y)
{
MapBlock *block = createBlankBlockNoInsert(y);
-
+
m_blocks[y] = block;
return block;
@@ -114,7 +114,7 @@ void MapSector::insertBlock(MapBlock *block)
v2s16 p2d(block->getPos().X, block->getPos().Z);
assert(p2d == m_pos);
-
+
// Insert into container
m_blocks[block_y] = block;
}
@@ -125,7 +125,7 @@ void MapSector::deleteBlock(MapBlock *block)
// Clear from cache
m_block_cache = NULL;
-
+
// Remove from container
m_blocks.erase(block_y);
@@ -133,7 +133,7 @@ void MapSector::deleteBlock(MapBlock *block)
delete block;
}
-void MapSector::getBlocks(std::list<MapBlock*> &dest)
+void MapSector::getBlocks(MapBlockVect &dest)
{
for(std::map<s16, MapBlock*>::iterator bi = m_blocks.begin();
bi != m_blocks.end(); ++bi)
@@ -142,6 +142,11 @@ void MapSector::getBlocks(std::list<MapBlock*> &dest)
}
}
+bool MapSector::empty()
+{
+ return m_blocks.empty();
+}
+
/*
ServerMapSector
*/
@@ -159,18 +164,18 @@ void ServerMapSector::serialize(std::ostream &os, u8 version)
{
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapSector format not supported");
-
+
/*
[0] u8 serialization version
+ heightmap data
*/
-
+
// Server has both of these, no need to support not having them.
//assert(m_objects != NULL);
// Write version
os.write((char*)&version, 1);
-
+
/*
Add stuff here, if needed
*/
@@ -193,18 +198,18 @@ ServerMapSector* ServerMapSector::deSerialize(
/*
Read stuff
*/
-
+
// Read version
u8 version = SER_FMT_VER_INVALID;
is.read((char*)&version, 1);
-
+
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapSector format not supported");
-
+
/*
Add necessary reading stuff here
*/
-
+
/*
Get or create sector
*/
diff --git a/src/mapsector.h b/src/mapsector.h
index dac4ee8d6..4c1ce86a3 100644
--- a/src/mapsector.h
+++ b/src/mapsector.h
@@ -22,11 +22,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h"
#include "irr_v2d.h"
+#include "mapblock.h"
#include <ostream>
#include <map>
-#include <list>
+#include <vector>
-class MapBlock;
class Map;
class IGameDef;
@@ -40,7 +40,7 @@ class IGameDef;
class MapSector
{
public:
-
+
MapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
virtual ~MapSector();
@@ -58,16 +58,18 @@ public:
MapBlock * createBlankBlock(s16 y);
void insertBlock(MapBlock *block);
-
+
void deleteBlock(MapBlock *block);
-
- void getBlocks(std::list<MapBlock*> &dest);
-
+
+ void getBlocks(MapBlockVect &dest);
+
+ bool empty();
+
// Always false at the moment, because sector contains no metadata.
bool differs_from_disk;
protected:
-
+
// The pile of MapBlocks
std::map<s16, MapBlock*> m_blocks;
@@ -76,12 +78,12 @@ protected:
v2s16 m_pos;
IGameDef *m_gamedef;
-
+
// Last-used block is cached here for quicker access.
- // Be sure to set this to NULL when the cached block is deleted
+ // Be sure to set this to NULL when the cached block is deleted
MapBlock *m_block_cache;
s16 m_block_cache_y;
-
+
/*
Private methods
*/
@@ -94,7 +96,7 @@ class ServerMapSector : public MapSector
public:
ServerMapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
~ServerMapSector();
-
+
u32 getId() const
{
return MAPSECTOR_SERVER;
@@ -106,7 +108,7 @@ public:
*/
void serialize(std::ostream &os, u8 version);
-
+
static ServerMapSector* deSerialize(
std::istream &is,
Map *parent,
@@ -114,7 +116,7 @@ public:
std::map<v2s16, MapSector*> & sectors,
IGameDef *gamedef
);
-
+
private:
};
@@ -124,7 +126,7 @@ class ClientMapSector : public MapSector
public:
ClientMapSector(Map *parent, v2s16 pos, IGameDef *gamedef);
~ClientMapSector();
-
+
u32 getId() const
{
return MAPSECTOR_CLIENT;
@@ -133,6 +135,6 @@ public:
private:
};
#endif
-
+
#endif
diff --git a/src/mesh.cpp b/src/mesh.cpp
index e021e4c92..dab1575f3 100644
--- a/src/mesh.cpp
+++ b/src/mesh.cpp
@@ -33,6 +33,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MY_ETLM_READ_ONLY video::ETLM_READ_ONLY
#endif
+static void applyFacesShading(video::SColor& color, float factor)
+{
+ color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
+ color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
+ color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255));
+}
+
scene::IAnimatedMesh* createCubeMesh(v3f scale)
{
video::SColor c(255,255,255,255);
@@ -94,26 +101,25 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale)
void scaleMesh(scene::IMesh *mesh, v3f scale)
{
- if(mesh == NULL)
+ if (mesh == NULL)
return;
core::aabbox3d<f32> bbox;
- bbox.reset(0,0,0);
+ bbox.reset(0, 0, 0);
- u16 mc = mesh->getMeshBufferCount();
- for(u16 j=0; j<mc; j++)
- {
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i=0; i<vc; i++)
- {
- vertices[i].Pos *= scale;
- }
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *)(vertices + i * stride))->Pos *= scale;
+
buf->recalculateBoundingBox();
// calculate total bounding box
- if(j == 0)
+ if (j == 0)
bbox = buf->getBoundingBox();
else
bbox.addInternalBox(buf->getBoundingBox());
@@ -123,26 +129,25 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
void translateMesh(scene::IMesh *mesh, v3f vec)
{
- if(mesh == NULL)
+ if (mesh == NULL)
return;
core::aabbox3d<f32> bbox;
- bbox.reset(0,0,0);
+ bbox.reset(0, 0, 0);
- u16 mc = mesh->getMeshBufferCount();
- for(u16 j=0; j<mc; j++)
- {
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i=0; i<vc; i++)
- {
- vertices[i].Pos += vec;
- }
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *)(vertices + i * stride))->Pos += vec;
+
buf->recalculateBoundingBox();
// calculate total bounding box
- if(j == 0)
+ if (j == 0)
bbox = buf->getBoundingBox();
else
bbox.addInternalBox(buf->getBoundingBox());
@@ -150,20 +155,48 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
mesh->setBoundingBox(bbox);
}
+
void setMeshColor(scene::IMesh *mesh, const video::SColor &color)
{
- if(mesh == NULL)
+ if (mesh == NULL)
return;
-
- u16 mc = mesh->getMeshBufferCount();
- for(u16 j=0; j<mc; j++)
- {
+
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 j = 0; j < mc; j++) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
- video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
- u16 vc = buf->getVertexCount();
- for(u16 i=0; i<vc; i++)
- {
- vertices[i].Color = color;
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++)
+ ((video::S3DVertex *)(vertices + i * stride))->Color = color;
+ }
+}
+
+void shadeMeshFaces(scene::IMesh *mesh)
+{
+ if (mesh == NULL)
+ return;
+
+ u32 mc = mesh->getMeshBufferCount();
+ for (u32 j = 0; j < mc; j++) {
+ scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
+ const u32 stride = getVertexPitchFromType(buf->getVertexType());
+ u32 vertex_count = buf->getVertexCount();
+ u8 *vertices = (u8 *)buf->getVertices();
+ for (u32 i = 0; i < vertex_count; i++) {
+ video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride);
+ video::SColor &vc = vertex->Color;
+ if (vertex->Normal.Y < -0.5) {
+ applyFacesShading (vc, 0.447213);
+ } else if (vertex->Normal.Z > 0.5) {
+ applyFacesShading (vc, 0.670820);
+ } else if (vertex->Normal.Z < -0.5) {
+ applyFacesShading (vc, 0.670820);
+ } else if (vertex->Normal.X > 0.5) {
+ applyFacesShading (vc, 0.836660);
+ } else if (vertex->Normal.X < -0.5) {
+ applyFacesShading (vc, 0.836660);
+ }
}
}
}
diff --git a/src/mesh.h b/src/mesh.h
index 761842b0d..ec109e9e9 100644
--- a/src/mesh.h
+++ b/src/mesh.h
@@ -49,6 +49,12 @@ void translateMesh(scene::IMesh *mesh, v3f vec);
void setMeshColor(scene::IMesh *mesh, const video::SColor &color);
/*
+ Shade mesh faces according to their normals
+*/
+
+void shadeMeshFaces(scene::IMesh *mesh);
+
+/*
Set the color of all vertices in the mesh.
For each vertex, determine the largest absolute entry in
the normal vector, and choose one of colorX, colorY or
diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp
index 0d17ae5ed..055ce0198 100644
--- a/src/mg_biome.cpp
+++ b/src/mg_biome.cpp
@@ -18,49 +18,46 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "mg_biome.h"
+#include "mg_decoration.h"
+#include "emerge.h"
#include "gamedef.h"
#include "nodedef.h"
#include "map.h" //for MMVManip
#include "log.h"
#include "util/numeric.h"
-#include "main.h"
#include "util/mathconstants.h"
#include "porting.h"
-const char *BiomeManager::ELEMENT_TITLE = "biome";
-
///////////////////////////////////////////////////////////////////////////////
+
BiomeManager::BiomeManager(IGameDef *gamedef) :
- GenElementManager(gamedef)
+ ObjDefManager(gamedef, OBJDEF_BIOME)
{
+ m_gamedef = gamedef;
+
// Create default biome to be used in case none exist
Biome *b = new Biome;
- b->id = 0;
b->name = "Default";
b->flags = 0;
b->depth_top = 0;
- b->depth_filler = 0;
- b->height_shore = 0;
+ b->depth_filler = -MAX_MAP_GENERATION_LIMIT;
b->depth_water_top = 0;
- b->y_min = -MAP_GENERATION_LIMIT;
- b->y_max = MAP_GENERATION_LIMIT;
+ b->y_min = -MAX_MAP_GENERATION_LIMIT;
+ b->y_max = MAX_MAP_GENERATION_LIMIT;
b->heat_point = 0.0;
b->humidity_point = 0.0;
- NodeResolveInfo *nri = new NodeResolveInfo(b);
- nri->nodenames.push_back("air");
- nri->nodenames.push_back("air");
- nri->nodenames.push_back("air");
- nri->nodenames.push_back("air");
- nri->nodenames.push_back("air");
- nri->nodenames.push_back("mapgen_stone");
- nri->nodenames.push_back("mapgen_water_source");
- nri->nodenames.push_back("mapgen_water_source");
- nri->nodenames.push_back("air");
- m_ndef->pendNodeResolve(nri);
+ b->m_nodenames.push_back("mapgen_stone");
+ b->m_nodenames.push_back("mapgen_stone");
+ b->m_nodenames.push_back("mapgen_stone");
+ b->m_nodenames.push_back("mapgen_water_source");
+ b->m_nodenames.push_back("mapgen_water_source");
+ b->m_nodenames.push_back("mapgen_river_water_source");
+ b->m_nodenames.push_back("air");
+ m_ndef->pendNodeResolve(b);
add(b);
}
@@ -79,8 +76,10 @@ BiomeManager::~BiomeManager()
void BiomeManager::calcBiomes(s16 sx, s16 sy, float *heat_map,
float *humidity_map, s16 *height_map, u8 *biomeid_map)
{
- for (s32 i = 0; i != sx * sy; i++)
- biomeid_map[i] = getBiome(heat_map[i], humidity_map[i], height_map[i])->id;
+ for (s32 i = 0; i != sx * sy; i++) {
+ Biome *biome = getBiome(heat_map[i], humidity_map[i], height_map[i]);
+ biomeid_map[i] = biome->index;
+ }
}
@@ -89,8 +88,8 @@ Biome *BiomeManager::getBiome(float heat, float humidity, s16 y)
Biome *b, *biome_closest = NULL;
float dist_min = FLT_MAX;
- for (size_t i = 1; i < m_elements.size(); i++) {
- b = (Biome *)m_elements[i];
+ for (size_t i = 1; i < m_objects.size(); i++) {
+ b = (Biome *)m_objects[i];
if (!b || y > b->y_max || y < b->y_min)
continue;
@@ -104,34 +103,40 @@ Biome *BiomeManager::getBiome(float heat, float humidity, s16 y)
}
}
- return biome_closest ? biome_closest : (Biome *)m_elements[0];
+ return biome_closest ? biome_closest : (Biome *)m_objects[0];
}
void BiomeManager::clear()
{
+ EmergeManager *emerge = m_gamedef->getEmergeManager();
- for (size_t i = 1; i < m_elements.size(); i++) {
- Biome *b = (Biome *)m_elements[i];
+ // Remove all dangling references in Decorations
+ DecorationManager *decomgr = emerge->decomgr;
+ for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
+ Decoration *deco = (Decoration *)decomgr->getRaw(i);
+ deco->biomes.clear();
+ }
+
+ // Don't delete the first biome
+ for (size_t i = 1; i < m_objects.size(); i++) {
+ Biome *b = (Biome *)m_objects[i];
delete b;
}
- m_elements.resize(1);
+ m_objects.resize(1);
}
///////////////////////////////////////////////////////////////////////////////
-void Biome::resolveNodeNames(NodeResolveInfo *nri)
+void Biome::resolveNodeNames()
{
- m_ndef->getIdFromResolveInfo(nri, "mapgen_dirt_with_grass", CONTENT_AIR, c_top);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_dirt", CONTENT_AIR, c_filler);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_sand", CONTENT_AIR, c_shore_top);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_sand", CONTENT_AIR, c_shore_filler);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_sand", CONTENT_AIR, c_underwater);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_stone", CONTENT_AIR, c_stone);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_water_source", CONTENT_AIR, c_water_top);
- m_ndef->getIdFromResolveInfo(nri, "mapgen_water_source", CONTENT_AIR, c_water);
- m_ndef->getIdFromResolveInfo(nri, "air", CONTENT_IGNORE, c_dust);
+ getIdFromNrBacklog(&c_top, "mapgen_stone", CONTENT_AIR);
+ getIdFromNrBacklog(&c_filler, "mapgen_stone", CONTENT_AIR);
+ getIdFromNrBacklog(&c_stone, "mapgen_stone", CONTENT_AIR);
+ getIdFromNrBacklog(&c_water_top, "mapgen_water_source", CONTENT_AIR);
+ getIdFromNrBacklog(&c_water, "mapgen_water_source", CONTENT_AIR);
+ getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR);
+ getIdFromNrBacklog(&c_dust, "air", CONTENT_IGNORE);
}
-
diff --git a/src/mg_biome.h b/src/mg_biome.h
index 3577960a9..8d519f808 100644
--- a/src/mg_biome.h
+++ b/src/mg_biome.h
@@ -20,36 +20,32 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef MG_BIOME_HEADER
#define MG_BIOME_HEADER
-#include "mapgen.h"
-
-struct NoiseParams;
+#include "objdef.h"
+#include "nodedef.h"
enum BiomeType
{
- BIOME_TYPE_NORMAL,
- BIOME_TYPE_LIQUID,
- BIOME_TYPE_NETHER,
- BIOME_TYPE_AETHER,
- BIOME_TYPE_FLAT
+ BIOME_NORMAL,
+ BIOME_LIQUID,
+ BIOME_NETHER,
+ BIOME_AETHER,
+ BIOME_FLAT
};
-class Biome : public GenElement, public NodeResolver {
+class Biome : public ObjDef, public NodeResolver {
public:
u32 flags;
content_t c_top;
content_t c_filler;
- content_t c_shore_top;
- content_t c_shore_filler;
- content_t c_underwater;
content_t c_stone;
content_t c_water_top;
content_t c_water;
+ content_t c_river_water;
content_t c_dust;
s16 depth_top;
s16 depth_filler;
- s16 height_shore;
s16 depth_water_top;
s16 y_min;
@@ -57,27 +53,34 @@ public:
float heat_point;
float humidity_point;
- virtual void resolveNodeNames(NodeResolveInfo *nri);
+ virtual void resolveNodeNames();
};
-class BiomeManager : public GenElementManager {
+class BiomeManager : public ObjDefManager {
public:
- static const char *ELEMENT_TITLE;
- static const size_t ELEMENT_LIMIT = 0x100;
+ static const char *OBJECT_TITLE;
BiomeManager(IGameDef *gamedef);
- ~BiomeManager();
+ virtual ~BiomeManager();
- Biome *create(int btt)
+ const char *getObjectTitle() const
+ {
+ return "biome";
+ }
+
+ static Biome *create(BiomeType type)
{
return new Biome;
}
- void clear();
+ virtual void clear();
void calcBiomes(s16 sx, s16 sy, float *heat_map, float *humidity_map,
s16 *height_map, u8 *biomeid_map);
Biome *getBiome(float heat, float humidity, s16 y);
+
+private:
+ IGameDef *m_gamedef;
};
#endif
diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp
index a67c3cd8c..0d6693929 100644
--- a/src/mg_decoration.cpp
+++ b/src/mg_decoration.cpp
@@ -25,12 +25,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "util/numeric.h"
-const char *DecorationManager::ELEMENT_TITLE = "decoration";
-
FlagDesc flagdesc_deco[] = {
{"place_center_x", DECO_PLACE_CENTER_X},
{"place_center_y", DECO_PLACE_CENTER_Y},
{"place_center_z", DECO_PLACE_CENTER_Z},
+ {"force_placement", DECO_FORCE_PLACEMENT},
{NULL, 0}
};
@@ -39,7 +38,7 @@ FlagDesc flagdesc_deco[] = {
DecorationManager::DecorationManager(IGameDef *gamedef) :
- GenElementManager(gamedef)
+ ObjDefManager(gamedef, OBJDEF_DECORATION)
{
}
@@ -49,8 +48,8 @@ size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
{
size_t nplaced = 0;
- for (size_t i = 0; i != m_elements.size(); i++) {
- Decoration *deco = (Decoration *)m_elements[i];
+ for (size_t i = 0; i != m_objects.size(); i++) {
+ Decoration *deco = (Decoration *)m_objects[i];
if (!deco)
continue;
@@ -62,16 +61,6 @@ size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
}
-void DecorationManager::clear()
-{
- for (size_t i = 0; i < m_elements.size(); i++) {
- Decoration *deco = (Decoration *)m_elements[i];
- delete deco;
- }
- m_elements.clear();
-}
-
-
///////////////////////////////////////////////////////////////////////////////
@@ -89,9 +78,9 @@ Decoration::~Decoration()
}
-void Decoration::resolveNodeNames(NodeResolveInfo *nri)
+void Decoration::resolveNodeNames()
{
- m_ndef->getIdsFromResolveInfo(nri, c_place_on);
+ getIdsFromNrBacklog(&c_place_on);
}
@@ -145,9 +134,7 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
y < y_min || y > y_max)
continue;
- int height = getHeight();
- int max_y = nmax.Y;// + MAP_BLOCKSIZE - 1;
- if (y + 1 + height > max_y) {
+ if (y + getHeight() >= mg->vm->m_area.MaxEdge.Y) {
continue;
#if 0
printf("Decoration at (%d %d %d) cut off\n", x, y, z);
@@ -168,8 +155,8 @@ size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
}
v3s16 pos(x, y, z);
- if (generate(mg->vm, &ps, max_y, pos))
- mg->gennotify.addEvent(GENNOTIFY_DECORATION, pos, id);
+ if (generate(mg->vm, &ps, pos))
+ mg->gennotify.addEvent(GENNOTIFY_DECORATION, pos, index);
}
}
@@ -234,11 +221,11 @@ void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
///////////////////////////////////////////////////////////////////////////////
-void DecoSimple::resolveNodeNames(NodeResolveInfo *nri)
+void DecoSimple::resolveNodeNames()
{
- Decoration::resolveNodeNames(nri);
- m_ndef->getIdsFromResolveInfo(nri, c_decos);
- m_ndef->getIdsFromResolveInfo(nri, c_spawnby);
+ Decoration::resolveNodeNames();
+ getIdsFromNrBacklog(&c_decos);
+ getIdsFromNrBacklog(&c_spawnby);
}
@@ -259,7 +246,7 @@ bool DecoSimple::canPlaceDecoration(MMVManip *vm, v3s16 p)
return true;
int nneighs = 0;
- v3s16 dirs[8] = {
+ v3s16 dirs[16] = {
v3s16( 0, 0, 1),
v3s16( 0, 0, -1),
v3s16( 1, 0, 0),
@@ -267,7 +254,16 @@ bool DecoSimple::canPlaceDecoration(MMVManip *vm, v3s16 p)
v3s16( 1, 0, 1),
v3s16(-1, 0, 1),
v3s16(-1, 0, -1),
- v3s16( 1, 0, -1)
+ v3s16( 1, 0, -1),
+
+ v3s16( 0, 1, 1),
+ v3s16( 0, 1, -1),
+ v3s16( 1, 1, 0),
+ v3s16(-1, 1, 0),
+ v3s16( 1, 1, 1),
+ v3s16(-1, 1, 1),
+ v3s16(-1, 1, -1),
+ v3s16( 1, 1, -1)
};
// Check a Moore neighborhood if there are enough spawnby nodes
@@ -287,7 +283,7 @@ bool DecoSimple::canPlaceDecoration(MMVManip *vm, v3s16 p)
}
-size_t DecoSimple::generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16 p)
+size_t DecoSimple::generate(MMVManip *vm, PseudoRandom *pr, v3s16 p)
{
if (!canPlaceDecoration(vm, p))
return 0;
@@ -297,8 +293,6 @@ size_t DecoSimple::generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16 p)
s16 height = (deco_height_max > 0) ?
pr->range(deco_height, deco_height_max) : deco_height;
- height = MYMIN(height, max_y - p.Y);
-
v3s16 em = vm->m_area.getExtent();
u32 vi = vm->m_area.index(p);
for (int i = 0; i < height; i++) {
@@ -324,16 +318,17 @@ int DecoSimple::getHeight()
///////////////////////////////////////////////////////////////////////////////
-size_t DecoSchematic::generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16 p)
+DecoSchematic::DecoSchematic()
{
- if (flags & DECO_PLACE_CENTER_X)
- p.X -= (schematic->size.X + 1) / 2;
- if (flags & DECO_PLACE_CENTER_Y)
- p.Y -= (schematic->size.Y + 1) / 2;
- if (flags & DECO_PLACE_CENTER_Z)
- p.Z -= (schematic->size.Z + 1) / 2;
+ schematic = NULL;
+}
+
- if (!vm->m_area.contains(p))
+size_t DecoSchematic::generate(MMVManip *vm, PseudoRandom *pr, v3s16 p)
+{
+ // Schematic could have been unloaded but not the decoration
+ // In this case generate() does nothing (but doesn't *fail*)
+ if (schematic == NULL)
return 0;
u32 vi = vm->m_area.index(p);
@@ -341,10 +336,19 @@ size_t DecoSchematic::generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16
if (!CONTAINS(c_place_on, c))
return 0;
+ if (flags & DECO_PLACE_CENTER_X)
+ p.X -= (schematic->size.X - 1) / 2;
+ if (flags & DECO_PLACE_CENTER_Y)
+ p.Y -= (schematic->size.Y - 1) / 2;
+ if (flags & DECO_PLACE_CENTER_Z)
+ p.Z -= (schematic->size.Z - 1) / 2;
+
Rotation rot = (rotation == ROTATE_RAND) ?
(Rotation)pr->range(ROTATE_0, ROTATE_270) : rotation;
- schematic->blitToVManip(p, vm, rot, false, m_ndef);
+ bool force_placement = (flags & DECO_FORCE_PLACEMENT);
+
+ schematic->blitToVManip(p, vm, rot, force_placement);
return 1;
}
diff --git a/src/mg_decoration.h b/src/mg_decoration.h
index ab4a9377b..056748918 100644
--- a/src/mg_decoration.h
+++ b/src/mg_decoration.h
@@ -21,9 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MG_DECORATION_HEADER
#include <set>
-#include "mapgen.h"
+#include "objdef.h"
+#include "noise.h"
+#include "nodedef.h"
-struct NoiseParams;
class Mapgen;
class MMVManip;
class PseudoRandom;
@@ -35,10 +36,11 @@ enum DecorationType {
DECO_LSYSTEM
};
-#define DECO_PLACE_CENTER_X 0x01
-#define DECO_PLACE_CENTER_Y 0x02
-#define DECO_PLACE_CENTER_Z 0x04
-#define DECO_USE_NOISE 0x08
+#define DECO_PLACE_CENTER_X 0x01
+#define DECO_PLACE_CENTER_Y 0x02
+#define DECO_PLACE_CENTER_Z 0x04
+#define DECO_USE_NOISE 0x08
+#define DECO_FORCE_PLACEMENT 0x10
extern FlagDesc flagdesc_deco[];
@@ -58,9 +60,18 @@ struct CutoffData {
};
#endif
-class Decoration : public GenElement, public NodeResolver {
+class Decoration : public ObjDef, public NodeResolver {
public:
- INodeDefManager *ndef;
+ Decoration();
+ virtual ~Decoration();
+
+ virtual void resolveNodeNames();
+
+ size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+ //size_t placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+
+ virtual size_t generate(MMVManip *vm, PseudoRandom *pr, v3s16 p) = 0;
+ virtual int getHeight() = 0;
u32 flags;
int mapseed;
@@ -74,42 +85,32 @@ public:
std::set<u8> biomes;
//std::list<CutoffData> cutoffs;
//JMutex cutoff_mutex;
-
- Decoration();
- virtual ~Decoration();
-
- virtual void resolveNodeNames(NodeResolveInfo *nri);
-
- size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
- //size_t placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
-
- virtual size_t generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16 p) = 0;
- virtual int getHeight() = 0;
};
class DecoSimple : public Decoration {
public:
+ virtual size_t generate(MMVManip *vm, PseudoRandom *pr, v3s16 p);
+ bool canPlaceDecoration(MMVManip *vm, v3s16 p);
+ virtual int getHeight();
+
+ virtual void resolveNodeNames();
+
std::vector<content_t> c_decos;
std::vector<content_t> c_spawnby;
s16 deco_height;
s16 deco_height_max;
s16 nspawnby;
-
- virtual void resolveNodeNames(NodeResolveInfo *nri);
-
- bool canPlaceDecoration(MMVManip *vm, v3s16 p);
- virtual size_t generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16 p);
- virtual int getHeight();
};
class DecoSchematic : public Decoration {
public:
- Rotation rotation;
- Schematic *schematic;
- std::string filename;
+ DecoSchematic();
- virtual size_t generate(MMVManip *vm, PseudoRandom *pr, s16 max_y, v3s16 p);
+ virtual size_t generate(MMVManip *vm, PseudoRandom *pr, v3s16 p);
virtual int getHeight();
+
+ Rotation rotation;
+ Schematic *schematic;
};
@@ -120,15 +121,17 @@ public:
};
*/
-class DecorationManager : public GenElementManager {
+class DecorationManager : public ObjDefManager {
public:
- static const char *ELEMENT_TITLE;
- static const size_t ELEMENT_LIMIT = 0x10000;
-
DecorationManager(IGameDef *gamedef);
- ~DecorationManager() {}
+ virtual ~DecorationManager() {}
- Decoration *create(int type)
+ const char *getObjectTitle() const
+ {
+ return "decoration";
+ }
+
+ static Decoration *create(DecorationType type)
{
switch (type) {
case DECO_SIMPLE:
@@ -142,8 +145,6 @@ public:
}
}
- void clear();
-
size_t placeAllDecos(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
};
diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp
index c62f05860..a94d1d6d9 100644
--- a/src/mg_ore.cpp
+++ b/src/mg_ore.cpp
@@ -24,8 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "log.h"
-const char *OreManager::ELEMENT_TITLE = "ore";
-
FlagDesc flagdesc_ore[] = {
{"absheight", OREFLAG_ABSHEIGHT},
{NULL, 0}
@@ -36,7 +34,7 @@ FlagDesc flagdesc_ore[] = {
OreManager::OreManager(IGameDef *gamedef) :
- GenElementManager(gamedef)
+ ObjDefManager(gamedef, OBJDEF_ORE)
{
}
@@ -45,8 +43,8 @@ size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nma
{
size_t nplaced = 0;
- for (size_t i = 0; i != m_elements.size(); i++) {
- Ore *ore = (Ore *)m_elements[i];
+ for (size_t i = 0; i != m_objects.size(); i++) {
+ Ore *ore = (Ore *)m_objects[i];
if (!ore)
continue;
@@ -60,11 +58,11 @@ size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nma
void OreManager::clear()
{
- for (size_t i = 0; i < m_elements.size(); i++) {
- Ore *ore = (Ore *)m_elements[i];
+ for (size_t i = 0; i < m_objects.size(); i++) {
+ Ore *ore = (Ore *)m_objects[i];
delete ore;
}
- m_elements.clear();
+ m_objects.clear();
}
@@ -84,10 +82,10 @@ Ore::~Ore()
}
-void Ore::resolveNodeNames(NodeResolveInfo *nri)
+void Ore::resolveNodeNames()
{
- m_ndef->getIdFromResolveInfo(nri, "", CONTENT_AIR, c_ore);
- m_ndef->getIdsFromResolveInfo(nri, c_wherein);
+ getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
+ getIdsFromNrBacklog(&c_wherein);
}
@@ -114,7 +112,7 @@ size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
nmin.Y = actual_ymin;
nmax.Y = actual_ymax;
- generate(mg->vm, mg->seed, blockseed, nmin, nmax);
+ generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);
return 1;
}
@@ -124,19 +122,20 @@ size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax)
+ v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
PseudoRandom pr(blockseed);
MapNode n_ore(c_ore, 0, ore_param2);
- int volume = (nmax.X - nmin.X + 1) *
+ u32 sizex = (nmax.X - nmin.X + 1);
+ u32 volume = (nmax.X - nmin.X + 1) *
(nmax.Y - nmin.Y + 1) *
(nmax.Z - nmin.Z + 1);
- int csize = clust_size;
- int orechance = (csize * csize * csize) / clust_num_ores;
- int nclusters = volume / clust_scarcity;
+ u32 csize = clust_size;
+ u32 orechance = (csize * csize * csize) / clust_num_ores;
+ u32 nclusters = volume / clust_scarcity;
- for (int i = 0; i != nclusters; i++) {
+ for (u32 i = 0; i != nclusters; i++) {
int x0 = pr.range(nmin.X, nmax.X - csize + 1);
int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
@@ -145,9 +144,16 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
(NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
continue;
- for (int z1 = 0; z1 != csize; z1++)
- for (int y1 = 0; y1 != csize; y1++)
- for (int x1 = 0; x1 != csize; x1++) {
+ if (biomemap && !biomes.empty()) {
+ u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
+ std::set<u8>::iterator it = biomes.find(biomemap[index]);
+ if (it == biomes.end())
+ continue;
+ }
+
+ for (u32 z1 = 0; z1 != csize; z1++)
+ for (u32 y1 = 0; y1 != csize; y1++)
+ for (u32 x1 = 0; x1 != csize; x1++) {
if (pr.range(1, orechance) != 1)
continue;
@@ -165,7 +171,7 @@ void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax)
+ v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
PseudoRandom pr(blockseed + 4234);
MapNode n_ore(c_ore, 0, ore_param2);
@@ -183,11 +189,17 @@ void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
size_t index = 0;
for (int z = nmin.Z; z <= nmax.Z; z++)
- for (int x = nmin.X; x <= nmax.X; x++) {
- float noiseval = noise->result[index++];
+ for (int x = nmin.X; x <= nmax.X; x++, index++) {
+ float noiseval = noise->result[index];
if (noiseval < nthresh)
continue;
+ if (biomemap && !biomes.empty()) {
+ std::set<u8>::iterator it = biomes.find(biomemap[index]);
+ if (it == biomes.end())
+ continue;
+ }
+
int height = max_height * (1. / pr.range(1, 3));
int y0 = y_start + np.scale * noiseval; //pr.range(1, 3) - 1;
int y1 = y0 + height;
@@ -207,32 +219,40 @@ void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
///////////////////////////////////////////////////////////////////////////////
void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax)
+ v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
PseudoRandom pr(blockseed + 2404);
MapNode n_ore(c_ore, 0, ore_param2);
- int volume = (nmax.X - nmin.X + 1) *
+ u32 sizex = (nmax.X - nmin.X + 1);
+ u32 volume = (nmax.X - nmin.X + 1) *
(nmax.Y - nmin.Y + 1) *
(nmax.Z - nmin.Z + 1);
- int csize = clust_size;
- int nblobs = volume / clust_scarcity;
+ u32 csize = clust_size;
+ u32 nblobs = volume / clust_scarcity;
if (!noise)
noise = new Noise(&np, mapseed, csize, csize, csize);
- for (int i = 0; i != nblobs; i++) {
+ for (u32 i = 0; i != nblobs; i++) {
int x0 = pr.range(nmin.X, nmax.X - csize + 1);
int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
+ if (biomemap && !biomes.empty()) {
+ u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
+ std::set<u8>::iterator it = biomes.find(biomemap[bmapidx]);
+ if (it == biomes.end())
+ continue;
+ }
+
bool noise_generated = false;
noise->seed = blockseed + i;
size_t index = 0;
- for (int z1 = 0; z1 != csize; z1++)
- for (int y1 = 0; y1 != csize; y1++)
- for (int x1 = 0; x1 != csize; x1++, index++) {
+ for (u32 z1 = 0; z1 != csize; z1++)
+ for (u32 y1 = 0; y1 != csize; y1++)
+ for (u32 x1 = 0; x1 != csize; x1++, index++) {
u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
continue;
@@ -276,11 +296,13 @@ OreVein::~OreVein()
void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax)
+ v3s16 nmin, v3s16 nmax, u8 *biomemap)
{
PseudoRandom pr(blockseed + 520);
MapNode n_ore(c_ore, 0, ore_param2);
+ u32 sizex = (nmax.X - nmin.X + 1);
+
if (!noise) {
int sx = nmax.X - nmin.X + 1;
int sy = nmax.Y - nmin.Y + 1;
@@ -300,6 +322,13 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
continue;
+ if (biomemap && !biomes.empty()) {
+ u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
+ std::set<u8>::iterator it = biomes.find(biomemap[bmapidx]);
+ if (it == biomes.end())
+ continue;
+ }
+
// Same lazy generation optimization as in OreBlob
if (!noise_generated) {
noise_generated = true;
@@ -308,7 +337,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
}
// randval ranges from -1..1
- float randval = (float)pr.next() / (PSEUDORANDOM_MAX / 2) - 1.f;
+ float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
float noiseval = contour(noise->result[index]);
float noiseval2 = contour(noise2->result[index]);
if (noiseval * noiseval2 + randval * random_factor < nthresh)
diff --git a/src/mg_ore.h b/src/mg_ore.h
index 67ca9a849..ffe8cfe50 100644
--- a/src/mg_ore.h
+++ b/src/mg_ore.h
@@ -20,10 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef MG_ORE_HEADER
#define MG_ORE_HEADER
-#include "util/string.h"
-#include "mapgen.h"
+#include "objdef.h"
+#include "noise.h"
+#include "nodedef.h"
-struct NoiseParams;
class Noise;
class Mapgen;
class MMVManip;
@@ -39,15 +39,15 @@ class MMVManip;
enum OreType {
- ORE_TYPE_SCATTER,
- ORE_TYPE_SHEET,
- ORE_TYPE_BLOB,
- ORE_TYPE_VEIN,
+ ORE_SCATTER,
+ ORE_SHEET,
+ ORE_BLOB,
+ ORE_VEIN,
};
extern FlagDesc flagdesc_ore[];
-class Ore : public GenElement, public NodeResolver {
+class Ore : public ObjDef, public NodeResolver {
public:
static const bool NEEDS_NOISE = false;
@@ -63,15 +63,16 @@ public:
float nthresh; // threshhold for noise at which an ore is placed
NoiseParams np; // noise for distribution of clusters (NULL for uniform scattering)
Noise *noise;
+ std::set<u8> biomes;
Ore();
virtual ~Ore();
- virtual void resolveNodeNames(NodeResolveInfo *nri);
+ virtual void resolveNodeNames();
size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax) = 0;
+ v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0;
};
class OreScatter : public Ore {
@@ -79,7 +80,7 @@ public:
static const bool NEEDS_NOISE = false;
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax);
+ v3s16 nmin, v3s16 nmax, u8 *biomemap);
};
class OreSheet : public Ore {
@@ -87,7 +88,7 @@ public:
static const bool NEEDS_NOISE = true;
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax);
+ v3s16 nmin, v3s16 nmax, u8 *biomemap);
};
class OreBlob : public Ore {
@@ -95,7 +96,7 @@ public:
static const bool NEEDS_NOISE = true;
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax);
+ v3s16 nmin, v3s16 nmax, u8 *biomemap);
};
class OreVein : public Ore {
@@ -109,27 +110,29 @@ public:
virtual ~OreVein();
virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
- v3s16 nmin, v3s16 nmax);
+ v3s16 nmin, v3s16 nmax, u8 *biomemap);
};
-class OreManager : public GenElementManager {
+class OreManager : public ObjDefManager {
public:
- static const char *ELEMENT_TITLE;
- static const size_t ELEMENT_LIMIT = 0x10000;
-
OreManager(IGameDef *gamedef);
- ~OreManager() {}
+ virtual ~OreManager() {}
+
+ const char *getObjectTitle() const
+ {
+ return "ore";
+ }
- Ore *create(int type)
+ static Ore *create(OreType type)
{
switch (type) {
- case ORE_TYPE_SCATTER:
+ case ORE_SCATTER:
return new OreScatter;
- case ORE_TYPE_SHEET:
+ case ORE_SHEET:
return new OreSheet;
- case ORE_TYPE_BLOB:
+ case ORE_BLOB:
return new OreBlob;
- case ORE_TYPE_VEIN:
+ case ORE_VEIN:
return new OreVein;
default:
return NULL;
diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp
index a3404e2dc..a5ffb20b8 100644
--- a/src/mg_schematic.cpp
+++ b/src/mg_schematic.cpp
@@ -18,8 +18,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include <fstream>
+#include <typeinfo>
#include "mg_schematic.h"
+#include "gamedef.h"
#include "mapgen.h"
+#include "emerge.h"
#include "map.h"
#include "mapblock.h"
#include "log.h"
@@ -28,14 +31,34 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "serialization.h"
#include "filesys.h"
-const char *SchematicManager::ELEMENT_TITLE = "schematic";
-
///////////////////////////////////////////////////////////////////////////////
SchematicManager::SchematicManager(IGameDef *gamedef) :
- GenElementManager(gamedef)
+ ObjDefManager(gamedef, OBJDEF_SCHEMATIC)
{
+ m_gamedef = gamedef;
+}
+
+
+void SchematicManager::clear()
+{
+ EmergeManager *emerge = m_gamedef->getEmergeManager();
+
+ // Remove all dangling references in Decorations
+ DecorationManager *decomgr = emerge->decomgr;
+ for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
+ Decoration *deco = (Decoration *)decomgr->getRaw(i);
+
+ try {
+ DecoSchematic *dschem = dynamic_cast<DecoSchematic *>(deco);
+ if (dschem)
+ dschem->schematic = NULL;
+ } catch (std::bad_cast) {
+ }
+ }
+
+ ObjDefManager::clear();
}
@@ -58,34 +81,27 @@ Schematic::~Schematic()
}
-void Schematic::resolveNodeNames(NodeResolveInfo *nri)
+void Schematic::resolveNodeNames()
{
- m_ndef->getIdsFromResolveInfo(nri, c_nodes);
-}
-
-
-void Schematic::updateContentIds()
-{
- if (flags & SCHEM_CIDS_UPDATED)
- return;
-
- flags |= SCHEM_CIDS_UPDATED;
+ getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR);
size_t bufsize = size.X * size.Y * size.Z;
- for (size_t i = 0; i != bufsize; i++)
- schemdata[i].setContent(c_nodes[schemdata[i].getContent()]);
+ for (size_t i = 0; i != bufsize; i++) {
+ content_t c_original = schemdata[i].getContent();
+ content_t c_new = c_nodes[c_original];
+ schemdata[i].setContent(c_new);
+ }
}
-void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot,
- bool force_placement, INodeDefManager *ndef)
+void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place)
{
+ sanity_check(m_ndef != NULL);
+
int xstride = 1;
int ystride = size.X;
int zstride = size.X * size.Y;
- updateContentIds();
-
s16 sx = size.X;
s16 sy = size.Y;
s16 sz = size.Z;
@@ -117,8 +133,8 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot,
s16 y_map = p.Y;
for (s16 y = 0; y != sy; y++) {
- if (slice_probs[y] != MTSCHEM_PROB_ALWAYS &&
- myrand_range(1, 255) > slice_probs[y])
+ if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
+ (slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
continue;
for (s16 z = 0; z != sz; z++) {
@@ -131,24 +147,27 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot,
if (schemdata[i].getContent() == CONTENT_IGNORE)
continue;
- if (schemdata[i].param1 == MTSCHEM_PROB_NEVER)
+ u8 placement_prob = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+ bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
+ if (placement_prob == MTSCHEM_PROB_NEVER)
continue;
- if (!force_placement) {
+ if (!force_place && !force_place_node) {
content_t c = vm->m_data[vi].getContent();
if (c != CONTENT_AIR && c != CONTENT_IGNORE)
continue;
}
- if (schemdata[i].param1 != MTSCHEM_PROB_ALWAYS &&
- myrand_range(1, 255) > schemdata[i].param1)
+ if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
+ (placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
continue;
vm->m_data[vi] = schemdata[i];
vm->m_data[vi].param1 = 0;
if (rot)
- vm->m_data[vi].rotateAlongYAxis(ndef, rot);
+ vm->m_data[vi].rotateAlongYAxis(m_ndef, rot);
}
}
y_map++;
@@ -156,10 +175,12 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot,
}
-void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot,
- bool force_placement, INodeDefManager *ndef)
+void Schematic::placeStructure(Map *map, v3s16 p, u32 flags,
+ Rotation rot, bool force_place)
{
- assert(schemdata != NULL);
+ assert(schemdata != NULL); // Pre-condition
+ sanity_check(m_ndef != NULL);
+
MMVManip *vm = new MMVManip(map);
if (rot == ROTATE_RAND)
@@ -179,7 +200,7 @@ void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot,
v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
vm->initialEmerge(bp1, bp2);
- blitToVManip(p, vm, rot, force_placement, ndef);
+ blitToVManip(p, vm, rot, force_place);
std::map<v3s16, MapBlock *> lighting_modified_blocks;
std::map<v3s16, MapBlock *> modified_blocks;
@@ -200,111 +221,89 @@ void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot,
}
-bool Schematic::loadSchematicFromFile(const char *filename, INodeDefManager *ndef,
- std::map<std::string, std::string> &replace_names)
+bool Schematic::deserializeFromMts(std::istream *is,
+ std::vector<std::string> *names)
{
+ std::istream &ss = *is;
content_t cignore = CONTENT_IGNORE;
bool have_cignore = false;
- std::ifstream is(filename, std::ios_base::binary);
-
- u32 signature = readU32(is);
+ //// Read signature
+ u32 signature = readU32(ss);
if (signature != MTSCHEM_FILE_SIGNATURE) {
- errorstream << "loadSchematicFile: invalid schematic "
+ errorstream << "Schematic::deserializeFromMts: invalid schematic "
"file" << std::endl;
return false;
}
- u16 version = readU16(is);
+ //// Read version
+ u16 version = readU16(ss);
if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
- errorstream << "loadSchematicFile: unsupported schematic "
+ errorstream << "Schematic::deserializeFromMts: unsupported schematic "
"file version" << std::endl;
return false;
}
- size = readV3S16(is);
+ //// Read size
+ size = readV3S16(ss);
+ //// Read Y-slice probability values
delete []slice_probs;
slice_probs = new u8[size.Y];
for (int y = 0; y != size.Y; y++)
- slice_probs[y] = (version >= 3) ? readU8(is) : MTSCHEM_PROB_ALWAYS;
-
- NodeResolveInfo *nri = new NodeResolveInfo(this);
+ slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
- u16 nidmapcount = readU16(is);
+ //// Read node names
+ u16 nidmapcount = readU16(ss);
for (int i = 0; i != nidmapcount; i++) {
- std::string name = deSerializeString(is);
+ std::string name = deSerializeString(ss);
+
+ // Instances of "ignore" from v1 are converted to air (and instances
+ // are fixed to have MTSCHEM_PROB_NEVER later on).
if (name == "ignore") {
name = "air";
cignore = i;
have_cignore = true;
}
- std::map<std::string, std::string>::iterator it;
- it = replace_names.find(name);
- if (it != replace_names.end())
- name = it->second;
-
- nri->nodenames.push_back(name);
+ names->push_back(name);
}
- nri->nodelistinfo.push_back(NodeListInfo(nidmapcount, CONTENT_AIR));
- ndef->pendNodeResolve(nri);
-
+ //// Read node data
size_t nodecount = size.X * size.Y * size.Z;
delete []schemdata;
schemdata = new MapNode[nodecount];
- MapNode::deSerializeBulk(is, SER_FMT_VER_HIGHEST_READ, schemdata,
+ MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
nodecount, 2, 2, true);
- if (version == 1) { // fix up the probability values
+ // Fix probability values for nodes that were ignore; removed in v2
+ if (version < 2) {
for (size_t i = 0; i != nodecount; i++) {
if (schemdata[i].param1 == 0)
- schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
+ schemdata[i].param1 = MTSCHEM_PROB_ALWAYS_OLD;
if (have_cignore && schemdata[i].getContent() == cignore)
schemdata[i].param1 = MTSCHEM_PROB_NEVER;
}
}
+ // Fix probability values for probability range truncation introduced in v4
+ if (version < 4) {
+ for (s16 y = 0; y != size.Y; y++)
+ slice_probs[y] >>= 1;
+ for (size_t i = 0; i != nodecount; i++)
+ schemdata[i].param1 >>= 1;
+ }
+
return true;
}
-/*
- Minetest Schematic File Format
-
- All values are stored in big-endian byte order.
- [u32] signature: 'MTSM'
- [u16] version: 3
- [u16] size X
- [u16] size Y
- [u16] size Z
- For each Y:
- [u8] slice probability value
- [Name-ID table] Name ID Mapping Table
- [u16] name-id count
- For each name-id mapping:
- [u16] name length
- [u8[]] name
- ZLib deflated {
- For each node in schematic: (for z, y, x)
- [u16] content
- For each node in schematic:
- [u8] probability of occurance (param1)
- For each node in schematic:
- [u8] param2
- }
-
- Version changes:
- 1 - Initial version
- 2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
- 3 - Added y-slice probabilities; this allows for variable height structures
-*/
-void Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef)
+bool Schematic::serializeToMts(std::ostream *os,
+ const std::vector<std::string> &names)
{
- std::ostringstream ss(std::ios_base::binary);
+ std::ostream &ss = *os;
writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature
writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
@@ -313,45 +312,159 @@ void Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef)
for (int y = 0; y != size.Y; y++) // Y slice probabilities
writeU8(ss, slice_probs[y]);
- std::vector<content_t> usednodes;
- int nodecount = size.X * size.Y * size.Z;
- build_nnlist_and_update_ids(schemdata, nodecount, &usednodes);
-
- u16 numids = usednodes.size();
- writeU16(ss, numids); // name count
- for (int i = 0; i != numids; i++)
- ss << serializeString(ndef->get(usednodes[i]).name); // node names
+ writeU16(ss, names.size()); // name count
+ for (size_t i = 0; i != names.size(); i++)
+ ss << serializeString(names[i]); // node names
// compressed bulk node data
- MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schemdata,
- nodecount, 2, 2, true);
+ MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
+ schemdata, size.X * size.Y * size.Z, 2, 2, true);
- fs::safeWriteToFile(filename, ss.str());
+ return true;
}
-void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
- std::vector<content_t> *usednodes)
+bool Schematic::serializeToLua(std::ostream *os,
+ const std::vector<std::string> &names, bool use_comments, u32 indent_spaces)
{
- std::map<content_t, content_t> nodeidmap;
- content_t numids = 0;
+ std::ostream &ss = *os;
+
+ std::string indent("\t");
+ if (indent_spaces > 0)
+ indent.assign(indent_spaces, ' ');
+
+ //// Write header
+ {
+ ss << "schematic = {" << std::endl;
+ ss << indent << "size = "
+ << "{x=" << size.X
+ << ", y=" << size.Y
+ << ", z=" << size.Z
+ << "}," << std::endl;
+ }
- for (u32 i = 0; i != nodecount; i++) {
- content_t id;
- content_t c = nodes[i].getContent();
+ //// Write y-slice probabilities
+ {
+ ss << indent << "yslice_prob = {" << std::endl;
- std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
- if (it == nodeidmap.end()) {
- id = numids;
- numids++;
+ for (u16 y = 0; y != size.Y; y++) {
+ u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
- usednodes->push_back(c);
- nodeidmap.insert(std::make_pair(c, id));
- } else {
- id = it->second;
+ ss << indent << indent << "{"
+ << "ypos=" << y
+ << ", prob=" << (u16)probability * 2
+ << "}," << std::endl;
}
- nodes[i].setContent(id);
+
+ ss << indent << "}," << std::endl;
+ }
+
+ //// Write node data
+ {
+ ss << indent << "data = {" << std::endl;
+
+ u32 i = 0;
+ for (u16 z = 0; z != size.Z; z++)
+ for (u16 y = 0; y != size.Y; y++) {
+ if (use_comments) {
+ ss << std::endl
+ << indent << indent
+ << "-- z=" << z
+ << ", y=" << y << std::endl;
+ }
+
+ for (u16 x = 0; x != size.X; x++, i++) {
+ u8 probability = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+ bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
+ ss << indent << indent << "{"
+ << "name=\"" << names[schemdata[i].getContent()]
+ << "\", prob=" << (u16)probability * 2
+ << ", param2=" << (u16)schemdata[i].param2;
+
+ if (force_place)
+ ss << ", force_place=true";
+
+ ss << "}," << std::endl;
+ }
+ }
+
+ ss << indent << "}," << std::endl;
}
+
+ ss << "}" << std::endl;
+
+ return true;
+}
+
+
+bool Schematic::loadSchematicFromFile(const std::string &filename,
+ INodeDefManager *ndef, StringMap *replace_names)
+{
+ std::ifstream is(filename.c_str(), std::ios_base::binary);
+ if (!is.good()) {
+ errorstream << "Schematic::loadSchematicFile: unable to open file '"
+ << filename << "'" << std::endl;
+ return false;
+ }
+
+ size_t origsize = m_nodenames.size();
+ if (!deserializeFromMts(&is, &m_nodenames))
+ return false;
+
+ if (replace_names) {
+ for (size_t i = origsize; i != m_nodenames.size(); i++) {
+ std::string &name = m_nodenames[i];
+ StringMap::iterator it = replace_names->find(name);
+ if (it != replace_names->end())
+ name = it->second;
+ }
+ }
+
+ m_nnlistsizes.push_back(m_nodenames.size() - origsize);
+
+ if (ndef)
+ ndef->pendNodeResolve(this);
+
+ return true;
+}
+
+
+bool Schematic::saveSchematicToFile(const std::string &filename,
+ INodeDefManager *ndef)
+{
+ MapNode *orig_schemdata = schemdata;
+ std::vector<std::string> ndef_nodenames;
+ std::vector<std::string> *names;
+
+ if (m_resolve_done && ndef == NULL)
+ ndef = m_ndef;
+
+ if (ndef) {
+ names = &ndef_nodenames;
+
+ u32 volume = size.X * size.Y * size.Z;
+ schemdata = new MapNode[volume];
+ for (u32 i = 0; i != volume; i++)
+ schemdata[i] = orig_schemdata[i];
+
+ generate_nodelist_and_update_ids(schemdata, volume, names, ndef);
+ } else { // otherwise, use the names we have on hand in the list
+ names = &m_nodenames;
+ }
+
+ std::ostringstream os(std::ios_base::binary);
+ bool status = serializeToMts(&os, *names);
+
+ if (ndef) {
+ delete []schemdata;
+ schemdata = orig_schemdata;
+ }
+
+ if (!status)
+ return false;
+
+ return fs::safeWriteToFile(filename, os.str());
}
@@ -408,3 +521,28 @@ void Schematic::applyProbabilities(v3s16 p0,
slice_probs[y] = (*splist)[i].second;
}
}
+
+
+void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
+ std::vector<std::string> *usednodes, INodeDefManager *ndef)
+{
+ std::map<content_t, content_t> nodeidmap;
+ content_t numids = 0;
+
+ for (size_t i = 0; i != nodecount; i++) {
+ content_t id;
+ content_t c = nodes[i].getContent();
+
+ std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
+ if (it == nodeidmap.end()) {
+ id = numids;
+ numids++;
+
+ usednodes->push_back(ndef->get(c).name);
+ nodeidmap.insert(std::make_pair(c, id));
+ } else {
+ id = it->second;
+ }
+ nodes[i].setContent(id);
+ }
+}
diff --git a/src/mg_schematic.h b/src/mg_schematic.h
index ad5afb15f..5c732648e 100644
--- a/src/mg_schematic.h
+++ b/src/mg_schematic.h
@@ -29,66 +29,119 @@ class Mapgen;
class MMVManip;
class PseudoRandom;
class NodeResolver;
+class IGameDef;
-/////////////////// Schematic flags
-#define SCHEM_CIDS_UPDATED 0x08
+/*
+ Minetest Schematic File Format
+
+ All values are stored in big-endian byte order.
+ [u32] signature: 'MTSM'
+ [u16] version: 4
+ [u16] size X
+ [u16] size Y
+ [u16] size Z
+ For each Y:
+ [u8] slice probability value
+ [Name-ID table] Name ID Mapping Table
+ [u16] name-id count
+ For each name-id mapping:
+ [u16] name length
+ [u8[]] name
+ ZLib deflated {
+ For each node in schematic: (for z, y, x)
+ [u16] content
+ For each node in schematic:
+ [u8] param1
+ bit 0-6: probability
+ bit 7: specific node force placement
+ For each node in schematic:
+ [u8] param2
+ }
+ Version changes:
+ 1 - Initial version
+ 2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
+ 3 - Added y-slice probabilities; this allows for variable height structures
+ 4 - Compressed range of node occurence prob., added per-node force placement bit
+*/
+//// Schematic constants
#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
-#define MTSCHEM_FILE_VER_HIGHEST_READ 3
-#define MTSCHEM_FILE_VER_HIGHEST_WRITE 3
+#define MTSCHEM_FILE_VER_HIGHEST_READ 4
+#define MTSCHEM_FILE_VER_HIGHEST_WRITE 4
-#define MTSCHEM_PROB_NEVER 0x00
-#define MTSCHEM_PROB_ALWAYS 0xFF
+#define MTSCHEM_PROB_MASK 0x7F
+#define MTSCHEM_PROB_NEVER 0x00
+#define MTSCHEM_PROB_ALWAYS 0x7F
+#define MTSCHEM_PROB_ALWAYS_OLD 0xFF
-class Schematic : public GenElement, public NodeResolver {
-public:
- std::vector<content_t> c_nodes;
+#define MTSCHEM_FORCE_PLACE 0x80
- u32 flags;
- v3s16 size;
- MapNode *schemdata;
- u8 *slice_probs;
+enum SchematicType
+{
+ SCHEMATIC_NORMAL,
+};
+enum SchematicFormatType {
+ SCHEM_FMT_HANDLE,
+ SCHEM_FMT_MTS,
+ SCHEM_FMT_LUA,
+};
+
+class Schematic : public ObjDef, public NodeResolver {
+public:
Schematic();
virtual ~Schematic();
- virtual void resolveNodeNames(NodeResolveInfo *nri);
+ virtual void resolveNodeNames();
- void updateContentIds();
+ bool loadSchematicFromFile(const std::string &filename, INodeDefManager *ndef,
+ StringMap *replace_names=NULL);
+ bool saveSchematicToFile(const std::string &filename, INodeDefManager *ndef);
+ bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
- void blitToVManip(v3s16 p, MMVManip *vm,
- Rotation rot, bool force_placement, INodeDefManager *ndef);
+ bool deserializeFromMts(std::istream *is, std::vector<std::string> *names);
+ bool serializeToMts(std::ostream *os, const std::vector<std::string> &names);
+ bool serializeToLua(std::ostream *os, const std::vector<std::string> &names,
+ bool use_comments, u32 indent_spaces);
- bool loadSchematicFromFile(const char *filename, INodeDefManager *ndef,
- std::map<std::string, std::string> &replace_names);
- void saveSchematicToFile(const char *filename, INodeDefManager *ndef);
- bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
+ void blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_place);
+ void placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
- void placeStructure(Map *map, v3s16 p, u32 flags,
- Rotation rot, bool force_placement, INodeDefManager *nef);
void applyProbabilities(v3s16 p0,
std::vector<std::pair<v3s16, u8> > *plist,
std::vector<std::pair<s16, u8> > *splist);
+
+ std::vector<content_t> c_nodes;
+ u32 flags;
+ v3s16 size;
+ MapNode *schemdata;
+ u8 *slice_probs;
};
-class SchematicManager : public GenElementManager {
+class SchematicManager : public ObjDefManager {
public:
- static const char *ELEMENT_TITLE;
- static const size_t ELEMENT_LIMIT = 0x10000;
-
SchematicManager(IGameDef *gamedef);
- ~SchematicManager() {}
+ virtual ~SchematicManager() {}
+
+ virtual void clear();
- Schematic *create(int type)
+ const char *getObjectTitle() const
+ {
+ return "schematic";
+ }
+
+ static Schematic *create(SchematicType type)
{
return new Schematic;
}
-};
-void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
- std::vector<content_t> *usednodes);
+private:
+ IGameDef *m_gamedef;
+};
+void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
+ std::vector<std::string> *usednodes, INodeDefManager *ndef);
#endif
diff --git a/src/minimap.cpp b/src/minimap.cpp
new file mode 100644
index 000000000..d1fb3867d
--- /dev/null
+++ b/src/minimap.cpp
@@ -0,0 +1,561 @@
+/*
+Minetest
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "minimap.h"
+#include <math.h>
+#include "logoutputbuffer.h"
+#include "jthread/jmutexautolock.h"
+#include "jthread/jsemaphore.h"
+#include "clientmap.h"
+#include "settings.h"
+#include "nodedef.h"
+#include "porting.h"
+#include "util/numeric.h"
+#include "util/string.h"
+
+
+////
+//// MinimapUpdateThread
+////
+
+MinimapUpdateThread::~MinimapUpdateThread()
+{
+ for (std::map<v3s16, MinimapMapblock *>::iterator
+ it = m_blocks_cache.begin();
+ it != m_blocks_cache.end(); ++it) {
+ delete it->second;
+ }
+
+ for (std::deque<QueuedMinimapUpdate>::iterator
+ it = m_update_queue.begin();
+ it != m_update_queue.end(); ++it) {
+ QueuedMinimapUpdate &q = *it;
+ delete q.data;
+ }
+}
+
+bool MinimapUpdateThread::pushBlockUpdate(v3s16 pos, MinimapMapblock *data)
+{
+ JMutexAutoLock lock(m_queue_mutex);
+
+ // Find if block is already in queue.
+ // If it is, update the data and quit.
+ for (std::deque<QueuedMinimapUpdate>::iterator
+ it = m_update_queue.begin();
+ it != m_update_queue.end(); ++it) {
+ QueuedMinimapUpdate &q = *it;
+ if (q.pos == pos) {
+ delete q.data;
+ q.data = data;
+ return false;
+ }
+ }
+
+ // Add the block
+ QueuedMinimapUpdate q;
+ q.pos = pos;
+ q.data = data;
+ m_update_queue.push_back(q);
+
+ return true;
+}
+
+bool MinimapUpdateThread::popBlockUpdate(QueuedMinimapUpdate *update)
+{
+ JMutexAutoLock lock(m_queue_mutex);
+
+ if (m_update_queue.empty())
+ return false;
+
+ *update = m_update_queue.front();
+ m_update_queue.pop_front();
+
+ return true;
+}
+
+void MinimapUpdateThread::enqueueBlock(v3s16 pos, MinimapMapblock *data)
+{
+ pushBlockUpdate(pos, data);
+ deferUpdate();
+}
+
+
+void MinimapUpdateThread::doUpdate()
+{
+ QueuedMinimapUpdate update;
+
+ while (popBlockUpdate(&update)) {
+ if (update.data) {
+ // Swap two values in the map using single lookup
+ std::pair<std::map<v3s16, MinimapMapblock*>::iterator, bool>
+ result = m_blocks_cache.insert(std::make_pair(update.pos, update.data));
+ if (result.second == false) {
+ delete result.first->second;
+ result.first->second = update.data;
+ }
+ } else {
+ std::map<v3s16, MinimapMapblock *>::iterator it;
+ it = m_blocks_cache.find(update.pos);
+ if (it != m_blocks_cache.end()) {
+ delete it->second;
+ m_blocks_cache.erase(it);
+ }
+ }
+ }
+
+ if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) {
+ getMap(data->pos, data->map_size, data->scan_height, data->is_radar);
+ data->map_invalidated = false;
+ }
+}
+
+MinimapPixel *MinimapUpdateThread::getMinimapPixel(v3s16 pos,
+ s16 scan_height, s16 *pixel_height)
+{
+ s16 height = scan_height - MAP_BLOCKSIZE;
+ v3s16 blockpos_max, blockpos_min, relpos;
+
+ getNodeBlockPosWithOffset(
+ v3s16(pos.X, pos.Y - scan_height / 2, pos.Z),
+ blockpos_min, relpos);
+ getNodeBlockPosWithOffset(
+ v3s16(pos.X, pos.Y + scan_height / 2, pos.Z),
+ blockpos_max, relpos);
+
+ for (s16 i = blockpos_max.Y; i > blockpos_min.Y - 1; i--) {
+ std::map<v3s16, MinimapMapblock *>::iterator it =
+ m_blocks_cache.find(v3s16(blockpos_max.X, i, blockpos_max.Z));
+ if (it != m_blocks_cache.end()) {
+ MinimapMapblock *mmblock = it->second;
+ MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X];
+ if (pixel->id != CONTENT_AIR) {
+ *pixel_height = height + pixel->height;
+ return pixel;
+ }
+ }
+
+ height -= MAP_BLOCKSIZE;
+ }
+
+ return NULL;
+}
+
+s16 MinimapUpdateThread::getAirCount(v3s16 pos, s16 height)
+{
+ s16 air_count = 0;
+ v3s16 blockpos_max, blockpos_min, relpos;
+
+ getNodeBlockPosWithOffset(
+ v3s16(pos.X, pos.Y - height / 2, pos.Z),
+ blockpos_min, relpos);
+ getNodeBlockPosWithOffset(
+ v3s16(pos.X, pos.Y + height / 2, pos.Z),
+ blockpos_max, relpos);
+
+ for (s16 i = blockpos_max.Y; i > blockpos_min.Y - 1; i--) {
+ std::map<v3s16, MinimapMapblock *>::iterator it =
+ m_blocks_cache.find(v3s16(blockpos_max.X, i, blockpos_max.Z));
+ if (it != m_blocks_cache.end()) {
+ MinimapMapblock *mmblock = it->second;
+ MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X];
+ air_count += pixel->air_count;
+ }
+ }
+
+ return air_count;
+}
+
+void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar)
+{
+ v3s16 p = v3s16(pos.X - size / 2, pos.Y, pos.Z - size / 2);
+
+ for (s16 x = 0; x < size; x++)
+ for (s16 z = 0; z < size; z++) {
+ u16 id = CONTENT_AIR;
+ MinimapPixel *mmpixel = &data->minimap_scan[x + z * size];
+
+ if (!is_radar) {
+ s16 pixel_height = 0;
+ MinimapPixel *cached_pixel =
+ getMinimapPixel(v3s16(p.X + x, p.Y, p.Z + z), height, &pixel_height);
+ if (cached_pixel) {
+ id = cached_pixel->id;
+ mmpixel->height = pixel_height;
+ }
+ } else {
+ mmpixel->air_count = getAirCount(v3s16(p.X + x, p.Y, p.Z + z), height);
+ }
+
+ mmpixel->id = id;
+ }
+}
+
+////
+//// Mapper
+////
+
+Mapper::Mapper(IrrlichtDevice *device, Client *client)
+{
+ this->driver = device->getVideoDriver();
+ this->m_tsrc = client->getTextureSource();
+ this->m_shdrsrc = client->getShaderSource();
+ this->m_ndef = client->getNodeDefManager();
+
+ // Initialize static settings
+ m_enable_shaders = g_settings->getBool("enable_shaders");
+ m_surface_mode_scan_height =
+ g_settings->getBool("minimap_double_scan_height") ? 256 : 128;
+
+ // Initialize minimap data
+ data = new MinimapData;
+ data->mode = MINIMAP_MODE_OFF;
+ data->is_radar = false;
+ data->map_invalidated = true;
+ data->heightmap_image = NULL;
+ data->minimap_image = NULL;
+ data->texture = NULL;
+ data->heightmap_texture = NULL;
+ data->minimap_shape_round = g_settings->getBool("minimap_shape_round");
+
+ // Get round minimap textures
+ data->minimap_mask_round = driver->createImage(
+ m_tsrc->getTexture("minimap_mask_round.png"),
+ core::position2d<s32>(0, 0),
+ core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
+ data->minimap_overlay_round = m_tsrc->getTexture("minimap_overlay_round.png");
+
+ // Get square minimap textures
+ data->minimap_mask_square = driver->createImage(
+ m_tsrc->getTexture("minimap_mask_square.png"),
+ core::position2d<s32>(0, 0),
+ core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
+ data->minimap_overlay_square = m_tsrc->getTexture("minimap_overlay_square.png");
+
+ // Create player marker texture
+ data->player_marker = m_tsrc->getTexture("player_marker.png");
+
+ // Create mesh buffer for minimap
+ m_meshbuffer = getMinimapMeshBuffer();
+
+ // Initialize and start thread
+ m_minimap_update_thread = new MinimapUpdateThread();
+ m_minimap_update_thread->data = data;
+ m_minimap_update_thread->Start();
+}
+
+Mapper::~Mapper()
+{
+ m_minimap_update_thread->Stop();
+ m_minimap_update_thread->Wait();
+
+ m_meshbuffer->drop();
+
+ data->minimap_mask_round->drop();
+ data->minimap_mask_square->drop();
+
+ driver->removeTexture(data->texture);
+ driver->removeTexture(data->heightmap_texture);
+ driver->removeTexture(data->minimap_overlay_round);
+ driver->removeTexture(data->minimap_overlay_square);
+
+ delete data;
+ delete m_minimap_update_thread;
+}
+
+void Mapper::addBlock(v3s16 pos, MinimapMapblock *data)
+{
+ m_minimap_update_thread->enqueueBlock(pos, data);
+}
+
+MinimapMode Mapper::getMinimapMode()
+{
+ return data->mode;
+}
+
+void Mapper::toggleMinimapShape()
+{
+ JMutexAutoLock lock(m_mutex);
+
+ data->minimap_shape_round = !data->minimap_shape_round;
+ g_settings->setBool("minimap_shape_round", data->minimap_shape_round);
+ m_minimap_update_thread->deferUpdate();
+}
+
+void Mapper::setMinimapMode(MinimapMode mode)
+{
+ static const MinimapModeDef modedefs[MINIMAP_MODE_COUNT] = {
+ {false, 0, 0},
+ {false, m_surface_mode_scan_height, 256},
+ {false, m_surface_mode_scan_height, 128},
+ {false, m_surface_mode_scan_height, 64},
+ {true, 32, 128},
+ {true, 32, 64},
+ {true, 32, 32}
+ };
+
+ if (mode >= MINIMAP_MODE_COUNT)
+ return;
+
+ JMutexAutoLock lock(m_mutex);
+
+ data->is_radar = modedefs[mode].is_radar;
+ data->scan_height = modedefs[mode].scan_height;
+ data->map_size = modedefs[mode].map_size;
+ data->mode = mode;
+
+ m_minimap_update_thread->deferUpdate();
+}
+
+void Mapper::setPos(v3s16 pos)
+{
+ bool do_update = false;
+
+ {
+ JMutexAutoLock lock(m_mutex);
+
+ if (pos != data->old_pos) {
+ data->old_pos = data->pos;
+ data->pos = pos;
+ do_update = true;
+ }
+ }
+
+ if (do_update)
+ m_minimap_update_thread->deferUpdate();
+}
+
+void Mapper::setAngle(f32 angle)
+{
+ m_angle = angle;
+}
+
+void Mapper::blitMinimapPixelsToImageRadar(video::IImage *map_image)
+{
+ for (s16 x = 0; x < data->map_size; x++)
+ for (s16 z = 0; z < data->map_size; z++) {
+ MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
+
+ video::SColor c(240, 0, 0, 0);
+ if (mmpixel->air_count > 0)
+ c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255));
+
+ map_image->setPixel(x, data->map_size - z - 1, c);
+ }
+}
+
+void Mapper::blitMinimapPixelsToImageSurface(
+ video::IImage *map_image, video::IImage *heightmap_image)
+{
+ for (s16 x = 0; x < data->map_size; x++)
+ for (s16 z = 0; z < data->map_size; z++) {
+ MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
+
+ video::SColor c = m_ndef->get(mmpixel->id).minimap_color;
+ c.setAlpha(240);
+
+ map_image->setPixel(x, data->map_size - z - 1, c);
+
+ u32 h = mmpixel->height;
+ heightmap_image->setPixel(x,data->map_size - z - 1,
+ video::SColor(255, h, h, h));
+ }
+}
+
+video::ITexture *Mapper::getMinimapTexture()
+{
+ // update minimap textures when new scan is ready
+ if (data->map_invalidated)
+ return data->texture;
+
+ // create minimap and heightmap images in memory
+ core::dimension2d<u32> dim(data->map_size, data->map_size);
+ video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim);
+ video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim);
+ video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8,
+ core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
+
+ // Blit MinimapPixels to images
+ if (data->is_radar)
+ blitMinimapPixelsToImageRadar(map_image);
+ else
+ blitMinimapPixelsToImageSurface(map_image, heightmap_image);
+
+ map_image->copyToScaling(minimap_image);
+ map_image->drop();
+
+ video::IImage *minimap_mask = data->minimap_shape_round ?
+ data->minimap_mask_round : data->minimap_mask_square;
+
+ if (minimap_mask) {
+ for (s16 y = 0; y < MINIMAP_MAX_SY; y++)
+ for (s16 x = 0; x < MINIMAP_MAX_SX; x++) {
+ video::SColor mask_col = minimap_mask->getPixel(x, y);
+ if (!mask_col.getAlpha())
+ minimap_image->setPixel(x, y, video::SColor(0,0,0,0));
+ }
+ }
+
+ if (data->texture)
+ driver->removeTexture(data->texture);
+ if (data->heightmap_texture)
+ driver->removeTexture(data->heightmap_texture);
+
+ data->texture = driver->addTexture("minimap__", minimap_image);
+ data->heightmap_texture =
+ driver->addTexture("minimap_heightmap__", heightmap_image);
+ minimap_image->drop();
+ heightmap_image->drop();
+
+ data->map_invalidated = true;
+
+ return data->texture;
+}
+
+v3f Mapper::getYawVec()
+{
+ if (data->minimap_shape_round) {
+ return v3f(
+ cos(m_angle * core::DEGTORAD),
+ sin(m_angle * core::DEGTORAD),
+ 1.0);
+ } else {
+ return v3f(1.0, 0.0, 1.0);
+ }
+}
+
+scene::SMeshBuffer *Mapper::getMinimapMeshBuffer()
+{
+ scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+ buf->Vertices.set_used(4);
+ buf->Indices.set_used(6);
+ video::SColor c(255, 255, 255, 255);
+
+ buf->Vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1);
+ buf->Vertices[1] = video::S3DVertex(-1, 1, 0, 0, 0, 1, c, 0, 0);
+ buf->Vertices[2] = video::S3DVertex( 1, 1, 0, 0, 0, 1, c, 1, 0);
+ buf->Vertices[3] = video::S3DVertex( 1, -1, 0, 0, 0, 1, c, 1, 1);
+
+ buf->Indices[0] = 0;
+ buf->Indices[1] = 1;
+ buf->Indices[2] = 2;
+ buf->Indices[3] = 2;
+ buf->Indices[4] = 3;
+ buf->Indices[5] = 0;
+
+ return buf;
+}
+
+void Mapper::drawMinimap()
+{
+ video::ITexture *minimap_texture = getMinimapTexture();
+ if (!minimap_texture)
+ return;
+
+ v2u32 screensize = porting::getWindowSize();
+ const u32 size = 0.25 * screensize.Y;
+
+ core::rect<s32> oldViewPort = driver->getViewPort();
+ core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
+ core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
+
+ driver->setViewPort(core::rect<s32>(
+ screensize.X - size - 10, 10,
+ screensize.X - 10, size + 10));
+ driver->setTransform(video::ETS_PROJECTION, core::matrix4());
+ driver->setTransform(video::ETS_VIEW, core::matrix4());
+
+ core::matrix4 matrix;
+ matrix.makeIdentity();
+
+ video::SMaterial &material = m_meshbuffer->getMaterial();
+ material.setFlag(video::EMF_TRILINEAR_FILTER, true);
+ material.Lighting = false;
+ material.TextureLayer[0].Texture = minimap_texture;
+ material.TextureLayer[1].Texture = data->heightmap_texture;
+
+ if (m_enable_shaders && !data->is_radar) {
+ u16 sid = m_shdrsrc->getShader("minimap_shader", 1, 1);
+ material.MaterialType = m_shdrsrc->getShaderInfo(sid).material;
+ } else {
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ }
+
+ if (data->minimap_shape_round)
+ matrix.setRotationDegrees(core::vector3df(0, 0, 360 - m_angle));
+
+ // Draw minimap
+ driver->setTransform(video::ETS_WORLD, matrix);
+ driver->setMaterial(material);
+ driver->drawMeshBuffer(m_meshbuffer);
+
+ // Draw overlay
+ video::ITexture *minimap_overlay = data->minimap_shape_round ?
+ data->minimap_overlay_round : data->minimap_overlay_square;
+ material.TextureLayer[0].Texture = minimap_overlay;
+ material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ driver->setMaterial(material);
+ driver->drawMeshBuffer(m_meshbuffer);
+
+ // If round minimap, draw player marker
+ if (!data->minimap_shape_round) {
+ matrix.setRotationDegrees(core::vector3df(0, 0, m_angle));
+ material.TextureLayer[0].Texture = data->player_marker;
+
+ driver->setTransform(video::ETS_WORLD, matrix);
+ driver->setMaterial(material);
+ driver->drawMeshBuffer(m_meshbuffer);
+ }
+
+ // Reset transformations
+ driver->setTransform(video::ETS_VIEW, oldViewMat);
+ driver->setTransform(video::ETS_PROJECTION, oldProjMat);
+ driver->setViewPort(oldViewPort);
+}
+
+////
+//// MinimapMapblock
+////
+
+void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos)
+{
+
+ for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
+ for (s16 z = 0; z < MAP_BLOCKSIZE; z++) {
+ s16 air_count = 0;
+ bool surface_found = false;
+ MinimapPixel *mmpixel = &data[z * MAP_BLOCKSIZE + x];
+
+ for (s16 y = MAP_BLOCKSIZE -1; y >= 0; y--) {
+ v3s16 p(x, y, z);
+ MapNode n = vmanip->getNodeNoEx(pos + p);
+ if (!surface_found && n.getContent() != CONTENT_AIR) {
+ mmpixel->height = y;
+ mmpixel->id = n.getContent();
+ surface_found = true;
+ } else if (n.getContent() == CONTENT_AIR) {
+ air_count++;
+ }
+ }
+
+ if (!surface_found)
+ mmpixel->id = CONTENT_AIR;
+
+ mmpixel->air_count = air_count;
+ }
+}
diff --git a/src/minimap.h b/src/minimap.h
new file mode 100644
index 000000000..628be7489
--- /dev/null
+++ b/src/minimap.h
@@ -0,0 +1,157 @@
+/*
+Minetest
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef MINIMAP_HEADER
+#define MINIMAP_HEADER
+
+#include <map>
+#include <string>
+#include <vector>
+#include "irrlichttypes_extrabloated.h"
+#include "client.h"
+#include "voxel.h"
+#include "jthread/jmutex.h"
+#include "jthread/jsemaphore.h"
+
+#define MINIMAP_MAX_SX 512
+#define MINIMAP_MAX_SY 512
+
+enum MinimapMode {
+ MINIMAP_MODE_OFF,
+ MINIMAP_MODE_SURFACEx1,
+ MINIMAP_MODE_SURFACEx2,
+ MINIMAP_MODE_SURFACEx4,
+ MINIMAP_MODE_RADARx1,
+ MINIMAP_MODE_RADARx2,
+ MINIMAP_MODE_RADARx4,
+ MINIMAP_MODE_COUNT,
+};
+
+struct MinimapModeDef {
+ bool is_radar;
+ u16 scan_height;
+ u16 map_size;
+};
+
+struct MinimapPixel {
+ u16 id;
+ u16 height;
+ u16 air_count;
+ u16 light;
+};
+
+struct MinimapMapblock {
+ void getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos);
+
+ MinimapPixel data[MAP_BLOCKSIZE * MAP_BLOCKSIZE];
+};
+
+struct MinimapData {
+ bool is_radar;
+ MinimapMode mode;
+ v3s16 pos;
+ v3s16 old_pos;
+ u16 scan_height;
+ u16 map_size;
+ MinimapPixel minimap_scan[MINIMAP_MAX_SX * MINIMAP_MAX_SY];
+ bool map_invalidated;
+ bool minimap_shape_round;
+ video::IImage *minimap_image;
+ video::IImage *heightmap_image;
+ video::IImage *minimap_mask_round;
+ video::IImage *minimap_mask_square;
+ video::ITexture *texture;
+ video::ITexture *heightmap_texture;
+ video::ITexture *minimap_overlay_round;
+ video::ITexture *minimap_overlay_square;
+ video::ITexture *player_marker;
+};
+
+struct QueuedMinimapUpdate {
+ v3s16 pos;
+ MinimapMapblock *data;
+};
+
+class MinimapUpdateThread : public UpdateThread {
+public:
+ virtual ~MinimapUpdateThread();
+
+ void getMap(v3s16 pos, s16 size, s16 height, bool radar);
+ MinimapPixel *getMinimapPixel(v3s16 pos, s16 height, s16 *pixel_height);
+ s16 getAirCount(v3s16 pos, s16 height);
+ video::SColor getColorFromId(u16 id);
+
+ void enqueueBlock(v3s16 pos, MinimapMapblock *data);
+
+ bool pushBlockUpdate(v3s16 pos, MinimapMapblock *data);
+ bool popBlockUpdate(QueuedMinimapUpdate *update);
+
+ MinimapData *data;
+
+protected:
+ const char *getName() { return "MinimapUpdateThread"; }
+ virtual void doUpdate();
+
+private:
+ JMutex m_queue_mutex;
+ std::deque<QueuedMinimapUpdate> m_update_queue;
+ std::map<v3s16, MinimapMapblock *> m_blocks_cache;
+};
+
+class Mapper {
+public:
+ Mapper(IrrlichtDevice *device, Client *client);
+ ~Mapper();
+
+ void addBlock(v3s16 pos, MinimapMapblock *data);
+
+ v3f getYawVec();
+ MinimapMode getMinimapMode();
+
+ void setPos(v3s16 pos);
+ void setAngle(f32 angle);
+ void setMinimapMode(MinimapMode mode);
+ void toggleMinimapShape();
+
+
+ video::ITexture *getMinimapTexture();
+
+ void blitMinimapPixelsToImageRadar(video::IImage *map_image);
+ void blitMinimapPixelsToImageSurface(video::IImage *map_image,
+ video::IImage *heightmap_image);
+
+ scene::SMeshBuffer *getMinimapMeshBuffer();
+ void drawMinimap();
+
+ video::IVideoDriver *driver;
+ MinimapData *data;
+
+private:
+ ITextureSource *m_tsrc;
+ IShaderSource *m_shdrsrc;
+ INodeDefManager *m_ndef;
+ MinimapUpdateThread *m_minimap_update_thread;
+ scene::SMeshBuffer *m_meshbuffer;
+ bool m_enable_shaders;
+ u16 m_surface_mode_scan_height;
+ f32 m_angle;
+ JMutex m_mutex;
+};
+
+#endif
diff --git a/src/mods.cpp b/src/mods.cpp
index 6126de7a1..a81dd4604 100644
--- a/src/mods.cpp
+++ b/src/mods.cpp
@@ -17,15 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <cctype>
+#include <fstream>
#include "mods.h"
-#include "main.h"
#include "filesys.h"
#include "strfnd.h"
#include "log.h"
#include "subgame.h"
#include "settings.h"
#include "strfnd.h"
-#include <cctype>
#include "convert_json.h"
static bool parseDependsLine(std::istream &is,
@@ -47,6 +47,11 @@ static bool parseDependsLine(std::istream &is,
void parseModContents(ModSpec &spec)
{
// NOTE: this function works in mutual recursion with getModsInPath
+ Settings info;
+ info.readConfigFile((spec.path+DIR_DELIM+"mod.conf").c_str());
+
+ if (info.exists("name"))
+ spec.name = info.get("name");
spec.depends.clear();
spec.optdepends.clear();
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
new file mode 100644
index 000000000..3805c323d
--- /dev/null
+++ b/src/network/CMakeLists.txt
@@ -0,0 +1,16 @@
+set(common_network_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/serveropcodes.cpp
+ PARENT_SCOPE
+)
+
+if (BUILD_CLIENT)
+ set(client_network_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/clientopcodes.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/clientpackethandler.cpp
+ PARENT_SCOPE
+ )
+endif()
+
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp
new file mode 100644
index 000000000..3364de8c5
--- /dev/null
+++ b/src/network/clientopcodes.cpp
@@ -0,0 +1,213 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#include "clientopcodes.h"
+
+const static ToClientCommandHandler null_command_handler = {"TOCLIENT_NULL", TOCLIENT_STATE_ALL, &Client::handleCommand_Null};
+
+const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
+{
+ null_command_handler, // 0x00 (never use this)
+ null_command_handler, // 0x01
+ { "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02
+ { "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03
+ { "TOCLIENT_ACCEPT_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AcceptSudoMode}, // 0x04
+ { "TOCLIENT_DENY_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DenySudoMode}, // 0x05
+ null_command_handler, // 0x06
+ null_command_handler, // 0x07
+ null_command_handler, // 0x08
+ null_command_handler, // 0x09
+ { "TOCLIENT_ACCESS_DENIED", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x0A
+ null_command_handler, // 0x0B
+ null_command_handler, // 0x0C
+ null_command_handler, // 0x0D
+ null_command_handler, // 0x0E
+ null_command_handler, // 0x0F
+ { "TOCLIENT_INIT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_InitLegacy }, // 0x10
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ { "TOCLIENT_BLOCKDATA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_BlockData }, // 0x20
+ { "TOCLIENT_ADDNODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddNode }, // 0x21
+ { "TOCLIENT_REMOVENODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_RemoveNode }, // 0x22
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ { "TOCLIENT_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Inventory }, // 0x27
+ null_command_handler,
+ { "TOCLIENT_TIME_OF_DAY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_TimeOfDay }, // 0x29
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ { "TOCLIENT_CHAT_MESSAGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ChatMessage }, // 0x30
+ { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectRemoveAdd }, // 0x31
+ { "TOCLIENT_ACTIVE_OBJECT_MESSAGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ActiveObjectMessages }, // 0x32
+ { "TOCLIENT_HP", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HP }, // 0x33
+ { "TOCLIENT_MOVE_PLAYER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34
+ { "TOCLIENT_ACCESS_DENIED_LEGACY", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35
+ { "TOCLIENT_PLAYERITEM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlayerItem }, // 0x36
+ { "TOCLIENT_DEATHSCREEN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37
+ { "TOCLIENT_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38
+ { "TOCLIENT_TOOLDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ToolDef }, // 0x39
+ { "TOCLIENT_NODEDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodeDef }, // 0x3a
+ { "TOCLIENT_CRAFTITEMDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_CraftItemDef }, // 0x3b
+ { "TOCLIENT_ANNOUNCE_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AnnounceMedia }, // 0x3c
+ { "TOCLIENT_ITEMDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ItemDef }, // 0x3d
+ null_command_handler,
+ { "TOCLIENT_PLAY_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_PlaySound }, // 0x3f
+ { "TOCLIENT_STOP_SOUND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_StopSound }, // 0x40
+ { "TOCLIENT_PRIVILEGES", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Privileges }, // 0x41
+ { "TOCLIENT_INVENTORY_FORMSPEC", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_InventoryFormSpec }, // 0x42
+ { "TOCLIENT_DETACHED_INVENTORY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DetachedInventory }, // 0x43
+ { "TOCLIENT_SHOW_FORMSPEC", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_ShowFormSpec }, // 0x44
+ { "TOCLIENT_MOVEMENT", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Movement }, // 0x45
+ { "TOCLIENT_SPAWN_PARTICLE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_SpawnParticle }, // 0x46
+ { "TOCLIENT_ADD_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AddParticleSpawner }, // 0x47
+ { "TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x48
+ { "TOCLIENT_HUDADD", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudAdd }, // 0x49
+ { "TOCLIENT_HUDRM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudRemove }, // 0x4a
+ { "TOCLIENT_HUDCHANGE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudChange }, // 0x4b
+ { "TOCLIENT_HUD_SET_FLAGS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetFlags }, // 0x4c
+ { "TOCLIENT_HUD_SET_PARAM", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetParam }, // 0x4d
+ { "TOCLIENT_BREATH", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Breath }, // 0x4e
+ { "TOCLIENT_SET_SKY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_HudSetSky }, // 0x4f
+ { "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_OverrideDayNightRatio }, // 0x50
+ { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51
+ { "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
+ { "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ null_command_handler,
+ { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60
+};
+
+const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false };
+
+const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
+{
+ null_command_factory, // 0x00
+ null_command_factory, // 0x01
+ { "TOSERVER_INIT", 1, false }, // 0x02
+ null_command_factory, // 0x03
+ null_command_factory, // 0x04
+ null_command_factory, // 0x05
+ null_command_factory, // 0x06
+ null_command_factory, // 0x07
+ null_command_factory, // 0x08
+ null_command_factory, // 0x09
+ null_command_factory, // 0x0a
+ null_command_factory, // 0x0b
+ null_command_factory, // 0x0c
+ null_command_factory, // 0x0d
+ null_command_factory, // 0x0e
+ null_command_factory, // 0x0F
+ { "TOSERVER_INIT_LEGACY", 1, false }, // 0x10
+ { "TOSERVER_INIT2", 1, true }, // 0x11
+ null_command_factory, // 0x12
+ null_command_factory, // 0x13
+ null_command_factory, // 0x14
+ null_command_factory, // 0x15
+ null_command_factory, // 0x16
+ null_command_factory, // 0x17
+ null_command_factory, // 0x18
+ null_command_factory, // 0x19
+ null_command_factory, // 0x1a
+ null_command_factory, // 0x1b
+ null_command_factory, // 0x1c
+ null_command_factory, // 0x1d
+ null_command_factory, // 0x1e
+ null_command_factory, // 0x1f
+ null_command_factory, // 0x20
+ null_command_factory, // 0x21
+ null_command_factory, // 0x22
+ { "TOSERVER_PLAYERPOS", 0, false }, // 0x23
+ { "TOSERVER_GOTBLOCKS", 2, true }, // 0x24
+ { "TOSERVER_DELETEDBLOCKS", 2, true }, // 0x25
+ null_command_factory, // 0x26
+ { "TOSERVER_CLICK_OBJECT", 0, false }, // 0x27
+ { "TOSERVER_GROUND_ACTION", 0, false }, // 0x28
+ { "TOSERVER_RELEASE", 0, false }, // 0x29
+ null_command_factory, // 0x2a
+ null_command_factory, // 0x2b
+ null_command_factory, // 0x2c
+ null_command_factory, // 0x2d
+ null_command_factory, // 0x2e
+ null_command_factory, // 0x2f
+ { "TOSERVER_SIGNTEXT", 0, false }, // 0x30
+ { "TOSERVER_INVENTORY_ACTION", 0, true }, // 0x31
+ { "TOSERVER_CHAT_MESSAGE", 0, true }, // 0x32
+ { "TOSERVER_SIGNNODETEXT", 0, false }, // 0x33
+ { "TOSERVER_CLICK_ACTIVEOBJECT", 0, false }, // 0x34
+ { "TOSERVER_DAMAGE", 0, true }, // 0x35
+ { "TOSERVER_PASSWORD_LEGACY", 0, true }, // 0x36
+ { "TOSERVER_PLAYERITEM", 0, true }, // 0x37
+ { "TOSERVER_RESPAWN", 0, true }, // 0x38
+ { "TOSERVER_INTERACT", 0, true }, // 0x39
+ { "TOSERVER_REMOVED_SOUNDS", 1, true }, // 0x3a
+ { "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b
+ { "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c
+ null_command_factory, // 0x3d
+ null_command_factory, // 0x3e
+ null_command_factory, // 0x3f
+ { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40
+ { "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41
+ { "TOSERVER_BREATH", 0, true }, // 0x42
+ { "TOSERVER_CLIENT_READY", 0, true }, // 0x43
+ null_command_factory, // 0x44
+ null_command_factory, // 0x45
+ null_command_factory, // 0x46
+ null_command_factory, // 0x47
+ null_command_factory, // 0x48
+ null_command_factory, // 0x49
+ null_command_factory, // 0x4a
+ null_command_factory, // 0x4b
+ null_command_factory, // 0x4c
+ null_command_factory, // 0x4d
+ null_command_factory, // 0x4e
+ null_command_factory, // 0x4f
+ { "TOSERVER_FIRST_SRP", 1, true }, // 0x50
+ { "TOSERVER_SRP_BYTES_A", 1, true }, // 0x51
+ { "TOSERVER_SRP_BYTES_M", 1, true }, // 0x52
+};
diff --git a/src/network/clientopcodes.h b/src/network/clientopcodes.h
new file mode 100644
index 000000000..9143865b8
--- /dev/null
+++ b/src/network/clientopcodes.h
@@ -0,0 +1,52 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#ifndef CLIENTOPCODES_HEADER
+#define CLIENTOPCODES_HEADER
+
+#include "client.h"
+#include "networkprotocol.h"
+#include "networkpacket.h"
+
+enum ToClientConnectionState {
+ TOCLIENT_STATE_NOT_CONNECTED,
+ TOCLIENT_STATE_CONNECTED,
+ TOCLIENT_STATE_ALL,
+};
+
+struct ToClientCommandHandler
+{
+ const char* name;
+ ToClientConnectionState state;
+ void (Client::*handler)(NetworkPacket* pkt);
+};
+
+struct ServerCommandFactory
+{
+ const char* name;
+ u16 channel;
+ bool reliable;
+};
+
+extern const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES];
+
+extern const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES];
+
+#endif
diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp
new file mode 100644
index 000000000..86091bc88
--- /dev/null
+++ b/src/network/clientpackethandler.cpp
@@ -0,0 +1,1211 @@
+/*
+Minetest
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#include "client.h"
+
+#include "util/base64.h"
+#include "clientmedia.h"
+#include "log.h"
+#include "map.h"
+#include "mapsector.h"
+#include "nodedef.h"
+#include "serialization.h"
+#include "server.h"
+#include "strfnd.h"
+#include "network/clientopcodes.h"
+#include "util/serialize.h"
+#include "util/srp.h"
+
+void Client::handleCommand_Deprecated(NetworkPacket* pkt)
+{
+ infostream << "Got deprecated command "
+ << toClientCommandTable[pkt->getCommand()].name << " from peer "
+ << pkt->getPeerId() << "!" << std::endl;
+}
+
+void Client::handleCommand_Hello(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 1)
+ return;
+
+ u8 serialization_ver;
+ u16 proto_ver;
+ u16 compression_mode;
+ u32 auth_mechs;
+ std::string username_legacy; // for case insensitivity
+ *pkt >> serialization_ver >> compression_mode >> proto_ver
+ >> auth_mechs >> username_legacy;
+
+ // Chose an auth method we support
+ AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs);
+
+ infostream << "Client: TOCLIENT_HELLO received with "
+ << "serialization_ver=" << (u32)serialization_ver
+ << ", auth_mechs=" << auth_mechs
+ << ", proto_ver=" << proto_ver
+ << ", compression_mode=" << compression_mode
+ << ". Doing auth with mech " << chosen_auth_mechanism << std::endl;
+
+ if (!ser_ver_supported(serialization_ver)) {
+ infostream << "Client: TOCLIENT_HELLO: Server sent "
+ << "unsupported ser_fmt_ver"<< std::endl;
+ return;
+ }
+
+ m_server_ser_ver = serialization_ver;
+ m_proto_ver = proto_ver;
+
+ //TODO verify that username_legacy matches sent username, only
+ // differs in casing (make both uppercase and compare)
+ // This is only neccessary though when we actually want to add casing support
+
+ if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) {
+ // we recieved a TOCLIENT_HELLO while auth was already going on
+ errorstream << "Client: TOCLIENT_HELLO while auth was already going on"
+ << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl;
+ if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP)
+ || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) {
+ srp_user_delete((SRPUser *) m_auth_data);
+ m_auth_data = 0;
+ }
+ }
+
+ // Authenticate using that method, or abort if there wasn't any method found
+ if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
+ startAuth(chosen_auth_mechanism);
+ } else {
+ m_chosen_auth_mech = AUTH_MECHANISM_NONE;
+ m_access_denied = true;
+ m_access_denied_reason = "Unknown";
+ m_con.Disconnect();
+ }
+
+}
+
+void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
+{
+ deleteAuthData();
+
+ v3f playerpos;
+ *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval
+ >> m_sudo_auth_methods;
+
+ playerpos -= v3f(0, BS / 2, 0);
+
+ // Set player position
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ player->setPosition(playerpos);
+
+ infostream << "Client: received map seed: " << m_map_seed << std::endl;
+ infostream << "Client: received recommended send interval "
+ << m_recommended_send_interval<<std::endl;
+
+ // Reply to server
+ NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
+ Send(&resp_pkt);
+
+ m_state = LC_Init;
+}
+void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
+{
+ deleteAuthData();
+
+ m_password = m_new_password;
+
+ verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
+
+ // send packet to actually set the password
+ startAuth(AUTH_MECHANISM_FIRST_SRP);
+
+ // reset again
+ m_chosen_auth_mech = AUTH_MECHANISM_NONE;
+}
+void Client::handleCommand_DenySudoMode(NetworkPacket* pkt)
+{
+ m_chat_queue.push(L"Password change denied. Password NOT changed.");
+ // reset everything and be sad
+ deleteAuthData();
+}
+void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 1)
+ return;
+
+ u8 server_ser_ver;
+ *pkt >> server_ser_ver;
+
+ infostream << "Client: TOCLIENT_INIT_LEGACY received with "
+ "server_ser_ver=" << ((int)server_ser_ver & 0xff) << std::endl;
+
+ if (!ser_ver_supported(server_ser_ver)) {
+ infostream << "Client: TOCLIENT_INIT_LEGACY: Server sent "
+ << "unsupported ser_fmt_ver"<< std::endl;
+ return;
+ }
+
+ m_server_ser_ver = server_ser_ver;
+
+ // We can be totally wrong with this guess
+ // but we only need some value < 25.
+ m_proto_ver = 24;
+
+ // Get player position
+ v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
+ if (pkt->getSize() >= 1 + 6) {
+ *pkt >> playerpos_s16;
+ }
+ v3f playerpos_f = intToFloat(playerpos_s16, BS) - v3f(0, BS / 2, 0);
+
+
+ // Set player position
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ player->setPosition(playerpos_f);
+
+ if (pkt->getSize() >= 1 + 6 + 8) {
+ // Get map seed
+ *pkt >> m_map_seed;
+ infostream << "Client: received map seed: " << m_map_seed << std::endl;
+ }
+
+ if (pkt->getSize() >= 1 + 6 + 8 + 4) {
+ *pkt >> m_recommended_send_interval;
+ infostream << "Client: received recommended send interval "
+ << m_recommended_send_interval<<std::endl;
+ }
+
+ // Reply to server
+ NetworkPacket resp_pkt(TOSERVER_INIT2, 0);
+ Send(&resp_pkt);
+
+ m_state = LC_Init;
+}
+
+void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
+{
+ // The server didn't like our password. Note, this needs
+ // to be processed even if the serialisation format has
+ // not been agreed yet, the same as TOCLIENT_INIT.
+ m_access_denied = true;
+ m_access_denied_reason = "Unknown";
+
+ if (pkt->getCommand() == TOCLIENT_ACCESS_DENIED) {
+ if (pkt->getSize() < 1)
+ return;
+
+ u8 denyCode = SERVER_ACCESSDENIED_UNEXPECTED_DATA;
+ *pkt >> denyCode;
+ if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN ||
+ denyCode == SERVER_ACCESSDENIED_CRASH) {
+ *pkt >> m_access_denied_reason;
+ if (m_access_denied_reason == "") {
+ m_access_denied_reason = accessDeniedStrings[denyCode];
+ }
+ u8 reconnect;
+ *pkt >> reconnect;
+ m_access_denied_reconnect = reconnect & 1;
+ } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) {
+ *pkt >> m_access_denied_reason;
+ } else if (denyCode < SERVER_ACCESSDENIED_MAX) {
+ m_access_denied_reason = accessDeniedStrings[denyCode];
+ } else {
+ // Allow us to add new error messages to the
+ // protocol without raising the protocol version, if we want to.
+ // Until then (which may be never), this is outside
+ // of the defined protocol.
+ *pkt >> m_access_denied_reason;
+ if (m_access_denied_reason == "") {
+ m_access_denied_reason = "Unknown";
+ }
+ }
+ }
+ // 13/03/15 Legacy code from 0.4.12 and lesser. must stay 1 year
+ // for compat with old clients
+ else {
+ if (pkt->getSize() >= 2) {
+ std::wstring wide_reason;
+ *pkt >> wide_reason;
+ m_access_denied_reason = wide_to_utf8(wide_reason);
+ }
+ }
+}
+
+void Client::handleCommand_RemoveNode(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 6)
+ return;
+
+ v3s16 p;
+ *pkt >> p;
+ removeNode(p);
+}
+
+void Client::handleCommand_AddNode(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 6 + MapNode::serializedLength(m_server_ser_ver))
+ return;
+
+ v3s16 p;
+ *pkt >> p;
+
+ MapNode n;
+ n.deSerialize(pkt->getU8Ptr(6), m_server_ser_ver);
+
+ bool remove_metadata = true;
+ u32 index = 6 + MapNode::serializedLength(m_server_ser_ver);
+ if ((pkt->getSize() >= index + 1) && pkt->getU8(index)) {
+ remove_metadata = false;
+ }
+
+ addNode(p, n, remove_metadata);
+}
+void Client::handleCommand_BlockData(NetworkPacket* pkt)
+{
+ // Ignore too small packet
+ if (pkt->getSize() < 6)
+ return;
+
+ v3s16 p;
+ *pkt >> p;
+
+ std::string datastring(pkt->getString(6), pkt->getSize() - 6);
+ std::istringstream istr(datastring, std::ios_base::binary);
+
+ MapSector *sector;
+ MapBlock *block;
+
+ v2s16 p2d(p.X, p.Z);
+ sector = m_env.getMap().emergeSector(p2d);
+
+ assert(sector->getPos() == p2d);
+
+ block = sector->getBlockNoCreateNoEx(p.Y);
+ if (block) {
+ /*
+ Update an existing block
+ */
+ block->deSerialize(istr, m_server_ser_ver, false);
+ block->deSerializeNetworkSpecific(istr);
+ }
+ else {
+ /*
+ Create a new block
+ */
+ block = new MapBlock(&m_env.getMap(), p, this);
+ block->deSerialize(istr, m_server_ser_ver, false);
+ block->deSerializeNetworkSpecific(istr);
+ sector->insertBlock(block);
+ }
+
+ if (m_localdb) {
+ ServerMap::saveBlock(block, m_localdb);
+ }
+
+ /*
+ Add it to mesh update queue and set it to be acknowledged after update.
+ */
+ addUpdateMeshTaskWithEdge(p, true);
+}
+
+void Client::handleCommand_Inventory(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 1)
+ return;
+
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ player->inventory.deSerialize(is);
+
+ m_inventory_updated = true;
+
+ delete m_inventory_from_server;
+ m_inventory_from_server = new Inventory(player->inventory);
+ m_inventory_from_server_age = 0.0;
+}
+
+void Client::handleCommand_TimeOfDay(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 2)
+ return;
+
+ u16 time_of_day;
+
+ *pkt >> time_of_day;
+
+ time_of_day = time_of_day % 24000;
+ float time_speed = 0;
+
+ if (pkt->getSize() >= 2 + 4) {
+ *pkt >> time_speed;
+ }
+ else {
+ // Old message; try to approximate speed of time by ourselves
+ float time_of_day_f = (float)time_of_day / 24000.0;
+ float tod_diff_f = 0;
+
+ if (time_of_day_f < 0.2 && m_last_time_of_day_f > 0.8)
+ tod_diff_f = time_of_day_f - m_last_time_of_day_f + 1.0;
+ else
+ tod_diff_f = time_of_day_f - m_last_time_of_day_f;
+
+ m_last_time_of_day_f = time_of_day_f;
+ float time_diff = m_time_of_day_update_timer;
+ m_time_of_day_update_timer = 0;
+
+ if (m_time_of_day_set) {
+ time_speed = (3600.0 * 24.0) * tod_diff_f / time_diff;
+ infostream << "Client: Measured time_of_day speed (old format): "
+ << time_speed << " tod_diff_f=" << tod_diff_f
+ << " time_diff=" << time_diff << std::endl;
+ }
+ }
+
+ // Update environment
+ m_env.setTimeOfDay(time_of_day);
+ m_env.setTimeOfDaySpeed(time_speed);
+ m_time_of_day_set = true;
+
+ u32 dr = m_env.getDayNightRatio();
+ infostream << "Client: time_of_day=" << time_of_day
+ << " time_speed=" << time_speed
+ << " dr=" << dr << std::endl;
+}
+
+void Client::handleCommand_ChatMessage(NetworkPacket* pkt)
+{
+ /*
+ u16 command
+ u16 length
+ wstring message
+ */
+ u16 len, read_wchar;
+
+ *pkt >> len;
+
+ std::wstring message;
+ for (u32 i = 0; i < len; i++) {
+ *pkt >> read_wchar;
+ message += (wchar_t)read_wchar;
+ }
+
+ m_chat_queue.push(message);
+}
+
+void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
+{
+ /*
+ u16 count of removed objects
+ for all removed objects {
+ u16 id
+ }
+ u16 count of added objects
+ for all added objects {
+ u16 id
+ u8 type
+ u32 initialization data length
+ string initialization data
+ }
+ */
+
+ try {
+ u8 type;
+ u16 removed_count, added_count, id;
+
+ // Read removed objects
+ *pkt >> removed_count;
+
+ for (u16 i = 0; i < removed_count; i++) {
+ *pkt >> id;
+ m_env.removeActiveObject(id);
+ }
+
+ // Read added objects
+ *pkt >> added_count;
+
+ for (u16 i = 0; i < added_count; i++) {
+ *pkt >> id >> type;
+ m_env.addActiveObject(id, type, pkt->readLongString());
+ }
+ } catch (PacketError &e) {
+ infostream << "handleCommand_ActiveObjectRemoveAdd: " << e.what()
+ << ". The packet is unreliable, ignoring" << std::endl;
+ }
+}
+
+void Client::handleCommand_ActiveObjectMessages(NetworkPacket* pkt)
+{
+ /*
+ for all objects
+ {
+ u16 id
+ u16 message length
+ string message
+ }
+ */
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ try {
+ while (is.good()) {
+ u16 id = readU16(is);
+ if (!is.good())
+ break;
+
+ std::string message = deSerializeString(is);
+
+ // Pass on to the environment
+ m_env.processActiveObjectMessage(id, message);
+ }
+ } catch (SerializationError &e) {
+ errorstream << "Client::handleCommand_ActiveObjectMessages: "
+ << "caught SerializationError: " << e.what() << std::endl;
+ }
+}
+
+void Client::handleCommand_Movement(NetworkPacket* pkt)
+{
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ float mad, maa, maf, msw, mscr, msf, mscl, msj, lf, lfs, ls, g;
+
+ *pkt >> mad >> maa >> maf >> msw >> mscr >> msf >> mscl >> msj
+ >> lf >> lfs >> ls >> g;
+
+ player->movement_acceleration_default = mad * BS;
+ player->movement_acceleration_air = maa * BS;
+ player->movement_acceleration_fast = maf * BS;
+ player->movement_speed_walk = msw * BS;
+ player->movement_speed_crouch = mscr * BS;
+ player->movement_speed_fast = msf * BS;
+ player->movement_speed_climb = mscl * BS;
+ player->movement_speed_jump = msj * BS;
+ player->movement_liquid_fluidity = lf * BS;
+ player->movement_liquid_fluidity_smooth = lfs * BS;
+ player->movement_liquid_sink = ls * BS;
+ player->movement_gravity = g * BS;
+}
+
+void Client::handleCommand_HP(NetworkPacket* pkt)
+{
+
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ u8 oldhp = player->hp;
+
+ u8 hp;
+ *pkt >> hp;
+
+ player->hp = hp;
+
+ if (hp < oldhp) {
+ // Add to ClientEvent queue
+ ClientEvent event;
+ event.type = CE_PLAYER_DAMAGE;
+ event.player_damage.amount = oldhp - hp;
+ m_client_event_queue.push(event);
+ }
+}
+
+void Client::handleCommand_Breath(NetworkPacket* pkt)
+{
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ u16 breath;
+
+ *pkt >> breath;
+
+ player->setBreath(breath);
+}
+
+void Client::handleCommand_MovePlayer(NetworkPacket* pkt)
+{
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ v3f pos;
+ f32 pitch, yaw;
+
+ *pkt >> pos >> pitch >> yaw;
+
+ player->setPosition(pos);
+
+ infostream << "Client got TOCLIENT_MOVE_PLAYER"
+ << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
+ << " pitch=" << pitch
+ << " yaw=" << yaw
+ << std::endl;
+
+ /*
+ Add to ClientEvent queue.
+ This has to be sent to the main program because otherwise
+ it would just force the pitch and yaw values to whatever
+ the camera points to.
+ */
+ ClientEvent event;
+ event.type = CE_PLAYER_FORCE_MOVE;
+ event.player_force_move.pitch = pitch;
+ event.player_force_move.yaw = yaw;
+ m_client_event_queue.push(event);
+
+ // Ignore damage for a few seconds, so that the player doesn't
+ // get damage from falling on ground
+ m_ignore_damage_timer = 3.0;
+}
+
+void Client::handleCommand_PlayerItem(NetworkPacket* pkt)
+{
+ infostream << "Client: WARNING: Ignoring TOCLIENT_PLAYERITEM" << std::endl;
+}
+
+void Client::handleCommand_DeathScreen(NetworkPacket* pkt)
+{
+ bool set_camera_point_target;
+ v3f camera_point_target;
+
+ *pkt >> set_camera_point_target;
+ *pkt >> camera_point_target;
+
+ ClientEvent event;
+ event.type = CE_DEATHSCREEN;
+ event.deathscreen.set_camera_point_target = set_camera_point_target;
+ event.deathscreen.camera_point_target_x = camera_point_target.X;
+ event.deathscreen.camera_point_target_y = camera_point_target.Y;
+ event.deathscreen.camera_point_target_z = camera_point_target.Z;
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_AnnounceMedia(NetworkPacket* pkt)
+{
+ u16 num_files;
+
+ *pkt >> num_files;
+
+ infostream << "Client: Received media announcement: packet size: "
+ << pkt->getSize() << std::endl;
+
+ if (m_media_downloader == NULL ||
+ m_media_downloader->isStarted()) {
+ const char *problem = m_media_downloader ?
+ "we already saw another announcement" :
+ "all media has been received already";
+ errorstream << "Client: Received media announcement but "
+ << problem << "! "
+ << " files=" << num_files
+ << " size=" << pkt->getSize() << std::endl;
+ return;
+ }
+
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ sanity_check(!m_mesh_update_thread.IsRunning());
+
+ for (u16 i = 0; i < num_files; i++) {
+ std::string name, sha1_base64;
+
+ *pkt >> name >> sha1_base64;
+
+ std::string sha1_raw = base64_decode(sha1_base64);
+ m_media_downloader->addFile(name, sha1_raw);
+ }
+
+ std::vector<std::string> remote_media;
+ try {
+ std::string str;
+
+ *pkt >> str;
+
+ Strfnd sf(str);
+ while(!sf.atend()) {
+ std::string baseurl = trim(sf.next(","));
+ if (baseurl != "")
+ m_media_downloader->addRemoteServer(baseurl);
+ }
+ }
+ catch(SerializationError& e) {
+ // not supported by server or turned off
+ }
+
+ m_media_downloader->step(this);
+}
+
+void Client::handleCommand_Media(NetworkPacket* pkt)
+{
+ /*
+ u16 command
+ u16 total number of file bunches
+ u16 index of this bunch
+ u32 number of files in this bunch
+ for each file {
+ u16 length of name
+ string name
+ u32 length of data
+ data
+ }
+ */
+ u16 num_bunches;
+ u16 bunch_i;
+ u32 num_files;
+
+ *pkt >> num_bunches >> bunch_i >> num_files;
+
+ infostream << "Client: Received files: bunch " << bunch_i << "/"
+ << num_bunches << " files=" << num_files
+ << " size=" << pkt->getSize() << std::endl;
+
+ if (num_files == 0)
+ return;
+
+ if (m_media_downloader == NULL ||
+ !m_media_downloader->isStarted()) {
+ const char *problem = m_media_downloader ?
+ "media has not been requested" :
+ "all media has been received already";
+ errorstream << "Client: Received media but "
+ << problem << "! "
+ << " bunch " << bunch_i << "/" << num_bunches
+ << " files=" << num_files
+ << " size=" << pkt->getSize() << std::endl;
+ return;
+ }
+
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ sanity_check(!m_mesh_update_thread.IsRunning());
+
+ for (u32 i=0; i < num_files; i++) {
+ std::string name;
+
+ *pkt >> name;
+
+ std::string data = pkt->readLongString();
+
+ m_media_downloader->conventionalTransferDone(
+ name, data, this);
+ }
+}
+
+void Client::handleCommand_ToolDef(NetworkPacket* pkt)
+{
+ infostream << "Client: WARNING: Ignoring TOCLIENT_TOOLDEF" << std::endl;
+}
+
+void Client::handleCommand_NodeDef(NetworkPacket* pkt)
+{
+ infostream << "Client: Received node definitions: packet size: "
+ << pkt->getSize() << std::endl;
+
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ sanity_check(!m_mesh_update_thread.IsRunning());
+
+ // Decompress node definitions
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+ std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+ std::ostringstream tmp_os;
+ decompressZlib(tmp_is, tmp_os);
+
+ // Deserialize node definitions
+ std::istringstream tmp_is2(tmp_os.str());
+ m_nodedef->deSerialize(tmp_is2);
+ m_nodedef_received = true;
+}
+
+void Client::handleCommand_CraftItemDef(NetworkPacket* pkt)
+{
+ infostream << "Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF" << std::endl;
+}
+
+void Client::handleCommand_ItemDef(NetworkPacket* pkt)
+{
+ infostream << "Client: Received item definitions: packet size: "
+ << pkt->getSize() << std::endl;
+
+ // Mesh update thread must be stopped while
+ // updating content definitions
+ sanity_check(!m_mesh_update_thread.IsRunning());
+
+ // Decompress item definitions
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+ std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+ std::ostringstream tmp_os;
+ decompressZlib(tmp_is, tmp_os);
+
+ // Deserialize node definitions
+ std::istringstream tmp_is2(tmp_os.str());
+ m_itemdef->deSerialize(tmp_is2);
+ m_itemdef_received = true;
+}
+
+void Client::handleCommand_PlaySound(NetworkPacket* pkt)
+{
+ s32 server_id;
+ std::string name;
+ float gain;
+ u8 type; // 0=local, 1=positional, 2=object
+ v3f pos;
+ u16 object_id;
+ bool loop;
+
+ *pkt >> server_id >> name >> gain >> type >> pos >> object_id >> loop;
+
+ // Start playing
+ int client_id = -1;
+ switch(type) {
+ case 0: // local
+ client_id = m_sound->playSound(name, loop, gain);
+ break;
+ case 1: // positional
+ client_id = m_sound->playSoundAt(name, loop, gain, pos);
+ break;
+ case 2:
+ { // object
+ ClientActiveObject *cao = m_env.getActiveObject(object_id);
+ if (cao)
+ pos = cao->getPosition();
+ client_id = m_sound->playSoundAt(name, loop, gain, pos);
+ // TODO: Set up sound to move with object
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (client_id != -1) {
+ m_sounds_server_to_client[server_id] = client_id;
+ m_sounds_client_to_server[client_id] = server_id;
+ if (object_id != 0)
+ m_sounds_to_objects[client_id] = object_id;
+ }
+}
+
+void Client::handleCommand_StopSound(NetworkPacket* pkt)
+{
+ s32 server_id;
+
+ *pkt >> server_id;
+
+ std::map<s32, int>::iterator i =
+ m_sounds_server_to_client.find(server_id);
+
+ if (i != m_sounds_server_to_client.end()) {
+ int client_id = i->second;
+ m_sound->stopSound(client_id);
+ }
+}
+
+void Client::handleCommand_Privileges(NetworkPacket* pkt)
+{
+ m_privileges.clear();
+ infostream << "Client: Privileges updated: ";
+ u16 num_privileges;
+
+ *pkt >> num_privileges;
+
+ for (u16 i = 0; i < num_privileges; i++) {
+ std::string priv;
+
+ *pkt >> priv;
+
+ m_privileges.insert(priv);
+ infostream << priv << " ";
+ }
+ infostream << std::endl;
+}
+
+void Client::handleCommand_InventoryFormSpec(NetworkPacket* pkt)
+{
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ // Store formspec in LocalPlayer
+ player->inventory_formspec = pkt->readLongString();
+}
+
+void Client::handleCommand_DetachedInventory(NetworkPacket* pkt)
+{
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ std::string name = deSerializeString(is);
+
+ infostream << "Client: Detached inventory update: \"" << name
+ << "\"" << std::endl;
+
+ Inventory *inv = NULL;
+ if (m_detached_inventories.count(name) > 0)
+ inv = m_detached_inventories[name];
+ else {
+ inv = new Inventory(m_itemdef);
+ m_detached_inventories[name] = inv;
+ }
+ inv->deSerialize(is);
+}
+
+void Client::handleCommand_ShowFormSpec(NetworkPacket* pkt)
+{
+ std::string formspec = pkt->readLongString();
+ std::string formname;
+
+ *pkt >> formname;
+
+ ClientEvent event;
+ event.type = CE_SHOW_FORMSPEC;
+ // pointer is required as event is a struct only!
+ // adding a std:string to a struct isn't possible
+ event.show_formspec.formspec = new std::string(formspec);
+ event.show_formspec.formname = new std::string(formname);
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_SpawnParticle(NetworkPacket* pkt)
+{
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ v3f pos = readV3F1000(is);
+ v3f vel = readV3F1000(is);
+ v3f acc = readV3F1000(is);
+ float expirationtime = readF1000(is);
+ float size = readF1000(is);
+ bool collisiondetection = readU8(is);
+ std::string texture = deSerializeLongString(is);
+ bool vertical = false;
+ try {
+ vertical = readU8(is);
+ } catch (...) {}
+
+ ClientEvent event;
+ event.type = CE_SPAWN_PARTICLE;
+ event.spawn_particle.pos = new v3f (pos);
+ event.spawn_particle.vel = new v3f (vel);
+ event.spawn_particle.acc = new v3f (acc);
+ event.spawn_particle.expirationtime = expirationtime;
+ event.spawn_particle.size = size;
+ event.spawn_particle.collisiondetection = collisiondetection;
+ event.spawn_particle.vertical = vertical;
+ event.spawn_particle.texture = new std::string(texture);
+
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt)
+{
+ u16 amount;
+ float spawntime;
+ v3f minpos;
+ v3f maxpos;
+ v3f minvel;
+ v3f maxvel;
+ v3f minacc;
+ v3f maxacc;
+ float minexptime;
+ float maxexptime;
+ float minsize;
+ float maxsize;
+ bool collisiondetection;
+ u32 id;
+
+ *pkt >> amount >> spawntime >> minpos >> maxpos >> minvel >> maxvel
+ >> minacc >> maxacc >> minexptime >> maxexptime >> minsize
+ >> maxsize >> collisiondetection;
+
+ std::string texture = pkt->readLongString();
+
+ *pkt >> id;
+
+ bool vertical = false;
+ try {
+ *pkt >> vertical;
+ } catch (...) {}
+
+ ClientEvent event;
+ event.type = CE_ADD_PARTICLESPAWNER;
+ event.add_particlespawner.amount = amount;
+ event.add_particlespawner.spawntime = spawntime;
+ event.add_particlespawner.minpos = new v3f (minpos);
+ event.add_particlespawner.maxpos = new v3f (maxpos);
+ event.add_particlespawner.minvel = new v3f (minvel);
+ event.add_particlespawner.maxvel = new v3f (maxvel);
+ event.add_particlespawner.minacc = new v3f (minacc);
+ event.add_particlespawner.maxacc = new v3f (maxacc);
+ event.add_particlespawner.minexptime = minexptime;
+ event.add_particlespawner.maxexptime = maxexptime;
+ event.add_particlespawner.minsize = minsize;
+ event.add_particlespawner.maxsize = maxsize;
+ event.add_particlespawner.collisiondetection = collisiondetection;
+ event.add_particlespawner.vertical = vertical;
+ event.add_particlespawner.texture = new std::string(texture);
+ event.add_particlespawner.id = id;
+
+ m_client_event_queue.push(event);
+}
+
+
+void Client::handleCommand_DeleteParticleSpawner(NetworkPacket* pkt)
+{
+ u16 legacy_id;
+ u32 id;
+
+ // Modification set 13/03/15, 1 year of compat for protocol v24
+ if (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY) {
+ *pkt >> legacy_id;
+ }
+ else {
+ *pkt >> id;
+ }
+
+
+ ClientEvent event;
+ event.type = CE_DELETE_PARTICLESPAWNER;
+ event.delete_particlespawner.id =
+ (pkt->getCommand() == TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY ? (u32) legacy_id : id);
+
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudAdd(NetworkPacket* pkt)
+{
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ u32 id;
+ u8 type;
+ v2f pos;
+ std::string name;
+ v2f scale;
+ std::string text;
+ u32 number;
+ u32 item;
+ u32 dir;
+ v2f align;
+ v2f offset;
+ v3f world_pos;
+ v2s32 size;
+
+ *pkt >> id >> type >> pos >> name >> scale >> text >> number >> item
+ >> dir >> align >> offset;
+ try {
+ *pkt >> world_pos;
+ }
+ catch(SerializationError &e) {};
+
+ try {
+ *pkt >> size;
+ } catch(SerializationError &e) {};
+
+ ClientEvent event;
+ event.type = CE_HUDADD;
+ event.hudadd.id = id;
+ event.hudadd.type = type;
+ event.hudadd.pos = new v2f(pos);
+ event.hudadd.name = new std::string(name);
+ event.hudadd.scale = new v2f(scale);
+ event.hudadd.text = new std::string(text);
+ event.hudadd.number = number;
+ event.hudadd.item = item;
+ event.hudadd.dir = dir;
+ event.hudadd.align = new v2f(align);
+ event.hudadd.offset = new v2f(offset);
+ event.hudadd.world_pos = new v3f(world_pos);
+ event.hudadd.size = new v2s32(size);
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudRemove(NetworkPacket* pkt)
+{
+ u32 id;
+
+ *pkt >> id;
+
+ ClientEvent event;
+ event.type = CE_HUDRM;
+ event.hudrm.id = id;
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudChange(NetworkPacket* pkt)
+{
+ std::string sdata;
+ v2f v2fdata;
+ v3f v3fdata;
+ u32 intdata = 0;
+ v2s32 v2s32data;
+ u32 id;
+ u8 stat;
+
+ *pkt >> id >> stat;
+
+ if (stat == HUD_STAT_POS || stat == HUD_STAT_SCALE ||
+ stat == HUD_STAT_ALIGN || stat == HUD_STAT_OFFSET)
+ *pkt >> v2fdata;
+ else if (stat == HUD_STAT_NAME || stat == HUD_STAT_TEXT)
+ *pkt >> sdata;
+ else if (stat == HUD_STAT_WORLD_POS)
+ *pkt >> v3fdata;
+ else if (stat == HUD_STAT_SIZE )
+ *pkt >> v2s32data;
+ else
+ *pkt >> intdata;
+
+ ClientEvent event;
+ event.type = CE_HUDCHANGE;
+ event.hudchange.id = id;
+ event.hudchange.stat = (HudElementStat)stat;
+ event.hudchange.v2fdata = new v2f(v2fdata);
+ event.hudchange.v3fdata = new v3f(v3fdata);
+ event.hudchange.sdata = new std::string(sdata);
+ event.hudchange.data = intdata;
+ event.hudchange.v2s32data = new v2s32(v2s32data);
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_HudSetFlags(NetworkPacket* pkt)
+{
+ u32 flags, mask;
+
+ *pkt >> flags >> mask;
+
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ player->hud_flags &= ~mask;
+ player->hud_flags |= flags;
+}
+
+void Client::handleCommand_HudSetParam(NetworkPacket* pkt)
+{
+ u16 param; std::string value;
+
+ *pkt >> param >> value;
+
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ if (param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4) {
+ s32 hotbar_itemcount = readS32((u8*) value.c_str());
+ if (hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX)
+ player->hud_hotbar_itemcount = hotbar_itemcount;
+ }
+ else if (param == HUD_PARAM_HOTBAR_IMAGE) {
+ ((LocalPlayer *) player)->hotbar_image = value;
+ }
+ else if (param == HUD_PARAM_HOTBAR_SELECTED_IMAGE) {
+ ((LocalPlayer *) player)->hotbar_selected_image = value;
+ }
+}
+
+void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
+{
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ video::SColor *bgcolor = new video::SColor(readARGB8(is));
+ std::string *type = new std::string(deSerializeString(is));
+ u16 count = readU16(is);
+ std::vector<std::string> *params = new std::vector<std::string>;
+
+ for (size_t i = 0; i < count; i++)
+ params->push_back(deSerializeString(is));
+
+ ClientEvent event;
+ event.type = CE_SET_SKY;
+ event.set_sky.bgcolor = bgcolor;
+ event.set_sky.type = type;
+ event.set_sky.params = params;
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_OverrideDayNightRatio(NetworkPacket* pkt)
+{
+ bool do_override;
+ u16 day_night_ratio_u;
+
+ *pkt >> do_override >> day_night_ratio_u;
+
+ float day_night_ratio_f = (float)day_night_ratio_u / 65536;
+
+ ClientEvent event;
+ event.type = CE_OVERRIDE_DAY_NIGHT_RATIO;
+ event.override_day_night_ratio.do_override = do_override;
+ event.override_day_night_ratio.ratio_f = day_night_ratio_f;
+ m_client_event_queue.push(event);
+}
+
+void Client::handleCommand_LocalPlayerAnimations(NetworkPacket* pkt)
+{
+ LocalPlayer *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ *pkt >> player->local_animations[0];
+ *pkt >> player->local_animations[1];
+ *pkt >> player->local_animations[2];
+ *pkt >> player->local_animations[3];
+ *pkt >> player->local_animation_speed;
+}
+
+void Client::handleCommand_EyeOffset(NetworkPacket* pkt)
+{
+ LocalPlayer *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+
+ *pkt >> player->eye_offset_first >> player->eye_offset_third;
+}
+
+void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
+{
+ if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD)
+ && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) {
+ errorstream << "Client: Recieved SRP S_B login message,"
+ << " but wasn't supposed to (chosen_mech="
+ << m_chosen_auth_mech << ")." << std::endl;
+ return;
+ }
+
+ char *bytes_M = 0;
+ size_t len_M = 0;
+ SRPUser *usr = (SRPUser *) m_auth_data;
+ std::string s;
+ std::string B;
+ *pkt >> s >> B;
+
+ infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl;
+
+ srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(),
+ (const unsigned char *) B.c_str(), B.size(),
+ (unsigned char **) &bytes_M, &len_M);
+
+ if ( !bytes_M ) {
+ errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
+ return;
+ }
+
+ NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0);
+ resp_pkt << std::string(bytes_M, len_M);
+ Send(&resp_pkt);
+}
diff --git a/src/connection.cpp b/src/network/connection.cpp
index 2ee6d2c6e..7794ce10f 100644
--- a/src/connection.cpp
+++ b/src/network/connection.cpp
@@ -20,16 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iomanip>
#include <errno.h>
#include "connection.h"
-#include "main.h"
#include "serialization.h"
#include "log.h"
#include "porting.h"
+#include "network/networkpacket.h"
#include "util/serialize.h"
#include "util/numeric.h"
#include "util/string.h"
#include "settings.h"
#include "profiler.h"
-#include "main.h" // for profiling
namespace con
{
@@ -134,7 +133,7 @@ std::list<SharedBuffer<u8> > makeSplitPacket(
u16 chunk_count = 0;
do{
end = start + maximum_data_size - 1;
- if(end > data.getSize() - 1)
+ if (end > data.getSize() - 1)
end = data.getSize() - 1;
u32 payload_size = end - start + 1;
@@ -173,7 +172,7 @@ std::list<SharedBuffer<u8> > makeAutoSplitPacket(
{
u32 original_header_size = 1;
std::list<SharedBuffer<u8> > list;
- if(data.getSize() + original_header_size > chunksize_max)
+ if (data.getSize() + original_header_size > chunksize_max)
{
list = makeSplitPacket(data, chunksize_max, split_seqnum);
split_seqnum++;
@@ -246,7 +245,7 @@ RPBSearchResult ReliablePacketBuffer::findPacket(u16 seqnum)
u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1]));
/*dout_con<<"findPacket(): finding seqnum="<<seqnum
<<", comparing to s="<<s<<std::endl;*/
- if(s == seqnum)
+ if (s == seqnum)
break;
}
return i;
@@ -258,7 +257,7 @@ RPBSearchResult ReliablePacketBuffer::notFound()
bool ReliablePacketBuffer::getFirstSeqnum(u16& result)
{
JMutexAutoLock listlock(m_list_mutex);
- if(m_list.empty())
+ if (m_list.empty())
return false;
BufferedPacket p = *m_list.begin();
result = readU16(&p.data[BASE_HEADER_SIZE+1]);
@@ -268,7 +267,7 @@ bool ReliablePacketBuffer::getFirstSeqnum(u16& result)
BufferedPacket ReliablePacketBuffer::popFirst()
{
JMutexAutoLock listlock(m_list_mutex);
- if(m_list.empty())
+ if (m_list.empty())
throw NotFoundException("Buffer is empty");
BufferedPacket p = *m_list.begin();
m_list.erase(m_list.begin());
@@ -286,7 +285,7 @@ BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
{
JMutexAutoLock listlock(m_list_mutex);
RPBSearchResult r = findPacket(seqnum);
- if(r == notFound()){
+ if (r == notFound()) {
LOG(dout_con<<"Sequence number: " << seqnum
<< " not found in reliable buffer"<<std::endl);
throw NotFoundException("seqnum not found in buffer");
@@ -313,20 +312,36 @@ BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum)
void ReliablePacketBuffer::insert(BufferedPacket &p,u16 next_expected)
{
JMutexAutoLock listlock(m_list_mutex);
- assert(p.data.getSize() >= BASE_HEADER_SIZE+3);
- u8 type = readU8(&p.data[BASE_HEADER_SIZE+0]);
- assert(type == TYPE_RELIABLE);
- u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]);
+ if (p.data.getSize() < BASE_HEADER_SIZE + 3) {
+ errorstream << "ReliablePacketBuffer::insert(): Invalid data size for "
+ "reliable packet" << std::endl;
+ return;
+ }
+ u8 type = readU8(&p.data[BASE_HEADER_SIZE + 0]);
+ if (type != TYPE_RELIABLE) {
+ errorstream << "ReliablePacketBuffer::insert(): type is not reliable"
+ << std::endl;
+ return;
+ }
+ u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE + 1]);
- assert(seqnum_in_window(seqnum,next_expected,MAX_RELIABLE_WINDOW_SIZE));
- assert(seqnum != next_expected);
+ if (!seqnum_in_window(seqnum, next_expected, MAX_RELIABLE_WINDOW_SIZE)) {
+ errorstream << "ReliablePacketBuffer::insert(): seqnum is outside of "
+ "expected window " << std::endl;
+ return;
+ }
+ if (seqnum == next_expected) {
+ errorstream << "ReliablePacketBuffer::insert(): seqnum is next expected"
+ << std::endl;
+ return;
+ }
++m_list_size;
- assert(m_list_size <= SEQNUM_MAX+1);
+ sanity_check(m_list_size <= SEQNUM_MAX+1); // FIXME: Handle the error?
// Find the right place for the packet and insert it there
// If list is empty, just add it
- if(m_list.empty())
+ if (m_list.empty())
{
m_list.push_back(p);
m_oldest_non_answered_ack = seqnum;
@@ -378,10 +393,6 @@ void ReliablePacketBuffer::insert(BufferedPacket &p,u16 next_expected)
throw IncomingDataCorruption("duplicated packet isn't same as original one");
}
- assert(readU16(&(i->data[BASE_HEADER_SIZE+1])) == seqnum);
- assert(i->data.getSize() == p.data.getSize());
- assert(i->address == p.address);
-
/* nothing to do this seems to be a resent packet */
/* for paranoia reason data should be compared */
--m_list_size;
@@ -417,7 +428,7 @@ std::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout,
for(std::list<BufferedPacket>::iterator i = m_list.begin();
i != m_list.end(); ++i)
{
- if(i->time >= timeout) {
+ if (i->time >= timeout) {
timed_outs.push_back(*i);
//this packet will be sent right afterwards reset timeout here
@@ -450,15 +461,23 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
{
JMutexAutoLock listlock(m_map_mutex);
u32 headersize = BASE_HEADER_SIZE + 7;
- assert(p.data.getSize() >= headersize);
+ if (p.data.getSize() < headersize) {
+ errorstream << "Invalid data size for split packet" << std::endl;
+ return SharedBuffer<u8>();
+ }
u8 type = readU8(&p.data[BASE_HEADER_SIZE+0]);
- assert(type == TYPE_SPLIT);
u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]);
u16 chunk_count = readU16(&p.data[BASE_HEADER_SIZE+3]);
u16 chunk_num = readU16(&p.data[BASE_HEADER_SIZE+5]);
+ if (type != TYPE_SPLIT) {
+ errorstream << "IncomingSplitBuffer::insert(): type is not split"
+ << std::endl;
+ return SharedBuffer<u8>();
+ }
+
// Add if doesn't exist
- if(m_buf.find(seqnum) == m_buf.end())
+ if (m_buf.find(seqnum) == m_buf.end())
{
IncomingSplitPacket *sp = new IncomingSplitPacket();
sp->chunk_count = chunk_count;
@@ -469,11 +488,11 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
IncomingSplitPacket *sp = m_buf[seqnum];
// TODO: These errors should be thrown or something? Dunno.
- if(chunk_count != sp->chunk_count)
+ if (chunk_count != sp->chunk_count)
LOG(derr_con<<"Connection: WARNING: chunk_count="<<chunk_count
<<" != sp->chunk_count="<<sp->chunk_count
<<std::endl);
- if(reliable != sp->reliable)
+ if (reliable != sp->reliable)
LOG(derr_con<<"Connection: WARNING: reliable="<<reliable
<<" != sp->reliable="<<sp->reliable
<<std::endl);
@@ -481,7 +500,7 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
// If chunk already exists, ignore it.
// Sometimes two identical packets may arrive when there is network
// lag and the server re-sends stuff.
- if(sp->chunks.find(chunk_num) != sp->chunks.end())
+ if (sp->chunks.find(chunk_num) != sp->chunks.end())
return SharedBuffer<u8>();
// Cut chunk data out of packet
@@ -493,7 +512,7 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable)
sp->chunks[chunk_num] = chunkdata;
// If not all chunks are received, return empty buffer
- if(sp->allReceived() == false)
+ if (sp->allReceived() == false)
return SharedBuffer<u8>();
// Calculate total size
@@ -533,10 +552,10 @@ void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout)
{
IncomingSplitPacket *p = i->second;
// Reliable ones are not removed by timeout
- if(p->reliable == true)
+ if (p->reliable == true)
continue;
p->time += dtime;
- if(p->time >= timeout)
+ if (p->time >= timeout)
remove_queue.push_back(i->first);
}
}
@@ -609,7 +628,7 @@ void Channel::setNextSplitSeqNum(u16 seqnum)
next_outgoing_split_seqnum = seqnum;
}
-u16 Channel::getOutgoingSequenceNumber(bool& successfull)
+u16 Channel::getOutgoingSequenceNumber(bool& successful)
{
JMutexAutoLock internal(m_internal_mutex);
u16 retval = next_outgoing_seqnum;
@@ -629,7 +648,7 @@ u16 Channel::getOutgoingSequenceNumber(bool& successfull)
// know about difference of two unsigned may be negative in general
// but we already made sure it won't happen in this case
if (((u16)(next_outgoing_seqnum - lowest_unacked_seqnumber)) > window_size) {
- successfull = false;
+ successful = false;
return 0;
}
}
@@ -639,7 +658,7 @@ u16 Channel::getOutgoingSequenceNumber(bool& successfull)
// but we already made sure it won't happen in this case
if ((next_outgoing_seqnum + (u16)(SEQNUM_MAX - lowest_unacked_seqnumber)) >
window_size) {
- successfull = false;
+ successful = false;
return 0;
}
}
@@ -899,7 +918,7 @@ void Peer::DecUseCount()
{
{
JMutexAutoLock lock(m_exclusive_access_mutex);
- assert(m_usage > 0);
+ sanity_check(m_usage > 0);
m_usage--;
if (!((m_pending_deletion) && (m_usage == 0)))
@@ -919,7 +938,7 @@ void Peer::RTTStatistics(float rtt, std::string profiler_id,
m_rtt.max_rtt = rtt;
/* do average calculation */
- if(m_rtt.avg_rtt < 0.0)
+ if (m_rtt.avg_rtt < 0.0)
m_rtt.avg_rtt = rtt;
else
m_rtt.avg_rtt = m_rtt.avg_rtt * (num_samples/(num_samples-1)) +
@@ -941,7 +960,7 @@ void Peer::RTTStatistics(float rtt, std::string profiler_id,
if (jitter >= m_rtt.jitter_max)
m_rtt.jitter_max = jitter;
- if(m_rtt.jitter_avg < 0.0)
+ if (m_rtt.jitter_avg < 0.0)
m_rtt.jitter_avg = jitter;
else
m_rtt.jitter_avg = m_rtt.jitter_avg * (num_samples/(num_samples-1)) +
@@ -1027,9 +1046,9 @@ void UDPPeer::reportRTT(float rtt)
RTTStatistics(rtt,"rudp",MAX_RELIABLE_WINDOW_SIZE*10);
float timeout = getStat(AVG_RTT) * RESEND_TIMEOUT_FACTOR;
- if(timeout < RESEND_TIMEOUT_MIN)
+ if (timeout < RESEND_TIMEOUT_MIN)
timeout = RESEND_TIMEOUT_MIN;
- if(timeout > RESEND_TIMEOUT_MAX)
+ if (timeout > RESEND_TIMEOUT_MAX)
timeout = RESEND_TIMEOUT_MAX;
JMutexAutoLock usage_lock(m_exclusive_access_mutex);
@@ -1039,7 +1058,7 @@ void UDPPeer::reportRTT(float rtt)
bool UDPPeer::Ping(float dtime,SharedBuffer<u8>& data)
{
m_ping_timer += dtime;
- if(m_ping_timer >= PING_TIMEOUT)
+ if (m_ping_timer >= PING_TIMEOUT)
{
// Create and send PING packet
writeU8(&data[0], TYPE_CONTROL);
@@ -1059,18 +1078,15 @@ void UDPPeer::PutReliableSendCommand(ConnectionCommand &c,
if ( channels[c.channelnum].queued_commands.empty() &&
/* don't queue more packets then window size */
(channels[c.channelnum].queued_reliables.size()
- < (channels[c.channelnum].getWindowSize()/2)))
- {
+ < (channels[c.channelnum].getWindowSize()/2))) {
LOG(dout_con<<m_connection->getDesc()
<<" processing reliable command for peer id: " << c.peer_id
<<" data size: " << c.data.getSize() << std::endl);
- if (!processReliableSendCommand(c,max_packet_size))
- {
+ if (!processReliableSendCommand(c,max_packet_size)) {
channels[c.channelnum].queued_commands.push_back(c);
}
}
- else
- {
+ else {
LOG(dout_con<<m_connection->getDesc()
<<" Queueing reliable command for peer id: " << c.peer_id
<<" data size: " << c.data.getSize() <<std::endl);
@@ -1089,7 +1105,7 @@ bool UDPPeer::processReliableSendCommand(
- BASE_HEADER_SIZE
- RELIABLE_HEADER_SIZE;
- assert(c.data.getSize() < MAX_RELIABLE_WINDOW_SIZE*512);
+ sanity_check(c.data.getSize() < MAX_RELIABLE_WINDOW_SIZE*512);
std::list<SharedBuffer<u8> > originals;
u16 split_sequence_number = channels[c.channelnum].readNextSplitSeqNum();
@@ -1105,7 +1121,7 @@ bool UDPPeer::processReliableSendCommand(
bool have_sequence_number = true;
bool have_initial_sequence_number = false;
- Queue<BufferedPacket> toadd;
+ std::queue<BufferedPacket> toadd;
volatile u16 initial_sequence_number = 0;
for(std::list<SharedBuffer<u8> >::iterator i = originals.begin();
@@ -1130,22 +1146,23 @@ bool UDPPeer::processReliableSendCommand(
m_connection->GetProtocolID(), m_connection->GetPeerID(),
c.channelnum);
- toadd.push_back(p);
+ toadd.push(p);
}
if (have_sequence_number) {
volatile u16 pcount = 0;
while(toadd.size() > 0) {
- BufferedPacket p = toadd.pop_front();
+ BufferedPacket p = toadd.front();
+ toadd.pop();
// LOG(dout_con<<connection->getDesc()
// << " queuing reliable packet for peer_id: " << c.peer_id
// << " channel: " << (c.channelnum&0xFF)
// << " seqnum: " << readU16(&p.data[BASE_HEADER_SIZE+1])
// << std::endl)
- channels[c.channelnum].queued_reliables.push_back(p);
+ channels[c.channelnum].queued_reliables.push(p);
pcount++;
}
- assert(channels[c.channelnum].queued_reliables.size() < 0xFFFF);
+ sanity_check(channels[c.channelnum].queued_reliables.size() < 0xFFFF);
return true;
}
else {
@@ -1157,13 +1174,13 @@ bool UDPPeer::processReliableSendCommand(
}
while(toadd.size() > 0) {
/* remove packet */
- toadd.pop_front();
+ toadd.pop();
bool successfully_put_back_sequence_number
= channels[c.channelnum].putBackSequenceNumber(
(initial_sequence_number+toadd.size() % (SEQNUM_MAX+1)));
- assert(successfully_put_back_sequence_number);
+ FATAL_ERROR_IF(!successfully_put_back_sequence_number, "error");
}
LOG(dout_con<<m_connection->getDesc()
<< " Windowsize exceeded on reliable sending "
@@ -1185,24 +1202,26 @@ void UDPPeer::RunCommandQueues(
unsigned int maxtransfer)
{
- for (unsigned int i = 0; i < CHANNEL_COUNT; i++)
- {
+ for (unsigned int i = 0; i < CHANNEL_COUNT; i++) {
unsigned int commands_processed = 0;
if ((channels[i].queued_commands.size() > 0) &&
(channels[i].queued_reliables.size() < maxtransfer) &&
- (commands_processed < maxcommands))
- {
+ (commands_processed < maxcommands)) {
try {
- ConnectionCommand c = channels[i].queued_commands.pop_front();
- LOG(dout_con<<m_connection->getDesc()
- <<" processing queued reliable command "<<std::endl);
- if (!processReliableSendCommand(c,max_packet_size)) {
- LOG(dout_con<<m_connection->getDesc()
+ ConnectionCommand c = channels[i].queued_commands.front();
+
+ LOG(dout_con << m_connection->getDesc()
+ << " processing queued reliable command " << std::endl);
+
+ // Packet is processed, remove it from queue
+ if (processReliableSendCommand(c,max_packet_size)) {
+ channels[i].queued_commands.pop_front();
+ } else {
+ LOG(dout_con << m_connection->getDesc()
<< " Failed to queue packets for peer_id: " << c.peer_id
<< ", delaying sending of " << c.data.getSize()
<< " bytes" << std::endl);
- channels[i].queued_commands.push_front(c);
}
}
catch (ItemNotFoundException &e) {
@@ -1214,13 +1233,13 @@ void UDPPeer::RunCommandQueues(
u16 UDPPeer::getNextSplitSequenceNumber(u8 channel)
{
- assert(channel < CHANNEL_COUNT);
+ assert(channel < CHANNEL_COUNT); // Pre-condition
return channels[channel].readNextIncomingSeqNum();
}
void UDPPeer::setNextSplitSequenceNumber(u8 channel, u16 seqnum)
{
- assert(channel < CHANNEL_COUNT);
+ assert(channel < CHANNEL_COUNT); // Pre-condition
channels[channel].setNextSplitSeqNum(seqnum);
}
@@ -1228,7 +1247,7 @@ SharedBuffer<u8> UDPPeer::addSpiltPacket(u8 channel,
BufferedPacket toadd,
bool reliable)
{
- assert(channel < CHANNEL_COUNT);
+ assert(channel < CHANNEL_COUNT); // Pre-condition
return channels[channel].incoming_splits.insert(toadd,reliable);
}
@@ -1330,12 +1349,10 @@ bool ConnectionSendThread::packetsQueued()
if (dynamic_cast<UDPPeer*>(&peer) == 0)
continue;
- for(u16 i=0; i<CHANNEL_COUNT; i++)
- {
+ for(u16 i=0; i < CHANNEL_COUNT; i++) {
Channel *channel = &(dynamic_cast<UDPPeer*>(&peer))->channels[i];
- if (channel->queued_commands.size() > 0)
- {
+ if (channel->queued_commands.size() > 0) {
return true;
}
}
@@ -1358,7 +1375,7 @@ void ConnectionSendThread::runTimeouts(float dtime)
if (!peer)
continue;
- if(dynamic_cast<UDPPeer*>(&peer) == 0)
+ if (dynamic_cast<UDPPeer*>(&peer) == 0)
continue;
PROFILE(std::stringstream peerIdentifier);
@@ -1371,7 +1388,7 @@ void ConnectionSendThread::runTimeouts(float dtime)
/*
Check peer timeout
*/
- if(peer->isTimedOut(m_timeout))
+ if (peer->isTimedOut(m_timeout))
{
infostream<<m_connection->getDesc()
<<"RunTimeouts(): Peer "<<peer->id
@@ -1477,7 +1494,7 @@ void ConnectionSendThread::rawSend(const BufferedPacket &packet)
LOG(dout_con <<m_connection->getDesc()
<< " rawSend: " << packet.data.getSize()
<< " bytes sent" << std::endl);
- } catch(SendFailedException &e){
+ } catch(SendFailedException &e) {
LOG(derr_con<<m_connection->getDesc()
<<"Connection::rawSend(): SendFailedException: "
<<packet.address.serializeString()<<std::endl);
@@ -1498,7 +1515,6 @@ void ConnectionSendThread::sendAsPacketReliable(BufferedPacket& p, Channel* chan
LOG(derr_con<<m_connection->getDesc()
<<"WARNING: Going to send a reliable packet"
<<" in outgoing buffer" <<std::endl);
- //assert(0);
}
// Send the packet
@@ -1509,16 +1525,16 @@ bool ConnectionSendThread::rawSendAsPacket(u16 peer_id, u8 channelnum,
SharedBuffer<u8> data, bool reliable)
{
PeerHelper peer = m_connection->getPeerNoEx(peer_id);
- if(!peer) {
+ if (!peer) {
LOG(dout_con<<m_connection->getDesc()
<<" INFO: dropped packet for non existent peer_id: "
<< peer_id << std::endl);
- assert(reliable && "trying to send raw packet reliable but no peer found!");
+ FATAL_ERROR_IF(!reliable, "Trying to send raw packet reliable but no peer found!");
return false;
}
Channel *channel = &(dynamic_cast<UDPPeer*>(&peer)->channels[channelnum]);
- if(reliable)
+ if (reliable)
{
bool have_sequence_number_for_raw_packet = true;
u16 seqnum =
@@ -1551,7 +1567,7 @@ bool ConnectionSendThread::rawSendAsPacket(u16 peer_id, u8 channelnum,
<<" INFO: queueing reliable packet for peer_id: " << peer_id
<<" channel: " << channelnum
<<" seqnum: " << seqnum << std::endl);
- channel->queued_reliables.push_back(p);
+ channel->queued_reliables.push(p);
return false;
}
}
@@ -1584,9 +1600,9 @@ bool ConnectionSendThread::rawSendAsPacket(u16 peer_id, u8 channelnum,
void ConnectionSendThread::processReliableCommand(ConnectionCommand &c)
{
- assert(c.reliable);
+ assert(c.reliable); // Pre-condition
- switch(c.type){
+ switch(c.type) {
case CONNCMD_NONE:
LOG(dout_con<<m_connection->getDesc()
<<"UDP processing reliable CONNCMD_NONE"<<std::endl);
@@ -1628,7 +1644,7 @@ void ConnectionSendThread::processReliableCommand(ConnectionCommand &c)
case CONNCMD_CONNECT:
case CONNCMD_DISCONNECT:
case CONCMD_ACK:
- assert("Got command that shouldn't be reliable as reliable command" == 0);
+ FATAL_ERROR("Got command that shouldn't be reliable as reliable command");
default:
LOG(dout_con<<m_connection->getDesc()
<<" Invalid reliable command type: " << c.type <<std::endl);
@@ -1638,9 +1654,9 @@ void ConnectionSendThread::processReliableCommand(ConnectionCommand &c)
void ConnectionSendThread::processNonReliableCommand(ConnectionCommand &c)
{
- assert(!c.reliable);
+ assert(!c.reliable); // Pre-condition
- switch(c.type){
+ switch(c.type) {
case CONNCMD_NONE:
LOG(dout_con<<m_connection->getDesc()
<<" UDP processing CONNCMD_NONE"<<std::endl);
@@ -1682,7 +1698,7 @@ void ConnectionSendThread::processNonReliableCommand(ConnectionCommand &c)
sendAsPacket(c.peer_id,c.channelnum,c.data,true);
return;
case CONCMD_CREATE_PEER:
- assert("Got command that should be reliable as unreliable command" == 0);
+ FATAL_ERROR("Got command that should be reliable as unreliable command");
default:
LOG(dout_con<<m_connection->getDesc()
<<" Invalid command type: " << c.type <<std::endl);
@@ -1697,7 +1713,7 @@ void ConnectionSendThread::serve(Address bind_address)
m_connection->m_udpSocket.Bind(bind_address);
m_connection->SetPeerID(PEER_ID_SERVER);
}
- catch(SocketException &e){
+ catch(SocketException &e) {
// Create event
ConnectionEvent ce;
ce.bindFailed();
@@ -1728,8 +1744,8 @@ void ConnectionSendThread::connect(Address address)
// Send a dummy packet to server with peer_id = PEER_ID_INEXISTENT
m_connection->SetPeerID(PEER_ID_INEXISTENT);
- SharedBuffer<u8> data(0);
- m_connection->Send(PEER_ID_SERVER, 0, data, true);
+ NetworkPacket pkt(0,0);
+ m_connection->Send(PEER_ID_SERVER, 0, &pkt, true);
}
void ConnectionSendThread::disconnect()
@@ -1779,10 +1795,10 @@ void ConnectionSendThread::disconnect_peer(u16 peer_id)
void ConnectionSendThread::send(u16 peer_id, u8 channelnum,
SharedBuffer<u8> data)
{
- assert(channelnum < CHANNEL_COUNT);
+ assert(channelnum < CHANNEL_COUNT); // Pre-condition
PeerHelper peer = m_connection->getPeerNoEx(peer_id);
- if(!peer)
+ if (!peer)
{
LOG(dout_con<<m_connection->getDesc()<<" peer: peer_id="<<peer_id
<< ">>>NOT<<< found on sending packet"
@@ -1919,7 +1935,8 @@ void ConnectionSendThread::sendPackets(float dtime)
< dynamic_cast<UDPPeer*>(&peer)->channels[i].getWindowSize())&&
(peer->m_increment_packets_remaining > 0))
{
- BufferedPacket p = dynamic_cast<UDPPeer*>(&peer)->channels[i].queued_reliables.pop_front();
+ BufferedPacket p = dynamic_cast<UDPPeer*>(&peer)->channels[i].queued_reliables.front();
+ dynamic_cast<UDPPeer*>(&peer)->channels[i].queued_reliables.pop();
Channel* channel = &(dynamic_cast<UDPPeer*>(&peer)->channels[i]);
LOG(dout_con<<m_connection->getDesc()
<<" INFO: sending a queued reliable packet "
@@ -1942,13 +1959,14 @@ void ConnectionSendThread::sendPackets(float dtime)
unsigned int initial_queuesize = m_outgoing_queue.size();
/* send non reliable packets*/
for(unsigned int i=0;i < initial_queuesize;i++) {
- OutgoingPacket packet = m_outgoing_queue.pop_front();
+ OutgoingPacket packet = m_outgoing_queue.front();
+ m_outgoing_queue.pop();
- assert(!packet.reliable &&
- "reliable packets are not allowed in outgoing queue!");
+ if (packet.reliable)
+ continue;
PeerHelper peer = m_connection->getPeerNoEx(packet.peer_id);
- if(!peer) {
+ if (!peer) {
LOG(dout_con<<m_connection->getDesc()
<<" Outgoing queue: peer_id="<<packet.peer_id
<< ">>>NOT<<< found on sending packet"
@@ -1966,13 +1984,13 @@ void ConnectionSendThread::sendPackets(float dtime)
}
else if (
( peer->m_increment_packets_remaining > 0) ||
- (StopRequested())){
+ (StopRequested())) {
rawSendAsPacket(packet.peer_id, packet.channelnum,
packet.data, packet.reliable);
peer->m_increment_packets_remaining--;
}
else {
- m_outgoing_queue.push_back(packet);
+ m_outgoing_queue.push(packet);
pending_unreliable[packet.peer_id] = true;
}
}
@@ -1992,7 +2010,7 @@ void ConnectionSendThread::sendAsPacket(u16 peer_id, u8 channelnum,
SharedBuffer<u8> data, bool ack)
{
OutgoingPacket packet(peer_id, channelnum, data, false, ack);
- m_outgoing_queue.push_back(packet);
+ m_outgoing_queue.push(packet);
}
ConnectionReceiveThread::ConnectionReceiveThread(unsigned int max_packet_size) :
@@ -2107,152 +2125,149 @@ void ConnectionReceiveThread::receive()
/* first of all read packets from socket */
/* check for incoming data available */
while( (loop_count < 10) &&
- (m_connection->m_udpSocket.WaitData(50)))
- {
+ (m_connection->m_udpSocket.WaitData(50))) {
loop_count++;
- try{
- if (packet_queued)
- {
- bool no_data_left = false;
- u16 peer_id;
- SharedBuffer<u8> resultdata;
- while(!no_data_left)
- {
- try {
- no_data_left = !getFromBuffers(peer_id, resultdata);
- if (!no_data_left) {
- ConnectionEvent e;
- e.dataReceived(peer_id, resultdata);
- m_connection->putEvent(e);
+ try {
+ if (packet_queued) {
+ bool data_left = true;
+ u16 peer_id;
+ SharedBuffer<u8> resultdata;
+ while(data_left) {
+ try {
+ data_left = getFromBuffers(peer_id, resultdata);
+ if (data_left) {
+ ConnectionEvent e;
+ e.dataReceived(peer_id, resultdata);
+ m_connection->putEvent(e);
+ }
+ }
+ catch(ProcessedSilentlyException &e) {
+ /* try reading again */
}
}
- catch(ProcessedSilentlyException &e) {
- /* try reading again */
- }
+ packet_queued = false;
}
- packet_queued = false;
- }
- Address sender;
- s32 received_size = m_connection->m_udpSocket.Receive(sender, *packetdata, packet_maxsize);
+ Address sender;
+ s32 received_size = m_connection->m_udpSocket.Receive(sender, *packetdata, packet_maxsize);
- if ((received_size < 0) ||
- (received_size < BASE_HEADER_SIZE) ||
- (readU32(&packetdata[0]) != m_connection->GetProtocolID()))
- {
- LOG(derr_con<<m_connection->getDesc()
- <<"Receive(): Invalid incoming packet, "
- <<"size: " << received_size
- <<", protocol: "
- << ((received_size >= 4) ? readU32(&packetdata[0]) : -1)
- << std::endl);
- continue;
- }
+ if ((received_size < BASE_HEADER_SIZE) ||
+ (readU32(&packetdata[0]) != m_connection->GetProtocolID()))
+ {
+ LOG(derr_con<<m_connection->getDesc()
+ <<"Receive(): Invalid incoming packet, "
+ <<"size: " << received_size
+ <<", protocol: "
+ << ((received_size >= 4) ? readU32(&packetdata[0]) : -1)
+ << std::endl);
+ continue;
+ }
- u16 peer_id = readPeerId(*packetdata);
- u8 channelnum = readChannel(*packetdata);
+ u16 peer_id = readPeerId(*packetdata);
+ u8 channelnum = readChannel(*packetdata);
- if(channelnum > CHANNEL_COUNT-1){
- LOG(derr_con<<m_connection->getDesc()
- <<"Receive(): Invalid channel "<<channelnum<<std::endl);
- throw InvalidIncomingDataException("Channel doesn't exist");
- }
+ if (channelnum > CHANNEL_COUNT-1) {
+ LOG(derr_con<<m_connection->getDesc()
+ <<"Receive(): Invalid channel "<<channelnum<<std::endl);
+ throw InvalidIncomingDataException("Channel doesn't exist");
+ }
- /* preserve original peer_id for later usage */
- u16 packet_peer_id = peer_id;
+ /* preserve original peer_id for later usage */
+ u16 packet_peer_id = peer_id;
- /* Try to identify peer by sender address (may happen on join) */
- if(peer_id == PEER_ID_INEXISTENT)
- {
- peer_id = m_connection->lookupPeer(sender);
- }
+ /* Try to identify peer by sender address (may happen on join) */
+ if (peer_id == PEER_ID_INEXISTENT) {
+ peer_id = m_connection->lookupPeer(sender);
+ }
- /* The peer was not found in our lists. Add it. */
- if(peer_id == PEER_ID_INEXISTENT)
- {
- peer_id = m_connection->createPeer(sender, MTP_MINETEST_RELIABLE_UDP, 0);
- }
+ /* The peer was not found in our lists. Add it. */
+ if (peer_id == PEER_ID_INEXISTENT) {
+ peer_id = m_connection->createPeer(sender, MTP_MINETEST_RELIABLE_UDP, 0);
+ }
- PeerHelper peer = m_connection->getPeerNoEx(peer_id);
+ PeerHelper peer = m_connection->getPeerNoEx(peer_id);
- if (!peer) {
- LOG(dout_con<<m_connection->getDesc()
- <<" got packet from unknown peer_id: "
- <<peer_id<<" Ignoring."<<std::endl);
- continue;
- }
+ if (!peer) {
+ LOG(dout_con<<m_connection->getDesc()
+ <<" got packet from unknown peer_id: "
+ <<peer_id<<" Ignoring."<<std::endl);
+ continue;
+ }
- // Validate peer address
+ // Validate peer address
- Address peer_address;
+ Address peer_address;
- if (peer->getAddress(MTP_UDP, peer_address)) {
- if (peer_address != sender) {
- LOG(derr_con<<m_connection->getDesc()
- <<m_connection->getDesc()
- <<" Peer "<<peer_id<<" sending from different address."
- " Ignoring."<<std::endl);
- continue;
+ if (peer->getAddress(MTP_UDP, peer_address)) {
+ if (peer_address != sender) {
+ LOG(derr_con<<m_connection->getDesc()
+ <<m_connection->getDesc()
+ <<" Peer "<<peer_id<<" sending from different address."
+ " Ignoring."<<std::endl);
+ continue;
+ }
}
- }
- else {
-
- bool invalid_address = true;
- if (invalid_address) {
- LOG(derr_con<<m_connection->getDesc()
- <<m_connection->getDesc()
- <<" Peer "<<peer_id<<" unknown."
- " Ignoring."<<std::endl);
- continue;
+ else {
+
+ bool invalid_address = true;
+ if (invalid_address) {
+ LOG(derr_con<<m_connection->getDesc()
+ <<m_connection->getDesc()
+ <<" Peer "<<peer_id<<" unknown."
+ " Ignoring."<<std::endl);
+ continue;
+ }
}
- }
- /* mark peer as seen with id */
- if (!(packet_peer_id == PEER_ID_INEXISTENT))
- peer->setSentWithID();
+ /* mark peer as seen with id */
+ if (!(packet_peer_id == PEER_ID_INEXISTENT))
+ peer->setSentWithID();
- peer->ResetTimeout();
+ peer->ResetTimeout();
- Channel *channel = 0;
+ Channel *channel = 0;
- if (dynamic_cast<UDPPeer*>(&peer) != 0)
- {
- channel = &(dynamic_cast<UDPPeer*>(&peer)->channels[channelnum]);
- }
+ if (dynamic_cast<UDPPeer*>(&peer) != 0)
+ {
+ channel = &(dynamic_cast<UDPPeer*>(&peer)->channels[channelnum]);
+ }
- if (channel != 0) {
- channel->UpdateBytesReceived(received_size);
- }
+ if (channel != 0) {
+ channel->UpdateBytesReceived(received_size);
+ }
- // Throw the received packet to channel->processPacket()
+ // Throw the received packet to channel->processPacket()
- // Make a new SharedBuffer from the data without the base headers
- SharedBuffer<u8> strippeddata(received_size - BASE_HEADER_SIZE);
- memcpy(*strippeddata, &packetdata[BASE_HEADER_SIZE],
- strippeddata.getSize());
+ // Make a new SharedBuffer from the data without the base headers
+ SharedBuffer<u8> strippeddata(received_size - BASE_HEADER_SIZE);
+ memcpy(*strippeddata, &packetdata[BASE_HEADER_SIZE],
+ strippeddata.getSize());
- try{
- // Process it (the result is some data with no headers made by us)
- SharedBuffer<u8> resultdata = processPacket
- (channel, strippeddata, peer_id, channelnum, false);
+ try{
+ // Process it (the result is some data with no headers made by us)
+ SharedBuffer<u8> resultdata = processPacket
+ (channel, strippeddata, peer_id, channelnum, false);
- LOG(dout_con<<m_connection->getDesc()
- <<" ProcessPacket from peer_id: " << peer_id
- << ",channel: " << (channelnum & 0xFF) << ", returned "
- << resultdata.getSize() << " bytes" <<std::endl);
+ LOG(dout_con<<m_connection->getDesc()
+ <<" ProcessPacket from peer_id: " << peer_id
+ << ",channel: " << (channelnum & 0xFF) << ", returned "
+ << resultdata.getSize() << " bytes" <<std::endl);
- ConnectionEvent e;
- e.dataReceived(peer_id, resultdata);
- m_connection->putEvent(e);
- }catch(ProcessedSilentlyException &e){
- }catch(ProcessedQueued &e){
- packet_queued = true;
+ ConnectionEvent e;
+ e.dataReceived(peer_id, resultdata);
+ m_connection->putEvent(e);
+ }
+ catch(ProcessedSilentlyException &e) {
+ }
+ catch(ProcessedQueued &e) {
+ packet_queued = true;
+ }
+ }
+ catch(InvalidIncomingDataException &e) {
+ }
+ catch(ProcessedSilentlyException &e) {
}
- }catch(InvalidIncomingDataException &e){
- }
- catch(ProcessedSilentlyException &e){
- }
}
}
@@ -2267,17 +2282,14 @@ bool ConnectionReceiveThread::getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst
if (!peer)
continue;
- if(dynamic_cast<UDPPeer*>(&peer) == 0)
+ if (dynamic_cast<UDPPeer*>(&peer) == 0)
continue;
for(u16 i=0; i<CHANNEL_COUNT; i++)
{
Channel *channel = &(dynamic_cast<UDPPeer*>(&peer))->channels[i];
- SharedBuffer<u8> resultdata;
- bool got = checkIncomingBuffers(channel, peer_id, resultdata);
- if(got){
- dst = resultdata;
+ if (checkIncomingBuffers(channel, peer_id, dst)) {
return true;
}
}
@@ -2291,7 +2303,7 @@ bool ConnectionReceiveThread::checkIncomingBuffers(Channel *channel,
u16 firstseqnum = 0;
if (channel->incoming_reliables.getFirstSeqnum(firstseqnum))
{
- if(firstseqnum == channel->readNextIncomingSeqNum())
+ if (firstseqnum == channel->readNextIncomingSeqNum())
{
BufferedPacket p = channel->incoming_reliables.popFirst();
peer_id = readPeerId(*p.data);
@@ -2329,29 +2341,32 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
throw ProcessedSilentlyException("Peer not found (possible timeout)");
}
- if(packetdata.getSize() < 1)
+ if (packetdata.getSize() < 1)
throw InvalidIncomingDataException("packetdata.getSize() < 1");
u8 type = readU8(&(packetdata[0]));
if (MAX_UDP_PEERS <= 65535 && peer_id >= MAX_UDP_PEERS) {
- errorstream << "Something is wrong with peer_id" << std::endl;
- assert(0);
+ std::string errmsg = "Invalid peer_id=" + itos(peer_id);
+ errorstream << errmsg << std::endl;
+ throw InvalidIncomingDataException(errmsg.c_str());
}
- if(type == TYPE_CONTROL)
+ if (type == TYPE_CONTROL)
{
- if(packetdata.getSize() < 2)
+ if (packetdata.getSize() < 2)
throw InvalidIncomingDataException("packetdata.getSize() < 2");
u8 controltype = readU8(&(packetdata[1]));
- if(controltype == CONTROLTYPE_ACK)
+ if (controltype == CONTROLTYPE_ACK)
{
- assert(channel != 0);
- if(packetdata.getSize() < 4)
- throw InvalidIncomingDataException
- ("packetdata.getSize() < 4 (ACK header size)");
+ assert(channel != NULL);
+
+ if (packetdata.getSize() < 4) {
+ throw InvalidIncomingDataException(
+ "packetdata.getSize() < 4 (ACK header size)");
+ }
u16 seqnum = readU16(&packetdata[2]);
LOG(dout_con<<m_connection->getDesc()
@@ -2394,7 +2409,7 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
m_connection->TriggerSend();
}
}
- catch(NotFoundException &e){
+ catch(NotFoundException &e) {
LOG(derr_con<<m_connection->getDesc()
<<"WARNING: ACKed packet not "
"in outgoing queue"
@@ -2403,17 +2418,16 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
}
throw ProcessedSilentlyException("Got an ACK");
}
- else if(controltype == CONTROLTYPE_SET_PEER_ID)
- {
+ else if (controltype == CONTROLTYPE_SET_PEER_ID) {
// Got a packet to set our peer id
- if(packetdata.getSize() < 4)
+ if (packetdata.getSize() < 4)
throw InvalidIncomingDataException
("packetdata.getSize() < 4 (SET_PEER_ID header size)");
u16 peer_id_new = readU16(&packetdata[2]);
LOG(dout_con<<m_connection->getDesc()
<<"Got new peer id: "<<peer_id_new<<"... "<<std::endl);
- if(m_connection->GetPeerID() != PEER_ID_INEXISTENT)
+ if (m_connection->GetPeerID() != PEER_ID_INEXISTENT)
{
LOG(derr_con<<m_connection->getDesc()
<<"WARNING: Not changing"
@@ -2435,21 +2449,21 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
throw ProcessedSilentlyException("Got a SET_PEER_ID");
}
- else if(controltype == CONTROLTYPE_PING)
+ else if (controltype == CONTROLTYPE_PING)
{
// Just ignore it, the incoming data already reset
// the timeout counter
LOG(dout_con<<m_connection->getDesc()<<"PING"<<std::endl);
throw ProcessedSilentlyException("Got a PING");
}
- else if(controltype == CONTROLTYPE_DISCO)
+ else if (controltype == CONTROLTYPE_DISCO)
{
// Just ignore it, the incoming data already reset
// the timeout counter
LOG(dout_con<<m_connection->getDesc()
<<"DISCO: Removing peer "<<(peer_id)<<std::endl);
- if(m_connection->deletePeer(peer_id, false) == false)
+ if (m_connection->deletePeer(peer_id, false) == false)
{
derr_con<<m_connection->getDesc()
<<"DISCO: Peer not found"<<std::endl;
@@ -2457,7 +2471,7 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
throw ProcessedSilentlyException("Got a DISCO");
}
- else if(controltype == CONTROLTYPE_ENABLE_BIG_SEND_WINDOW)
+ else if (controltype == CONTROLTYPE_ENABLE_BIG_SEND_WINDOW)
{
dynamic_cast<UDPPeer*>(&peer)->setNonLegacyPeer();
throw ProcessedSilentlyException("Got non legacy control");
@@ -2469,9 +2483,9 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
throw InvalidIncomingDataException("Invalid control type");
}
}
- else if(type == TYPE_ORIGINAL)
+ else if (type == TYPE_ORIGINAL)
{
- if(packetdata.getSize() <= ORIGINAL_HEADER_SIZE)
+ if (packetdata.getSize() <= ORIGINAL_HEADER_SIZE)
throw InvalidIncomingDataException
("packetdata.getSize() <= ORIGINAL_HEADER_SIZE");
LOG(dout_con<<m_connection->getDesc()
@@ -2482,7 +2496,7 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
memcpy(*payload, &(packetdata[ORIGINAL_HEADER_SIZE]), payload.getSize());
return payload;
}
- else if(type == TYPE_SPLIT)
+ else if (type == TYPE_SPLIT)
{
Address peer_address;
@@ -2501,7 +2515,7 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
SharedBuffer<u8> data =
peer->addSpiltPacket(channelnum,packet,reliable);
- if(data.getSize() != 0)
+ if (data.getSize() != 0)
{
LOG(dout_con<<m_connection->getDesc()
<<"RETURNING TYPE_SPLIT: Constructed full data, "
@@ -2515,14 +2529,15 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
//TODO throw some error
}
}
- else if(type == TYPE_RELIABLE)
+ else if (type == TYPE_RELIABLE)
{
- assert(channel != 0);
+ assert(channel != NULL);
+
// Recursive reliable packets not allowed
- if(reliable)
+ if (reliable)
throw InvalidIncomingDataException("Found nested reliable packets");
- if(packetdata.getSize() < RELIABLE_HEADER_SIZE)
+ if (packetdata.getSize() < RELIABLE_HEADER_SIZE)
throw InvalidIncomingDataException
("packetdata.getSize() < RELIABLE_HEADER_SIZE");
@@ -2641,10 +2656,7 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
}
// We should never get here.
- // If you get here, add an exception or a return to some of the
- // above conditionals.
- assert(0);
- throw BaseException("Error in Channel::ProcessPacket()");
+ FATAL_ERROR("Invalid execution point");
}
/*
@@ -2652,30 +2664,6 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
*/
Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout,
- bool ipv6) :
- m_udpSocket(ipv6),
- m_command_queue(),
- m_event_queue(),
- m_peer_id(0),
- m_protocol_id(protocol_id),
- m_sendThread(max_packet_size, timeout),
- m_receiveThread(max_packet_size),
- m_info_mutex(),
- m_bc_peerhandler(0),
- m_bc_receive_timeout(0),
- m_shutting_down(false),
- m_next_remote_peer_id(2)
-{
- m_udpSocket.setTimeoutMs(5);
-
- m_sendThread.setParent(this);
- m_receiveThread.setParent(this);
-
- m_sendThread.Start();
- m_receiveThread.Start();
-}
-
-Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout,
bool ipv6, PeerHandler *peerhandler) :
m_udpSocket(ipv6),
m_command_queue(),
@@ -2730,7 +2718,7 @@ Connection::~Connection()
/* Internal stuff */
void Connection::putEvent(ConnectionEvent &e)
{
- assert(e.type != CONNEVENT_NONE);
+ assert(e.type != CONNEVENT_NONE); // Pre-condition
m_event_queue.push_back(e);
}
@@ -2739,12 +2727,12 @@ PeerHelper Connection::getPeer(u16 peer_id)
JMutexAutoLock peerlock(m_peers_mutex);
std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
- if(node == m_peers.end()){
+ if (node == m_peers.end()) {
throw PeerNotFoundException("GetPeer: Peer not found (possible timeout)");
}
// Error checking
- assert(node->second->id == peer_id);
+ FATAL_ERROR_IF(node->second->id != peer_id, "Invalid peer id");
return PeerHelper(node->second);
}
@@ -2754,12 +2742,12 @@ PeerHelper Connection::getPeerNoEx(u16 peer_id)
JMutexAutoLock peerlock(m_peers_mutex);
std::map<u16, Peer*>::iterator node = m_peers.find(peer_id);
- if(node == m_peers.end()){
+ if (node == m_peers.end()) {
return PeerHelper(NULL);
}
// Error checking
- assert(node->second->id == peer_id);
+ FATAL_ERROR_IF(node->second->id != peer_id, "Invalid peer id");
return PeerHelper(node->second);
}
@@ -2773,7 +2761,7 @@ u16 Connection::lookupPeer(Address& sender)
for(; j != m_peers.end(); ++j)
{
Peer *peer = j->second;
- if(peer->isActive())
+ if (peer->isActive())
continue;
Address tocheck;
@@ -2807,10 +2795,11 @@ bool Connection::deletePeer(u16 peer_id, bool timeout)
/* lock list as short as possible */
{
JMutexAutoLock peerlock(m_peers_mutex);
- if(m_peers.find(peer_id) == m_peers.end())
+ if (m_peers.find(peer_id) == m_peers.end())
return false;
peer = m_peers[peer_id];
m_peers.erase(peer_id);
+ m_peer_ids.remove(peer_id);
}
Address peer_address;
@@ -2828,21 +2817,11 @@ bool Connection::deletePeer(u16 peer_id, bool timeout)
/* Interface */
-ConnectionEvent Connection::getEvent()
-{
- if(m_event_queue.empty()){
- ConnectionEvent e;
- e.type = CONNEVENT_NONE;
- return e;
- }
- return m_event_queue.pop_frontNoEx();
-}
-
ConnectionEvent Connection::waitEvent(u32 timeout_ms)
{
- try{
+ try {
return m_event_queue.pop_front(timeout_ms);
- } catch(ItemNotFoundException &ex){
+ } catch(ItemNotFoundException &ex) {
ConnectionEvent e;
e.type = CONNEVENT_NONE;
return e;
@@ -2851,8 +2830,7 @@ ConnectionEvent Connection::waitEvent(u32 timeout_ms)
void Connection::putCommand(ConnectionCommand &c)
{
- if (!m_shutting_down)
- {
+ if (!m_shutting_down) {
m_command_queue.push_back(c);
m_sendThread.Trigger();
}
@@ -2876,14 +2854,14 @@ bool Connection::Connected()
{
JMutexAutoLock peerlock(m_peers_mutex);
- if(m_peers.size() != 1)
+ if (m_peers.size() != 1)
return false;
std::map<u16, Peer*>::iterator node = m_peers.find(PEER_ID_SERVER);
- if(node == m_peers.end())
+ if (node == m_peers.end())
return false;
- if(m_peer_id == PEER_ID_INEXISTENT)
+ if (m_peer_id == PEER_ID_INEXISTENT)
return false;
return true;
@@ -2896,30 +2874,36 @@ void Connection::Disconnect()
putCommand(c);
}
-u32 Connection::Receive(u16 &peer_id, SharedBuffer<u8> &data)
+void Connection::Receive(NetworkPacket* pkt)
{
- for(;;){
+ for(;;) {
ConnectionEvent e = waitEvent(m_bc_receive_timeout);
- if(e.type != CONNEVENT_NONE)
- LOG(dout_con<<getDesc()<<": Receive: got event: "
- <<e.describe()<<std::endl);
- switch(e.type){
+ if (e.type != CONNEVENT_NONE)
+ LOG(dout_con << getDesc() << ": Receive: got event: "
+ << e.describe() << std::endl);
+ switch(e.type) {
case CONNEVENT_NONE:
throw NoIncomingDataException("No incoming data");
case CONNEVENT_DATA_RECEIVED:
- peer_id = e.peer_id;
- data = SharedBuffer<u8>(e.data);
- return e.data.getSize();
+ // Data size is lesser than command size, ignoring packet
+ if (e.data.getSize() < 2) {
+ continue;
+ }
+
+ pkt->putRawPacket(*e.data, e.data.getSize(), e.peer_id);
+ return;
case CONNEVENT_PEER_ADDED: {
UDPPeer tmp(e.peer_id, e.address, this);
- if(m_bc_peerhandler)
+ if (m_bc_peerhandler)
m_bc_peerhandler->peerAdded(&tmp);
- continue; }
+ continue;
+ }
case CONNEVENT_PEER_REMOVED: {
UDPPeer tmp(e.peer_id, e.address, this);
- if(m_bc_peerhandler)
+ if (m_bc_peerhandler)
m_bc_peerhandler->deletingPeer(&tmp, e.timeout);
- continue; }
+ continue;
+ }
case CONNEVENT_BIND_FAILED:
throw ConnectionBindFailed("Failed to bind socket "
"(port already in use?)");
@@ -2928,22 +2912,14 @@ u32 Connection::Receive(u16 &peer_id, SharedBuffer<u8> &data)
throw NoIncomingDataException("No incoming data");
}
-void Connection::SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable)
-{
- assert(channelnum < CHANNEL_COUNT);
-
- ConnectionCommand c;
- c.sendToAll(channelnum, data, reliable);
- putCommand(c);
-}
-
void Connection::Send(u16 peer_id, u8 channelnum,
- SharedBuffer<u8> data, bool reliable)
+ NetworkPacket* pkt, bool reliable)
{
- assert(channelnum < CHANNEL_COUNT);
+ assert(channelnum < CHANNEL_COUNT); // Pre-condition
ConnectionCommand c;
- c.send(peer_id, channelnum, data, reliable);
+
+ c.send(peer_id, channelnum, pkt, reliable);
putCommand(c);
}
@@ -2969,9 +2945,7 @@ float Connection::getLocalStat(rate_stat_type type)
{
PeerHelper peer = getPeerNoEx(PEER_ID_SERVER);
- if (!peer) {
- assert("Connection::getLocalStat we couldn't get our own peer? are you serious???" == 0);
- }
+ FATAL_ERROR_IF(!peer, "Connection::getLocalStat we couldn't get our own peer? are you serious???");
float retval = 0.0;
@@ -2996,7 +2970,7 @@ float Connection::getLocalStat(rate_stat_type type)
retval += dynamic_cast<UDPPeer*>(&peer)->channels[j].getCurrentLossRateKB();
break;
default:
- assert("Connection::getLocalStat Invalid stat type" == 0);
+ FATAL_ERROR("Connection::getLocalStat Invalid stat type");
}
}
return retval;
@@ -3013,37 +2987,37 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
/*
Find an unused peer id
*/
- {
JMutexAutoLock lock(m_peers_mutex);
- bool out_of_ids = false;
- for(;;)
- {
- // Check if exists
- if(m_peers.find(peer_id_new) == m_peers.end())
- break;
- // Check for overflow
- if(peer_id_new == overflow){
- out_of_ids = true;
- break;
- }
- peer_id_new++;
- }
- if(out_of_ids){
- errorstream<<getDesc()<<" ran out of peer ids"<<std::endl;
- return PEER_ID_INEXISTENT;
- }
+ bool out_of_ids = false;
+ for(;;) {
+ // Check if exists
+ if (m_peers.find(peer_id_new) == m_peers.end())
- // Create a peer
- Peer *peer = 0;
- peer = new UDPPeer(peer_id_new, sender, this);
+ break;
+ // Check for overflow
+ if (peer_id_new == overflow) {
+ out_of_ids = true;
+ break;
+ }
+ peer_id_new++;
+ }
- m_peers[peer->id] = peer;
+ if (out_of_ids) {
+ errorstream << getDesc() << " ran out of peer ids" << std::endl;
+ return PEER_ID_INEXISTENT;
}
- m_next_remote_peer_id = (peer_id_new +1) % MAX_UDP_PEERS;
+ // Create a peer
+ Peer *peer = 0;
+ peer = new UDPPeer(peer_id_new, sender, this);
- LOG(dout_con<<getDesc()
- <<"createPeer(): giving peer_id="<<peer_id_new<<std::endl);
+ m_peers[peer->id] = peer;
+ m_peer_ids.push_back(peer->id);
+
+ m_next_remote_peer_id = (peer_id_new +1 ) % MAX_UDP_PEERS;
+
+ LOG(dout_con << getDesc()
+ << "createPeer(): giving peer_id=" << peer_id_new << std::endl);
ConnectionCommand cmd;
SharedBuffer<u8> reply(4);
@@ -3051,7 +3025,7 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
writeU8(&reply[1], CONTROLTYPE_SET_PEER_ID);
writeU16(&reply[2], peer_id_new);
cmd.createPeer(peer_id_new,reply);
- this->putCommand(cmd);
+ putCommand(cmd);
// Create peer addition event
ConnectionEvent e;
@@ -3089,7 +3063,7 @@ void Connection::DisconnectPeer(u16 peer_id)
void Connection::sendAck(u16 peer_id, u8 channelnum, u16 seqnum)
{
- assert(channelnum < CHANNEL_COUNT);
+ assert(channelnum < CHANNEL_COUNT); // Pre-condition
LOG(dout_con<<getDesc()
<<" Queuing ACK command to peer_id: " << peer_id <<
@@ -3119,22 +3093,10 @@ UDPPeer* Connection::createServerPeer(Address& address)
{
JMutexAutoLock lock(m_peers_mutex);
m_peers[peer->id] = peer;
+ m_peer_ids.push_back(peer->id);
}
return peer;
}
-std::list<u16> Connection::getPeerIDs()
-{
- std::list<u16> retval;
-
- JMutexAutoLock lock(m_peers_mutex);
- for(std::map<u16, Peer*>::iterator j = m_peers.begin();
- j != m_peers.end(); ++j)
- {
- retval.push_back(j->first);
- }
- return retval;
-}
-
} // namespace
diff --git a/src/connection.h b/src/network/connection.h
index be1627dfa..15ea7e20f 100644
--- a/src/connection.h
+++ b/src/network/connection.h
@@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "socket.h"
#include "exceptions.h"
#include "constants.h"
+#include "network/networkpacket.h"
#include "util/pointer.h"
#include "util/container.h"
#include "util/thread.h"
@@ -33,6 +34,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <list>
#include <map>
+class NetworkPacket;
+
namespace con
{
@@ -130,14 +133,14 @@ inline bool seqnum_higher(u16 totest, u16 base)
{
if (totest > base)
{
- if((totest - base) > (SEQNUM_MAX/2))
+ if ((totest - base) > (SEQNUM_MAX/2))
return false;
else
return true;
}
else
{
- if((base - totest) > (SEQNUM_MAX/2))
+ if ((base - totest) > (SEQNUM_MAX/2))
return true;
else
return false;
@@ -169,7 +172,7 @@ struct BufferedPacket
data(a_size), time(0.0), totaltime(0.0), absolute_send_time(-1),
resend_count(0)
{}
- SharedBuffer<u8> data; // Data of the packet, including headers
+ Buffer<u8> data; // Data of the packet, including headers
float time; // Seconds from buffering the packet or re-sending
float totaltime; // Seconds from buffering the packet
unsigned int absolute_send_time;
@@ -362,9 +365,9 @@ public:
packet is constructed. If not, returns one of length 0.
*/
SharedBuffer<u8> insert(BufferedPacket &p, bool reliable);
-
+
void removeUnreliableTimedOuts(float dtime, float timeout);
-
+
private:
// Key is seqnum
std::map<u16, IncomingSplitPacket*> m_buf;
@@ -436,19 +439,12 @@ struct ConnectionCommand
peer_id = peer_id_;
}
void send(u16 peer_id_, u8 channelnum_,
- SharedBuffer<u8> data_, bool reliable_)
+ NetworkPacket* pkt, bool reliable_)
{
type = CONNCMD_SEND;
peer_id = peer_id_;
channelnum = channelnum_;
- data = data_;
- reliable = reliable_;
- }
- void sendToAll(u8 channelnum_, SharedBuffer<u8> data_, bool reliable_)
- {
- type = CONNCMD_SEND_TO_ALL;
- channelnum = channelnum_;
- data = data_;
+ data = pkt->oldForgePacket();
reliable = reliable_;
}
@@ -495,7 +491,7 @@ public:
u16 readNextSplitSeqNum();
void setNextSplitSeqNum(u16 seqnum);
-
+
// This is for buffering the incoming packets that are coming in
// the wrong order
ReliablePacketBuffer incoming_reliables;
@@ -504,10 +500,10 @@ public:
ReliablePacketBuffer outgoing_reliables_sent;
//queued reliable packets
- Queue<BufferedPacket> queued_reliables;
+ std::queue<BufferedPacket> queued_reliables;
//queue commands prior splitting to packets
- Queue<ConnectionCommand> queued_commands;
+ std::deque<ConnectionCommand> queued_commands;
IncomingSplitBuffer incoming_splits;
@@ -680,7 +676,7 @@ class Peer {
virtual ~Peer() {
JMutexAutoLock usage_lock(m_exclusive_access_mutex);
- assert(m_usage == 0);
+ FATAL_ERROR_IF(m_usage != 0, "Reference counting failure");
};
// Unique id of the peer
@@ -869,11 +865,12 @@ struct ConnectionEvent
bool timeout;
Address address;
- ConnectionEvent(): type(CONNEVENT_NONE) {}
+ ConnectionEvent(): type(CONNEVENT_NONE), peer_id(0),
+ timeout(false) {}
std::string describe()
{
- switch(type){
+ switch(type) {
case CONNEVENT_NONE:
return "CONNEVENT_NONE";
case CONNEVENT_DATA_RECEIVED:
@@ -887,7 +884,7 @@ struct ConnectionEvent
}
return "Invalid ConnectionEvent";
}
-
+
void dataReceived(u16 peer_id_, SharedBuffer<u8> data_)
{
type = CONNEVENT_DATA_RECEIVED;
@@ -925,7 +922,7 @@ public:
void Trigger();
void setParent(Connection* parent) {
- assert(parent != NULL);
+ assert(parent != NULL); // Pre-condition
m_connection = parent;
}
@@ -963,7 +960,7 @@ private:
Connection* m_connection;
unsigned int m_max_packet_size;
float m_timeout;
- Queue<OutgoingPacket> m_outgoing_queue;
+ std::queue<OutgoingPacket> m_outgoing_queue;
JSemaphore m_send_sleep_semaphore;
unsigned int m_iteration_packets_avaialble;
@@ -979,7 +976,7 @@ public:
void * Thread ();
void setParent(Connection* parent) {
- assert(parent != NULL);
+ assert(parent != NULL); // Pre-condition
m_connection = parent;
}
@@ -1016,25 +1013,22 @@ public:
friend class ConnectionSendThread;
friend class ConnectionReceiveThread;
- Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6);
Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6,
PeerHandler *peerhandler);
~Connection();
/* Interface */
- ConnectionEvent getEvent();
ConnectionEvent waitEvent(u32 timeout_ms);
void putCommand(ConnectionCommand &c);
-
- void SetTimeoutMs(int timeout){ m_bc_receive_timeout = timeout; }
+
+ void SetTimeoutMs(int timeout) { m_bc_receive_timeout = timeout; }
void Serve(Address bind_addr);
void Connect(Address address);
bool Connected();
void Disconnect();
- u32 Receive(u16 &peer_id, SharedBuffer<u8> &data);
- void SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable);
- void Send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable);
- u16 GetPeerID(){ return m_peer_id; }
+ void Receive(NetworkPacket* pkt);
+ void Send(u16 peer_id, u8 channelnum, NetworkPacket* pkt, bool reliable);
+ u16 GetPeerID() { return m_peer_id; }
Address GetPeerAddress(u16 peer_id);
float getPeerStat(u16 peer_id, rtt_stat_type type);
float getLocalStat(rate_stat_type type);
@@ -1051,14 +1045,18 @@ protected:
UDPPeer* createServerPeer(Address& sender);
bool deletePeer(u16 peer_id, bool timeout);
- void SetPeerID(u16 id){ m_peer_id = id; }
+ void SetPeerID(u16 id) { m_peer_id = id; }
void sendAck(u16 peer_id, u8 channelnum, u16 seqnum);
void PrintInfo(std::ostream &out);
void PrintInfo();
- std::list<u16> getPeerIDs();
+ std::list<u16> getPeerIDs()
+ {
+ JMutexAutoLock peerlock(m_peers_mutex);
+ return m_peer_ids;
+ }
UDPSocket m_udpSocket;
MutexedQueue<ConnectionCommand> m_command_queue;
@@ -1074,8 +1072,9 @@ private:
u16 m_peer_id;
u32 m_protocol_id;
-
+
std::map<u16, Peer*> m_peers;
+ std::list<u16> m_peer_ids;
JMutex m_peers_mutex;
ConnectionSendThread m_sendThread;
@@ -1095,4 +1094,3 @@ private:
} // namespace
#endif
-
diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp
new file mode 100644
index 000000000..b5e451cdb
--- /dev/null
+++ b/src/network/networkpacket.cpp
@@ -0,0 +1,520 @@
+/*
+Minetest
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#include "networkpacket.h"
+#include "debug.h"
+#include "exceptions.h"
+#include "util/serialize.h"
+
+NetworkPacket::NetworkPacket(u16 command, u32 datasize, u16 peer_id):
+m_datasize(datasize), m_read_offset(0), m_command(command), m_peer_id(peer_id)
+{
+ m_data.resize(m_datasize);
+}
+
+NetworkPacket::NetworkPacket(u16 command, u32 datasize):
+m_datasize(datasize), m_read_offset(0), m_command(command), m_peer_id(0)
+{
+ m_data.resize(m_datasize);
+}
+
+NetworkPacket::~NetworkPacket()
+{
+ m_data.clear();
+}
+
+void NetworkPacket::checkReadOffset(u32 from_offset, u32 field_size)
+{
+ if (from_offset + field_size > m_datasize) {
+ std::stringstream ss;
+ ss << "Reading outside packet (offset: " <<
+ from_offset << ", packet size: " << getSize() << ")";
+ throw PacketError(ss.str());
+ }
+}
+
+void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id)
+{
+ // If a m_command is already set, we are rewriting on same packet
+ // This is not permitted
+ assert(m_command == 0);
+
+ m_datasize = datasize - 2;
+ m_peer_id = peer_id;
+
+ // split command and datas
+ m_command = readU16(&data[0]);
+ m_data = std::vector<u8>(&data[2], &data[2 + m_datasize]);
+}
+
+char* NetworkPacket::getString(u32 from_offset)
+{
+ checkReadOffset(from_offset, 0);
+
+ return (char*)&m_data[from_offset];
+}
+
+void NetworkPacket::putRawString(const char* src, u32 len)
+{
+ if (m_read_offset + len > m_datasize) {
+ m_datasize = m_read_offset + len;
+ m_data.resize(m_datasize);
+ }
+
+ memcpy(&m_data[m_read_offset], src, len);
+ m_read_offset += len;
+}
+
+NetworkPacket& NetworkPacket::operator>>(std::string& dst)
+{
+ checkReadOffset(m_read_offset, 2);
+ u16 strLen = readU16(&m_data[m_read_offset]);
+ m_read_offset += 2;
+
+ dst.clear();
+
+ if (strLen == 0) {
+ return *this;
+ }
+
+ checkReadOffset(m_read_offset, strLen);
+
+ dst.reserve(strLen);
+ dst.append((char*)&m_data[m_read_offset], strLen);
+
+ m_read_offset += strLen;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(std::string src)
+{
+ u16 msgsize = src.size();
+ if (msgsize > STRING_MAX_LEN) {
+ throw PacketError("String too long");
+ }
+
+ *this << msgsize;
+
+ putRawString(src.c_str(), (u32)msgsize);
+
+ return *this;
+}
+
+void NetworkPacket::putLongString(std::string src)
+{
+ u32 msgsize = src.size();
+ if (msgsize > LONG_STRING_MAX_LEN) {
+ throw PacketError("String too long");
+ }
+
+ *this << msgsize;
+
+ putRawString(src.c_str(), msgsize);
+}
+
+NetworkPacket& NetworkPacket::operator>>(std::wstring& dst)
+{
+ checkReadOffset(m_read_offset, 2);
+ u16 strLen = readU16(&m_data[m_read_offset]);
+ m_read_offset += 2;
+
+ dst.clear();
+
+ if (strLen == 0) {
+ return *this;
+ }
+
+ checkReadOffset(m_read_offset, strLen * 2);
+
+ dst.reserve(strLen);
+ for(u16 i=0; i<strLen; i++) {
+ wchar_t c16 = readU16(&m_data[m_read_offset]);
+ dst.append(&c16, 1);
+ m_read_offset += sizeof(u16);
+ }
+
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(std::wstring src)
+{
+ u16 msgsize = src.size();
+ if (msgsize > WIDE_STRING_MAX_LEN) {
+ throw PacketError("String too long");
+ }
+
+ *this << msgsize;
+
+ // Write string
+ for (u16 i=0; i<msgsize; i++) {
+ *this << (u16) src[i];
+ }
+
+ return *this;
+}
+
+std::string NetworkPacket::readLongString()
+{
+ checkReadOffset(m_read_offset, 4);
+ u32 strLen = readU32(&m_data[m_read_offset]);
+ m_read_offset += 4;
+
+ if (strLen == 0) {
+ return "";
+ }
+
+ if (strLen > LONG_STRING_MAX_LEN) {
+ throw PacketError("String too long");
+ }
+
+ checkReadOffset(m_read_offset, strLen);
+
+ std::string dst;
+
+ dst.reserve(strLen);
+ dst.append((char*)&m_data[m_read_offset], strLen);
+
+ m_read_offset += strLen;
+
+ return dst;
+}
+
+NetworkPacket& NetworkPacket::operator>>(char& dst)
+{
+ checkReadOffset(m_read_offset, 1);
+
+ dst = readU8(&m_data[m_read_offset]);
+
+ m_read_offset += 1;
+ return *this;
+}
+
+char NetworkPacket::getChar(u32 offset)
+{
+ checkReadOffset(offset, 1);
+
+ return readU8(&m_data[offset]);
+}
+
+NetworkPacket& NetworkPacket::operator<<(char src)
+{
+ checkDataSize(1);
+
+ writeU8(&m_data[m_read_offset], src);
+
+ m_read_offset += 1;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(u8 src)
+{
+ checkDataSize(1);
+
+ writeU8(&m_data[m_read_offset], src);
+
+ m_read_offset += 1;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(bool src)
+{
+ checkDataSize(1);
+
+ writeU8(&m_data[m_read_offset], src);
+
+ m_read_offset += 1;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(u16 src)
+{
+ checkDataSize(2);
+
+ writeU16(&m_data[m_read_offset], src);
+
+ m_read_offset += 2;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(u32 src)
+{
+ checkDataSize(4);
+
+ writeU32(&m_data[m_read_offset], src);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(u64 src)
+{
+ checkDataSize(8);
+
+ writeU64(&m_data[m_read_offset], src);
+
+ m_read_offset += 8;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(float src)
+{
+ checkDataSize(4);
+
+ writeF1000(&m_data[m_read_offset], src);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(bool& dst)
+{
+ checkReadOffset(m_read_offset, 1);
+
+ dst = readU8(&m_data[m_read_offset]);
+
+ m_read_offset += 1;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(u8& dst)
+{
+ checkReadOffset(m_read_offset, 1);
+
+ dst = readU8(&m_data[m_read_offset]);
+
+ m_read_offset += 1;
+ return *this;
+}
+
+u8 NetworkPacket::getU8(u32 offset)
+{
+ checkReadOffset(offset, 1);
+
+ return readU8(&m_data[offset]);
+}
+
+u8* NetworkPacket::getU8Ptr(u32 from_offset)
+{
+ if (m_datasize == 0) {
+ return NULL;
+ }
+
+ checkReadOffset(from_offset, 1);
+
+ return (u8*)&m_data[from_offset];
+}
+
+NetworkPacket& NetworkPacket::operator>>(u16& dst)
+{
+ checkReadOffset(m_read_offset, 2);
+
+ dst = readU16(&m_data[m_read_offset]);
+
+ m_read_offset += 2;
+ return *this;
+}
+
+u16 NetworkPacket::getU16(u32 from_offset)
+{
+ checkReadOffset(from_offset, 2);
+
+ return readU16(&m_data[from_offset]);
+}
+
+NetworkPacket& NetworkPacket::operator>>(u32& dst)
+{
+ checkReadOffset(m_read_offset, 4);
+
+ dst = readU32(&m_data[m_read_offset]);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(u64& dst)
+{
+ checkReadOffset(m_read_offset, 8);
+
+ dst = readU64(&m_data[m_read_offset]);
+
+ m_read_offset += 8;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(float& dst)
+{
+ checkReadOffset(m_read_offset, 4);
+
+ dst = readF1000(&m_data[m_read_offset]);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v2f& dst)
+{
+ checkReadOffset(m_read_offset, 8);
+
+ dst = readV2F1000(&m_data[m_read_offset]);
+
+ m_read_offset += 8;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v3f& dst)
+{
+ checkReadOffset(m_read_offset, 12);
+
+ dst = readV3F1000(&m_data[m_read_offset]);
+
+ m_read_offset += 12;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(s16& dst)
+{
+ checkReadOffset(m_read_offset, 2);
+
+ dst = readS16(&m_data[m_read_offset]);
+
+ m_read_offset += 2;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(s16 src)
+{
+ *this << (u16) src;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(s32& dst)
+{
+ checkReadOffset(m_read_offset, 4);
+
+ dst = readS32(&m_data[m_read_offset]);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(s32 src)
+{
+ *this << (u32) src;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v3s16& dst)
+{
+ checkReadOffset(m_read_offset, 6);
+
+ dst = readV3S16(&m_data[m_read_offset]);
+
+ m_read_offset += 6;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v2s32& dst)
+{
+ checkReadOffset(m_read_offset, 8);
+
+ dst = readV2S32(&m_data[m_read_offset]);
+
+ m_read_offset += 8;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(v3s32& dst)
+{
+ checkReadOffset(m_read_offset, 12);
+
+ dst = readV3S32(&m_data[m_read_offset]);
+
+ m_read_offset += 12;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(v2f src)
+{
+ *this << (float) src.X;
+ *this << (float) src.Y;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(v3f src)
+{
+ *this << (float) src.X;
+ *this << (float) src.Y;
+ *this << (float) src.Z;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(v3s16 src)
+{
+ *this << (s16) src.X;
+ *this << (s16) src.Y;
+ *this << (s16) src.Z;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(v2s32 src)
+{
+ *this << (s32) src.X;
+ *this << (s32) src.Y;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(v3s32 src)
+{
+ *this << (s32) src.X;
+ *this << (s32) src.Y;
+ *this << (s32) src.Z;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator>>(video::SColor& dst)
+{
+ checkReadOffset(m_read_offset, 4);
+
+ dst = readARGB8(&m_data[m_read_offset]);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+NetworkPacket& NetworkPacket::operator<<(video::SColor src)
+{
+ checkDataSize(4);
+
+ writeU32(&m_data[m_read_offset], src.color);
+
+ m_read_offset += 4;
+ return *this;
+}
+
+Buffer<u8> NetworkPacket::oldForgePacket()
+{
+ Buffer<u8> sb(m_datasize + 2);
+ writeU16(&sb[0], m_command);
+
+ u8* datas = getU8Ptr(0);
+
+ if (datas != NULL)
+ memcpy(&sb[2], datas, m_datasize);
+ return sb;
+}
diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h
new file mode 100644
index 000000000..72f8cabe2
--- /dev/null
+++ b/src/network/networkpacket.h
@@ -0,0 +1,131 @@
+/*
+Minetest
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#ifndef NETWORKPACKET_HEADER
+#define NETWORKPACKET_HEADER
+
+#include "util/pointer.h"
+#include "util/numeric.h"
+#include "networkprotocol.h"
+
+class NetworkPacket
+{
+
+public:
+ NetworkPacket(u16 command, u32 datasize, u16 peer_id);
+ NetworkPacket(u16 command, u32 datasize);
+ NetworkPacket(): m_datasize(0), m_read_offset(0), m_command(0),
+ m_peer_id(0) {}
+ ~NetworkPacket();
+
+ void putRawPacket(u8 *data, u32 datasize, u16 peer_id);
+
+ // Getters
+ u32 getSize() { return m_datasize; }
+ u16 getPeerId() { return m_peer_id; }
+ u16 getCommand() { return m_command; }
+
+ // Returns a c-string without copying.
+ // A better name for this would be getRawString()
+ char* getString(u32 from_offset);
+ // major difference to putCString(): doesn't write len into the buffer
+ void putRawString(const char* src, u32 len);
+
+ NetworkPacket& operator>>(std::string& dst);
+ NetworkPacket& operator<<(std::string src);
+
+ void putLongString(std::string src);
+
+ NetworkPacket& operator>>(std::wstring& dst);
+ NetworkPacket& operator<<(std::wstring src);
+
+ std::string readLongString();
+
+ char getChar(u32 offset);
+ NetworkPacket& operator>>(char& dst);
+ NetworkPacket& operator<<(char src);
+
+ NetworkPacket& operator>>(bool& dst);
+ NetworkPacket& operator<<(bool src);
+
+ u8 getU8(u32 offset);
+
+ NetworkPacket& operator>>(u8& dst);
+ NetworkPacket& operator<<(u8 src);
+
+ u8* getU8Ptr(u32 offset);
+
+ u16 getU16(u32 from_offset);
+ NetworkPacket& operator>>(u16& dst);
+ NetworkPacket& operator<<(u16 src);
+
+ NetworkPacket& operator>>(u32& dst);
+ NetworkPacket& operator<<(u32 src);
+
+ NetworkPacket& operator>>(u64& dst);
+ NetworkPacket& operator<<(u64 src);
+
+ NetworkPacket& operator>>(float& dst);
+ NetworkPacket& operator<<(float src);
+
+ NetworkPacket& operator>>(v2f& dst);
+ NetworkPacket& operator<<(v2f src);
+
+ NetworkPacket& operator>>(v3f& dst);
+ NetworkPacket& operator<<(v3f src);
+
+ NetworkPacket& operator>>(s16& dst);
+ NetworkPacket& operator<<(s16 src);
+
+ NetworkPacket& operator>>(s32& dst);
+ NetworkPacket& operator<<(s32 src);
+
+ NetworkPacket& operator>>(v2s32& dst);
+ NetworkPacket& operator<<(v2s32 src);
+
+ NetworkPacket& operator>>(v3s16& dst);
+ NetworkPacket& operator<<(v3s16 src);
+
+ NetworkPacket& operator>>(v3s32& dst);
+ NetworkPacket& operator<<(v3s32 src);
+
+ NetworkPacket& operator>>(video::SColor& dst);
+ NetworkPacket& operator<<(video::SColor src);
+
+ // Temp, we remove SharedBuffer when migration finished
+ Buffer<u8> oldForgePacket();
+private:
+ void checkReadOffset(u32 from_offset, u32 field_size);
+
+ inline void checkDataSize(u32 field_size)
+ {
+ if (m_read_offset + field_size > m_datasize) {
+ m_datasize = m_read_offset + field_size;
+ m_data.resize(m_datasize);
+ }
+ }
+
+ std::vector<u8> m_data;
+ u32 m_datasize;
+ u32 m_read_offset;
+ u16 m_command;
+ u16 m_peer_id;
+};
+
+#endif
diff --git a/src/clientserver.h b/src/network/networkprotocol.h
index 5f7864768..82c82f79e 100644
--- a/src/clientserver.h
+++ b/src/network/networkprotocol.h
@@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef CLIENTSERVER_HEADER
-#define CLIENTSERVER_HEADER
+#ifndef NETWORKPROTOCOL_HEADER
+#define NETWORKPROTOCOL_HEADER
#include "util/string.h"
/*
@@ -108,9 +108,33 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 24:
ContentFeatures version 7
ContentFeatures: change number of special tiles to 6 (CF_SPECIAL_COUNT)
+ PROTOCOL_VERSION 25:
+ Rename TOCLIENT_ACCESS_DENIED to TOCLIENT_ACCESS_DENIED_LEGAGY
+ Rename TOCLIENT_DELETE_PARTICLESPAWNER to
+ TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY
+ Rename TOSERVER_PASSWORD to TOSERVER_PASSWORD_LEGACY
+ Rename TOSERVER_INIT to TOSERVER_INIT_LEGACY
+ Rename TOCLIENT_INIT to TOCLIENT_INIT_LEGACY
+ Add TOCLIENT_ACCESS_DENIED new opcode (0x0A), using error codes
+ for standard error, keeping customisation possible. This
+ permit translation
+ Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and
+ reading u32
+ Add new opcode TOSERVER_INIT for client presentation to server
+ Add new opcodes TOSERVER_FIRST_SRP, TOSERVER_SRP_BYTES_A,
+ TOSERVER_SRP_BYTES_M, TOCLIENT_SRP_BYTES_S_B
+ for the three supported auth mechanisms around srp
+ Add new opcodes TOCLIENT_ACCEPT_SUDO_MODE and TOCLIENT_DENY_SUDO_MODE
+ for sudo mode handling (auth mech generic way of changing password).
+ Add TOCLIENT_HELLO for presenting server to client after client
+ presentation
+ Add TOCLIENT_AUTH_ACCEPT to accept connection from client
+ Rename GENERIC_CMD_SET_ATTACHMENT to GENERIC_CMD_ATTACH_TO
+ PROTOCOL_VERSION 26:
+ Add TileDef tileable_horizontal, tileable_vertical flags
*/
-#define LATEST_PROTOCOL_VERSION 24
+#define LATEST_PROTOCOL_VERSION 26
// Server's supported network protocol range
#define SERVER_PROTOCOL_VERSION_MIN 13
@@ -133,7 +157,35 @@ with this program; if not, write to the Free Software Foundation, Inc.,
enum ToClientCommand
{
- TOCLIENT_INIT = 0x10,
+ TOCLIENT_HELLO = 0x02,
+ /*
+ Sent after TOSERVER_INIT.
+
+ u8 deployed serialisation version
+ u16 deployed network compression mode
+ u16 deployed protocol version
+ u32 supported auth methods
+ std::string username that should be used for legacy hash (for proper casing)
+ */
+ TOCLIENT_AUTH_ACCEPT = 0x03,
+ /*
+ Message from server to accept auth.
+
+ v3s16 player's position + v3f(0,BS/2,0) floatToInt'd
+ u64 map seed
+ f1000 recommended send interval
+ u32 : supported auth methods for sudo mode
+ (where the user can change their password)
+ */
+ TOCLIENT_ACCEPT_SUDO_MODE = 0x04,
+ /*
+ Sent to client to show it is in sudo mode now.
+ */
+ TOCLIENT_DENY_SUDO_MODE = 0x05,
+ /*
+ Signals client that sudo mode auth failed.
+ */
+ TOCLIENT_INIT_LEGACY = 0x10,
/*
Server's reply to TOSERVER_INIT.
Sent second after connected.
@@ -147,7 +199,12 @@ enum ToClientCommand
NOTE: The position in here is deprecated; position is
explicitly sent afterwards
*/
-
+ TOCLIENT_ACCESS_DENIED = 0x0A,
+ /*
+ u8 reason
+ std::string custom reason (if needed, otherwise "")
+ u8 (bool) reconnect
+ */
TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks
TOCLIENT_ADDNODE = 0x21,
/*
@@ -270,7 +327,7 @@ enum ToClientCommand
f1000 player yaw
*/
- TOCLIENT_ACCESS_DENIED = 0x35,
+ TOCLIENT_ACCESS_DENIED_LEGACY = 0x35,
/*
u16 command
u16 reason_length
@@ -457,10 +514,10 @@ enum ToClientCommand
u32 id
*/
- TOCLIENT_DELETE_PARTICLESPAWNER = 0x48,
+ TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY = 0x48,
/*
u16 command
- u32 id
+ u16 id
*/
TOCLIENT_HUDADD = 0x49,
@@ -556,15 +613,43 @@ enum ToClientCommand
v3f1000 first
v3f1000 third
*/
+
+ TOCLIENT_DELETE_PARTICLESPAWNER = 0x53,
+ /*
+ u16 command
+ u32 id
+ */
+
+ TOCLIENT_SRP_BYTES_S_B = 0x60,
+ /*
+ Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.
+
+ u16 command
+ std::string bytes_s
+ std::string bytes_B
+ */
+
+ TOCLIENT_NUM_MSG_TYPES = 0x61,
};
enum ToServerCommand
{
- TOSERVER_INIT=0x10,
+ TOSERVER_INIT = 0x02,
/*
Sent first after connected.
- [0] u16 TOSERVER_INIT
+ u8 serialisation version (=SER_FMT_VER_HIGHEST_READ)
+ u16 supported network compression modes
+ u16 minimum supported network protocol version
+ u16 maximum supported network protocol version
+ std::string player name
+ */
+
+ TOSERVER_INIT_LEGACY = 0x10,
+ /*
+ Sent first after connected.
+
+ [0] u16 TOSERVER_INIT_LEGACY
[2] u8 SER_FMT_VER_HIGHEST_READ
[3] u8[20] player_name
[23] u8[28] password (new in some version)
@@ -659,7 +744,7 @@ enum ToServerCommand
TOSERVER_INVENTORY_ACTION = 0x31,
/*
- See InventoryAction in inventory.h
+ See InventoryAction in inventorymanager.h
*/
TOSERVER_CHAT_MESSAGE = 0x32,
@@ -692,7 +777,7 @@ enum ToServerCommand
u8 amount
*/
- TOSERVER_PASSWORD=0x36,
+ TOSERVER_PASSWORD_LEGACY = 0x36,
/*
Sent to change password.
@@ -701,7 +786,7 @@ enum ToServerCommand
[30] u8[28] new password
*/
- TOSERVER_PLAYERITEM=0x37,
+ TOSERVER_PLAYERITEM = 0x37,
/*
Sent to change selected item.
@@ -709,7 +794,7 @@ enum ToServerCommand
[2] u16 item
*/
- TOSERVER_RESPAWN=0x38,
+ TOSERVER_RESPAWN = 0x38,
/*
u16 TOSERVER_RESPAWN
*/
@@ -795,7 +880,87 @@ enum ToServerCommand
u16 len
u8[len] full_version_string
*/
+
+ TOSERVER_FIRST_SRP = 0x50,
+ /*
+ Belonging to AUTH_MECHANISM_FIRST_SRP.
+
+ std::string srp salt
+ std::string srp verification key
+ u8 is_empty (=1 if password is empty, 0 otherwise)
+ */
+
+ TOSERVER_SRP_BYTES_A = 0x51,
+ /*
+ Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP,
+ depending on current_login_based_on.
+
+ std::string bytes_A
+ u8 current_login_based_on : on which version of the password's
+ hash this login is based on (0 legacy hash,
+ or 1 directly the password)
+ */
+
+ TOSERVER_SRP_BYTES_M = 0x52,
+ /*
+ Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP.
+
+ std::string bytes_M
+ */
+
+ TOSERVER_NUM_MSG_TYPES = 0x53,
};
-#endif
+enum AuthMechanism
+{
+ // reserved
+ AUTH_MECHANISM_NONE = 0,
+
+ // SRP based on the legacy hash
+ AUTH_MECHANISM_LEGACY_PASSWORD = 1 << 0,
+
+ // SRP based on the srp verification key
+ AUTH_MECHANISM_SRP = 1 << 1,
+
+ // Establishes a srp verification key, for first login and password changing
+ AUTH_MECHANISM_FIRST_SRP = 1 << 2,
+};
+enum AccessDeniedCode {
+ SERVER_ACCESSDENIED_WRONG_PASSWORD,
+ SERVER_ACCESSDENIED_UNEXPECTED_DATA,
+ SERVER_ACCESSDENIED_SINGLEPLAYER,
+ SERVER_ACCESSDENIED_WRONG_VERSION,
+ SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME,
+ SERVER_ACCESSDENIED_WRONG_NAME,
+ SERVER_ACCESSDENIED_TOO_MANY_USERS,
+ SERVER_ACCESSDENIED_EMPTY_PASSWORD,
+ SERVER_ACCESSDENIED_ALREADY_CONNECTED,
+ SERVER_ACCESSDENIED_SERVER_FAIL,
+ SERVER_ACCESSDENIED_CUSTOM_STRING,
+ SERVER_ACCESSDENIED_SHUTDOWN,
+ SERVER_ACCESSDENIED_CRASH,
+ SERVER_ACCESSDENIED_MAX,
+};
+
+enum NetProtoCompressionMode {
+ NETPROTO_COMPRESSION_NONE = 0,
+};
+
+const static std::string accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = {
+ "Invalid password",
+ "Your client sent something the server didn't expect. Try reconnecting or updating your client",
+ "The server is running in simple singleplayer mode. You cannot connect.",
+ "Your client's version is not supported.\nPlease contact server administrator.",
+ "Player name contains disallowed characters.",
+ "Player name not allowed.",
+ "Too many users.",
+ "Empty passwords are disallowed. Set a password and try again.",
+ "Another client is connected with this name. If your client closed unexpectedly, try again in a minute.",
+ "Server authentication failed. This is likely a server error.",
+ "",
+ "Server shutting down.",
+ "This server has experienced an internal error. You will now be disconnected."
+};
+
+#endif
diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp
new file mode 100644
index 000000000..9b14a1be3
--- /dev/null
+++ b/src/network/serveropcodes.cpp
@@ -0,0 +1,213 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#include "serveropcodes.h"
+
+const static ToServerCommandHandler null_command_handler = { "TOSERVER_NULL", TOSERVER_STATE_ALL, &Server::handleCommand_Null };
+
+const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
+{
+ null_command_handler, // 0x00 (never use this)
+ null_command_handler, // 0x01
+ { "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02
+ null_command_handler, // 0x03
+ null_command_handler, // 0x04
+ null_command_handler, // 0x05
+ null_command_handler, // 0x06
+ null_command_handler, // 0x07
+ null_command_handler, // 0x08
+ null_command_handler, // 0x09
+ null_command_handler, // 0x0a
+ null_command_handler, // 0x0b
+ null_command_handler, // 0x0c
+ null_command_handler, // 0x0d
+ null_command_handler, // 0x0e
+ null_command_handler, // 0x0f
+ { "TOSERVER_INIT_LEGACY", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init_Legacy }, // 0x10
+ { "TOSERVER_INIT2", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init2 }, // 0x11
+ null_command_handler, // 0x12
+ null_command_handler, // 0x13
+ null_command_handler, // 0x14
+ null_command_handler, // 0x15
+ null_command_handler, // 0x16
+ null_command_handler, // 0x17
+ null_command_handler, // 0x18
+ null_command_handler, // 0x19
+ null_command_handler, // 0x1a
+ null_command_handler, // 0x1b
+ null_command_handler, // 0x1c
+ null_command_handler, // 0x1d
+ null_command_handler, // 0x1e
+ null_command_handler, // 0x1f
+ null_command_handler, // 0x20
+ null_command_handler, // 0x21
+ null_command_handler, // 0x22
+ { "TOSERVER_PLAYERPOS", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerPos }, // 0x23
+ { "TOSERVER_GOTBLOCKS", TOSERVER_STATE_STARTUP, &Server::handleCommand_GotBlocks }, // 0x24
+ { "TOSERVER_DELETEDBLOCKS", TOSERVER_STATE_INGAME, &Server::handleCommand_DeletedBlocks }, // 0x25
+ null_command_handler, // 0x26
+ { "TOSERVER_CLICK_OBJECT", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x27
+ { "TOSERVER_GROUND_ACTION", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x28
+ { "TOSERVER_RELEASE", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x29
+ null_command_handler, // 0x2a
+ null_command_handler, // 0x2b
+ null_command_handler, // 0x2c
+ null_command_handler, // 0x2d
+ null_command_handler, // 0x2e
+ null_command_handler, // 0x2f
+ { "TOSERVER_SIGNTEXT", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x30
+ { "TOSERVER_INVENTORY_ACTION", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryAction }, // 0x31
+ { "TOSERVER_CHAT_MESSAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_ChatMessage }, // 0x32
+ { "TOSERVER_SIGNNODETEXT", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x33
+ { "TOSERVER_CLICK_ACTIVEOBJECT", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x34
+ { "TOSERVER_DAMAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35
+ { "TOSERVER_PASSWORD_LEGACY", TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 0x36
+ { "TOSERVER_PLAYERITEM", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerItem }, // 0x37
+ { "TOSERVER_RESPAWN", TOSERVER_STATE_INGAME, &Server::handleCommand_Respawn }, // 0x38
+ { "TOSERVER_INTERACT", TOSERVER_STATE_INGAME, &Server::handleCommand_Interact }, // 0x39
+ { "TOSERVER_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a
+ { "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
+ { "TOSERVER_INVENTORY_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c
+ null_command_handler, // 0x3d
+ null_command_handler, // 0x3e
+ null_command_handler, // 0x3f
+ { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
+ { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41
+ { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42
+ { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
+ null_command_handler, // 0x44
+ null_command_handler, // 0x45
+ null_command_handler, // 0x46
+ null_command_handler, // 0x47
+ null_command_handler, // 0x48
+ null_command_handler, // 0x49
+ null_command_handler, // 0x4a
+ null_command_handler, // 0x4b
+ null_command_handler, // 0x4c
+ null_command_handler, // 0x4d
+ null_command_handler, // 0x4e
+ null_command_handler, // 0x4f
+ { "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50
+ { "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51
+ { "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52
+};
+
+const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false };
+
+const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
+{
+ null_command_factory, // 0x00
+ null_command_factory, // 0x01
+ { "TOCLIENT_HELLO", 0, true }, // 0x02
+ { "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03
+ { "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04
+ { "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05
+ null_command_factory, // 0x06
+ null_command_factory, // 0x07
+ null_command_factory, // 0x08
+ null_command_factory, // 0x09
+ { "TOCLIENT_ACCESS_DENIED", 0, true }, // 0x0A
+ null_command_factory, // 0x0B
+ null_command_factory, // 0x0C
+ null_command_factory, // 0x0D
+ null_command_factory, // 0x0E
+ null_command_factory, // 0x0F
+ { "TOCLIENT_INIT", 0, true }, // 0x10
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ { "TOCLIENT_BLOCKDATA", 2, true }, // 0x20
+ { "TOCLIENT_ADDNODE", 0, true }, // 0x21
+ { "TOCLIENT_REMOVENODE", 0, true }, // 0x22
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ { "TOCLIENT_INVENTORY", 0, true }, // 0x27
+ null_command_factory,
+ { "TOCLIENT_TIME_OF_DAY", 0, true }, // 0x29
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ { "TOCLIENT_CHAT_MESSAGE", 0, true }, // 0x30
+ { "TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD", 0, true }, // 0x31
+ { "TOCLIENT_ACTIVE_OBJECT_MESSAGES", 0, true }, // 0x32 Special packet, sent by 0 (rel) and 1 (unrel) channel
+ { "TOCLIENT_HP", 0, true }, // 0x33
+ { "TOCLIENT_MOVE_PLAYER", 0, true }, // 0x34
+ { "TOCLIENT_ACCESS_DENIED_LEGACY", 0, true }, // 0x35
+ { "TOCLIENT_PLAYERITEM", 0, false }, // 0x36 obsolete
+ { "TOCLIENT_DEATHSCREEN", 0, true }, // 0x37
+ { "TOCLIENT_MEDIA", 2, true }, // 0x38
+ { "TOCLIENT_TOOLDEF", 0, false }, // 0x39 obsolete
+ { "TOCLIENT_NODEDEF", 0, true }, // 0x3a
+ { "TOCLIENT_CRAFTITEMDEF", 0, false }, // 0x3b obsolete
+ { "TOCLIENT_ANNOUNCE_MEDIA", 0, true }, // 0x3c
+ { "TOCLIENT_ITEMDEF", 0, true }, // 0x3d
+ null_command_factory,
+ { "TOCLIENT_PLAY_SOUND", 0, true }, // 0x3f
+ { "TOCLIENT_STOP_SOUND", 0, true }, // 0x40
+ { "TOCLIENT_PRIVILEGES", 0, true }, // 0x41
+ { "TOCLIENT_INVENTORY_FORMSPEC", 0, true }, // 0x42
+ { "TOCLIENT_DETACHED_INVENTORY", 0, true }, // 0x43
+ { "TOCLIENT_SHOW_FORMSPEC", 0, true }, // 0x44
+ { "TOCLIENT_MOVEMENT", 0, true }, // 0x45
+ { "TOCLIENT_SPAWN_PARTICLE", 0, true }, // 0x46
+ { "TOCLIENT_ADD_PARTICLESPAWNER", 0, true }, // 0x47
+ { "TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY", 0, true }, // 0x48
+ { "TOCLIENT_HUDADD", 1, true }, // 0x49
+ { "TOCLIENT_HUDRM", 1, true }, // 0x4a
+ { "TOCLIENT_HUDCHANGE", 0, true }, // 0x4b
+ { "TOCLIENT_HUD_SET_FLAGS", 0, true }, // 0x4c
+ { "TOCLIENT_HUD_SET_PARAM", 0, true }, // 0x4d
+ { "TOCLIENT_BREATH", 0, true }, // 0x4e
+ { "TOCLIENT_SET_SKY", 0, true }, // 0x4f
+ { "TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO", 0, true }, // 0x50
+ { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51
+ { "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52
+ { "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ null_command_factory,
+ { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60
+};
diff --git a/src/network/serveropcodes.h b/src/network/serveropcodes.h
new file mode 100644
index 000000000..aa3301069
--- /dev/null
+++ b/src/network/serveropcodes.h
@@ -0,0 +1,52 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#ifndef SERVEROPCODES_HEADER
+#define SERVEROPCODES_HEADER
+
+#include "server.h"
+#include "networkprotocol.h"
+#include "networkpacket.h"
+
+enum ToServerConnectionState {
+ TOSERVER_STATE_NOT_CONNECTED,
+ TOSERVER_STATE_STARTUP,
+ TOSERVER_STATE_INGAME,
+ TOSERVER_STATE_ALL,
+};
+struct ToServerCommandHandler
+{
+ const std::string name;
+ ToServerConnectionState state;
+ void (Server::*handler)(NetworkPacket* pkt);
+};
+
+struct ClientCommandFactory
+{
+ const char* name;
+ u16 channel;
+ bool reliable;
+};
+
+extern const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES];
+
+extern const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES];
+
+#endif
diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp
new file mode 100644
index 000000000..f756d80ef
--- /dev/null
+++ b/src/network/serverpackethandler.cpp
@@ -0,0 +1,2086 @@
+/*
+Minetest
+Copyright (C) 2015 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+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.
+*/
+
+#include "server.h"
+#include "log.h"
+
+#include "content_abm.h"
+#include "content_sao.h"
+#include "emerge.h"
+#include "nodedef.h"
+#include "player.h"
+#include "rollback_interface.h"
+#include "scripting_game.h"
+#include "settings.h"
+#include "tool.h"
+#include "version.h"
+#include "network/networkprotocol.h"
+#include "network/serveropcodes.h"
+#include "util/auth.h"
+#include "util/base64.h"
+#include "util/pointedthing.h"
+#include "util/serialize.h"
+#include "util/srp.h"
+
+void Server::handleCommand_Deprecated(NetworkPacket* pkt)
+{
+ infostream << "Server: " << toServerCommandTable[pkt->getCommand()].name
+ << " not supported anymore" << std::endl;
+}
+
+void Server::handleCommand_Init(NetworkPacket* pkt)
+{
+
+ if(pkt->getSize() < 1)
+ return;
+
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
+
+ std::string addr_s;
+ try {
+ Address address = getPeerAddress(pkt->getPeerId());
+ addr_s = address.serializeString();
+ }
+ catch (con::PeerNotFoundException &e) {
+ /*
+ * no peer for this packet found
+ * most common reason is peer timeout, e.g. peer didn't
+ * respond for some time, your server was overloaded or
+ * things like that.
+ */
+ infostream << "Server::ProcessData(): Canceling: peer "
+ << pkt->getPeerId() << " not found" << std::endl;
+ return;
+ }
+
+ // If net_proto_version is set, this client has already been handled
+ if (client->getState() > CS_Created) {
+ verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
+ << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+ return;
+ }
+
+ verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id="
+ << pkt->getPeerId() << ")" << std::endl;
+
+ // Do not allow multiple players in simple singleplayer mode.
+ // This isn't a perfect way to do it, but will suffice for now
+ if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
+ infostream << "Server: Not allowing another client (" << addr_s
+ << ") to connect in simple singleplayer mode" << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SINGLEPLAYER);
+ return;
+ }
+
+ // First byte after command is maximum supported
+ // serialization version
+ u8 client_max;
+ u16 supp_compr_modes;
+ u16 min_net_proto_version = 0;
+ u16 max_net_proto_version;
+ std::string playerName;
+
+ *pkt >> client_max >> supp_compr_modes >> min_net_proto_version
+ >> max_net_proto_version >> playerName;
+
+ u8 our_max = SER_FMT_VER_HIGHEST_READ;
+ // Use the highest version supported by both
+ u8 depl_serial_v = std::min(client_max, our_max);
+ // If it's lower than the lowest supported, give up.
+ if (depl_serial_v < SER_FMT_VER_LOWEST)
+ depl_serial_v = SER_FMT_VER_INVALID;
+
+ if (depl_serial_v == SER_FMT_VER_INVALID) {
+ actionstream << "Server: A mismatched client tried to connect from "
+ << addr_s << std::endl;
+ infostream<<"Server: Cannot negotiate serialization version with "
+ << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+ return;
+ }
+
+ client->setPendingSerializationVersion(depl_serial_v);
+
+ /*
+ Read and check network protocol version
+ */
+
+ u16 net_proto_version = 0;
+
+ // Figure out a working version if it is possible at all
+ if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
+ min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
+ // If maximum is larger than our maximum, go with our maximum
+ if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
+ net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
+ // Else go with client's maximum
+ else
+ net_proto_version = max_net_proto_version;
+ }
+
+ verbosestream << "Server: " << addr_s << ": Protocol version: min: "
+ << min_net_proto_version << ", max: " << max_net_proto_version
+ << ", chosen: " << net_proto_version << std::endl;
+
+ client->net_proto_version = net_proto_version;
+
+ // On this handler at least protocol version 25 is required
+ if (net_proto_version < 25 ||
+ net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
+ net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
+ actionstream << "Server: A mismatched client tried to connect from "
+ << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+ return;
+ }
+
+ if (g_settings->getBool("strict_protocol_version_checking")) {
+ if (net_proto_version != LATEST_PROTOCOL_VERSION) {
+ actionstream << "Server: A mismatched (strict) client tried to "
+ << "connect from " << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_VERSION);
+ return;
+ }
+ }
+
+ /*
+ Validate player name
+ */
+ const char* playername = playerName.c_str();
+
+ size_t pns = playerName.size();
+ if (pns == 0 || pns > PLAYERNAME_SIZE) {
+ actionstream << "Server: Player with "
+ << ((pns > PLAYERNAME_SIZE) ? "a too long" : "an empty")
+ << " name tried to connect from " << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
+ return;
+ }
+
+ if (string_allowed(playerName, PLAYERNAME_ALLOWED_CHARS) == false) {
+ actionstream << "Server: Player with an invalid name "
+ << "tried to connect from " << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_CHARS_IN_NAME);
+ return;
+ }
+
+ m_clients.setPlayerName(pkt->getPeerId(), playername);
+ //TODO (later) case insensitivity
+
+ std::string legacyPlayerNameCasing = playerName;
+
+ if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
+ actionstream << "Server: Player with the name \"singleplayer\" "
+ << "tried to connect from " << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_NAME);
+ return;
+ }
+
+ {
+ std::string reason;
+ if (m_script->on_prejoinplayer(playername, addr_s, &reason)) {
+ actionstream << "Server: Player with the name \"" << playerName << "\" "
+ << "tried to connect from " << addr_s << " "
+ << "but it was disallowed for the following reason: "
+ << reason << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING,
+ reason.c_str());
+ return;
+ }
+ }
+
+ infostream << "Server: New connection: \"" << playerName << "\" from "
+ << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+
+ // Enforce user limit.
+ // Don't enforce for users that have some admin right
+ if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
+ !checkPriv(playername, "server") &&
+ !checkPriv(playername, "ban") &&
+ !checkPriv(playername, "privs") &&
+ !checkPriv(playername, "password") &&
+ playername != g_settings->get("name")) {
+ actionstream << "Server: " << playername << " tried to join from "
+ << addr_s << ", but there" << " are already max_users="
+ << g_settings->getU16("max_users") << " players." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
+ return;
+ }
+
+ /*
+ Compose auth methods for answer
+ */
+ std::string encpwd; // encrypted Password field for the user
+ bool has_auth = m_script->getAuth(playername, &encpwd, NULL);
+ u32 auth_mechs = 0;
+
+ client->chosen_mech = AUTH_MECHANISM_NONE;
+
+ if (has_auth) {
+ std::vector<std::string> pwd_components = str_split(encpwd, '#');
+ if (pwd_components.size() == 4) {
+ if (pwd_components[1] == "1") { // 1 means srp
+ auth_mechs |= AUTH_MECHANISM_SRP;
+ client->enc_pwd = encpwd;
+ } else {
+ actionstream << "User " << playername
+ << " tried to log in, but password field"
+ << " was invalid (unknown mechcode)." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+ return;
+ }
+ } else if (base64_is_valid(encpwd)) {
+ auth_mechs |= AUTH_MECHANISM_LEGACY_PASSWORD;
+ client->enc_pwd = encpwd;
+ } else {
+ actionstream << "User " << playername
+ << " tried to log in, but password field"
+ << " was invalid (invalid base64)." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+ return;
+ }
+ } else {
+ std::string default_password = g_settings->get("default_password");
+ if (default_password.length() == 0) {
+ auth_mechs |= AUTH_MECHANISM_FIRST_SRP;
+ } else {
+ // Take care of default passwords.
+ client->enc_pwd = getSRPVerifier(playerName, default_password);
+ auth_mechs |= AUTH_MECHANISM_SRP;
+ // Create auth, but only on successful login
+ client->create_player_on_auth_success = true;
+ }
+ }
+
+ /*
+ Answer with a TOCLIENT_HELLO
+ */
+
+ verbosestream << "Sending TOCLIENT_HELLO with auth method field: "
+ << auth_mechs << std::endl;
+
+ NetworkPacket resp_pkt(TOCLIENT_HELLO, 1 + 4
+ + legacyPlayerNameCasing.size(), pkt->getPeerId());
+
+ u16 depl_compress_mode = NETPROTO_COMPRESSION_NONE;
+ resp_pkt << depl_serial_v << depl_compress_mode << net_proto_version
+ << auth_mechs << legacyPlayerNameCasing;
+
+ Send(&resp_pkt);
+
+ client->allowed_auth_mechs = auth_mechs;
+ client->setDeployedCompressionMode(depl_compress_mode);
+
+ m_clients.event(pkt->getPeerId(), CSE_Hello);
+}
+
+void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
+{
+ // [0] u8 SER_FMT_VER_HIGHEST_READ
+ // [1] u8[20] player_name
+ // [21] u8[28] password <--- can be sent without this, from old versions
+
+ if (pkt->getSize() < 1+PLAYERNAME_SIZE)
+ return;
+
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
+
+ std::string addr_s;
+ try {
+ Address address = getPeerAddress(pkt->getPeerId());
+ addr_s = address.serializeString();
+ }
+ catch (con::PeerNotFoundException &e) {
+ /*
+ * no peer for this packet found
+ * most common reason is peer timeout, e.g. peer didn't
+ * respond for some time, your server was overloaded or
+ * things like that.
+ */
+ infostream << "Server::ProcessData(): Canceling: peer "
+ << pkt->getPeerId() << " not found" << std::endl;
+ return;
+ }
+
+ // If net_proto_version is set, this client has already been handled
+ if (client->getState() > CS_Created) {
+ verbosestream << "Server: Ignoring multiple TOSERVER_INITs from "
+ << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
+ return;
+ }
+
+ verbosestream << "Server: Got TOSERVER_INIT_LEGACY from " << addr_s << " (peer_id="
+ << pkt->getPeerId() << ")" << std::endl;
+
+ // Do not allow multiple players in simple singleplayer mode.
+ // This isn't a perfect way to do it, but will suffice for now
+ if (m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1) {
+ infostream << "Server: Not allowing another client (" << addr_s
+ << ") to connect in simple singleplayer mode" << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Running in simple singleplayer mode.");
+ return;
+ }
+
+ // First byte after command is maximum supported
+ // serialization version
+ u8 client_max;
+
+ *pkt >> client_max;
+
+ u8 our_max = SER_FMT_VER_HIGHEST_READ;
+ // Use the highest version supported by both
+ int deployed = std::min(client_max, our_max);
+ // If it's lower than the lowest supported, give up.
+ if (deployed < SER_FMT_VER_LOWEST)
+ deployed = SER_FMT_VER_INVALID;
+
+ if (deployed == SER_FMT_VER_INVALID) {
+ actionstream << "Server: A mismatched client tried to connect from "
+ << addr_s << std::endl;
+ infostream<<"Server: Cannot negotiate serialization version with "
+ << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
+ L"Your client's version is not supported.\n"
+ L"Server version is ")
+ + utf8_to_wide(g_version_string) + L"."
+ );
+ return;
+ }
+
+ client->setPendingSerializationVersion(deployed);
+
+ /*
+ Read and check network protocol version
+ */
+
+ u16 min_net_proto_version = 0;
+ if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2)
+ min_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE);
+
+ // Use same version as minimum and maximum if maximum version field
+ // doesn't exist (backwards compatibility)
+ u16 max_net_proto_version = min_net_proto_version;
+ if (pkt->getSize() >= 1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2 + 2)
+ max_net_proto_version = pkt->getU16(1 + PLAYERNAME_SIZE + PASSWORD_SIZE + 2);
+
+ // Start with client's maximum version
+ u16 net_proto_version = max_net_proto_version;
+
+ // Figure out a working version if it is possible at all
+ if (max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
+ min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX) {
+ // If maximum is larger than our maximum, go with our maximum
+ if (max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
+ net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
+ // Else go with client's maximum
+ else
+ net_proto_version = max_net_proto_version;
+ }
+
+ // The client will send up to date init packet, ignore this one
+ if (net_proto_version >= 25)
+ return;
+
+ verbosestream << "Server: " << addr_s << ": Protocol version: min: "
+ << min_net_proto_version << ", max: " << max_net_proto_version
+ << ", chosen: " << net_proto_version << std::endl;
+
+ client->net_proto_version = net_proto_version;
+
+ if (net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
+ net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
+ actionstream << "Server: A mismatched client tried to connect from "
+ << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
+ L"Your client's version is not supported.\n"
+ L"Server version is ")
+ + utf8_to_wide(g_version_string) + L",\n"
+ + L"server's PROTOCOL_VERSION is "
+ + utf8_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
+ + L"..."
+ + utf8_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
+ + L", client's PROTOCOL_VERSION is "
+ + utf8_to_wide(itos(min_net_proto_version))
+ + L"..."
+ + utf8_to_wide(itos(max_net_proto_version))
+ );
+ return;
+ }
+
+ if (g_settings->getBool("strict_protocol_version_checking")) {
+ if (net_proto_version != LATEST_PROTOCOL_VERSION) {
+ actionstream << "Server: A mismatched (strict) client tried to "
+ << "connect from " << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), std::wstring(
+ L"Your client's version is not supported.\n"
+ L"Server version is ")
+ + utf8_to_wide(g_version_string) + L",\n"
+ + L"server's PROTOCOL_VERSION (strict) is "
+ + utf8_to_wide(itos(LATEST_PROTOCOL_VERSION))
+ + L", client's PROTOCOL_VERSION is "
+ + utf8_to_wide(itos(min_net_proto_version))
+ + L"..."
+ + utf8_to_wide(itos(max_net_proto_version))
+ );
+ return;
+ }
+ }
+
+ /*
+ Set up player
+ */
+ char playername[PLAYERNAME_SIZE];
+ unsigned int playername_length = 0;
+ for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
+ playername[playername_length] = pkt->getChar(1+playername_length);
+ if (pkt->getChar(1+playername_length) == 0)
+ break;
+ }
+
+ if (playername_length == PLAYERNAME_SIZE) {
+ actionstream << "Server: Player with name exceeding max length "
+ << "tried to connect from " << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Name too long");
+ return;
+ }
+
+
+ if (playername[0]=='\0') {
+ actionstream << "Server: Player with an empty name "
+ << "tried to connect from " << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Empty name");
+ return;
+ }
+
+ if (string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false) {
+ actionstream << "Server: Player with an invalid name "
+ << "tried to connect from " << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Name contains unallowed characters");
+ return;
+ }
+
+ if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
+ actionstream << "Server: Player with the name \"singleplayer\" "
+ << "tried to connect from " << addr_s << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Name is not allowed");
+ return;
+ }
+
+ {
+ std::string reason;
+ if (m_script->on_prejoinplayer(playername, addr_s, &reason)) {
+ actionstream << "Server: Player with the name \"" << playername << "\" "
+ << "tried to connect from " << addr_s << " "
+ << "but it was disallowed for the following reason: "
+ << reason << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), utf8_to_wide(reason.c_str()));
+ return;
+ }
+ }
+
+ infostream<<"Server: New connection: \""<<playername<<"\" from "
+ <<addr_s<<" (peer_id="<<pkt->getPeerId()<<")"<<std::endl;
+
+ // Get password
+ char given_password[PASSWORD_SIZE];
+ if (pkt->getSize() < 1 + PLAYERNAME_SIZE + PASSWORD_SIZE) {
+ // old version - assume blank password
+ given_password[0] = 0;
+ }
+ else {
+ for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+ given_password[i] = pkt->getChar(21 + i);
+ }
+ given_password[PASSWORD_SIZE - 1] = 0;
+ }
+
+ if (!base64_is_valid(given_password)) {
+ actionstream << "Server: " << playername
+ << " supplied invalid password hash" << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Invalid password hash");
+ return;
+ }
+
+ // Enforce user limit.
+ // Don't enforce for users that have some admin right
+ if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
+ !checkPriv(playername, "server") &&
+ !checkPriv(playername, "ban") &&
+ !checkPriv(playername, "privs") &&
+ !checkPriv(playername, "password") &&
+ playername != g_settings->get("name")) {
+ actionstream << "Server: " << playername << " tried to join, but there"
+ << " are already max_users="
+ << g_settings->getU16("max_users") << " players." << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Too many users.");
+ return;
+ }
+
+ std::string checkpwd; // Password hash to check against
+ bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+ // If no authentication info exists for user, create it
+ if (!has_auth) {
+ if (!isSingleplayer() &&
+ g_settings->getBool("disallow_empty_password") &&
+ std::string(given_password) == "") {
+ actionstream << "Server: " << playername
+ << " supplied empty password" << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Empty passwords are "
+ L"disallowed. Set a password and try again.");
+ return;
+ }
+ std::string raw_default_password =
+ g_settings->get("default_password");
+ std::string initial_password =
+ translatePassword(playername, raw_default_password);
+
+ // If default_password is empty, allow any initial password
+ if (raw_default_password.length() == 0)
+ initial_password = given_password;
+
+ m_script->createAuth(playername, initial_password);
+ }
+
+ has_auth = m_script->getAuth(playername, &checkpwd, NULL);
+
+ if (!has_auth) {
+ actionstream << "Server: " << playername << " cannot be authenticated"
+ << " (auth handler does not work?)" << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Not allowed to login");
+ return;
+ }
+
+ if (given_password != checkpwd) {
+ actionstream << "Server: " << playername << " supplied wrong password"
+ << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Wrong password");
+ return;
+ }
+
+ RemotePlayer *player =
+ static_cast<RemotePlayer*>(m_env->getPlayer(playername));
+
+ if (player && player->peer_id != 0) {
+ actionstream << "Server: " << playername << ": Failed to emerge player"
+ << " (player allocated to an another client)" << std::endl;
+ DenyAccess_Legacy(pkt->getPeerId(), L"Another client is connected with this "
+ L"name. If your client closed unexpectedly, try again in "
+ L"a minute.");
+ }
+
+ m_clients.setPlayerName(pkt->getPeerId(), playername);
+
+ /*
+ Answer with a TOCLIENT_INIT
+ */
+
+ NetworkPacket resp_pkt(TOCLIENT_INIT_LEGACY, 1 + 6 + 8 + 4,
+ pkt->getPeerId());
+
+ resp_pkt << (u8) deployed << (v3s16) floatToInt(v3f(0,0,0), BS)
+ << (u64) m_env->getServerMap().getSeed()
+ << g_settings->getFloat("dedicated_server_step");
+
+ Send(&resp_pkt);
+ m_clients.event(pkt->getPeerId(), CSE_InitLegacy);
+}
+
+void Server::handleCommand_Init2(NetworkPacket* pkt)
+{
+ verbosestream << "Server: Got TOSERVER_INIT2 from "
+ << pkt->getPeerId() << std::endl;
+
+ m_clients.event(pkt->getPeerId(), CSE_GotInit2);
+ u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId());
+
+
+ ///// begin compatibility code
+ PlayerSAO* playersao = NULL;
+ if (protocol_version <= 22) {
+ playersao = StageTwoClientInit(pkt->getPeerId());
+
+ if (playersao == NULL) {
+ actionstream
+ << "TOSERVER_INIT2 stage 2 client init failed for peer "
+ << pkt->getPeerId() << std::endl;
+ return;
+ }
+ }
+ ///// end compatibility code
+
+ /*
+ Send some initialization data
+ */
+
+ infostream << "Server: Sending content to "
+ << getPlayerName(pkt->getPeerId()) << std::endl;
+
+ // Send player movement settings
+ SendMovement(pkt->getPeerId());
+
+ // Send item definitions
+ SendItemDef(pkt->getPeerId(), m_itemdef, protocol_version);
+
+ // Send node definitions
+ SendNodeDef(pkt->getPeerId(), m_nodedef, protocol_version);
+
+ m_clients.event(pkt->getPeerId(), CSE_SetDefinitionsSent);
+
+ // Send media announcement
+ sendMediaAnnouncement(pkt->getPeerId());
+
+ // Send detached inventories
+ sendDetachedInventories(pkt->getPeerId());
+
+ // Send time of day
+ u16 time = m_env->getTimeOfDay();
+ float time_speed = g_settings->getFloat("time_speed");
+ SendTimeOfDay(pkt->getPeerId(), time, time_speed);
+
+ ///// begin compatibility code
+ if (protocol_version <= 22) {
+ m_clients.event(pkt->getPeerId(), CSE_SetClientReady);
+ m_script->on_joinplayer(playersao);
+ }
+ ///// end compatibility code
+
+ // Warnings about protocol version can be issued here
+ if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) {
+ SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S "
+ L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
+ }
+}
+
+void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
+{
+ std::vector<std::string> tosend;
+ u16 numfiles;
+
+ *pkt >> numfiles;
+
+ infostream << "Sending " << numfiles << " files to "
+ << getPlayerName(pkt->getPeerId()) << std::endl;
+ verbosestream << "TOSERVER_REQUEST_MEDIA: " << std::endl;
+
+ for (u16 i = 0; i < numfiles; i++) {
+ std::string name;
+
+ *pkt >> name;
+
+ tosend.push_back(name);
+ verbosestream << "TOSERVER_REQUEST_MEDIA: requested file "
+ << name << std::endl;
+ }
+
+ sendRequestedMedia(pkt->getPeerId(), tosend);
+}
+
+void Server::handleCommand_ReceivedMedia(NetworkPacket* pkt)
+{
+}
+
+void Server::handleCommand_ClientReady(NetworkPacket* pkt)
+{
+ u16 peer_id = pkt->getPeerId();
+ u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
+
+ // clients <= protocol version 22 did not send ready message,
+ // they're already initialized
+ if (peer_proto_ver <= 22) {
+ infostream << "Client sent message not expected by a "
+ << "client using protocol version <= 22,"
+ << "disconnecting peer_id: " << peer_id << std::endl;
+ m_con.DisconnectPeer(peer_id);
+ return;
+ }
+
+ PlayerSAO* playersao = StageTwoClientInit(peer_id);
+
+ if (playersao == NULL) {
+ actionstream
+ << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
+ << peer_id << std::endl;
+ m_con.DisconnectPeer(peer_id);
+ return;
+ }
+
+
+ if (pkt->getSize() < 8) {
+ errorstream
+ << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
+ << peer_id << std::endl;
+ m_con.DisconnectPeer(peer_id);
+ return;
+ }
+
+ u8 major_ver, minor_ver, patch_ver, reserved;
+ std::string full_ver;
+ *pkt >> major_ver >> minor_ver >> patch_ver >> reserved >> full_ver;
+
+ m_clients.setClientVersion(
+ peer_id, major_ver, minor_ver, patch_ver,
+ full_ver);
+
+ m_clients.event(peer_id, CSE_SetClientReady);
+ m_script->on_joinplayer(playersao);
+}
+
+void Server::handleCommand_GotBlocks(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 1)
+ return;
+
+ /*
+ [0] u16 command
+ [2] u8 count
+ [3] v3s16 pos_0
+ [3+6] v3s16 pos_1
+ ...
+ */
+
+ u8 count;
+ *pkt >> count;
+
+ RemoteClient *client = getClient(pkt->getPeerId());
+
+ for (u16 i = 0; i < count; i++) {
+ if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
+ throw con::InvalidIncomingDataException
+ ("GOTBLOCKS length is too short");
+ v3s16 p;
+
+ *pkt >> p;
+
+ client->GotBlock(p);
+ }
+}
+
+void Server::handleCommand_PlayerPos(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 12 + 12 + 4 + 4)
+ return;
+
+ v3s32 ps, ss;
+ s32 f32pitch, f32yaw;
+
+ *pkt >> ps;
+ *pkt >> ss;
+ *pkt >> f32pitch;
+ *pkt >> f32yaw;
+
+ f32 pitch = (f32)f32pitch / 100.0;
+ f32 yaw = (f32)f32yaw / 100.0;
+ u32 keyPressed = 0;
+
+ if (pkt->getSize() >= 12 + 12 + 4 + 4 + 4)
+ *pkt >> keyPressed;
+
+ v3f position((f32)ps.X / 100.0, (f32)ps.Y / 100.0, (f32)ps.Z / 100.0);
+ v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0);
+
+ pitch = modulo360f(pitch);
+ yaw = modulo360f(yaw);
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ // If player is dead we don't care of this packet
+ if (player->isDead()) {
+ verbosestream << "TOSERVER_PLAYERPOS: " << player->getName()
+ << " is dead. Ignoring packet";
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ player->setPosition(position);
+ player->setSpeed(speed);
+ player->setPitch(pitch);
+ player->setYaw(yaw);
+ player->keyPressed = keyPressed;
+ player->control.up = (keyPressed & 1);
+ player->control.down = (keyPressed & 2);
+ player->control.left = (keyPressed & 4);
+ player->control.right = (keyPressed & 8);
+ player->control.jump = (keyPressed & 16);
+ player->control.aux1 = (keyPressed & 32);
+ player->control.sneak = (keyPressed & 64);
+ player->control.LMB = (keyPressed & 128);
+ player->control.RMB = (keyPressed & 256);
+
+ if (playersao->checkMovementCheat()) {
+ // Call callbacks
+ m_script->on_cheat(playersao, "moved_too_fast");
+ SendMovePlayer(pkt->getPeerId());
+ }
+}
+
+void Server::handleCommand_DeletedBlocks(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 1)
+ return;
+
+ /*
+ [0] u16 command
+ [2] u8 count
+ [3] v3s16 pos_0
+ [3+6] v3s16 pos_1
+ ...
+ */
+
+ u8 count;
+ *pkt >> count;
+
+ RemoteClient *client = getClient(pkt->getPeerId());
+
+ for (u16 i = 0; i < count; i++) {
+ if ((s16)pkt->getSize() < 1 + (i + 1) * 6)
+ throw con::InvalidIncomingDataException
+ ("DELETEDBLOCKS length is too short");
+ v3s16 p;
+ *pkt >> p;
+
+ client->SetBlockNotSent(p);
+ }
+}
+
+void Server::handleCommand_InventoryAction(NetworkPacket* pkt)
+{
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ // Strip command and create a stream
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ verbosestream << "TOSERVER_INVENTORY_ACTION: data=" << datastring
+ << std::endl;
+ std::istringstream is(datastring, std::ios_base::binary);
+ // Create an action
+ InventoryAction *a = InventoryAction::deSerialize(is);
+ if (a == NULL) {
+ infostream << "TOSERVER_INVENTORY_ACTION: "
+ << "InventoryAction::deSerialize() returned NULL"
+ << std::endl;
+ return;
+ }
+
+ // If something goes wrong, this player is to blame
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:")+player->getName());
+
+ /*
+ Note: Always set inventory not sent, to repair cases
+ where the client made a bad prediction.
+ */
+
+ /*
+ Handle restrictions and special cases of the move action
+ */
+ if (a->getType() == IACTION_MOVE) {
+ IMoveAction *ma = (IMoveAction*)a;
+
+ ma->from_inv.applyCurrentPlayer(player->getName());
+ ma->to_inv.applyCurrentPlayer(player->getName());
+
+ setInventoryModified(ma->from_inv, false);
+ setInventoryModified(ma->to_inv, false);
+
+ bool from_inv_is_current_player =
+ (ma->from_inv.type == InventoryLocation::PLAYER) &&
+ (ma->from_inv.name == player->getName());
+
+ bool to_inv_is_current_player =
+ (ma->to_inv.type == InventoryLocation::PLAYER) &&
+ (ma->to_inv.name == player->getName());
+
+ /*
+ Disable moving items out of craftpreview
+ */
+ if (ma->from_list == "craftpreview") {
+ infostream << "Ignoring IMoveAction from "
+ << (ma->from_inv.dump()) << ":" << ma->from_list
+ << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
+ << " because src is " << ma->from_list << std::endl;
+ delete a;
+ return;
+ }
+
+ /*
+ Disable moving items into craftresult and craftpreview
+ */
+ if (ma->to_list == "craftpreview" || ma->to_list == "craftresult") {
+ infostream << "Ignoring IMoveAction from "
+ << (ma->from_inv.dump()) << ":" << ma->from_list
+ << " to " << (ma->to_inv.dump()) << ":" << ma->to_list
+ << " because dst is " << ma->to_list << std::endl;
+ delete a;
+ return;
+ }
+
+ // Disallow moving items in elsewhere than player's inventory
+ // if not allowed to interact
+ if (!checkPriv(player->getName(), "interact") &&
+ (!from_inv_is_current_player ||
+ !to_inv_is_current_player)) {
+ infostream << "Cannot move outside of player's inventory: "
+ << "No interact privilege" << std::endl;
+ delete a;
+ return;
+ }
+ }
+ /*
+ Handle restrictions and special cases of the drop action
+ */
+ else if (a->getType() == IACTION_DROP) {
+ IDropAction *da = (IDropAction*)a;
+
+ da->from_inv.applyCurrentPlayer(player->getName());
+
+ setInventoryModified(da->from_inv, false);
+
+ /*
+ Disable dropping items out of craftpreview
+ */
+ if (da->from_list == "craftpreview") {
+ infostream << "Ignoring IDropAction from "
+ << (da->from_inv.dump()) << ":" << da->from_list
+ << " because src is " << da->from_list << std::endl;
+ delete a;
+ return;
+ }
+
+ // Disallow dropping items if not allowed to interact
+ if (!checkPriv(player->getName(), "interact")) {
+ delete a;
+ return;
+ }
+ }
+ /*
+ Handle restrictions and special cases of the craft action
+ */
+ else if (a->getType() == IACTION_CRAFT) {
+ ICraftAction *ca = (ICraftAction*)a;
+
+ ca->craft_inv.applyCurrentPlayer(player->getName());
+
+ setInventoryModified(ca->craft_inv, false);
+
+ //bool craft_inv_is_current_player =
+ // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
+ // (ca->craft_inv.name == player->getName());
+
+ // Disallow crafting if not allowed to interact
+ if (!checkPriv(player->getName(), "interact")) {
+ infostream << "Cannot craft: "
+ << "No interact privilege" << std::endl;
+ delete a;
+ return;
+ }
+ }
+
+ // Do the action
+ a->apply(this, playersao, this);
+ // Eat the action
+ delete a;
+
+ SendInventory(playersao);
+}
+
+void Server::handleCommand_ChatMessage(NetworkPacket* pkt)
+{
+ /*
+ u16 command
+ u16 length
+ wstring message
+ */
+ u16 len;
+ *pkt >> len;
+
+ std::wstring message;
+ for (u16 i = 0; i < len; i++) {
+ u16 tmp_wchar;
+ *pkt >> tmp_wchar;
+
+ message += (wchar_t)tmp_wchar;
+ }
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ // If something goes wrong, this player is to blame
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:")+player->getName());
+
+ // Get player name of this client
+ std::wstring name = narrow_to_wide(player->getName());
+
+ // Run script hook
+ bool ate = m_script->on_chat_message(player->getName(),
+ wide_to_narrow(message));
+ // If script ate the message, don't proceed
+ if (ate)
+ return;
+
+ // Line to send to players
+ std::wstring line;
+ // Whether to send to the player that sent the line
+ bool send_to_sender_only = false;
+
+ // Commands are implemented in Lua, so only catch invalid
+ // commands that were not "eaten" and send an error back
+ if (message[0] == L'/') {
+ message = message.substr(1);
+ send_to_sender_only = true;
+ if (message.length() == 0)
+ line += L"-!- Empty command";
+ else
+ line += L"-!- Invalid command: " + str_split(message, L' ')[0];
+ }
+ else {
+ if (checkPriv(player->getName(), "shout")) {
+ line += L"<";
+ line += name;
+ line += L"> ";
+ line += message;
+ } else {
+ line += L"-!- You don't have permission to shout.";
+ send_to_sender_only = true;
+ }
+ }
+
+ if (line != L"")
+ {
+ /*
+ Send the message to sender
+ */
+ if (send_to_sender_only) {
+ SendChatMessage(pkt->getPeerId(), line);
+ }
+ /*
+ Send the message to others
+ */
+ else {
+ actionstream << "CHAT: " << wide_to_narrow(line)<<std::endl;
+
+ std::vector<u16> clients = m_clients.getClientIDs();
+
+ for (std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
+ if (*i != pkt->getPeerId())
+ SendChatMessage(*i, line);
+ }
+ }
+ }
+}
+
+void Server::handleCommand_Damage(NetworkPacket* pkt)
+{
+ u8 damage;
+
+ *pkt >> damage;
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ if (g_settings->getBool("enable_damage")) {
+ actionstream << player->getName() << " damaged by "
+ << (int)damage << " hp at " << PP(player->getPosition() / BS)
+ << std::endl;
+
+ playersao->setHP(playersao->getHP() - damage);
+ SendPlayerHPOrDie(playersao);
+ }
+}
+
+void Server::handleCommand_Breath(NetworkPacket* pkt)
+{
+ u16 breath;
+
+ *pkt >> breath;
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ /*
+ * If player is dead, we don't need to update the breath
+ * He is dead !
+ */
+ if (player->isDead()) {
+ verbosestream << "TOSERVER_BREATH: " << player->getName()
+ << " is dead. Ignoring packet";
+ return;
+ }
+
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ playersao->setBreath(breath);
+ SendPlayerBreath(pkt->getPeerId());
+}
+
+void Server::handleCommand_Password(NetworkPacket* pkt)
+{
+ if (pkt->getSize() != PASSWORD_SIZE * 2)
+ return;
+
+ std::string oldpwd;
+ std::string newpwd;
+
+ // Deny for clients using the new protocol
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Created);
+ if (client->net_proto_version >= 25) {
+ infostream << "Server::handleCommand_Password(): Denying change: "
+ << " Client protocol version for peer_id=" << pkt->getPeerId()
+ << " too new!" << std::endl;
+ return;
+ }
+
+ for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+ char c = pkt->getChar(i);
+ if (c == 0)
+ break;
+ oldpwd += c;
+ }
+
+ for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
+ char c = pkt->getChar(PASSWORD_SIZE + i);
+ if (c == 0)
+ break;
+ newpwd += c;
+ }
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ if (!base64_is_valid(newpwd)) {
+ infostream<<"Server: " << player->getName() <<
+ " supplied invalid password hash" << std::endl;
+ // Wrong old password supplied!!
+ SendChatMessage(pkt->getPeerId(), L"Invalid new password hash supplied. Password NOT changed.");
+ return;
+ }
+
+ infostream << "Server: Client requests a password change from "
+ << "'" << oldpwd << "' to '" << newpwd << "'" << std::endl;
+
+ std::string playername = player->getName();
+
+ std::string checkpwd;
+ m_script->getAuth(playername, &checkpwd, NULL);
+
+ if (oldpwd != checkpwd) {
+ infostream << "Server: invalid old password" << std::endl;
+ // Wrong old password supplied!!
+ SendChatMessage(pkt->getPeerId(), L"Invalid old password supplied. Password NOT changed.");
+ return;
+ }
+
+ bool success = m_script->setPassword(playername, newpwd);
+ if (success) {
+ actionstream << player->getName() << " changes password" << std::endl;
+ SendChatMessage(pkt->getPeerId(), L"Password change successful.");
+ } else {
+ actionstream << player->getName() << " tries to change password but "
+ << "it fails" << std::endl;
+ SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
+ }
+}
+
+void Server::handleCommand_PlayerItem(NetworkPacket* pkt)
+{
+ if (pkt->getSize() < 2)
+ return;
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ u16 item;
+
+ *pkt >> item;
+
+ playersao->setWieldIndex(item);
+}
+
+void Server::handleCommand_Respawn(NetworkPacket* pkt)
+{
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ if (!player->isDead())
+ return;
+
+ RespawnPlayer(pkt->getPeerId());
+
+ actionstream << player->getName() << " respawns at "
+ << PP(player->getPosition()/BS) << std::endl;
+
+ // ActiveObject is added to environment in AsyncRunStep after
+ // the previous addition has been successfully removed
+}
+
+void Server::handleCommand_Interact(NetworkPacket* pkt)
+{
+ std::string datastring(pkt->getString(0), pkt->getSize());
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ /*
+ [0] u16 command
+ [2] u8 action
+ [3] u16 item
+ [5] u32 length of the next item
+ [9] serialized PointedThing
+ actions:
+ 0: start digging (from undersurface) or use
+ 1: stop digging (all parameters ignored)
+ 2: digging completed
+ 3: place block or item (to abovesurface)
+ 4: use item
+ */
+ u8 action = readU8(is);
+ u16 item_i = readU16(is);
+ std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
+ PointedThing pointed;
+ pointed.deSerialize(tmp_is);
+
+ verbosestream << "TOSERVER_INTERACT: action=" << (int)action << ", item="
+ << item_i << ", pointed=" << pointed.dump() << std::endl;
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ if (player->isDead()) {
+ verbosestream << "TOSERVER_INTERACT: " << player->getName()
+ << " is dead. Ignoring packet";
+ return;
+ }
+
+ v3f player_pos = playersao->getLastGoodPosition();
+
+ // Update wielded item
+ playersao->setWieldIndex(item_i);
+
+ // Get pointed to node (undefined if not POINTEDTYPE_NODE)
+ v3s16 p_under = pointed.node_undersurface;
+ v3s16 p_above = pointed.node_abovesurface;
+
+ // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
+ ServerActiveObject *pointed_object = NULL;
+ if (pointed.type == POINTEDTHING_OBJECT) {
+ pointed_object = m_env->getActiveObject(pointed.object_id);
+ if (pointed_object == NULL) {
+ verbosestream << "TOSERVER_INTERACT: "
+ "pointed object is NULL" << std::endl;
+ return;
+ }
+
+ }
+
+ v3f pointed_pos_under = player_pos;
+ v3f pointed_pos_above = player_pos;
+ if (pointed.type == POINTEDTHING_NODE) {
+ pointed_pos_under = intToFloat(p_under, BS);
+ pointed_pos_above = intToFloat(p_above, BS);
+ }
+ else if (pointed.type == POINTEDTHING_OBJECT) {
+ pointed_pos_under = pointed_object->getBasePosition();
+ pointed_pos_above = pointed_pos_under;
+ }
+
+ /*
+ Check that target is reasonably close
+ (only when digging or placing things)
+ */
+ if (action == 0 || action == 2 || action == 3) {
+ float d = player_pos.getDistanceFrom(pointed_pos_under);
+ float max_d = BS * 14; // Just some large enough value
+ if (d > max_d) {
+ actionstream << "Player " << player->getName()
+ << " tried to access " << pointed.dump()
+ << " from too far: "
+ << "d=" << d <<", max_d=" << max_d
+ << ". ignoring." << std::endl;
+ // Re-send block to revert change on client-side
+ RemoteClient *client = getClient(pkt->getPeerId());
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+ client->SetBlockNotSent(blockpos);
+ // Call callbacks
+ m_script->on_cheat(playersao, "interacted_too_far");
+ // Do nothing else
+ return;
+ }
+ }
+
+ /*
+ Make sure the player is allowed to do it
+ */
+ if (!checkPriv(player->getName(), "interact")) {
+ actionstream<<player->getName()<<" attempted to interact with "
+ <<pointed.dump()<<" without 'interact' privilege"
+ <<std::endl;
+ // Re-send block to revert change on client-side
+ RemoteClient *client = getClient(pkt->getPeerId());
+ // Digging completed -> under
+ if (action == 2) {
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+ client->SetBlockNotSent(blockpos);
+ }
+ // Placement -> above
+ if (action == 3) {
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+ client->SetBlockNotSent(blockpos);
+ }
+ return;
+ }
+
+ /*
+ If something goes wrong, this player is to blame
+ */
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:")+player->getName());
+
+ /*
+ 0: start digging or punch object
+ */
+ if (action == 0) {
+ if (pointed.type == POINTEDTHING_NODE) {
+ /*
+ NOTE: This can be used in the future to check if
+ somebody is cheating, by checking the timing.
+ */
+ MapNode n(CONTENT_IGNORE);
+ bool pos_ok;
+ n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+ if (pos_ok)
+ n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+
+ if (!pos_ok) {
+ infostream << "Server: Not punching: Node not found."
+ << " Adding block to emerge queue."
+ << std::endl;
+ m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
+ }
+
+ if (n.getContent() != CONTENT_IGNORE)
+ m_script->node_on_punch(p_under, n, playersao, pointed);
+ // Cheat prevention
+ playersao->noCheatDigStart(p_under);
+ }
+ else if (pointed.type == POINTEDTHING_OBJECT) {
+ // Skip if object has been removed
+ if (pointed_object->m_removed)
+ return;
+
+ actionstream<<player->getName()<<" punches object "
+ <<pointed.object_id<<": "
+ <<pointed_object->getDescription()<<std::endl;
+
+ ItemStack punchitem = playersao->getWieldedItem();
+ ToolCapabilities toolcap =
+ punchitem.getToolCapabilities(m_itemdef);
+ v3f dir = (pointed_object->getBasePosition() -
+ (player->getPosition() + player->getEyeOffset())
+ ).normalize();
+ float time_from_last_punch =
+ playersao->resetTimeFromLastPunch();
+
+ s16 src_original_hp = pointed_object->getHP();
+ s16 dst_origin_hp = playersao->getHP();
+
+ pointed_object->punch(dir, &toolcap, playersao,
+ time_from_last_punch);
+
+ // If the object is a player and its HP changed
+ if (src_original_hp != pointed_object->getHP() &&
+ pointed_object->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ SendPlayerHPOrDie((PlayerSAO *)pointed_object);
+ }
+
+ // If the puncher is a player and its HP changed
+ if (dst_origin_hp != playersao->getHP())
+ SendPlayerHPOrDie(playersao);
+ }
+
+ } // action == 0
+
+ /*
+ 1: stop digging
+ */
+ else if (action == 1) {
+ } // action == 1
+
+ /*
+ 2: Digging completed
+ */
+ else if (action == 2) {
+ // Only digging of nodes
+ if (pointed.type == POINTEDTHING_NODE) {
+ bool pos_ok;
+ MapNode n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
+ if (!pos_ok) {
+ infostream << "Server: Not finishing digging: Node not found."
+ << " Adding block to emerge queue."
+ << std::endl;
+ m_emerge->enqueueBlockEmerge(pkt->getPeerId(), getNodeBlockPos(p_above), false);
+ }
+
+ /* Cheat prevention */
+ bool is_valid_dig = true;
+ if (!isSingleplayer() && !g_settings->getBool("disable_anticheat")) {
+ v3s16 nocheat_p = playersao->getNoCheatDigPos();
+ float nocheat_t = playersao->getNoCheatDigTime();
+ playersao->noCheatDigEnd();
+ // If player didn't start digging this, ignore dig
+ if (nocheat_p != p_under) {
+ infostream << "Server: NoCheat: " << player->getName()
+ << " started digging "
+ << PP(nocheat_p) << " and completed digging "
+ << PP(p_under) << "; not digging." << std::endl;
+ is_valid_dig = false;
+ // Call callbacks
+ m_script->on_cheat(playersao, "finished_unknown_dig");
+ }
+ // Get player's wielded item
+ ItemStack playeritem;
+ InventoryList *mlist = playersao->getInventory()->getList("main");
+ if (mlist != NULL)
+ playeritem = mlist->getItem(playersao->getWieldIndex());
+ ToolCapabilities playeritem_toolcap =
+ playeritem.getToolCapabilities(m_itemdef);
+ // Get diggability and expected digging time
+ DigParams params = getDigParams(m_nodedef->get(n).groups,
+ &playeritem_toolcap);
+ // If can't dig, try hand
+ if (!params.diggable) {
+ const ItemDefinition &hand = m_itemdef->get("");
+ const ToolCapabilities *tp = hand.tool_capabilities;
+ if (tp)
+ params = getDigParams(m_nodedef->get(n).groups, tp);
+ }
+ // If can't dig, ignore dig
+ if (!params.diggable) {
+ infostream << "Server: NoCheat: " << player->getName()
+ << " completed digging " << PP(p_under)
+ << ", which is not diggable with tool. not digging."
+ << std::endl;
+ is_valid_dig = false;
+ // Call callbacks
+ m_script->on_cheat(playersao, "dug_unbreakable");
+ }
+ // Check digging time
+ // If already invalidated, we don't have to
+ if (!is_valid_dig) {
+ // Well not our problem then
+ }
+ // Clean and long dig
+ else if (params.time > 2.0 && nocheat_t * 1.2 > params.time) {
+ // All is good, but grab time from pool; don't care if
+ // it's actually available
+ playersao->getDigPool().grab(params.time);
+ }
+ // Short or laggy dig
+ // Try getting the time from pool
+ else if (playersao->getDigPool().grab(params.time)) {
+ // All is good
+ }
+ // Dig not possible
+ else {
+ infostream << "Server: NoCheat: " << player->getName()
+ << " completed digging " << PP(p_under)
+ << "too fast; not digging." << std::endl;
+ is_valid_dig = false;
+ // Call callbacks
+ m_script->on_cheat(playersao, "dug_too_fast");
+ }
+ }
+
+ /* Actually dig node */
+
+ if (is_valid_dig && n.getContent() != CONTENT_IGNORE)
+ m_script->node_on_dig(p_under, n, playersao);
+
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+ RemoteClient *client = getClient(pkt->getPeerId());
+ // Send unusual result (that is, node not being removed)
+ if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) {
+ // Re-send block to revert change on client-side
+ client->SetBlockNotSent(blockpos);
+ }
+ else {
+ client->ResendBlockIfOnWire(blockpos);
+ }
+ }
+ } // action == 2
+
+ /*
+ 3: place block or right-click object
+ */
+ else if (action == 3) {
+ ItemStack item = playersao->getWieldedItem();
+
+ // Reset build time counter
+ if (pointed.type == POINTEDTHING_NODE &&
+ item.getDefinition(m_itemdef).type == ITEM_NODE)
+ getClient(pkt->getPeerId())->m_time_from_building = 0.0;
+
+ if (pointed.type == POINTEDTHING_OBJECT) {
+ // Right click object
+
+ // Skip if object has been removed
+ if (pointed_object->m_removed)
+ return;
+
+ actionstream << player->getName() << " right-clicks object "
+ << pointed.object_id << ": "
+ << pointed_object->getDescription() << std::endl;
+
+ // Do stuff
+ pointed_object->rightClick(playersao);
+ }
+ else if (m_script->item_OnPlace(
+ item, playersao, pointed)) {
+ // Placement was handled in lua
+
+ // Apply returned ItemStack
+ if (playersao->setWieldedItem(item)) {
+ SendInventory(playersao);
+ }
+ }
+
+ // If item has node placement prediction, always send the
+ // blocks to make sure the client knows what exactly happened
+ RemoteClient *client = getClient(pkt->getPeerId());
+ v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
+ v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
+ if (item.getDefinition(m_itemdef).node_placement_prediction != "") {
+ client->SetBlockNotSent(blockpos);
+ if (blockpos2 != blockpos) {
+ client->SetBlockNotSent(blockpos2);
+ }
+ }
+ else {
+ client->ResendBlockIfOnWire(blockpos);
+ if (blockpos2 != blockpos) {
+ client->ResendBlockIfOnWire(blockpos2);
+ }
+ }
+ } // action == 3
+
+ /*
+ 4: use
+ */
+ else if (action == 4) {
+ ItemStack item = playersao->getWieldedItem();
+
+ actionstream << player->getName() << " uses " << item.name
+ << ", pointing at " << pointed.dump() << std::endl;
+
+ if (m_script->item_OnUse(
+ item, playersao, pointed)) {
+ // Apply returned ItemStack
+ if (playersao->setWieldedItem(item)) {
+ SendInventory(playersao);
+ }
+ }
+
+ } // action == 4
+
+
+ /*
+ Catch invalid actions
+ */
+ else {
+ infostream << "WARNING: Server: Invalid action "
+ << action << std::endl;
+ }
+}
+
+void Server::handleCommand_RemovedSounds(NetworkPacket* pkt)
+{
+ u16 num;
+ *pkt >> num;
+ for (u16 k = 0; k < num; k++) {
+ s32 id;
+
+ *pkt >> id;
+
+ std::map<s32, ServerPlayingSound>::iterator i =
+ m_playing_sounds.find(id);
+
+ if (i == m_playing_sounds.end())
+ continue;
+
+ ServerPlayingSound &psound = i->second;
+ psound.clients.erase(pkt->getPeerId());
+ if (psound.clients.empty())
+ m_playing_sounds.erase(i++);
+ }
+}
+
+void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
+{
+ v3s16 p;
+ std::string formname;
+ u16 num;
+
+ *pkt >> p >> formname >> num;
+
+ StringMap fields;
+ for (u16 k = 0; k < num; k++) {
+ std::string fieldname;
+ *pkt >> fieldname;
+ fields[fieldname] = pkt->readLongString();
+ }
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ // If something goes wrong, this player is to blame
+ RollbackScopeActor rollback_scope(m_rollback,
+ std::string("player:")+player->getName());
+
+ // Check the target node for rollback data; leave others unnoticed
+ RollbackNode rn_old(&m_env->getMap(), p, this);
+
+ m_script->node_on_receive_fields(p, formname, fields, playersao);
+
+ // Report rollback data
+ RollbackNode rn_new(&m_env->getMap(), p, this);
+ if (rollback() && rn_new != rn_old) {
+ RollbackAction action;
+ action.setSetNode(p, rn_old, rn_new);
+ rollback()->reportAction(action);
+ }
+}
+
+void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
+{
+ std::string formname;
+ u16 num;
+
+ *pkt >> formname >> num;
+
+ StringMap fields;
+ for (u16 k = 0; k < num; k++) {
+ std::string fieldname;
+ *pkt >> fieldname;
+ fields[fieldname] = pkt->readLongString();
+ }
+
+ Player *player = m_env->getPlayer(pkt->getPeerId());
+ if (player == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ PlayerSAO *playersao = player->getPlayerSAO();
+ if (playersao == NULL) {
+ errorstream << "Server::ProcessData(): Canceling: "
+ "No player object for peer_id=" << pkt->getPeerId()
+ << " disconnecting peer!" << std::endl;
+ m_con.DisconnectPeer(pkt->getPeerId());
+ return;
+ }
+
+ m_script->on_playerReceiveFields(playersao, formname, fields);
+}
+
+void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
+{
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
+ ClientState cstate = client->getState();
+
+ std::string playername = client->getName();
+
+ std::string salt;
+ std::string verification_key;
+
+ std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString();
+ u8 is_empty;
+
+ *pkt >> salt >> verification_key >> is_empty;
+
+ verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s
+ << ", with is_empty= " << is_empty << std::endl;
+
+ // Either this packet is sent because the user is new or to change the password
+ if (cstate == CS_HelloSent) {
+ if (!client->isMechAllowed(AUTH_MECHANISM_FIRST_SRP)) {
+ actionstream << "Server: Client from " << addr_s
+ << " tried to set password without being "
+ << "authenticated, or the username being new." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+
+ if (!isSingleplayer() &&
+ g_settings->getBool("disallow_empty_password") &&
+ is_empty == 1) {
+ actionstream << "Server: " << playername
+ << " supplied empty password from " << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
+ return;
+ }
+
+ std::string initial_ver_key;
+
+ initial_ver_key = encodeSRPVerifier(verification_key, salt);
+ m_script->createAuth(playername, initial_ver_key);
+
+ acceptAuth(pkt->getPeerId(), false);
+ } else {
+ if (cstate < CS_SudoMode) {
+ infostream << "Server::ProcessData(): Ignoring TOSERVER_FIRST_SRP from "
+ << addr_s << ": " << "Client has wrong state " << cstate << "."
+ << std::endl;
+ return;
+ }
+ m_clients.event(pkt->getPeerId(), CSE_SudoLeave);
+ std::string pw_db_field = encodeSRPVerifier(verification_key, salt);
+ bool success = m_script->setPassword(playername, pw_db_field);
+ if (success) {
+ actionstream << playername << " changes password" << std::endl;
+ SendChatMessage(pkt->getPeerId(), L"Password change successful.");
+ } else {
+ actionstream << playername << " tries to change password but "
+ << "it fails" << std::endl;
+ SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
+ }
+ }
+}
+
+void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
+{
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
+ ClientState cstate = client->getState();
+
+ bool wantSudo = (cstate == CS_Active);
+
+ if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
+ actionstream << "Server: got SRP _A packet in wrong state "
+ << cstate << " from "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << ". Ignoring." << std::endl;
+ return;
+ }
+
+ if (client->chosen_mech != AUTH_MECHANISM_NONE) {
+ actionstream << "Server: got SRP _A packet, while auth"
+ << "is already going on with mech " << client->chosen_mech
+ << " from " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl;
+ if (wantSudo) {
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ std::string bytes_A;
+ u8 based_on;
+ *pkt >> bytes_A >> based_on;
+
+ infostream << "Server: TOSERVER_SRP_BYTES_A received with "
+ << "based_on=" << int(based_on) << " and len_A="
+ << bytes_A.length() << "." << std::endl;
+
+ AuthMechanism chosen = (based_on == 0) ?
+ AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP;
+
+ if (wantSudo) {
+ if (!client->isSudoMechAllowed(chosen)) {
+ actionstream << "Server: Player \"" << client->getName()
+ << "\" at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " tried to change password using unallowed mech "
+ << chosen << "." << std::endl;
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ }
+ } else {
+ if (!client->isMechAllowed(chosen)) {
+ actionstream << "Server: Client tried to authenticate from "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " using unallowed mech " << chosen << "." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ client->chosen_mech = chosen;
+
+ std::string bytes_s;
+ std::string bytes_v;
+
+ if (based_on == 0) {
+ char *p_bytes_s = 0;
+ size_t len_s = 0;
+ char *p_bytes_v = 0;
+ size_t len_v = 0;
+ getSRPVerifier(client->getName(), client->enc_pwd,
+ &p_bytes_s, &len_s,
+ &p_bytes_v, &len_v);
+ bytes_s = std::string(p_bytes_s, len_s);
+ bytes_v = std::string(p_bytes_v, len_v);
+ free(p_bytes_s);
+ free(p_bytes_v);
+ } else if (!decodeSRPVerifier(client->enc_pwd, &bytes_s, &bytes_v)) {
+ // Non-base64 errors should have been catched in the init handler
+ actionstream << "Server: User " << client->getName()
+ << " tried to log in, but srp verifier field"
+ << " was invalid (most likely invalid base64)." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+ return;
+ }
+
+ char *bytes_B = 0;
+ size_t len_B = 0;
+
+ client->auth_data = srp_verifier_new(SRP_SHA256, SRP_NG_2048,
+ client->getName().c_str(),
+ (const unsigned char *) bytes_s.c_str(), bytes_s.size(),
+ (const unsigned char *) bytes_v.c_str(), bytes_v.size(),
+ (const unsigned char *) bytes_A.c_str(), bytes_A.size(),
+ NULL, 0,
+ (unsigned char **) &bytes_B, &len_B, NULL, NULL);
+
+ if (!bytes_B) {
+ actionstream << "Server: User " << client->getName()
+ << " tried to log in, SRP-6a safety check violated in _A handler."
+ << std::endl;
+ if (wantSudo) {
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId());
+ resp_pkt << bytes_s << std::string(bytes_B, len_B);
+ Send(&resp_pkt);
+}
+
+void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
+{
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
+ ClientState cstate = client->getState();
+
+ bool wantSudo = (cstate == CS_Active);
+
+ verbosestream << "Server: Recieved TOCLIENT_SRP_BYTES_M." << std::endl;
+
+ if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
+ actionstream << "Server: got SRP _M packet in wrong state "
+ << cstate << " from "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << ". Ignoring." << std::endl;
+ return;
+ }
+
+ if ((client->chosen_mech != AUTH_MECHANISM_SRP)
+ && (client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD)) {
+ actionstream << "Server: got SRP _M packet, while auth"
+ << "is going on with mech " << client->chosen_mech
+ << " from " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " (wantSudo=" << wantSudo << "). Denying." << std::endl;
+ if (wantSudo) {
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ std::string bytes_M;
+ *pkt >> bytes_M;
+
+ if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data)
+ != bytes_M.size()) {
+ actionstream << "Server: User " << client->getName()
+ << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " sent bytes_M with invalid length " << bytes_M.size() << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+
+ unsigned char *bytes_HAMK = 0;
+
+ srp_verifier_verify_session((SRPVerifier *) client->auth_data,
+ (unsigned char *)bytes_M.c_str(), &bytes_HAMK);
+
+ if (!bytes_HAMK) {
+ if (wantSudo) {
+ actionstream << "Server: User " << client->getName()
+ << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " tried to change their password, but supplied wrong"
+ << " (SRP) password for authentication." << std::endl;
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ actionstream << "Server: User " << client->getName()
+ << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " supplied wrong (SRP) password from address "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << "." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+ return;
+ }
+ }
+
+ if (client->create_player_on_auth_success) {
+ std::string playername = client->getName();
+ m_script->createAuth(playername, client->enc_pwd);
+
+ std::string checkpwd; // not used, but needed for passing something
+ if (!m_script->getAuth(playername, &checkpwd, NULL)) {
+ actionstream << "Server: " << playername << " cannot be authenticated"
+ << " (auth handler does not work?)" << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+ return;
+ }
+ client->create_player_on_auth_success = false;
+ }
+
+ acceptAuth(pkt->getPeerId(), wantSudo);
+}
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index bcf51a072..269c2b9d6 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -19,10 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
-#include "main.h" // For g_settings
#include "itemdef.h"
#ifndef SERVER
-#include "tile.h"
+#include "client/tile.h"
#include "mesh.h"
#include <IMeshManipulator.h>
#endif
@@ -34,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "debug.h"
#include "gamedef.h"
+#include <fstream> // Used in applyTextureOverrides()
/*
NodeBox
@@ -120,7 +120,9 @@ void NodeBox::deSerialize(std::istream &is)
void TileDef::serialize(std::ostream &os, u16 protocol_version) const
{
- if(protocol_version >= 17)
+ if (protocol_version >= 26)
+ writeU8(os, 2);
+ else if (protocol_version >= 17)
writeU8(os, 1);
else
writeU8(os, 0);
@@ -129,8 +131,12 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const
writeU16(os, animation.aspect_w);
writeU16(os, animation.aspect_h);
writeF1000(os, animation.length);
- if(protocol_version >= 17)
+ if (protocol_version >= 17)
writeU8(os, backface_culling);
+ if (protocol_version >= 26) {
+ writeU8(os, tileable_horizontal);
+ writeU8(os, tileable_vertical);
+ }
}
void TileDef::deSerialize(std::istream &is)
@@ -141,10 +147,15 @@ void TileDef::deSerialize(std::istream &is)
animation.aspect_w = readU16(is);
animation.aspect_h = readU16(is);
animation.length = readF1000(is);
- if(version >= 1)
+ if (version >= 1)
backface_culling = readU8(is);
+ if (version >= 2) {
+ tileable_horizontal = readU8(is);
+ tileable_vertical = readU8(is);
+ }
}
+
/*
SimpleSoundSpec serialization
*/
@@ -183,6 +194,7 @@ void ContentFeatures::reset()
solidness = 2;
visual_solidness = 0;
backface_culling = true;
+
#endif
has_on_construct = false;
has_on_destruct = false;
@@ -202,6 +214,7 @@ void ContentFeatures::reset()
#ifndef SERVER
for(u32 i = 0; i < 24; i++)
mesh_ptr[i] = NULL;
+ minimap_color = video::SColor(0, 0, 0, 0);
#endif
visual_scale = 1.0;
for(u32 i = 0; i < 6; i++)
@@ -242,7 +255,7 @@ void ContentFeatures::reset()
sound_dug = SimpleSoundSpec();
}
-void ContentFeatures::serialize(std::ostream &os, u16 protocol_version)
+void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
{
if(protocol_version < 24){
serializeOld(os, protocol_version);
@@ -398,21 +411,20 @@ public:
virtual content_t set(const std::string &name, const ContentFeatures &def);
virtual content_t allocateDummy(const std::string &name);
virtual void updateAliases(IItemDefManager *idef);
- virtual void updateTextures(IGameDef *gamedef);
- void serialize(std::ostream &os, u16 protocol_version);
+ virtual void applyTextureOverrides(const std::string &override_filepath);
+ virtual void updateTextures(IGameDef *gamedef,
+ void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
+ void *progress_cbk_args);
+ void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
inline virtual bool getNodeRegistrationStatus() const;
inline virtual void setNodeRegistrationStatus(bool completed);
- virtual void pendNodeResolve(NodeResolveInfo *nri);
- virtual void cancelNodeResolve(NodeResolver *resolver);
- virtual void runNodeResolverCallbacks();
-
- virtual bool getIdFromResolveInfo(NodeResolveInfo *nri,
- const std::string &node_alt, content_t c_fallback, content_t &result);
- virtual bool getIdsFromResolveInfo(NodeResolveInfo *nri,
- std::vector<content_t> &result);
+ virtual void pendNodeResolve(NodeResolver *nr);
+ virtual bool cancelNodeResolveCallback(NodeResolver *nr);
+ virtual void runNodeResolveCallbacks();
+ virtual void resetNodeResolveState();
private:
void addNameIdMapping(content_t i, std::string name);
@@ -442,8 +454,8 @@ private:
// Next possibly free id
content_t m_next_id;
- // List of node strings and node resolver callbacks to perform
- std::list<NodeResolveInfo *> m_pending_node_lookups;
+ // NodeResolvers to callback once node registration has ended
+ std::vector<NodeResolver *> m_pending_resolve_callbacks;
// True when all nodes have been registered
bool m_node_registration_complete;
@@ -478,13 +490,7 @@ void CNodeDefManager::clear()
m_group_to_items.clear();
m_next_id = 0;
- m_node_registration_complete = false;
- for (std::list<NodeResolveInfo *>::iterator
- it = m_pending_node_lookups.begin();
- it != m_pending_node_lookups.end();
- ++it)
- delete *it;
- m_pending_node_lookups.clear();
+ resetNodeResolveState();
u32 initial_length = 0;
initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1);
@@ -641,6 +647,7 @@ content_t CNodeDefManager::allocateId()
// IWritableNodeDefManager
content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def)
{
+ // Pre-conditions
assert(name != "");
assert(name == def.name);
@@ -678,7 +685,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
j = m_group_to_items.find(group_name);
if (j == m_group_to_items.end()) {
m_group_to_items[group_name].push_back(
- std::make_pair(id, i->second));
+ std::make_pair(id, i->second));
} else {
GroupItems &items = j->second;
items.push_back(std::make_pair(id, i->second));
@@ -690,7 +697,7 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d
content_t CNodeDefManager::allocateDummy(const std::string &name)
{
- assert(name != "");
+ assert(name != ""); // Pre-condition
ContentFeatures f;
f.name = name;
return set(name, f);
@@ -708,38 +715,104 @@ void CNodeDefManager::updateAliases(IItemDefManager *idef)
content_t id;
if (m_name_id_mapping.getId(convert_to, id)) {
m_name_id_mapping_with_aliases.insert(
- std::make_pair(name, id));
+ std::make_pair(name, id));
}
}
}
+void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath)
+{
+ infostream << "CNodeDefManager::applyTextureOverrides(): Applying "
+ "overrides to textures from " << override_filepath << std::endl;
+
+ std::ifstream infile(override_filepath.c_str());
+ std::string line;
+ int line_c = 0;
+ while (std::getline(infile, line)) {
+ line_c++;
+ if (trim(line) == "")
+ continue;
+ std::vector<std::string> splitted = str_split(line, ' ');
+ if (splitted.size() != 3) {
+ errorstream << override_filepath
+ << ":" << line_c << " Could not apply texture override \""
+ << line << "\": Syntax error" << std::endl;
+ continue;
+ }
+
+ content_t id;
+ if (!getId(splitted[0], id)) {
+ errorstream << override_filepath
+ << ":" << line_c << " Could not apply texture override \""
+ << line << "\": Unknown node \""
+ << splitted[0] << "\"" << std::endl;
+ continue;
+ }
+
+ ContentFeatures &nodedef = m_content_features[id];
+
+ if (splitted[1] == "top")
+ nodedef.tiledef[0].name = splitted[2];
+ else if (splitted[1] == "bottom")
+ nodedef.tiledef[1].name = splitted[2];
+ else if (splitted[1] == "right")
+ nodedef.tiledef[2].name = splitted[2];
+ else if (splitted[1] == "left")
+ nodedef.tiledef[3].name = splitted[2];
+ else if (splitted[1] == "back")
+ nodedef.tiledef[4].name = splitted[2];
+ else if (splitted[1] == "front")
+ nodedef.tiledef[5].name = splitted[2];
+ else if (splitted[1] == "all" || splitted[1] == "*")
+ for (int i = 0; i < 6; i++)
+ nodedef.tiledef[i].name = splitted[2];
+ else if (splitted[1] == "sides")
+ for (int i = 2; i < 6; i++)
+ nodedef.tiledef[i].name = splitted[2];
+ else {
+ errorstream << override_filepath
+ << ":" << line_c << " Could not apply texture override \""
+ << line << "\": Unknown node side \""
+ << splitted[1] << "\"" << std::endl;
+ continue;
+ }
+ }
+}
-void CNodeDefManager::updateTextures(IGameDef *gamedef)
+void CNodeDefManager::updateTextures(IGameDef *gamedef,
+ void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress),
+ void *progress_callback_args)
{
#ifndef SERVER
infostream << "CNodeDefManager::updateTextures(): Updating "
"textures in node definitions" << std::endl;
-
ITextureSource *tsrc = gamedef->tsrc();
IShaderSource *shdsrc = gamedef->getShaderSource();
scene::ISceneManager* smgr = gamedef->getSceneManager();
scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
bool new_style_water = g_settings->getBool("new_style_water");
- bool new_style_leaves = g_settings->getBool("new_style_leaves");
bool connected_glass = g_settings->getBool("connected_glass");
bool opaque_water = g_settings->getBool("opaque_water");
bool enable_shaders = g_settings->getBool("enable_shaders");
bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
+ bool enable_minimap = g_settings->getBool("enable_minimap");
+ std::string leaves_style = g_settings->get("leaves_style");
bool use_normal_texture = enable_shaders &&
(enable_bumpmapping || enable_parallax_occlusion);
- for (u32 i = 0; i < m_content_features.size(); i++) {
+ u32 size = m_content_features.size();
+
+ for (u32 i = 0; i < size; i++) {
ContentFeatures *f = &m_content_features[i];
+ // minimap pixel color - the average color of a texture
+ if (enable_minimap && f->tiledef[0].name != "")
+ f->minimap_color = tsrc->getTextureAverageColor(f->tiledef[0].name);
+
// Figure out the actual tiles to use
TileDef tiledef[6];
for (u32 j = 0; j < 6; j++) {
@@ -799,10 +872,18 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef)
f->visual_solidness = 1;
break;
case NDT_ALLFACES_OPTIONAL:
- if (new_style_leaves) {
+ if (leaves_style == "fancy") {
f->drawtype = NDT_ALLFACES;
f->solidness = 0;
f->visual_solidness = 1;
+ } else if (leaves_style == "simple") {
+ for (u32 j = 0; j < 6; j++) {
+ if (f->tiledef_special[j].name != "")
+ tiledef[j].name = f->tiledef_special[j].name;
+ }
+ f->drawtype = NDT_GLASSLIKE;
+ f->solidness = 0;
+ f->visual_solidness = 1;
} else {
f->drawtype = NDT_NORMAL;
f->solidness = 2;
@@ -910,6 +991,8 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef)
recalculateBoundingBox(f->mesh_ptr[0]);
meshmanip->recalculateNormals(f->mesh_ptr[0], true, false);
}
+
+ progress_callback(progress_callback_args, i, size);
}
#endif
}
@@ -921,13 +1004,15 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
bool backface_culling, u8 alpha, u8 material_type)
{
tile->shader_id = shader_id;
- tile->texture = tsrc->getTexture(tiledef->name, &tile->texture_id);
+ tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id);
tile->alpha = alpha;
tile->material_type = material_type;
- // Normal texture
- if (use_normal_texture)
+ // Normal texture and shader flags texture
+ if (use_normal_texture) {
tile->normal_texture = tsrc->getNormalTexture(tiledef->name);
+ }
+ tile->flags_texture = tsrc->getShaderFlagsTexture(tile->normal_texture ? true : false);
// Material flags
tile->material_flags = 0;
@@ -935,6 +1020,10 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
if (tiledef->animation.type == TAT_VERTICAL_FRAMES)
tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES;
+ if (tiledef->tileable_horizontal)
+ tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL;
+ if (tiledef->tileable_vertical)
+ tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
// Animation parameters
int frame_count = 1;
@@ -964,9 +1053,10 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
os << tiledef->name << "^[verticalframe:"
<< frame_count << ":" << i;
- frame.texture = tsrc->getTexture(os.str(), &frame.texture_id);
+ frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id);
if (tile->normal_texture)
frame.normal_texture = tsrc->getNormalTexture(os.str());
+ frame.flags_texture = tile->flags_texture;
tile->frames[i] = frame;
}
}
@@ -974,7 +1064,7 @@ void CNodeDefManager::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile,
#endif
-void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
+void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version) const
{
writeU8(os, 1); // version
u16 count = 0;
@@ -983,7 +1073,7 @@ void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
if (i == CONTENT_IGNORE || i == CONTENT_AIR
|| i == CONTENT_UNKNOWN)
continue;
- ContentFeatures *f = &m_content_features[i];
+ const ContentFeatures *f = &m_content_features[i];
if (f->name == "")
continue;
writeU16(os2, i);
@@ -993,7 +1083,9 @@ void CNodeDefManager::serialize(std::ostream &os, u16 protocol_version)
f->serialize(wrapper_os, protocol_version);
os2<<serializeString(wrapper_os.str());
- assert(count + 1 > count); // must not overflow
+ // must not overflow
+ u16 next = count + 1;
+ FATAL_ERROR_IF(next < count, "Overflow");
count++;
}
writeU16(os, count);
@@ -1062,7 +1154,7 @@ IWritableNodeDefManager *createNodeDefManager()
//// Serialization of old ContentFeatures formats
-void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version)
+void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const
{
if (protocol_version == 13)
{
@@ -1294,114 +1386,152 @@ inline void CNodeDefManager::setNodeRegistrationStatus(bool completed)
}
-void CNodeDefManager::pendNodeResolve(NodeResolveInfo *nri)
+void CNodeDefManager::pendNodeResolve(NodeResolver *nr)
{
- nri->resolver->m_ndef = this;
- if (m_node_registration_complete) {
- nri->resolver->resolveNodeNames(nri);
- nri->resolver->m_lookup_done = true;
- delete nri;
- } else {
- m_pending_node_lookups.push_back(nri);
- }
+ nr->m_ndef = this;
+ if (m_node_registration_complete)
+ nr->nodeResolveInternal();
+ else
+ m_pending_resolve_callbacks.push_back(nr);
}
-void CNodeDefManager::cancelNodeResolve(NodeResolver *resolver)
+bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr)
{
- for (std::list<NodeResolveInfo *>::iterator
- it = m_pending_node_lookups.begin();
- it != m_pending_node_lookups.end();
- ++it) {
- NodeResolveInfo *nri = *it;
- if (resolver == nri->resolver) {
- it = m_pending_node_lookups.erase(it);
- delete nri;
- }
+ size_t len = m_pending_resolve_callbacks.size();
+ for (size_t i = 0; i != len; i++) {
+ if (nr != m_pending_resolve_callbacks[i])
+ continue;
+
+ len--;
+ m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len];
+ m_pending_resolve_callbacks.resize(len);
+ return true;
}
+
+ return false;
}
-void CNodeDefManager::runNodeResolverCallbacks()
+void CNodeDefManager::runNodeResolveCallbacks()
{
- while (!m_pending_node_lookups.empty()) {
- NodeResolveInfo *nri = m_pending_node_lookups.front();
- m_pending_node_lookups.pop_front();
- nri->resolver->resolveNodeNames(nri);
- nri->resolver->m_lookup_done = true;
- delete nri;
+ for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) {
+ NodeResolver *nr = m_pending_resolve_callbacks[i];
+ nr->nodeResolveInternal();
}
+
+ m_pending_resolve_callbacks.clear();
+}
+
+
+void CNodeDefManager::resetNodeResolveState()
+{
+ m_node_registration_complete = false;
+ m_pending_resolve_callbacks.clear();
+}
+
+
+////
+//// NodeResolver
+////
+
+NodeResolver::NodeResolver()
+{
+ m_ndef = NULL;
+ m_nodenames_idx = 0;
+ m_nnlistsizes_idx = 0;
+ m_resolve_done = false;
+
+ m_nodenames.reserve(16);
+ m_nnlistsizes.reserve(4);
+}
+
+
+NodeResolver::~NodeResolver()
+{
+ if (!m_resolve_done && m_ndef)
+ m_ndef->cancelNodeResolveCallback(this);
+}
+
+
+void NodeResolver::nodeResolveInternal()
+{
+ m_nodenames_idx = 0;
+ m_nnlistsizes_idx = 0;
+
+ resolveNodeNames();
+ m_resolve_done = true;
+
+ m_nodenames.clear();
+ m_nnlistsizes.clear();
}
-bool CNodeDefManager::getIdFromResolveInfo(NodeResolveInfo *nri,
- const std::string &node_alt, content_t c_fallback, content_t &result)
+bool NodeResolver::getIdFromNrBacklog(content_t *result_out,
+ const std::string &node_alt, content_t c_fallback)
{
- if (nri->nodenames.empty()) {
- result = c_fallback;
- errorstream << "Resolver empty nodename list" << std::endl;
+ if (m_nodenames_idx == m_nodenames.size()) {
+ *result_out = c_fallback;
+ errorstream << "NodeResolver: no more nodes in list" << std::endl;
return false;
}
content_t c;
- std::string name = nri->nodenames.front();
- nri->nodenames.pop_front();
+ std::string name = m_nodenames[m_nodenames_idx++];
- bool success = getId(name, c);
+ bool success = m_ndef->getId(name, c);
if (!success && node_alt != "") {
name = node_alt;
- success = getId(name, c);
+ success = m_ndef->getId(name, c);
}
if (!success) {
- errorstream << "Resolver: Failed to resolve node name '" << name
+ errorstream << "NodeResolver: failed to resolve node name '" << name
<< "'." << std::endl;
c = c_fallback;
}
- result = c;
+ *result_out = c;
return success;
}
-bool CNodeDefManager::getIdsFromResolveInfo(NodeResolveInfo *nri,
- std::vector<content_t> &result)
+bool NodeResolver::getIdsFromNrBacklog(std::vector<content_t> *result_out,
+ bool all_required, content_t c_fallback)
{
bool success = true;
- if (nri->nodelistinfo.empty()) {
- errorstream << "Resolver: Empty nodelistinfo list" << std::endl;
+ if (m_nnlistsizes_idx == m_nnlistsizes.size()) {
+ errorstream << "NodeResolver: no more node lists" << std::endl;
return false;
}
- NodeListInfo listinfo = nri->nodelistinfo.front();
- nri->nodelistinfo.pop_front();
+ size_t length = m_nnlistsizes[m_nnlistsizes_idx++];
- while (listinfo.length--) {
- if (nri->nodenames.empty()) {
- errorstream << "Resolver: Empty nodename list" << std::endl;
+ while (length--) {
+ if (m_nodenames_idx == m_nodenames.size()) {
+ errorstream << "NodeResolver: no more nodes in list" << std::endl;
return false;
}
content_t c;
- std::string name = nri->nodenames.front();
- nri->nodenames.pop_front();
+ std::string &name = m_nodenames[m_nodenames_idx++];
if (name.substr(0,6) != "group:") {
- if (getId(name, c)) {
- result.push_back(c);
- } else if (listinfo.all_required) {
- errorstream << "Resolver: Failed to resolve node name '" << name
- << "'." << std::endl;
- result.push_back(listinfo.c_fallback);
+ if (m_ndef->getId(name, c)) {
+ result_out->push_back(c);
+ } else if (all_required) {
+ errorstream << "NodeResolver: failed to resolve node name '"
+ << name << "'." << std::endl;
+ result_out->push_back(c_fallback);
success = false;
}
} else {
std::set<content_t> cids;
std::set<content_t>::iterator it;
- getIds(name, cids);
+ m_ndef->getIds(name, cids);
for (it = cids.begin(); it != cids.end(); ++it)
- result.push_back(*it);
+ result_out->push_back(*it);
}
}
diff --git a/src/nodedef.h b/src/nodedef.h
index 7280bb8ea..a4a7b6e41 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -25,19 +25,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include <map>
#include <list>
+#include "util/numeric.h"
#include "mapnode.h"
#ifndef SERVER
-#include "tile.h"
+#include "client/tile.h"
#include "shader.h"
#endif
#include "itemgroup.h"
#include "sound.h" // SimpleSoundSpec
#include "constants.h" // BS
+class INodeDefManager;
class IItemDefManager;
class ITextureSource;
class IShaderSource;
class IGameDef;
+class NodeResolver;
typedef std::list<std::pair<content_t, int> > GroupItems;
@@ -110,6 +113,8 @@ struct TileDef
{
std::string name;
bool backface_culling; // Takes effect only in special cases
+ bool tileable_horizontal;
+ bool tileable_vertical;
struct{
enum TileAnimationType type;
int aspect_w; // width for aspect ratio
@@ -121,6 +126,8 @@ struct TileDef
{
name = "";
backface_culling = true;
+ tileable_horizontal = true;
+ tileable_vertical = true;
animation.type = TAT_NONE;
animation.aspect_w = 1;
animation.aspect_h = 1;
@@ -191,6 +198,7 @@ struct ContentFeatures
std::string mesh;
#ifndef SERVER
scene::IMesh *mesh_ptr[24];
+ video::SColor minimap_color;
#endif
float visual_scale; // Misc. scale parameter
TileDef tiledef[6];
@@ -199,6 +207,7 @@ struct ContentFeatures
// Post effect color, drawn when the camera is inside the node.
video::SColor post_effect_color;
+
// Type of MapNode::param1
ContentParamType param_type;
// Type of MapNode::param2
@@ -263,9 +272,9 @@ struct ContentFeatures
ContentFeatures();
~ContentFeatures();
void reset();
- void serialize(std::ostream &os, u16 protocol_version);
+ void serialize(std::ostream &os, u16 protocol_version) const;
void deSerialize(std::istream &is);
- void serializeOld(std::ostream &os, u16 protocol_version);
+ void serializeOld(std::ostream &os, u16 protocol_version) const;
void deSerializeOld(std::istream &is, int version);
/*
@@ -280,87 +289,44 @@ struct ContentFeatures
}
};
-class NodeResolver;
-class INodeDefManager;
-
-struct NodeListInfo {
- NodeListInfo(u32 len)
- {
- length = len;
- all_required = false;
- c_fallback = CONTENT_IGNORE;
- }
-
- NodeListInfo(u32 len, content_t fallback)
- {
- length = len;
- all_required = true;
- c_fallback = fallback;
- }
-
- u32 length;
- bool all_required;
- content_t c_fallback;
-};
-
-struct NodeResolveInfo {
- NodeResolveInfo(NodeResolver *nr)
- {
- resolver = nr;
- }
-
- std::list<std::string> nodenames;
- std::list<NodeListInfo> nodelistinfo;
- NodeResolver *resolver;
-};
-
-class INodeDefManager
-{
+class INodeDefManager {
public:
INodeDefManager(){}
virtual ~INodeDefManager(){}
// Get node definition
- virtual const ContentFeatures& get(content_t c) const=0;
- virtual const ContentFeatures& get(const MapNode &n) const=0;
+ virtual const ContentFeatures &get(content_t c) const=0;
+ virtual const ContentFeatures &get(const MapNode &n) const=0;
virtual bool getId(const std::string &name, content_t &result) const=0;
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
virtual void getIds(const std::string &name, std::set<content_t> &result)
const=0;
- virtual const ContentFeatures& get(const std::string &name) const=0;
+ virtual const ContentFeatures &get(const std::string &name) const=0;
- virtual void serialize(std::ostream &os, u16 protocol_version)=0;
+ virtual void serialize(std::ostream &os, u16 protocol_version) const=0;
virtual bool getNodeRegistrationStatus() const=0;
- virtual void setNodeRegistrationStatus(bool completed)=0;
-
- virtual void pendNodeResolve(NodeResolveInfo *nri)=0;
- virtual void cancelNodeResolve(NodeResolver *resolver)=0;
- virtual void runNodeResolverCallbacks()=0;
- virtual bool getIdFromResolveInfo(NodeResolveInfo *nri,
- const std::string &node_alt, content_t c_fallback, content_t &result)=0;
- virtual bool getIdsFromResolveInfo(NodeResolveInfo *nri,
- std::vector<content_t> &result)=0;
+ virtual void pendNodeResolve(NodeResolver *nr)=0;
+ virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
};
-class IWritableNodeDefManager : public INodeDefManager
-{
+class IWritableNodeDefManager : public INodeDefManager {
public:
IWritableNodeDefManager(){}
virtual ~IWritableNodeDefManager(){}
virtual IWritableNodeDefManager* clone()=0;
// Get node definition
- virtual const ContentFeatures& get(content_t c) const=0;
- virtual const ContentFeatures& get(const MapNode &n) const=0;
+ virtual const ContentFeatures &get(content_t c) const=0;
+ virtual const ContentFeatures &get(const MapNode &n) const=0;
virtual bool getId(const std::string &name, content_t &result) const=0;
// If not found, returns CONTENT_IGNORE
virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names
virtual void getIds(const std::string &name, std::set<content_t> &result)
- const=0;
+ const=0;
// If not found, returns the features of CONTENT_UNKNOWN
- virtual const ContentFeatures& get(const std::string &name) const=0;
+ virtual const ContentFeatures &get(const std::string &name) const=0;
// Register node definition by name (allocate an id)
// If returns CONTENT_IGNORE, could not allocate id
@@ -376,48 +342,50 @@ public:
virtual void updateAliases(IItemDefManager *idef)=0;
/*
+ Override textures from servers with ones specified in texturepack/override.txt
+ */
+ virtual void applyTextureOverrides(const std::string &override_filepath)=0;
+
+ /*
Update tile textures to latest return values of TextueSource.
*/
- virtual void updateTextures(IGameDef *gamedef)=0;
+ virtual void updateTextures(IGameDef *gamedef,
+ void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress),
+ void *progress_cbk_args)=0;
- virtual void serialize(std::ostream &os, u16 protocol_version)=0;
+ virtual void serialize(std::ostream &os, u16 protocol_version) const=0;
virtual void deSerialize(std::istream &is)=0;
virtual bool getNodeRegistrationStatus() const=0;
virtual void setNodeRegistrationStatus(bool completed)=0;
- virtual void pendNodeResolve(NodeResolveInfo *nri)=0;
- virtual void cancelNodeResolve(NodeResolver *resolver)=0;
- virtual void runNodeResolverCallbacks()=0;
-
- virtual bool getIdFromResolveInfo(NodeResolveInfo *nri,
- const std::string &node_alt, content_t c_fallback, content_t &result)=0;
- virtual bool getIdsFromResolveInfo(NodeResolveInfo *nri,
- std::vector<content_t> &result)=0;
+ virtual void pendNodeResolve(NodeResolver *nr)=0;
+ virtual bool cancelNodeResolveCallback(NodeResolver *nr)=0;
+ virtual void runNodeResolveCallbacks()=0;
+ virtual void resetNodeResolveState()=0;
};
IWritableNodeDefManager *createNodeDefManager();
class NodeResolver {
public:
- NodeResolver()
- {
- m_lookup_done = false;
- m_ndef = NULL;
- }
+ NodeResolver();
+ virtual ~NodeResolver();
+ virtual void resolveNodeNames() = 0;
- virtual ~NodeResolver()
- {
- if (!m_lookup_done && m_ndef)
- m_ndef->cancelNodeResolve(this);
- }
+ bool getIdFromNrBacklog(content_t *result_out,
+ const std::string &node_alt, content_t c_fallback);
+ bool getIdsFromNrBacklog(std::vector<content_t> *result_out,
+ bool all_required=false, content_t c_fallback=CONTENT_IGNORE);
- virtual void resolveNodeNames(NodeResolveInfo *nri) = 0;
+ void nodeResolveInternal();
- bool m_lookup_done;
+ u32 m_nodenames_idx;
+ u32 m_nnlistsizes_idx;
+ std::vector<std::string> m_nodenames;
+ std::vector<size_t> m_nnlistsizes;
INodeDefManager *m_ndef;
+ bool m_resolve_done;
};
-
#endif
-
diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp
index 1e40a1630..d4da7a5ed 100644
--- a/src/nodemetadata.cpp
+++ b/src/nodemetadata.cpp
@@ -45,10 +45,11 @@ void NodeMetadata::serialize(std::ostream &os) const
{
int num_vars = m_stringvars.size();
writeU32(os, num_vars);
- for(std::map<std::string, std::string>::const_iterator
- i = m_stringvars.begin(); i != m_stringvars.end(); i++){
- os<<serializeString(i->first);
- os<<serializeLongString(i->second);
+ for (StringMap::const_iterator
+ it = m_stringvars.begin();
+ it != m_stringvars.end(); ++it) {
+ os << serializeString(it->first);
+ os << serializeLongString(it->second);
}
m_inventory->serialize(os);
@@ -157,10 +158,21 @@ NodeMetadataList::~NodeMetadataList()
clear();
}
-NodeMetadata* NodeMetadataList::get(v3s16 p)
+std::vector<v3s16> NodeMetadataList::getAllKeys()
{
- std::map<v3s16, NodeMetadata*>::const_iterator n = m_data.find(p);
- if(n == m_data.end())
+ std::vector<v3s16> keys;
+
+ std::map<v3s16, NodeMetadata *>::const_iterator it;
+ for (it = m_data.begin(); it != m_data.end(); ++it)
+ keys.push_back(it->first);
+
+ return keys;
+}
+
+NodeMetadata *NodeMetadataList::get(v3s16 p)
+{
+ std::map<v3s16, NodeMetadata *>::const_iterator n = m_data.find(p);
+ if (n == m_data.end())
return NULL;
return n->second;
}
@@ -168,8 +180,7 @@ NodeMetadata* NodeMetadataList::get(v3s16 p)
void NodeMetadataList::remove(v3s16 p)
{
NodeMetadata *olddata = get(p);
- if(olddata)
- {
+ if (olddata) {
delete olddata;
m_data.erase(p);
}
@@ -183,22 +194,20 @@ void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
void NodeMetadataList::clear()
{
- for(std::map<v3s16, NodeMetadata*>::iterator
- i = m_data.begin();
- i != m_data.end(); i++)
- {
- delete i->second;
+ std::map<v3s16, NodeMetadata*>::iterator it;
+ for (it = m_data.begin(); it != m_data.end(); ++it) {
+ delete it->second;
}
m_data.clear();
}
-std::string NodeMetadata::getString(const std::string &name, unsigned short recursion) const
+std::string NodeMetadata::getString(const std::string &name,
+ unsigned short recursion) const
{
- std::map<std::string, std::string>::const_iterator it;
- it = m_stringvars.find(name);
- if (it == m_stringvars.end()) {
+ StringMap::const_iterator it = m_stringvars.find(name);
+ if (it == m_stringvars.end())
return "";
- }
+
return resolveString(it->second, recursion);
}
@@ -211,7 +220,8 @@ void NodeMetadata::setString(const std::string &name, const std::string &var)
}
}
-std::string NodeMetadata::resolveString(const std::string &str, unsigned short recursion) const
+std::string NodeMetadata::resolveString(const std::string &str,
+ unsigned short recursion) const
{
if (recursion > 1) {
return str;
diff --git a/src/nodemetadata.h b/src/nodemetadata.h
index 6baf3b3d3..8e84e5af3 100644
--- a/src/nodemetadata.h
+++ b/src/nodemetadata.h
@@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define NODEMETADATA_HEADER
#include "irr_v3d.h"
-#include <string>
#include <iostream>
-#include <map>
+#include <vector>
+#include "util/string.h"
/*
NodeMetadata stores arbitary amounts of data for special blocks.
@@ -42,10 +42,10 @@ class NodeMetadata
public:
NodeMetadata(IGameDef *gamedef);
~NodeMetadata();
-
+
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is);
-
+
void clear();
// Generic key/value store
@@ -53,19 +53,19 @@ public:
void setString(const std::string &name, const std::string &var);
// Support variable names in values
std::string resolveString(const std::string &str, unsigned short recursion = 0) const;
- std::map<std::string, std::string> getStrings() const
+ StringMap getStrings() const
{
return m_stringvars;
}
// The inventory
- Inventory* getInventory()
+ Inventory *getInventory()
{
return m_inventory;
}
private:
- std::map<std::string, std::string> m_stringvars;
+ StringMap m_stringvars;
Inventory *m_inventory;
};
@@ -81,18 +81,20 @@ public:
void serialize(std::ostream &os) const;
void deSerialize(std::istream &is, IGameDef *gamedef);
-
+
+ // Add all keys in this list to the vector keys
+ std::vector<v3s16> getAllKeys();
// Get pointer to data
- NodeMetadata* get(v3s16 p);
+ NodeMetadata *get(v3s16 p);
// Deletes data
void remove(v3s16 p);
// Deletes old data and sets a new one
void set(v3s16 p, NodeMetadata *d);
// Deletes all
void clear();
-
+
private:
- std::map<v3s16, NodeMetadata*> m_data;
+ std::map<v3s16, NodeMetadata *> m_data;
};
#endif
diff --git a/src/noise.cpp b/src/noise.cpp
index 5223450dc..2948fb765 100644
--- a/src/noise.cpp
+++ b/src/noise.cpp
@@ -62,6 +62,99 @@ FlagDesc flagdesc_noiseparams[] = {
///////////////////////////////////////////////////////////////////////////////
+PcgRandom::PcgRandom(u64 state, u64 seq)
+{
+ seed(state, seq);
+}
+
+void PcgRandom::seed(u64 state, u64 seq)
+{
+ m_state = 0U;
+ m_inc = (seq << 1u) | 1u;
+ next();
+ m_state += state;
+ next();
+}
+
+
+u32 PcgRandom::next()
+{
+ u64 oldstate = m_state;
+ m_state = oldstate * 6364136223846793005ULL + m_inc;
+
+ u32 xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
+ u32 rot = oldstate >> 59u;
+ return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
+}
+
+
+u32 PcgRandom::range(u32 bound)
+{
+ // If the bound is 0, we cover the whole RNG's range
+ if (bound == 0)
+ return next();
+ /*
+ If the bound is not a multiple of the RNG's range, it may cause bias,
+ e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2.
+ Using rand() % 3, the number 0 would be twice as likely to appear.
+ With a very large RNG range, the effect becomes less prevalent but
+ still present. This can be solved by modifying the range of the RNG
+ to become a multiple of bound by dropping values above the a threshhold.
+ In our example, threshhold == 4 - 3 = 1 % 3 == 1, so reject 0, thus
+ making the range 3 with no bias.
+
+ This loop looks dangerous, but will always terminate due to the
+ RNG's property of uniformity.
+ */
+ u32 threshhold = -bound % bound;
+ u32 r;
+
+ while ((r = next()) < threshhold)
+ ;
+
+ return r % bound;
+}
+
+
+s32 PcgRandom::range(s32 min, s32 max)
+{
+ if (max < min)
+ throw PrngException("Invalid range (max < min)");
+
+ u32 bound = max - min + 1;
+ return range(bound) + min;
+}
+
+
+void PcgRandom::bytes(void *out, size_t len)
+{
+ u8 *outb = (u8 *)out;
+ int bytes_left = 0;
+ u32 r;
+
+ while (len--) {
+ if (bytes_left == 0) {
+ bytes_left = sizeof(u32);
+ r = next();
+ }
+
+ *outb = r & 0xFF;
+ outb++;
+ bytes_left--;
+ r >>= CHAR_BIT;
+ }
+}
+
+
+s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials)
+{
+ s32 accum = 0;
+ for (int i = 0; i != num_trials; i++)
+ accum += range(min, max);
+ return myround((float)accum / num_trials);
+}
+
+///////////////////////////////////////////////////////////////////////////////
float noise2d(int x, int y, int seed)
{
@@ -102,14 +195,6 @@ inline float biLinearInterpolation(
{
float tx = easeCurve(x);
float ty = easeCurve(y);
-#if 0
- return (
- v00 * (1 - tx) * (1 - ty) +
- v10 * tx * (1 - ty) +
- v01 * (1 - tx) * ty +
- v11 * tx * ty
- );
-#endif
float u = linearInterpolation(v00, v10, tx);
float v = linearInterpolation(v01, v11, tx);
return linearInterpolation(u, v, ty);
@@ -135,18 +220,6 @@ float triLinearInterpolation(
float tx = easeCurve(x);
float ty = easeCurve(y);
float tz = easeCurve(z);
-#if 0
- return (
- v000 * (1 - tx) * (1 - ty) * (1 - tz) +
- v100 * tx * (1 - ty) * (1 - tz) +
- v010 * (1 - tx) * ty * (1 - tz) +
- v110 * tx * ty * (1 - tz) +
- v001 * (1 - tx) * (1 - ty) * tz +
- v101 * tx * (1 - ty) * tz +
- v011 * (1 - tx) * ty * tz +
- v111 * tx * ty * tz
- );
-#endif
float u = biLinearInterpolationNoEase(v000, v100, v010, v110, tx, ty);
float v = biLinearInterpolationNoEase(v001, v101, v011, v111, tx, ty);
return linearInterpolation(u, v, tz);
@@ -162,33 +235,6 @@ float triLinearInterpolationNoEase(
return linearInterpolation(u, v, z);
}
-
-#if 0
-float noise2d_gradient(float x, float y, int seed)
-{
- // Calculate the integer coordinates
- int x0 = (x > 0.0 ? (int)x : (int)x - 1);
- int y0 = (y > 0.0 ? (int)y : (int)y - 1);
- // Calculate the remaining part of the coordinates
- float xl = x - (float)x0;
- float yl = y - (float)y0;
- // Calculate random cosine lookup table indices for the integer corners.
- // They are looked up as unit vector gradients from the lookup table.
- int n00 = (int)((noise2d(x0, y0, seed)+1)*8);
- int n10 = (int)((noise2d(x0+1, y0, seed)+1)*8);
- int n01 = (int)((noise2d(x0, y0+1, seed)+1)*8);
- int n11 = (int)((noise2d(x0+1, y0+1, seed)+1)*8);
- // Make a dot product for the gradients and the positions, to get the values
- float s = dotProduct(cos_lookup[n00], cos_lookup[(n00+12)%16], xl, yl);
- float u = dotProduct(-cos_lookup[n10], cos_lookup[(n10+12)%16], 1.-xl, yl);
- float v = dotProduct(cos_lookup[n01], -cos_lookup[(n01+12)%16], xl, 1.-yl);
- float w = dotProduct(-cos_lookup[n11], -cos_lookup[(n11+12)%16], 1.-xl, 1.-yl);
- // Interpolate between the values
- return biLinearInterpolation(s,u,v,w,xl,yl);
-}
-#endif
-
-
float noise2d_gradient(float x, float y, int seed, bool eased)
{
// Calculate the integer coordinates
@@ -370,7 +416,7 @@ float NoisePerlin3D(NoiseParams *np, float x, float y, float z, int seed)
}
-Noise::Noise(NoiseParams *np_, int seed, int sx, int sy, int sz)
+Noise::Noise(NoiseParams *np_, int seed, u32 sx, u32 sy, u32 sz)
{
memcpy(&np, np_, sizeof(np));
this->seed = seed;
@@ -397,6 +443,13 @@ Noise::~Noise()
void Noise::allocBuffers()
{
+ if (sx < 1)
+ sx = 1;
+ if (sy < 1)
+ sy = 1;
+ if (sz < 1)
+ sz = 1;
+
this->noise_buf = NULL;
resizeNoiseBuf(sz > 1);
@@ -415,7 +468,7 @@ void Noise::allocBuffers()
}
-void Noise::setSize(int sx, int sy, int sz)
+void Noise::setSize(u32 sx, u32 sy, u32 sz)
{
this->sx = sx;
this->sy = sy;
@@ -443,19 +496,28 @@ void Noise::setOctaves(int octaves)
void Noise::resizeNoiseBuf(bool is3d)
{
- int nlx, nly, nlz;
- float ofactor;
-
//maximum possible spread value factor
- ofactor = pow(np.lacunarity, np.octaves - 1);
+ float ofactor = (np.lacunarity > 1.0) ?
+ pow(np.lacunarity, np.octaves - 1) :
+ np.lacunarity;
+
+ // noise lattice point count
+ // (int)(sz * spread * ofactor) is # of lattice points crossed due to length
+ float num_noise_points_x = sx * ofactor / np.spread.X;
+ float num_noise_points_y = sy * ofactor / np.spread.Y;
+ float num_noise_points_z = sz * ofactor / np.spread.Z;
+
+ // protect against obviously invalid parameters
+ if (num_noise_points_x > 1000000000.f ||
+ num_noise_points_y > 1000000000.f ||
+ num_noise_points_z > 1000000000.f)
+ throw InvalidNoiseParamsException();
- //noise lattice point count
- //(int)(sz * spread * ofactor) is # of lattice points crossed due to length
// + 2 for the two initial endpoints
// + 1 for potentially crossing a boundary due to offset
- nlx = (int)ceil(sx * ofactor / np.spread.X) + 3;
- nly = (int)ceil(sy * ofactor / np.spread.Y) + 3;
- nlz = is3d ? (int)ceil(sz * ofactor / np.spread.Z) + 3 : 1;
+ size_t nlx = (size_t)ceil(num_noise_points_x) + 3;
+ size_t nly = (size_t)ceil(num_noise_points_y) + 3;
+ size_t nlz = is3d ? (size_t)ceil(num_noise_points_z) + 3 : 1;
delete[] noise_buf;
try {
@@ -484,8 +546,9 @@ void Noise::gradientMap2D(
int seed)
{
float v00, v01, v10, v11, u, v, orig_u;
- int index, i, j, x0, y0, noisex, noisey;
- int nlx, nly;
+ u32 index, i, j, noisex, noisey;
+ u32 nlx, nly;
+ s32 x0, y0;
bool eased = np.flags & (NOISE_FLAG_DEFAULTS | NOISE_FLAG_EASED);
Interp2dFxn interpolate = eased ?
@@ -498,8 +561,8 @@ void Noise::gradientMap2D(
orig_u = u;
//calculate noise point lattice
- nlx = (int)(u + sx * step_x) + 2;
- nly = (int)(v + sy * step_y) + 2;
+ nlx = (u32)(u + sx * step_x) + 2;
+ nly = (u32)(v + sy * step_y) + 2;
index = 0;
for (j = 0; j != nly; j++)
for (i = 0; i != nlx; i++)
@@ -549,8 +612,9 @@ void Noise::gradientMap3D(
float v000, v010, v100, v110;
float v001, v011, v101, v111;
float u, v, w, orig_u, orig_v;
- int index, i, j, k, x0, y0, z0, noisex, noisey, noisez;
- int nlx, nly, nlz;
+ u32 index, i, j, k, noisex, noisey, noisez;
+ u32 nlx, nly, nlz;
+ s32 x0, y0, z0;
Interp3dFxn interpolate = (np.flags & NOISE_FLAG_EASED) ?
triLinearInterpolation : triLinearInterpolationNoEase;
@@ -565,9 +629,9 @@ void Noise::gradientMap3D(
orig_v = v;
//calculate noise point lattice
- nlx = (int)(u + sx * step_x) + 2;
- nly = (int)(v + sy * step_y) + 2;
- nlz = (int)(w + sz * step_z) + 2;
+ nlx = (u32)(u + sx * step_x) + 2;
+ nly = (u32)(v + sy * step_y) + 2;
+ nlz = (u32)(w + sz * step_z) + 2;
index = 0;
for (k = 0; k != nlz; k++)
for (j = 0; j != nly; j++)
diff --git a/src/noise.h b/src/noise.h
index 05c877b32..0e4252dd4 100644
--- a/src/noise.h
+++ b/src/noise.h
@@ -26,51 +26,73 @@
#ifndef NOISE_HEADER
#define NOISE_HEADER
-#include "debug.h"
#include "irr_v3d.h"
+#include "exceptions.h"
#include "util/string.h"
-#define PSEUDORANDOM_MAX 32767
-
extern FlagDesc flagdesc_noiseparams[];
-class PseudoRandom
-{
+// Note: this class is not polymorphic so that its high level of
+// optimizability may be preserved in the common use case
+class PseudoRandom {
public:
- PseudoRandom(): m_next(0)
- {
- }
- PseudoRandom(int seed): m_next(seed)
+ const static u32 RANDOM_RANGE = 32767;
+
+ inline PseudoRandom(int seed=0):
+ m_next(seed)
{
}
- void seed(int seed)
+
+ inline void seed(int seed)
{
m_next = seed;
}
- // Returns 0...PSEUDORANDOM_MAX
- int next()
+
+ inline int next()
{
m_next = m_next * 1103515245 + 12345;
- return((unsigned)(m_next/65536) % (PSEUDORANDOM_MAX + 1));
+ return (unsigned)(m_next / 65536) % (RANDOM_RANGE + 1);
}
- int range(int min, int max)
+
+ inline int range(int min, int max)
{
- if (max-min > (PSEUDORANDOM_MAX + 1) / 10)
- {
- //dstream<<"WARNING: PseudoRandom::range: max > 32767"<<std::endl;
- assert(0);
- }
- if(min > max)
- {
- assert(0);
- return max;
- }
- return (next()%(max-min+1))+min;
+ if (max < min)
+ throw PrngException("Invalid range (max < min)");
+ /*
+ Here, we ensure the range is not too large relative to RANDOM_MAX,
+ as otherwise the effects of bias would become noticable. Unlike
+ PcgRandom, we cannot modify this RNG's range as it would change the
+ output of this RNG for reverse compatibility.
+ */
+ if ((u32)(max - min) > (RANDOM_RANGE + 1) / 10)
+ throw PrngException("Range too large");
+
+ return (next() % (max - min + 1)) + min;
}
+
private:
int m_next;
};
+class PcgRandom {
+public:
+ const static s32 RANDOM_MIN = -0x7fffffff - 1;
+ const static s32 RANDOM_MAX = 0x7fffffff;
+ const static u32 RANDOM_RANGE = 0xffffffff;
+
+ PcgRandom(u64 state=0x853c49e6748fea9bULL, u64 seq=0xda3e39cb94b95bdbULL);
+ void seed(u64 state, u64 seq=0xda3e39cb94b95bdbULL);
+ u32 next();
+ u32 range(u32 bound);
+ s32 range(s32 min, s32 max);
+ void bytes(void *out, size_t len);
+ s32 randNormalDist(s32 min, s32 max, int num_trials=6);
+
+private:
+ u64 m_state;
+ u64 m_inc;
+};
+
#define NOISE_FLAG_DEFAULTS 0x01
#define NOISE_FLAG_EASED 0x02
#define NOISE_FLAG_ABSVALUE 0x04
@@ -89,7 +111,8 @@ struct NoiseParams {
float lacunarity;
u32 flags;
- NoiseParams() {
+ NoiseParams()
+ {
offset = 0.0f;
scale = 1.0f;
spread = v3f(250, 250, 250);
@@ -126,18 +149,18 @@ class Noise {
public:
NoiseParams np;
int seed;
- int sx;
- int sy;
- int sz;
+ u32 sx;
+ u32 sy;
+ u32 sz;
float *noise_buf;
float *gradient_buf;
float *persist_buf;
float *result;
- Noise(NoiseParams *np, int seed, int sx, int sy, int sz=1);
+ Noise(NoiseParams *np, int seed, u32 sx, u32 sy, u32 sz=1);
~Noise();
- void setSize(int sx, int sy, int sz=1);
+ void setSize(u32 sx, u32 sy, u32 sz=1);
void setSpreadFactor(v3f spread);
void setOctaves(int octaves);
diff --git a/src/objdef.cpp b/src/objdef.cpp
new file mode 100644
index 000000000..08d6844fc
--- /dev/null
+++ b/src/objdef.cpp
@@ -0,0 +1,184 @@
+/*
+Minetest
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#include "objdef.h"
+#include "util/numeric.h"
+#include "log.h"
+#include "gamedef.h"
+
+ObjDefManager::ObjDefManager(IGameDef *gamedef, ObjDefType type)
+{
+ m_objtype = type;
+ m_ndef = gamedef ? gamedef->getNodeDefManager() : NULL;
+}
+
+
+ObjDefManager::~ObjDefManager()
+{
+ for (size_t i = 0; i != m_objects.size(); i++)
+ delete m_objects[i];
+}
+
+
+ObjDefHandle ObjDefManager::add(ObjDef *obj)
+{
+ assert(obj);
+
+ if (obj->name.length() && getByName(obj->name))
+ return OBJDEF_INVALID_HANDLE;
+
+ u32 index = addRaw(obj);
+ if (index == OBJDEF_INVALID_INDEX)
+ return OBJDEF_INVALID_HANDLE;
+
+ obj->handle = createHandle(index, m_objtype, obj->uid);
+ return obj->handle;
+}
+
+
+ObjDef *ObjDefManager::get(ObjDefHandle handle) const
+{
+ u32 index = validateHandle(handle);
+ return (index != OBJDEF_INVALID_INDEX) ? getRaw(index) : NULL;
+}
+
+
+ObjDef *ObjDefManager::set(ObjDefHandle handle, ObjDef *obj)
+{
+ u32 index = validateHandle(handle);
+ if (index == OBJDEF_INVALID_INDEX)
+ return NULL;
+
+ ObjDef *oldobj = setRaw(index, obj);
+
+ obj->uid = oldobj->uid;
+ obj->index = oldobj->index;
+ obj->handle = oldobj->handle;
+
+ return oldobj;
+}
+
+
+u32 ObjDefManager::addRaw(ObjDef *obj)
+{
+ size_t nobjects = m_objects.size();
+ if (nobjects >= OBJDEF_MAX_ITEMS)
+ return -1;
+
+ obj->index = nobjects;
+
+ // Ensure UID is nonzero so that a valid handle == OBJDEF_INVALID_HANDLE
+ // is not possible. The slight randomness bias isn't very significant.
+ obj->uid = myrand() & OBJDEF_UID_MASK;
+ if (obj->uid == 0)
+ obj->uid = 1;
+
+ m_objects.push_back(obj);
+
+ infostream << "ObjDefManager: added " << getObjectTitle()
+ << ": name=\"" << obj->name
+ << "\" index=" << obj->index
+ << " uid=" << obj->uid
+ << std::endl;
+
+ return nobjects;
+}
+
+
+ObjDef *ObjDefManager::getRaw(u32 index) const
+{
+ return m_objects[index];
+}
+
+
+ObjDef *ObjDefManager::setRaw(u32 index, ObjDef *obj)
+{
+ ObjDef *old_obj = m_objects[index];
+ m_objects[index] = obj;
+ return old_obj;
+}
+
+
+ObjDef *ObjDefManager::getByName(const std::string &name) const
+{
+ for (size_t i = 0; i != m_objects.size(); i++) {
+ ObjDef *obj = m_objects[i];
+ if (obj && !strcasecmp(name.c_str(), obj->name.c_str()))
+ return obj;
+ }
+
+ return NULL;
+}
+
+
+void ObjDefManager::clear()
+{
+ for (size_t i = 0; i != m_objects.size(); i++)
+ delete m_objects[i];
+
+ m_objects.clear();
+}
+
+
+u32 ObjDefManager::validateHandle(ObjDefHandle handle) const
+{
+ ObjDefType type;
+ u32 index;
+ u32 uid;
+
+ bool is_valid =
+ (handle != OBJDEF_INVALID_HANDLE) &&
+ decodeHandle(handle, &index, &type, &uid) &&
+ (type == m_objtype) &&
+ (index < m_objects.size()) &&
+ (m_objects[index]->uid == uid);
+
+ return is_valid ? index : -1;
+}
+
+
+ObjDefHandle ObjDefManager::createHandle(u32 index, ObjDefType type, u32 uid)
+{
+ ObjDefHandle handle = 0;
+ set_bits(&handle, 0, 18, index);
+ set_bits(&handle, 18, 6, type);
+ set_bits(&handle, 24, 7, uid);
+
+ u32 parity = calc_parity(handle);
+ set_bits(&handle, 31, 1, parity);
+
+ return handle ^ OBJDEF_HANDLE_SALT;
+}
+
+
+bool ObjDefManager::decodeHandle(ObjDefHandle handle, u32 *index,
+ ObjDefType *type, u32 *uid)
+{
+ handle ^= OBJDEF_HANDLE_SALT;
+
+ u32 parity = get_bits(handle, 31, 1);
+ set_bits(&handle, 31, 1, 0);
+ if (parity != calc_parity(handle))
+ return false;
+
+ *index = get_bits(handle, 0, 18);
+ *type = (ObjDefType)get_bits(handle, 18, 6);
+ *uid = get_bits(handle, 24, 7);
+ return true;
+}
diff --git a/src/objdef.h b/src/objdef.h
new file mode 100644
index 000000000..65e5c0176
--- /dev/null
+++ b/src/objdef.h
@@ -0,0 +1,95 @@
+/*
+Minetest
+Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#ifndef OBJDEF_HEADER
+#define OBJDEF_HEADER
+
+#include "porting.h"
+
+class IGameDef;
+class INodeDefManager;
+
+#define OBJDEF_INVALID_INDEX ((u32)(-1))
+#define OBJDEF_INVALID_HANDLE 0
+#define OBJDEF_HANDLE_SALT 0x00585e6fu
+#define OBJDEF_MAX_ITEMS (1 << 18)
+#define OBJDEF_UID_MASK ((1 << 7) - 1)
+
+typedef u32 ObjDefHandle;
+
+enum ObjDefType {
+ OBJDEF_GENERIC,
+ OBJDEF_BIOME,
+ OBJDEF_ORE,
+ OBJDEF_DECORATION,
+ OBJDEF_SCHEMATIC,
+};
+
+class ObjDef {
+public:
+ virtual ~ObjDef() {}
+
+ u32 index;
+ u32 uid;
+ ObjDefHandle handle;
+ std::string name;
+};
+
+// WARNING: Ownership of ObjDefs is transferred to the ObjDefManager it is
+// added/set to. Note that ObjDefs managed by ObjDefManager are NOT refcounted,
+// so the same ObjDef instance must not be referenced multiple
+class ObjDefManager {
+public:
+ ObjDefManager(IGameDef *gamedef, ObjDefType type);
+ virtual ~ObjDefManager();
+
+ virtual const char *getObjectTitle() const { return "ObjDef"; }
+
+ virtual void clear();
+ virtual ObjDef *getByName(const std::string &name) const;
+
+ //// Add new/get/set object definitions by handle
+ virtual ObjDefHandle add(ObjDef *obj);
+ virtual ObjDef *get(ObjDefHandle handle) const;
+ virtual ObjDef *set(ObjDefHandle handle, ObjDef *obj);
+
+ //// Raw variants that work on indexes
+ virtual u32 addRaw(ObjDef *obj);
+
+ // It is generally assumed that getRaw() will always return a valid object
+ // This won't be true if people do odd things such as call setRaw() with NULL
+ virtual ObjDef *getRaw(u32 index) const;
+ virtual ObjDef *setRaw(u32 index, ObjDef *obj);
+
+ size_t getNumObjects() const { return m_objects.size(); }
+ ObjDefType getType() const { return m_objtype; }
+ INodeDefManager *getNodeDef() const { return m_ndef; }
+
+ u32 validateHandle(ObjDefHandle handle) const;
+ static ObjDefHandle createHandle(u32 index, ObjDefType type, u32 uid);
+ static bool decodeHandle(ObjDefHandle handle, u32 *index,
+ ObjDefType *type, u32 *uid);
+
+protected:
+ INodeDefManager *m_ndef;
+ std::vector<ObjDef *> m_objects;
+ ObjDefType m_objtype;
+};
+
+#endif
diff --git a/src/particles.cpp b/src/particles.cpp
index 603e38cdd..15e2a6597 100644
--- a/src/particles.cpp
+++ b/src/particles.cpp
@@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "particles.h"
#include "constants.h"
#include "debug.h"
-#include "main.h" // For g_profiler and g_settings
#include "settings.h"
-#include "tile.h"
+#include "client/tile.h"
#include "gamedef.h"
#include "collision.h"
#include <stdlib.h>
@@ -107,20 +106,13 @@ Particle::~Particle()
void Particle::OnRegisterSceneNode()
{
if (IsVisible)
- {
- SceneManager->registerNodeForRendering
- (this, scene::ESNRP_TRANSPARENT);
- SceneManager->registerNodeForRendering
- (this, scene::ESNRP_SOLID);
- }
+ SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT);
ISceneNode::OnRegisterSceneNode();
}
void Particle::render()
{
- // TODO: Render particles in front of water and the selectionbox
-
video::IVideoDriver* driver = SceneManager->getVideoDriver();
driver->setMaterial(m_material);
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
@@ -316,7 +308,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
*(m_maxsize-m_minsize)
+m_minsize;
- new Particle(
+ Particle* toadd = new Particle(
m_gamedef,
m_smgr,
m_player,
@@ -331,6 +323,7 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env)
m_texture,
v2f(0.0, 0.0),
v2f(1.0, 1.0));
+ m_particlemanager->addParticle(toadd);
}
}
}
@@ -441,7 +434,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
}
}
video::ITexture *texture =
- gamedef->tsrc()->getTexture(*(event->add_particlespawner.texture));
+ gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture));
ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player,
event->add_particlespawner.amount,
@@ -484,7 +477,7 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef,
if (event->type == CE_SPAWN_PARTICLE) {
video::ITexture *texture =
- gamedef->tsrc()->getTexture(*(event->spawn_particle.texture));
+ gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture));
Particle* toadd = new Particle(gamedef, smgr, player, m_env,
*event->spawn_particle.pos,
diff --git a/src/particles.h b/src/particles.h
index d7f1147f1..2bc2e7bfa 100644
--- a/src/particles.h
+++ b/src/particles.h
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include "irrlichttypes_extrabloated.h"
-#include "tile.h"
+#include "client/tile.h"
#include "localplayer.h"
#include "environment.h"
diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp
index d39bdab3a..8eb52bfd1 100644
--- a/src/pathfinder.cpp
+++ b/src/pathfinder.cpp
@@ -310,27 +310,16 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
print_path(path);
#endif
- //optimize path
- std::vector<v3s16> optimized_path;
-
- std::vector<v3s16>::iterator startpos = path.begin();
- optimized_path.push_back(source);
-
+ //finalize path
+ std::vector<v3s16> full_path;
for (std::vector<v3s16>::iterator i = path.begin();
i != path.end(); i++) {
- if (!m_env->line_of_sight(
- tov3f(getIndexElement(*startpos).pos),
- tov3f(getIndexElement(*i).pos))) {
- optimized_path.push_back(getIndexElement(*(i-1)).pos);
- startpos = (i-1);
- }
+ full_path.push_back(getIndexElement(*i).pos);
}
- optimized_path.push_back(destination);
-
#ifdef PATHFINDER_DEBUG
- std::cout << "Optimized path:" << std::endl;
- print_path(optimized_path);
+ std::cout << "full path:" << std::endl;
+ print_path(full_path);
#endif
#ifdef PATHFINDER_CALC_TIME
timespec ts2;
@@ -344,7 +333,7 @@ std::vector<v3s16> pathfinder::get_Path(ServerEnvironment* env,
std::cout << "Calculating path took: " << (ts2.tv_sec - ts.tv_sec) <<
"s " << ms << "ms " << us << "us " << ns << "ns " << std::endl;
#endif
- return optimized_path;
+ return full_path;
}
else {
#ifdef PATHFINDER_DEBUG
@@ -532,7 +521,7 @@ path_cost pathfinder::calc_cost(v3s16 pos,v3s16 dir) {
if ((testpos.Y >= m_limits.Y.min) &&
(node_at_pos.param0 != CONTENT_IGNORE) &&
(node_at_pos.param0 != CONTENT_AIR)) {
- if (((pos2.Y - testpos.Y)*-1) <= m_maxdrop) {
+ if ((pos2.Y - testpos.Y - 1) <= m_maxdrop) {
retval.valid = true;
retval.value = 2;
//difference of y-pos +1 (target node is ABOVE solid node)
diff --git a/src/player.cpp b/src/player.cpp
index 0da761eed..cb2286ef6 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "player.h"
#include <fstream>
+#include "jthread/jmutexautolock.h"
#include "util/numeric.h"
#include "hud.h"
#include "constants.h"
@@ -43,6 +44,7 @@ Player::Player(IGameDef *gamedef, const char *name):
hp(PLAYER_MAX_HP),
hurt_tilt_timer(0),
hurt_tilt_strength(0),
+ protocol_version(0),
peer_id(PEER_ID_INEXISTENT),
keyPressed(0),
// protected
@@ -70,9 +72,11 @@ Player::Player(IGameDef *gamedef, const char *name):
//"image[1,0.6;1,2;player.png]"
"list[current_player;main;0,3.5;8,4;]"
"list[current_player;craft;3,0;3,3;]"
+ "listring[]"
"list[current_player;craftpreview;7,1;1,1;]";
- // Initialize movement settings at default values, so movement can work if the server fails to send them
+ // Initialize movement settings at default values, so movement can work
+ // if the server fails to send them
movement_acceleration_default = 3 * BS;
movement_acceleration_air = 2 * BS;
movement_acceleration_fast = 10 * BS;
@@ -85,6 +89,7 @@ Player::Player(IGameDef *gamedef, const char *name):
movement_liquid_fluidity_smooth = 0.5 * BS;
movement_liquid_sink = 10 * BS;
movement_gravity = 9.81 * BS;
+ local_animation_speed = 0.0;
// Movement overrides are multipliers and must be 1 by default
physics_override_speed = 1;
@@ -93,9 +98,10 @@ Player::Player(IGameDef *gamedef, const char *name):
physics_override_sneak = true;
physics_override_sneak_glitch = true;
- hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
- HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
- HUD_FLAG_BREATHBAR_VISIBLE;
+ hud_flags =
+ HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE |
+ HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
+ HUD_FLAG_BREATHBAR_VISIBLE | HUD_FLAG_MINIMAP_VISIBLE;
hud_hotbar_itemcount = HUD_HOTBAR_ITEMCOUNT_DEFAULT;
}
@@ -116,31 +122,12 @@ void Player::accelerateHorizontal(v3f target_speed, f32 max_increase)
f32 dl = d_wanted.getLength();
if(dl > max_increase)
dl = max_increase;
-
+
v3f d = d_wanted.normalize() * dl;
m_speed.X += d.X;
m_speed.Z += d.Z;
-#if 0 // old code
- if(m_speed.X < target_speed.X - max_increase)
- m_speed.X += max_increase;
- else if(m_speed.X > target_speed.X + max_increase)
- m_speed.X -= max_increase;
- else if(m_speed.X < target_speed.X)
- m_speed.X = target_speed.X;
- else if(m_speed.X > target_speed.X)
- m_speed.X = target_speed.X;
-
- if(m_speed.Z < target_speed.Z - max_increase)
- m_speed.Z += max_increase;
- else if(m_speed.Z > target_speed.Z + max_increase)
- m_speed.Z -= max_increase;
- else if(m_speed.Z < target_speed.Z)
- m_speed.Z = target_speed.Z;
- else if(m_speed.Z > target_speed.Z)
- m_speed.Z = target_speed.Z;
-#endif
}
// Vertical acceleration (Y), X and Z directions are ignored
@@ -157,16 +144,6 @@ void Player::accelerateVertical(v3f target_speed, f32 max_increase)
m_speed.Y += d_wanted;
-#if 0 // old code
- if(m_speed.Y < target_speed.Y - max_increase)
- m_speed.Y += max_increase;
- else if(m_speed.Y > target_speed.Y + max_increase)
- m_speed.Y -= max_increase;
- else if(m_speed.Y < target_speed.Y)
- m_speed.Y = target_speed.Y;
- else if(m_speed.Y > target_speed.Y)
- m_speed.Y = target_speed.Y;
-#endif
}
v3s16 Player::getLightPosition() const
@@ -240,6 +217,8 @@ void Player::deSerialize(std::istream &is, std::string playername)
u32 Player::addHud(HudElement *toadd)
{
+ JMutexAutoLock lock(m_mutex);
+
u32 id = getFreeHudID();
if (id < hud.size())
@@ -252,6 +231,8 @@ u32 Player::addHud(HudElement *toadd)
HudElement* Player::getHud(u32 id)
{
+ JMutexAutoLock lock(m_mutex);
+
if (id < hud.size())
return hud[id];
@@ -260,6 +241,8 @@ HudElement* Player::getHud(u32 id)
HudElement* Player::removeHud(u32 id)
{
+ JMutexAutoLock lock(m_mutex);
+
HudElement* retval = NULL;
if (id < hud.size()) {
retval = hud[id];
@@ -270,6 +253,8 @@ HudElement* Player::removeHud(u32 id)
void Player::clearHud()
{
+ JMutexAutoLock lock(m_mutex);
+
while(!hud.empty()) {
delete hud.back();
hud.pop_back();
diff --git a/src/player.h b/src/player.h
index 435875233..3a336afc4 100644
--- a/src/player.h
+++ b/src/player.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include "inventory.h"
#include "constants.h" // BS
+#include "jthread/jmutex.h"
#include <list>
#define PLAYERNAME_SIZE 20
@@ -91,6 +92,9 @@ class PlayerSAO;
struct HudElement;
class Environment;
+// IMPORTANT:
+// Do *not* perform an assignment or copy operation on a Player or
+// RemotePlayer object! This will copy the lock held for HUD synchronization
class Player
{
public:
@@ -101,7 +105,7 @@ public:
virtual void move(f32 dtime, Environment *env, f32 pos_max_d)
{}
virtual void move(f32 dtime, Environment *env, f32 pos_max_d,
- std::list<CollisionInfo> *collision_info)
+ std::vector<CollisionInfo> *collision_info)
{}
v3f getSpeed()
@@ -113,7 +117,7 @@ public:
{
m_speed = speed;
}
-
+
void accelerateHorizontal(v3f target_speed, f32 max_increase);
void accelerateVertical(v3f target_speed, f32 max_increase);
@@ -126,13 +130,8 @@ public:
v3f getEyeOffset()
{
- // This is at the height of the eyes of the current figure
- // return v3f(0, BS*1.5, 0);
- // This is more like in minecraft
- if(camera_barely_in_ceiling)
- return v3f(0,BS*1.5,0);
- else
- return v3f(0,BS*1.625,0);
+ float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f;
+ return v3f(0, BS * eye_height, 0);
}
v3f getEyePosition()
@@ -193,16 +192,17 @@ public:
return (m_yaw + 90.) * core::DEGTORAD;
}
- const char * getName() const
+ const char *getName() const
{
return m_name;
}
- core::aabbox3d<f32> getCollisionbox() {
+ core::aabbox3d<f32> getCollisionbox()
+ {
return m_collisionbox;
}
- u32 getFreeHudID() const {
+ u32 getFreeHudID() {
size_t size = hud.size();
for (size_t i = 0; i != size; i++) {
if (!hud[i])
@@ -211,12 +211,91 @@ public:
return size;
}
+ void setHotbarItemcount(s32 hotbar_itemcount)
+ {
+ hud_hotbar_itemcount = hotbar_itemcount;
+ }
+
+ s32 getHotbarItemcount()
+ {
+ return hud_hotbar_itemcount;
+ }
+
+ void setHotbarImage(const std::string &name)
+ {
+ hud_hotbar_image = name;
+ }
+
+ std::string getHotbarImage()
+ {
+ return hud_hotbar_image;
+ }
+
+ void setHotbarSelectedImage(const std::string &name)
+ {
+ hud_hotbar_selected_image = name;
+ }
+
+ std::string getHotbarSelectedImage() {
+ return hud_hotbar_selected_image;
+ }
+
+ void setSky(const video::SColor &bgcolor, const std::string &type,
+ const std::vector<std::string> &params)
+ {
+ m_sky_bgcolor = bgcolor;
+ m_sky_type = type;
+ m_sky_params = params;
+ }
+
+ void getSky(video::SColor *bgcolor, std::string *type,
+ std::vector<std::string> *params)
+ {
+ *bgcolor = m_sky_bgcolor;
+ *type = m_sky_type;
+ *params = m_sky_params;
+ }
+
+ void overrideDayNightRatio(bool do_override, float ratio)
+ {
+ m_day_night_ratio_do_override = do_override;
+ m_day_night_ratio = ratio;
+ }
+
+ void getDayNightRatio(bool *do_override, float *ratio)
+ {
+ *do_override = m_day_night_ratio_do_override;
+ *ratio = m_day_night_ratio;
+ }
+
+ void setLocalAnimations(v2s32 frames[4], float frame_speed)
+ {
+ for (int i = 0; i < 4; i++)
+ local_animations[i] = frames[i];
+ local_animation_speed = frame_speed;
+ }
+
+ void getLocalAnimations(v2s32 *frames, float *frame_speed)
+ {
+ for (int i = 0; i < 4; i++)
+ frames[i] = local_animations[i];
+ *frame_speed = local_animation_speed;
+ }
+
virtual bool isLocal() const
- { return false; }
+ {
+ return false;
+ }
+
virtual PlayerSAO *getPlayerSAO()
- { return NULL; }
+ {
+ return NULL;
+ }
+
virtual void setPlayerSAO(PlayerSAO *sao)
- { assert(0); }
+ {
+ FATAL_ERROR("FIXME");
+ }
/*
serialize() writes a bunch of text that can contain
@@ -238,6 +317,9 @@ public:
inventory.setModified(x);
}
+ // Use a function, if isDead can be defined by other conditions
+ bool isDead() { return hp == 0; }
+
bool touching_ground;
// This oscillates so that the player jumps a bit above the surface
bool in_liquid;
@@ -248,7 +330,9 @@ public:
bool is_climbing;
bool swimming_vertical;
bool camera_barely_in_ceiling;
-
+ v3f eye_offset_first;
+ v3f eye_offset_third;
+
Inventory inventory;
f32 movement_acceleration_default;
@@ -278,18 +362,19 @@ public:
float hurt_tilt_timer;
float hurt_tilt_strength;
+ u16 protocol_version;
u16 peer_id;
std::string inventory_formspec;
-
+
PlayerControl control;
PlayerControl getPlayerControl()
{
return control;
}
-
+
u32 keyPressed;
-
+
HudElement* getHud(u32 id);
u32 addHud(HudElement* hud);
@@ -301,6 +386,8 @@ public:
u32 hud_flags;
s32 hud_hotbar_itemcount;
+ std::string hud_hotbar_image;
+ std::string hud_hotbar_selected_image;
protected:
IGameDef *m_gamedef;
@@ -315,6 +402,18 @@ protected:
bool m_dirty;
std::vector<HudElement *> hud;
+
+ std::string m_sky_type;
+ video::SColor m_sky_bgcolor;
+ std::vector<std::string> m_sky_params;
+
+ bool m_day_night_ratio_do_override;
+ float m_day_night_ratio;
+private:
+ // Protect some critical areas
+ // hud for example can be modified by EmergeThread
+ // and ServerThread
+ JMutex m_mutex;
};
@@ -337,7 +436,7 @@ public:
void setPlayerSAO(PlayerSAO *sao)
{ m_sao = sao; }
void setPosition(const v3f &position);
-
+
private:
PlayerSAO *m_sao;
};
diff --git a/src/porting.cpp b/src/porting.cpp
index 8a685539b..44f1fcff1 100644
--- a/src/porting.cpp
+++ b/src/porting.cpp
@@ -35,12 +35,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <unistd.h>
#include <sys/utsname.h>
#endif
-
+#if defined(__hpux)
+ #define _PSTAT64
+ #include <sys/pstat.h>
+#endif
#if !defined(_WIN32) && !defined(__APPLE__) && \
!defined(__ANDROID__) && !defined(SERVER)
#define XORG_USED
#endif
-
#ifdef XORG_USED
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -51,7 +53,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
#include "log.h"
#include "util/string.h"
-#include "main.h"
#include "settings.h"
#include <list>
@@ -74,8 +75,7 @@ bool * signal_handler_killstatus(void)
void sigint_handler(int sig)
{
- if(g_killed == false)
- {
+ if(!g_killed) {
dstream<<DTIME<<"INFO: sigint_handler(): "
<<"Ctrl-C pressed, shutting down."<<std::endl;
@@ -85,9 +85,7 @@ void sigint_handler(int sig)
debug_stacks_print();*/
g_killed = true;
- }
- else
- {
+ } else {
(void)signal(SIGINT, SIG_DFL);
}
}
@@ -100,42 +98,32 @@ void signal_handler_init(void)
#else // _WIN32
#include <signal.h>
- BOOL WINAPI event_handler(DWORD sig)
- {
- switch(sig)
- {
- case CTRL_C_EVENT:
- case CTRL_CLOSE_EVENT:
- case CTRL_LOGOFF_EVENT:
- case CTRL_SHUTDOWN_EVENT:
-
- if(g_killed == false)
- {
- dstream<<DTIME<<"INFO: event_handler(): "
- <<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
- // Comment out for less clutter when testing scripts
- /*dstream<<DTIME<<"INFO: event_handler(): "
- <<"Printing debug stacks"<<std::endl;
- debug_stacks_print();*/
-
- g_killed = true;
- }
- else
- {
- (void)signal(SIGINT, SIG_DFL);
- }
-
- break;
- case CTRL_BREAK_EVENT:
- break;
+BOOL WINAPI event_handler(DWORD sig)
+{
+ switch (sig) {
+ case CTRL_C_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ if (g_killed == false) {
+ dstream << DTIME << "INFO: event_handler(): "
+ << "Ctrl+C, Close Event, Logoff Event or Shutdown Event,"
+ " shutting down." << std::endl;
+ g_killed = true;
+ } else {
+ (void)signal(SIGINT, SIG_DFL);
}
-
- return TRUE;
+ break;
+ case CTRL_BREAK_EVENT:
+ break;
}
+ return TRUE;
+}
+
void signal_handler_init(void)
{
- SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
+ SetConsoleCtrlHandler((PHANDLER_ROUTINE)event_handler, TRUE);
}
#endif
@@ -144,7 +132,8 @@ void signal_handler_init(void)
/*
Multithreading support
*/
-int getNumberOfProcessors() {
+int getNumberOfProcessors()
+{
#if defined(_SC_NPROCESSORS_ONLN)
return sysconf(_SC_NPROCESSORS_ONLN);
@@ -178,7 +167,8 @@ int getNumberOfProcessors() {
#ifndef __ANDROID__
-bool threadBindToProcessor(threadid_t tid, int pnumber) {
+bool threadBindToProcessor(threadid_t tid, int pnumber)
+{
#if defined(_WIN32)
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
@@ -202,7 +192,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
#elif defined(__sun) || defined(sun)
return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(tid),
- pnumber, NULL) == 0;
+ pnumber, NULL) == 0;
#elif defined(_AIX)
@@ -213,7 +203,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
pthread_spu_t answer;
return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
- &answer, pnumber, tid) == 0;
+ &answer, pnumber, tid) == 0;
#elif defined(__APPLE__)
@@ -222,7 +212,7 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
thread_port_t threadport = pthread_mach_thread_np(tid);
tapol.affinity_tag = pnumber + 1;
return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
- (thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
+ (thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
#else
@@ -232,7 +222,8 @@ bool threadBindToProcessor(threadid_t tid, int pnumber) {
}
#endif
-bool threadSetPriority(threadid_t tid, int prio) {
+bool threadSetPriority(threadid_t tid, int prio)
+{
#if defined(_WIN32)
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
@@ -287,14 +278,14 @@ void pathRemoveFile(char *path, char delim)
path[i] = 0;
}
-bool detectMSVCBuildDir(char *c_path)
+bool detectMSVCBuildDir(const std::string &path)
{
- std::string path(c_path);
const char *ends[] = {
"bin\\Release",
"bin\\Debug",
"bin\\Build",
- NULL};
+ NULL
+ };
return (removeStringEnd(path, ends) != "");
}
@@ -312,14 +303,14 @@ std::string get_sysinfo()
oss << "Windows/" << osvi.dwMajorVersion << "."
<< osvi.dwMinorVersion;
- if(osvi.szCSDVersion[0])
+ if (osvi.szCSDVersion[0])
oss << "-" << tmp;
oss << " ";
#ifdef _WIN64
oss << "x86_64";
#else
BOOL is64 = FALSE;
- if(IsWow64Process(GetCurrentProcess(), &is64) && is64)
+ if (IsWow64Process(GetCurrentProcess(), &is64) && is64)
oss << "x86_64"; // 32-bit app on 64-bit OS
else
oss << "x86";
@@ -334,232 +325,323 @@ std::string get_sysinfo()
#endif
}
-void initializePaths()
+
+bool getCurrentWorkingDir(char *buf, size_t len)
{
-#if RUN_IN_PLACE
- /*
- Use relative paths if RUN_IN_PLACE
- */
+#ifdef _WIN32
+ DWORD ret = GetCurrentDirectory(len, buf);
+ return (ret != 0) && (ret <= len);
+#else
+ return getcwd(buf, len);
+#endif
+}
- infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
- /*
- Windows
- */
- #if defined(_WIN32)
+bool getExecPathFromProcfs(char *buf, size_t buflen)
+{
+#ifndef _WIN32
+ buflen--;
- const DWORD buflen = 1000;
- char buf[buflen];
- DWORD len;
+ ssize_t len;
+ if ((len = readlink("/proc/self/exe", buf, buflen)) == -1 &&
+ (len = readlink("/proc/curproc/file", buf, buflen)) == -1 &&
+ (len = readlink("/proc/curproc/exe", buf, buflen)) == -1)
+ return false;
- // Find path of executable and set path_share relative to it
- len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
- assert(len < buflen);
- pathRemoveFile(buf, '\\');
+ buf[len] = '\0';
+ return true;
+#else
+ return false;
+#endif
+}
- if(detectMSVCBuildDir(buf)){
- infostream<<"MSVC build directory detected"<<std::endl;
- path_share = std::string(buf) + "\\..\\..";
- path_user = std::string(buf) + "\\..\\..";
- }
- else{
- path_share = std::string(buf) + "\\..";
- path_user = std::string(buf) + "\\..";
- }
+//// Windows
+#if defined(_WIN32)
- /*
- Linux
- */
- #elif defined(linux)
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ DWORD written = GetModuleFileNameA(NULL, buf, len);
+ if (written == 0 || written == len)
+ return false;
- char buf[BUFSIZ];
- memset(buf, 0, BUFSIZ);
- // Get path to executable
- assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
+ return true;
+}
- pathRemoveFile(buf, '/');
- path_share = std::string(buf) + "/..";
- path_user = std::string(buf) + "/..";
+//// Linux
+#elif defined(linux) || defined(__linux) || defined(__linux__)
- /*
- OS X
- */
- #elif defined(__APPLE__)
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ if (!getExecPathFromProcfs(buf, len))
+ return false;
- //https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
- //TODO: Test this code
- char buf[BUFSIZ];
- uint32_t len = sizeof(buf);
- assert(_NSGetExecutablePath(buf, &len) != -1);
+ return true;
+}
+
+
+//// Mac OS X, Darwin
+#elif defined(__APPLE__)
+
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ uint32_t lenb = (uint32_t)len;
+ if (_NSGetExecutablePath(buf, &lenb) == -1)
+ return false;
+
+ return true;
+}
- pathRemoveFile(buf, '/');
- path_share = std::string(buf) + "/..";
- path_user = std::string(buf) + "/..";
+//// FreeBSD, NetBSD, DragonFlyBSD
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
- /*
- FreeBSD
- */
- #elif defined(__FreeBSD__)
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ // Try getting path from procfs first, since valgrind
+ // doesn't work with the latter
+ if (getExecPathFromProcfs(buf, len))
+ return true;
int mib[4];
- char buf[BUFSIZ];
- size_t len = sizeof(buf);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
- assert(sysctl(mib, 4, buf, &len, NULL, 0) != -1);
- pathRemoveFile(buf, '/');
+ if (sysctl(mib, 4, buf, &len, NULL, 0) == -1)
+ return false;
- path_share = std::string(buf) + "/..";
- path_user = std::string(buf) + "/..";
+ return true;
+}
- #else
- //TODO: Get path of executable. This assumes working directory is bin/
- dstream<<"WARNING: Relative path not properly supported on this platform"
- <<std::endl;
+//// Solaris
+#elif defined(__sun) || defined(sun)
+
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ const char *exec = getexecname();
+ if (exec == NULL)
+ return false;
+
+ if (strlcpy(buf, exec, len) >= len)
+ return false;
+
+ return true;
+}
- /* scriptapi no longer allows paths that start with "..", so assuming that
- the current working directory is bin/, strip off the last component. */
- char *cwd = getcwd(NULL, 0);
- pathRemoveFile(cwd, '/');
- path_share = std::string(cwd);
- path_user = std::string(cwd);
- #endif
+// HP-UX
+#elif defined(__hpux)
+
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ struct pst_status psts;
+
+ if (pstat_getproc(&psts, sizeof(psts), 0, getpid()) == -1)
+ return false;
+
+ if (pstat_getpathname(buf, len, &psts.pst_fid_text) == -1)
+ return false;
-#else // RUN_IN_PLACE
+ return true;
+}
- /*
- Use platform-specific paths otherwise
- */
- infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
+#else
- /*
- Windows
- */
- #if defined(_WIN32)
+bool getCurrentExecPath(char *buf, size_t len)
+{
+ return false;
+}
+
+#endif
- const DWORD buflen = 1000;
- char buf[buflen];
- DWORD len;
+
+//// Windows
+#if defined(_WIN32)
+
+bool setSystemPaths()
+{
+ char buf[BUFSIZ];
// Find path of executable and set path_share relative to it
- len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
- assert(len < buflen);
+ FATAL_ERROR_IF(!getCurrentExecPath(buf, sizeof(buf)),
+ "Failed to get current executable path");
pathRemoveFile(buf, '\\');
// Use ".\bin\.."
path_share = std::string(buf) + "\\..";
// Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
- len = GetEnvironmentVariable("APPDATA", buf, buflen);
- assert(len < buflen);
+ DWORD len = GetEnvironmentVariable("APPDATA", buf, sizeof(buf));
+ FATAL_ERROR_IF(len == 0 || len > sizeof(buf), "Failed to get APPDATA");
+
path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
+ return true;
+}
- /*
- Linux
- */
- #elif defined(linux)
- // Get path to executable
- std::string bindir = "";
- {
- char buf[BUFSIZ];
- memset(buf, 0, BUFSIZ);
- if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) {
- errorstream << "Unable to read bindir "<< std::endl;
-#ifndef __ANDROID__
- assert("Unable to read bindir" == 0);
+//// Linux
+#elif defined(linux) || defined(__linux)
+
+bool setSystemPaths()
+{
+ char buf[BUFSIZ];
+
+ if (!getCurrentExecPath(buf, sizeof(buf))) {
+#ifdef __ANDROID__
+ errorstream << "Unable to read bindir "<< std::endl;
+#else
+ FATAL_ERROR("Unable to read bindir");
#endif
- } else {
- pathRemoveFile(buf, '/');
- bindir = buf;
- }
+ return false;
}
+ pathRemoveFile(buf, '/');
+ std::string bindir(buf);
+
// Find share directory from these.
// It is identified by containing the subdirectory "builtin".
std::list<std::string> trylist;
std::string static_sharedir = STATIC_SHAREDIR;
- if(static_sharedir != "" && static_sharedir != ".")
+ if (static_sharedir != "" && static_sharedir != ".")
trylist.push_back(static_sharedir);
- trylist.push_back(
- bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME);
- trylist.push_back(bindir + DIR_DELIM + "..");
+
+ trylist.push_back(bindir + DIR_DELIM ".." DIR_DELIM "share"
+ DIR_DELIM + PROJECT_NAME);
+ trylist.push_back(bindir + DIR_DELIM "..");
+
#ifdef __ANDROID__
trylist.push_back(path_user);
#endif
- for(std::list<std::string>::const_iterator i = trylist.begin();
- i != trylist.end(); i++)
- {
+ for (std::list<std::string>::const_iterator
+ i = trylist.begin(); i != trylist.end(); i++) {
const std::string &trypath = *i;
- if(!fs::PathExists(trypath) || !fs::PathExists(trypath + DIR_DELIM + "builtin")){
- dstream<<"WARNING: system-wide share not found at \""
- <<trypath<<"\""<<std::endl;
+ if (!fs::PathExists(trypath) ||
+ !fs::PathExists(trypath + DIR_DELIM + "builtin")) {
+ dstream << "WARNING: system-wide share not found at \""
+ << trypath << "\""<< std::endl;
continue;
}
+
// Warn if was not the first alternative
- if(i != trylist.begin()){
- dstream<<"WARNING: system-wide share found at \""
- <<trypath<<"\""<<std::endl;
+ if (i != trylist.begin()) {
+ dstream << "WARNING: system-wide share found at \""
+ << trypath << "\"" << std::endl;
}
+
path_share = trypath;
break;
}
+
#ifndef __ANDROID__
- path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
+ path_user = std::string(getenv("HOME")) + DIR_DELIM "."
+ + PROJECT_NAME;
#endif
- /*
- OS X
- */
- #elif defined(__APPLE__)
+ return true;
+}
+
- // Code based on
- // http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
+//// Mac OS X
+#elif defined(__APPLE__)
+
+bool setSystemPaths()
+{
CFBundleRef main_bundle = CFBundleGetMainBundle();
CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
char path[PATH_MAX];
- if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
- {
- dstream<<"Bundle resource path: "<<path<<std::endl;
- //chdir(path);
- path_share = std::string(path) + DIR_DELIM + STATIC_SHAREDIR;
- }
- else
- {
- // error!
- dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
+ if (CFURLGetFileSystemRepresentation(resources_url,
+ TRUE, (UInt8 *)path, PATH_MAX)) {
+ path_share = std::string(path);
+ } else {
+ dstream << "WARNING: Could not determine bundle resource path" << std::endl;
}
CFRelease(resources_url);
- path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
+ path_user = std::string(getenv("HOME"))
+ + "/Library/Application Support/"
+ + PROJECT_NAME;
+ return true;
+}
+
- #else // FreeBSD, and probably many other POSIX-like systems.
+#else
+bool setSystemPaths()
+{
path_share = STATIC_SHAREDIR;
- path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
+ path_user = std::string(getenv("HOME")) + DIR_DELIM "."
+ + lowercase(PROJECT_NAME);
+ return true;
+}
- #endif
-#endif // RUN_IN_PLACE
-}
+#endif
-static irr::IrrlichtDevice *device;
-void initIrrlicht(irr::IrrlichtDevice *device_)
+void initializePaths()
{
- device = device_;
+#if RUN_IN_PLACE
+ char buf[BUFSIZ];
+
+ infostream << "Using relative paths (RUN_IN_PLACE)" << std::endl;
+
+ bool success =
+ getCurrentExecPath(buf, sizeof(buf)) ||
+ getExecPathFromProcfs(buf, sizeof(buf));
+
+ if (success) {
+ pathRemoveFile(buf, DIR_DELIM_CHAR);
+ std::string execpath(buf);
+
+ path_share = execpath + DIR_DELIM "..";
+ path_user = execpath + DIR_DELIM "..";
+
+ if (detectMSVCBuildDir(execpath)) {
+ path_share += DIR_DELIM "..";
+ path_user += DIR_DELIM "..";
+ }
+ } else {
+ errorstream << "Failed to get paths by executable location, "
+ "trying cwd" << std::endl;
+
+ if (!getCurrentWorkingDir(buf, sizeof(buf)))
+ FATAL_ERROR("Ran out of methods to get paths");
+
+ size_t cwdlen = strlen(buf);
+ if (cwdlen >= 1 && buf[cwdlen - 1] == DIR_DELIM_CHAR) {
+ cwdlen--;
+ buf[cwdlen] = '\0';
+ }
+
+ if (cwdlen >= 4 && !strcmp(buf + cwdlen - 4, DIR_DELIM "bin"))
+ pathRemoveFile(buf, DIR_DELIM_CHAR);
+
+ std::string execpath(buf);
+
+ path_share = execpath;
+ path_user = execpath;
+ }
+
+#else
+ infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl;
+
+ if (!setSystemPaths())
+ errorstream << "Failed to get one or more system-wide path" << std::endl;
+
+#endif
+
+ infostream << "Detected share path: " << path_share << std::endl;
+ infostream << "Detected user path: " << path_user << std::endl;
}
+
+
void setXorgClassHint(const video::SExposedVideoData &video_data,
const std::string &name)
{
@@ -577,17 +659,33 @@ void setXorgClassHint(const video::SExposedVideoData &video_data,
#endif
}
+
+////
+//// Video/Display Information (Client-only)
+////
+
#ifndef SERVER
+
+static irr::IrrlichtDevice *device;
+
+void initIrrlicht(irr::IrrlichtDevice *device_)
+{
+ device = device_;
+}
+
v2u32 getWindowSize()
{
return device->getVideoDriver()->getScreenSize();
}
-std::vector<core::vector3d<u32> > getVideoModes()
+std::vector<core::vector3d<u32> > getSupportedVideoModes()
{
+ IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
+ sanity_check(nulldevice != NULL);
+
std::vector<core::vector3d<u32> > mlist;
- video::IVideoModeList *modelist = device->getVideoModeList();
+ video::IVideoModeList *modelist = nulldevice->getVideoModeList();
u32 num_modes = modelist->getVideoModeCount();
for (u32 i = 0; i != num_modes; i++) {
@@ -596,6 +694,8 @@ std::vector<core::vector3d<u32> > getVideoModes()
mlist.push_back(core::vector3d<u32>(mode_res.Width, mode_res.Height, mode_depth));
}
+ nulldevice->drop();
+
return mlist;
}
@@ -644,31 +744,28 @@ const char *getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type)
return driver_names[type];
}
-
-#ifndef __ANDROID__
-#ifdef XORG_USED
+# ifndef __ANDROID__
+# ifdef XORG_USED
static float calcDisplayDensity()
{
- const char* current_display = getenv("DISPLAY");
+ const char *current_display = getenv("DISPLAY");
if (current_display != NULL) {
- Display * x11display = XOpenDisplay(current_display);
+ Display *x11display = XOpenDisplay(current_display);
- if (x11display != NULL) {
- /* try x direct */
- float dpi_height =
- floor(DisplayHeight(x11display, 0) /
- (DisplayHeightMM(x11display, 0) * 0.039370) + 0.5);
- float dpi_width =
- floor(DisplayWidth(x11display, 0) /
- (DisplayWidthMM(x11display, 0) * 0.039370) +0.5);
+ if (x11display != NULL) {
+ /* try x direct */
+ float dpi_height = floor(DisplayHeight(x11display, 0) /
+ (DisplayHeightMM(x11display, 0) * 0.039370) + 0.5);
+ float dpi_width = floor(DisplayWidth(x11display, 0) /
+ (DisplayWidthMM(x11display, 0) * 0.039370) + 0.5);
- XCloseDisplay(x11display);
+ XCloseDisplay(x11display);
- return std::max(dpi_height,dpi_width) / 96.0;
- }
+ return std::max(dpi_height,dpi_width) / 96.0;
}
+ }
/* return manually specified dpi */
return g_settings->getFloat("screen_dpi")/96.0;
@@ -682,12 +779,12 @@ float getDisplayDensity()
}
-#else
+# else // XORG_USED
float getDisplayDensity()
{
return g_settings->getFloat("screen_dpi")/96.0;
}
-#endif
+# endif // XORG_USED
v2u32 getDisplaySize()
{
@@ -698,8 +795,8 @@ v2u32 getDisplaySize()
return deskres;
}
-#endif
-#endif
+# endif // __ANDROID__
+#endif // SERVER
} //namespace porting
diff --git a/src/porting.h b/src/porting.h
index 3d2677564..2a91fdd06 100644
--- a/src/porting.h
+++ b/src/porting.h
@@ -60,7 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <unistd.h>
#include <stdint.h> //for uintptr_t
-#if (defined(linux) || defined(__linux) || defined(__GNU__)) && !defined(_GNU_SOURCE)
+ #if (defined(linux) || defined(__linux) || defined(__GNU__)) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
@@ -371,12 +371,13 @@ float getDisplayDensity();
v2u32 getDisplaySize();
v2u32 getWindowSize();
+std::vector<core::vector3d<u32> > getSupportedVideoModes();
std::vector<irr::video::E_DRIVER_TYPE> getSupportedVideoDrivers();
const char *getVideoDriverName(irr::video::E_DRIVER_TYPE type);
const char *getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type);
#endif
-inline const char * getPlatformName()
+inline const char *getPlatformName()
{
return
#if defined(ANDROID)
@@ -400,8 +401,12 @@ inline const char * getPlatformName()
"AIX"
#elif defined(__hpux)
"HP-UX"
-#elif defined(__sun) && defined(__SVR4)
- "Solaris"
+#elif defined(__sun) || defined(sun)
+ #if defined(__SVR4)
+ "Solaris"
+ #else
+ "SunOS"
+ #endif
#elif defined(__CYGWIN__)
"Cygwin"
#elif defined(__unix__) || defined(__unix)
diff --git a/src/test.h b/src/profiler.cpp
index 547e9a986..197e094f6 100644
--- a/src/test.h
+++ b/src/profiler.cpp
@@ -1,6 +1,6 @@
/*
Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2015 celeron55, Perttu Ahola <celeron55@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
@@ -17,10 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef TEST_HEADER
-#define TEST_HEADER
-
-void run_tests();
-
-#endif
+#include "profiler.h"
+static Profiler main_profiler;
+Profiler *g_profiler = &main_profiler;
diff --git a/src/profiler.h b/src/profiler.h
index 5816f05ca..78d3b08e0 100644
--- a/src/profiler.h
+++ b/src/profiler.h
@@ -27,11 +27,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "jthread/jmutex.h"
#include "jthread/jmutexautolock.h"
#include "util/timetaker.h"
-#include "util/numeric.h" // paging()
-#include "debug.h" // assert()
+#include "util/numeric.h" // paging()
+#include "debug.h" // assert()
#define MAX_PROFILER_TEXT_ROWS 20
+// Global profiler
+class Profiler;
+extern Profiler *g_profiler;
+
/*
Time profiler
*/
@@ -69,23 +73,11 @@ public:
void avg(const std::string &name, float value)
{
JMutexAutoLock lock(m_mutex);
- {
- std::map<std::string, int>::iterator n = m_avgcounts.find(name);
- if(n == m_avgcounts.end())
- m_avgcounts[name] = 1;
- else{
- /* No add shall have been used */
- assert(n->second != -2);
- n->second = MYMAX(n->second, 0) + 1;
- }
- }
- {
- std::map<std::string, float>::iterator n = m_data.find(name);
- if(n == m_data.end())
- m_data[name] = value;
- else
- n->second += value;
- }
+ int &count = m_avgcounts[name];
+
+ assert(count != -2);
+ count = MYMAX(count, 0) + 1;
+ m_data[name] += value;
}
void clear()
@@ -105,6 +97,21 @@ public:
printPage(o, 1, 1);
}
+ float getValue(const std::string &name) const
+ {
+ std::map<std::string, float>::const_iterator numerator = m_data.find(name);
+ if (numerator == m_data.end())
+ return 0.f;
+
+ std::map<std::string, int>::const_iterator denominator = m_avgcounts.find(name);
+ if (denominator != m_avgcounts.end()){
+ if (denominator->second >= 1)
+ return numerator->second / denominator->second;
+ }
+
+ return numerator->second;
+ }
+
void printPage(std::ostream &o, u32 page, u32 pagecount)
{
JMutexAutoLock lock(m_mutex);
diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp
index c35ad5781..b3f457029 100644
--- a/src/rollback_interface.cpp
+++ b/src/rollback_interface.cpp
@@ -178,7 +178,7 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
if (block) {
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "NodeMetaRef::reportMetadataChange");
+ MOD_REASON_REPORT_META_CHANGE);
}
} catch (InvalidPositionException &e) {
infostream << "RollbackAction::applyRevert(): "
@@ -210,6 +210,7 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
<< inventory_index << " too large in "
<< "inventory list \"" << inventory_list << "\" in "
<< inventory_location << std::endl;
+ return false;
}
// If item was added, take away item, otherwise add removed item
if (inventory_add) {
diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt
index 491c05a1e..5ef672ca9 100644
--- a/src/script/CMakeLists.txt
+++ b/src/script/CMakeLists.txt
@@ -11,9 +11,10 @@ set(common_SCRIPT_SRCS
PARENT_SCOPE)
# Used by client only
-set(minetest_SCRIPT_SRCS
+set(client_SCRIPT_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp
- ${minetest_SCRIPT_COMMON_SRCS}
- ${minetest_SCRIPT_CPP_API_SRCS}
- ${minetest_SCRIPT_LUA_API_SRCS}
+ ${client_SCRIPT_COMMON_SRCS}
+ ${client_SCRIPT_CPP_API_SRCS}
+ ${client_SCRIPT_LUA_API_SRCS}
PARENT_SCOPE)
+
diff --git a/src/script/common/CMakeLists.txt b/src/script/common/CMakeLists.txt
index 27e2fb4d5..4a8e6bab5 100644
--- a/src/script/common/CMakeLists.txt
+++ b/src/script/common/CMakeLists.txt
@@ -1,4 +1,3 @@
-# Used by server and client
set(common_SCRIPT_COMMON_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/c_content.cpp
${CMAKE_CURRENT_SOURCE_DIR}/c_converter.cpp
@@ -6,6 +5,6 @@ set(common_SCRIPT_COMMON_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/c_internal.cpp
PARENT_SCOPE)
-# Used by client only
-set(minetest_SCRIPT_COMMON_SRCS
+set(client_SCRIPT_COMMON_SRCS
PARENT_SCOPE)
+
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index ff9aee8ed..3754fc2ff 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -162,18 +162,13 @@ void read_object_properties(lua_State *L, int index,
lua_pop(L, 1);
lua_getfield(L, -1, "colors");
- if(lua_istable(L, -1)){
- prop->colors.clear();
+ if (lua_istable(L, -1)) {
int table = lua_gettop(L);
- lua_pushnil(L);
- while(lua_next(L, table) != 0){
- // key at index -2 and value at index -1
- if(lua_isstring(L, -1))
- prop->colors.push_back(readARGB8(L, -1));
- else
- prop->colors.push_back(video::SColor(255, 255, 255, 255));
- // removes value, keeps key for next iteration
- lua_pop(L, 1);
+ prop->colors.clear();
+ for (lua_pushnil(L); lua_next(L, table); lua_pop(L, 1)) {
+ video::SColor color(255, 255, 255, 255);
+ read_color(L, -1, &color);
+ prop->colors.push_back(color);
}
}
lua_pop(L, 1);
@@ -205,17 +200,78 @@ void read_object_properties(lua_State *L, int index,
}
/******************************************************************************/
-TileDef read_tiledef(lua_State *L, int index)
+void push_object_properties(lua_State *L, ObjectProperties *prop)
+{
+ lua_newtable(L);
+ lua_pushnumber(L, prop->hp_max);
+ lua_setfield(L, -2, "hp_max");
+ lua_pushboolean(L, prop->physical);
+ lua_setfield(L, -2, "physical");
+ lua_pushboolean(L, prop->collideWithObjects);
+ lua_setfield(L, -2, "collide_with_objects");
+ lua_pushnumber(L, prop->weight);
+ lua_setfield(L, -2, "weight");
+ push_aabb3f(L, prop->collisionbox);
+ lua_setfield(L, -2, "collisionbox");
+ lua_pushlstring(L, prop->visual.c_str(), prop->visual.size());
+ lua_setfield(L, -2, "visual");
+ lua_pushlstring(L, prop->mesh.c_str(), prop->mesh.size());
+ lua_setfield(L, -2, "mesh");
+ push_v2f(L, prop->visual_size);
+ lua_setfield(L, -2, "visual_size");
+
+ lua_newtable(L);
+ u16 i = 1;
+ for (std::vector<std::string>::iterator it = prop->textures.begin();
+ it != prop->textures.end(); ++it) {
+ lua_pushlstring(L, it->c_str(), it->size());
+ lua_rawseti(L, -2, i);
+ }
+ lua_setfield(L, -2, "textures");
+
+ lua_newtable(L);
+ i = 1;
+ for (std::vector<video::SColor>::iterator it = prop->colors.begin();
+ it != prop->colors.end(); ++it) {
+ push_ARGB8(L, *it);
+ lua_rawseti(L, -2, i);
+ }
+ lua_setfield(L, -2, "colors");
+
+ push_v2s16(L, prop->spritediv);
+ lua_setfield(L, -2, "spritediv");
+ push_v2s16(L, prop->initial_sprite_basepos);
+ lua_setfield(L, -2, "initial_sprite_basepos");
+ lua_pushboolean(L, prop->is_visible);
+ lua_setfield(L, -2, "is_visible");
+ lua_pushboolean(L, prop->makes_footstep_sound);
+ lua_setfield(L, -2, "makes_footstep_sound");
+ lua_pushnumber(L, prop->automatic_rotate);
+ lua_setfield(L, -2, "automatic_rotate");
+ lua_pushnumber(L, prop->stepheight / BS);
+ lua_setfield(L, -2, "stepheight");
+ if (prop->automatic_face_movement_dir)
+ lua_pushnumber(L, prop->automatic_face_movement_dir_offset);
+ else
+ lua_pushboolean(L, false);
+ lua_setfield(L, -2, "automatic_face_movement_dir");
+}
+
+/******************************************************************************/
+TileDef read_tiledef(lua_State *L, int index, u8 drawtype)
{
if(index < 0)
index = lua_gettop(L) + 1 + index;
TileDef tiledef;
-
+ bool default_tiling = (drawtype == NDT_PLANTLIKE || drawtype == NDT_FIRELIKE)
+ ? false : true;
// key at index -2 and value at index
if(lua_isstring(L, index)){
// "default_lava.png"
tiledef.name = lua_tostring(L, index);
+ tiledef.tileable_vertical = default_tiling;
+ tiledef.tileable_horizontal = default_tiling;
}
else if(lua_istable(L, index))
{
@@ -224,20 +280,24 @@ TileDef read_tiledef(lua_State *L, int index)
getstringfield(L, index, "name", tiledef.name);
getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat.
tiledef.backface_culling = getboolfield_default(
- L, index, "backface_culling", true);
+ L, index, "backface_culling", true);
+ tiledef.tileable_horizontal = getboolfield_default(
+ L, index, "tileable_horizontal", default_tiling);
+ tiledef.tileable_vertical = getboolfield_default(
+ L, index, "tileable_vertical", default_tiling);
// animation = {}
lua_getfield(L, index, "animation");
if(lua_istable(L, -1)){
// {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}
tiledef.animation.type = (TileAnimationType)
- getenumfield(L, -1, "type", es_TileAnimationType,
- TAT_NONE);
+ getenumfield(L, -1, "type", es_TileAnimationType,
+ TAT_NONE);
tiledef.animation.aspect_w =
- getintfield_default(L, -1, "aspect_w", 16);
+ getintfield_default(L, -1, "aspect_w", 16);
tiledef.animation.aspect_h =
- getintfield_default(L, -1, "aspect_h", 16);
+ getintfield_default(L, -1, "aspect_h", 16);
tiledef.animation.length =
- getfloatfield_default(L, -1, "length", 1.0);
+ getfloatfield_default(L, -1, "length", 1.0);
}
lua_pop(L, 1);
}
@@ -300,7 +360,7 @@ ContentFeatures read_content_features(lua_State *L, int index)
int i = 0;
while(lua_next(L, table) != 0){
// Read tiledef from value
- f.tiledef[i] = read_tiledef(L, -1);
+ f.tiledef[i] = read_tiledef(L, -1, f.drawtype);
// removes value, keeps key for next iteration
lua_pop(L, 1);
i++;
@@ -335,7 +395,7 @@ ContentFeatures read_content_features(lua_State *L, int index)
int i = 0;
while(lua_next(L, table) != 0){
// Read tiledef from value
- f.tiledef_special[i] = read_tiledef(L, -1);
+ f.tiledef_special[i] = read_tiledef(L, -1, f.drawtype);
// removes value, keeps key for next iteration
lua_pop(L, 1);
i++;
@@ -357,8 +417,7 @@ ContentFeatures read_content_features(lua_State *L, int index)
/* Other stuff */
lua_getfield(L, index, "post_effect_color");
- if(!lua_isnil(L, -1))
- f.post_effect_color = readARGB8(L, -1);
+ read_color(L, -1, &f.post_effect_color);
lua_pop(L, 1);
f.param_type = (ContentParamType)getenumfield(L, index, "paramtype",
@@ -546,22 +605,23 @@ NodeBox read_nodebox(lua_State *L, int index)
MapNode readnode(lua_State *L, int index, INodeDefManager *ndef)
{
lua_getfield(L, index, "name");
- const char *name = luaL_checkstring(L, -1);
+ if (!lua_isstring(L, -1))
+ throw LuaError("Node name is not set or is not a string!");
+ const char *name = lua_tostring(L, -1);
lua_pop(L, 1);
- u8 param1;
+
+ u8 param1 = 0;
lua_getfield(L, index, "param1");
- if(lua_isnil(L, -1))
- param1 = 0;
- else
+ if (!lua_isnil(L, -1))
param1 = lua_tonumber(L, -1);
lua_pop(L, 1);
- u8 param2;
+
+ u8 param2 = 0;
lua_getfield(L, index, "param2");
- if(lua_isnil(L, -1))
- param2 = 0;
- else
+ if (!lua_isnil(L, -1))
param2 = lua_tonumber(L, -1);
lua_pop(L, 1);
+
return MapNode(ndef, name, param1, param2);
}
@@ -901,6 +961,12 @@ u32 read_flags_table(lua_State *L, int table, FlagDesc *flagdesc, u32 *flagmask)
return flags;
}
+void push_flags_string(lua_State *L, FlagDesc *flagdesc, u32 flags, u32 flagmask)
+{
+ std::string flagstring = writeFlagString(flags, flagdesc, flagmask);
+ lua_pushlstring(L, flagstring.c_str(), flagstring.size());
+}
+
/******************************************************************************/
/* Lua Stored data! */
/******************************************************************************/
@@ -926,14 +992,23 @@ void read_groups(lua_State *L, int index,
}
/******************************************************************************/
+void push_groups(lua_State *L, const std::map<std::string, int> &groups)
+{
+ lua_newtable(L);
+ std::map<std::string, int>::const_iterator it;
+ for (it = groups.begin(); it != groups.end(); ++it) {
+ lua_pushnumber(L, it->second);
+ lua_setfield(L, -2, it->first.c_str());
+ }
+}
+
+/******************************************************************************/
void push_items(lua_State *L, const std::vector<ItemStack> &items)
{
- // Create and fill table
lua_createtable(L, items.size(), 0);
- std::vector<ItemStack>::const_iterator iter = items.begin();
- for (u32 i = 0; iter != items.end(); iter++) {
- LuaItemStack::create(L, *iter);
- lua_rawseti(L, -2, ++i);
+ for (u32 i = 0; i != items.size(); i++) {
+ LuaItemStack::create(L, items[i]);
+ lua_rawseti(L, -2, i + 1);
}
}
@@ -982,14 +1057,16 @@ bool read_noiseparams(lua_State *L, int index, NoiseParams *np)
if (!lua_istable(L, index))
return false;
- np->offset = getfloatfield_default(L, index, "offset", 0.0);
- np->scale = getfloatfield_default(L, index, "scale", 0.0);
- np->persist = getfloatfield_default(L, index, "persist", 0.0);
- np->lacunarity = getfloatfield_default(L, index, "lacunarity", 2.0);
- np->seed = getintfield_default(L, index, "seed", 0);
- np->octaves = getintfield_default(L, index, "octaves", 0);
+ getfloatfield(L, index, "offset", np->offset);
+ getfloatfield(L, index, "scale", np->scale);
+ getfloatfield(L, index, "persist", np->persist);
+ getfloatfield(L, index, "persistence", np->persist);
+ getfloatfield(L, index, "lacunarity", np->lacunarity);
+ getintfield(L, index, "seed", np->seed);
+ getintfield(L, index, "octaves", np->octaves);
- u32 flags = 0, flagmask = 0;
+ u32 flags = 0;
+ u32 flagmask = 0;
np->flags = getflagsfield(L, index, "flags", flagdesc_noiseparams,
&flags, &flagmask) ? flags : NOISE_FLAG_DEFAULTS;
@@ -1000,6 +1077,30 @@ bool read_noiseparams(lua_State *L, int index, NoiseParams *np)
return true;
}
+void push_noiseparams(lua_State *L, NoiseParams *np)
+{
+ lua_newtable(L);
+ lua_pushnumber(L, np->offset);
+ lua_setfield(L, -2, "offset");
+ lua_pushnumber(L, np->scale);
+ lua_setfield(L, -2, "scale");
+ lua_pushnumber(L, np->persist);
+ lua_setfield(L, -2, "persistence");
+ lua_pushnumber(L, np->lacunarity);
+ lua_setfield(L, -2, "lacunarity");
+ lua_pushnumber(L, np->seed);
+ lua_setfield(L, -2, "seed");
+ lua_pushnumber(L, np->octaves);
+ lua_setfield(L, -2, "octaves");
+
+ push_flags_string(L, flagdesc_noiseparams, np->flags,
+ np->flags);
+ lua_setfield(L, -2, "flags");
+
+ push_v3f(L, np->spread);
+ lua_setfield(L, -2, "spread");
+}
+
/******************************************************************************/
// Returns depth of json value tree
static int push_json_value_getdepth(const Json::Value &value)
diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h
index 241b1ca76..46416ad8e 100644
--- a/src/script/common/c_content.h
+++ b/src/script/common/c_content.h
@@ -62,59 +62,57 @@ struct NoiseParams;
class Schematic;
-ContentFeatures read_content_features (lua_State *L, int index);
-TileDef read_tiledef (lua_State *L, int index);
-void read_soundspec (lua_State *L, int index,
- SimpleSoundSpec &spec);
-NodeBox read_nodebox (lua_State *L, int index);
+ContentFeatures read_content_features (lua_State *L, int index);
+TileDef read_tiledef (lua_State *L, int index,
+ u8 drawtype);
+void read_soundspec (lua_State *L, int index,
+ SimpleSoundSpec &spec);
+NodeBox read_nodebox (lua_State *L, int index);
-void read_server_sound_params (lua_State *L, int index,
- ServerSoundParams &params);
+void read_server_sound_params (lua_State *L, int index,
+ ServerSoundParams &params);
-void push_dig_params (lua_State *L,const DigParams &params);
-void push_hit_params (lua_State *L,const HitParams &params);
+void push_dig_params (lua_State *L,
+ const DigParams &params);
+void push_hit_params (lua_State *L,
+ const HitParams &params);
-ItemStack read_item (lua_State *L, int index, Server* srv);
+ItemStack read_item (lua_State *L, int index, Server *srv);
-ToolCapabilities read_tool_capabilities (lua_State *L,
- int table);
+ToolCapabilities read_tool_capabilities (lua_State *L, int table);
void push_tool_capabilities (lua_State *L,
const ToolCapabilities &prop);
-ItemDefinition read_item_definition (lua_State *L,
- int index,
+ItemDefinition read_item_definition (lua_State *L, int index,
ItemDefinition default_def);
-void read_object_properties (lua_State *L,
- int index,
+void read_object_properties (lua_State *L, int index,
+ ObjectProperties *prop);
+void push_object_properties (lua_State *L,
ObjectProperties *prop);
void push_inventory_list (lua_State *L,
Inventory *inv,
const char *name);
-void read_inventory_list (lua_State *L,
- int tableindex,
- Inventory *inv,
- const char *name,
- Server* srv,
- int forcesize=-1);
+void read_inventory_list (lua_State *L, int tableindex,
+ Inventory *inv, const char *name,
+ Server *srv, int forcesize=-1);
-MapNode readnode (lua_State *L,
- int index,
+MapNode readnode (lua_State *L, int index,
INodeDefManager *ndef);
-void pushnode (lua_State *L,
- const MapNode &n,
+void pushnode (lua_State *L, const MapNode &n,
INodeDefManager *ndef);
NodeBox read_nodebox (lua_State *L, int index);
-void read_groups (lua_State *L,
- int index,
+void read_groups (lua_State *L, int index,
std::map<std::string, int> &result);
+void push_groups (lua_State *L,
+ const std::map<std::string, int> &groups);
+
//TODO rename to "read_enum_field"
-int getenumfield (lua_State *L,
- int table,
+int getenumfield (lua_State *L, int table,
const char *fieldname,
const EnumString *spec,
int default_);
@@ -128,6 +126,9 @@ bool read_flags (lua_State *L, int index,
FlagDesc *flagdesc,
u32 *flags, u32 *flagmask);
+void push_flags_string (lua_State *L, FlagDesc *flagdesc,
+ u32 flags, u32 flagmask);
+
u32 read_flags_table (lua_State *L, int table,
FlagDesc *flagdesc, u32 *flagmask);
@@ -142,23 +143,21 @@ void read_soundspec (lua_State *L,
int index,
SimpleSoundSpec &spec);
-
bool string_to_enum (const EnumString *spec,
int &result,
const std::string &str);
bool read_noiseparams (lua_State *L, int index,
NoiseParams *np);
+void push_noiseparams (lua_State *L, NoiseParams *np);
void luaentity_get (lua_State *L,u16 id);
bool push_json_value (lua_State *L,
const Json::Value &value,
int nullindex);
-void read_json_value (lua_State *L,
- Json::Value &root,
- int index,
- u8 recursion = 0);
+void read_json_value (lua_State *L, Json::Value &root,
+ int index, u8 recursion = 0);
extern struct EnumString es_TileAnimationType[];
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index 66eeec68e..f1d3cc421 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -23,9 +23,23 @@ extern "C" {
}
#include "util/numeric.h"
+#include "util/string.h"
#include "common/c_converter.h"
#include "constants.h"
+
+#define CHECK_TYPE(index, name, type) do { \
+ int t = lua_type(L, (index)); \
+ if (t != (type)) { \
+ throw LuaError(std::string("Invalid ") + (name) + \
+ " (expected " + lua_typename(L, (type)) + \
+ " got " + lua_typename(L, t) + ")."); \
+ } \
+ } while(0)
+#define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER)
+#define CHECK_POS_TAB(index) CHECK_TYPE(index, "position", LUA_TTABLE)
+
+
void push_v3f(lua_State *L, v3f p)
{
lua_newtable(L);
@@ -49,7 +63,7 @@ void push_v2f(lua_State *L, v2f p)
v2s16 read_v2s16(lua_State *L, int index)
{
v2s16 p;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
@@ -59,10 +73,43 @@ v2s16 read_v2s16(lua_State *L, int index)
return p;
}
+v2s16 check_v2s16(lua_State *L, int index)
+{
+ v2s16 p;
+ CHECK_POS_TAB(index);
+ lua_getfield(L, index, "x");
+ CHECK_POS_COORD("x");
+ p.X = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "y");
+ CHECK_POS_COORD("y");
+ p.Y = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return p;
+}
+
+void push_v2s16(lua_State *L, v2s16 p)
+{
+ lua_newtable(L);
+ lua_pushnumber(L, p.X);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, p.Y);
+ lua_setfield(L, -2, "y");
+}
+
+void push_v2s32(lua_State *L, v2s32 p)
+{
+ lua_newtable(L);
+ lua_pushnumber(L, p.X);
+ lua_setfield(L, -2, "x");
+ lua_pushnumber(L, p.Y);
+ lua_setfield(L, -2, "y");
+}
+
v2s32 read_v2s32(lua_State *L, int index)
{
v2s32 p;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
@@ -75,11 +122,26 @@ v2s32 read_v2s32(lua_State *L, int index)
v2f read_v2f(lua_State *L, int index)
{
v2f p;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
+ lua_getfield(L, index, "x");
+ p.X = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "y");
+ p.Y = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return p;
+}
+
+v2f check_v2f(lua_State *L, int index)
+{
+ v2f p;
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
+ CHECK_POS_COORD("x");
p.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
+ CHECK_POS_COORD("y");
p.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
return p;
@@ -88,7 +150,7 @@ v2f read_v2f(lua_State *L, int index)
v3f read_v3f(lua_State *L, int index)
{
v3f pos;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
pos.X = lua_tonumber(L, -1);
lua_pop(L, 1);
@@ -104,19 +166,35 @@ v3f read_v3f(lua_State *L, int index)
v3f check_v3f(lua_State *L, int index)
{
v3f pos;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
- pos.X = luaL_checknumber(L, -1);
+ CHECK_POS_COORD("x");
+ pos.X = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "y");
- pos.Y = luaL_checknumber(L, -1);
+ CHECK_POS_COORD("y");
+ pos.Y = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_getfield(L, index, "z");
- pos.Z = luaL_checknumber(L, -1);
+ CHECK_POS_COORD("z");
+ pos.Z = lua_tonumber(L, -1);
lua_pop(L, 1);
return pos;
}
+void push_ARGB8(lua_State *L, video::SColor color)
+{
+ lua_newtable(L);
+ lua_pushnumber(L, color.getAlpha());
+ lua_setfield(L, -2, "a");
+ lua_pushnumber(L, color.getRed());
+ lua_setfield(L, -2, "r");
+ lua_pushnumber(L, color.getGreen());
+ lua_setfield(L, -2, "g");
+ lua_pushnumber(L, color.getBlue());
+ lua_setfield(L, -2, "b");
+}
+
void pushFloatPos(lua_State *L, v3f p)
{
p /= BS;
@@ -153,13 +231,31 @@ v3s16 check_v3s16(lua_State *L, int index)
return floatToInt(pf, 1.0);
}
-video::SColor readARGB8(lua_State *L, int index)
+bool read_color(lua_State *L, int index, video::SColor *color)
+{
+ if (lua_istable(L, index)) {
+ *color = read_ARGB8(L, index);
+ } else if (lua_isnumber(L, index)) {
+ color->set(lua_tonumber(L, index));
+ } else if (lua_isstring(L, index)) {
+ video::SColor parsed_color;
+ if (!parseColorString(lua_tostring(L, index), parsed_color, true))
+ return false;
+
+ *color = parsed_color;
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+video::SColor read_ARGB8(lua_State *L, int index)
{
video::SColor color(0);
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
lua_getfield(L, index, "a");
- if(lua_isnumber(L, -1))
- color.setAlpha(lua_tonumber(L, -1));
+ color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF);
lua_pop(L, 1);
lua_getfield(L, index, "r");
color.setRed(lua_tonumber(L, -1));
@@ -199,6 +295,23 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
return box;
}
+void push_aabb3f(lua_State *L, aabb3f box)
+{
+ lua_newtable(L);
+ lua_pushnumber(L, box.MinEdge.X);
+ lua_rawseti(L, -2, 1);
+ lua_pushnumber(L, box.MinEdge.Y);
+ lua_rawseti(L, -2, 2);
+ lua_pushnumber(L, box.MinEdge.Z);
+ lua_rawseti(L, -2, 3);
+ lua_pushnumber(L, box.MaxEdge.X);
+ lua_rawseti(L, -2, 4);
+ lua_pushnumber(L, box.MaxEdge.Y);
+ lua_rawseti(L, -2, 5);
+ lua_pushnumber(L, box.MaxEdge.Z);
+ lua_rawseti(L, -2, 6);
+}
+
std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
{
std::vector<aabb3f> boxes;
@@ -227,24 +340,28 @@ std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
return boxes;
}
-bool read_stringlist(lua_State *L, int index, std::vector<const char *> &result)
+size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result)
{
if (index < 0)
index = lua_gettop(L) + 1 + index;
+ size_t num_strings = 0;
+
if (lua_istable(L, index)) {
lua_pushnil(L);
while (lua_next(L, index)) {
- if (lua_isstring(L, -1))
- result.push_back(lua_tostring(L, -1));
+ if (lua_isstring(L, -1)) {
+ result->push_back(lua_tostring(L, -1));
+ num_strings++;
+ }
lua_pop(L, 1);
}
} else if (lua_isstring(L, index)) {
- result.push_back(lua_tostring(L, index));
- } else {
- return false;
+ result->push_back(lua_tostring(L, index));
+ num_strings++;
}
- return true;
+
+ return num_strings;
}
/*
@@ -281,6 +398,45 @@ bool getintfield(lua_State *L, int table,
return got;
}
+bool getintfield(lua_State *L, int table,
+ const char *fieldname, u8 &result)
+{
+ lua_getfield(L, table, fieldname);
+ bool got = false;
+ if(lua_isnumber(L, -1)){
+ result = lua_tonumber(L, -1);
+ got = true;
+ }
+ lua_pop(L, 1);
+ return got;
+}
+
+bool getintfield(lua_State *L, int table,
+ const char *fieldname, u16 &result)
+{
+ lua_getfield(L, table, fieldname);
+ bool got = false;
+ if(lua_isnumber(L, -1)){
+ result = lua_tonumber(L, -1);
+ got = true;
+ }
+ lua_pop(L, 1);
+ return got;
+}
+
+bool getintfield(lua_State *L, int table,
+ const char *fieldname, u32 &result)
+{
+ lua_getfield(L, table, fieldname);
+ bool got = false;
+ if(lua_isnumber(L, -1)){
+ result = lua_tonumber(L, -1);
+ got = true;
+ }
+ lua_pop(L, 1);
+ return got;
+}
+
bool getfloatfield(lua_State *L, int table,
const char *fieldname, float &result)
{
@@ -307,24 +463,26 @@ bool getboolfield(lua_State *L, int table,
return got;
}
-bool getstringlistfield(lua_State *L, int table, const char *fieldname,
- std::vector<const char *> &result)
+size_t getstringlistfield(lua_State *L, int table, const char *fieldname,
+ std::vector<std::string> *result)
{
lua_getfield(L, table, fieldname);
- bool got = read_stringlist(L, -1, result);
+ size_t num_strings_read = read_stringlist(L, -1, result);
lua_pop(L, 1);
- return got;
+ return num_strings_read;
}
std::string checkstringfield(lua_State *L, int table,
const char *fieldname)
{
lua_getfield(L, table, fieldname);
- std::string s = luaL_checkstring(L, -1);
+ CHECK_TYPE(-1, std::string("field \"") + fieldname + '"', LUA_TSTRING);
+ size_t len;
+ const char *s = lua_tolstring(L, -1, &len);
lua_pop(L, 1);
- return s;
+ return std::string(s, len);
}
std::string getstringfield_default(lua_State *L, int table,
@@ -387,3 +545,95 @@ void setboolfield(lua_State *L, int table,
}
+////
+//// Array table slices
+////
+
+size_t write_array_slice_float(
+ lua_State *L,
+ int table_index,
+ float *data,
+ v3u16 data_size,
+ v3u16 slice_offset,
+ v3u16 slice_size)
+{
+ v3u16 pmin, pmax(data_size);
+
+ if (slice_offset.X > 0) {
+ slice_offset.X--;
+ pmin.X = slice_offset.X;
+ pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
+ }
+
+ if (slice_offset.Y > 0) {
+ slice_offset.Y--;
+ pmin.Y = slice_offset.Y;
+ pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
+ }
+
+ if (slice_offset.Z > 0) {
+ slice_offset.Z--;
+ pmin.Z = slice_offset.Z;
+ pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
+ }
+
+ const u32 ystride = data_size.X;
+ const u32 zstride = data_size.X * data_size.Y;
+
+ u32 elem_index = 1;
+ for (u32 z = pmin.Z; z != pmax.Z; z++)
+ for (u32 y = pmin.Y; y != pmax.Y; y++)
+ for (u32 x = pmin.X; x != pmax.X; x++) {
+ u32 i = z * zstride + y * ystride + x;
+ lua_pushnumber(L, data[i]);
+ lua_rawseti(L, table_index, elem_index);
+ elem_index++;
+ }
+
+ return elem_index - 1;
+}
+
+
+size_t write_array_slice_u16(
+ lua_State *L,
+ int table_index,
+ u16 *data,
+ v3u16 data_size,
+ v3u16 slice_offset,
+ v3u16 slice_size)
+{
+ v3u16 pmin, pmax(data_size);
+
+ if (slice_offset.X > 0) {
+ slice_offset.X--;
+ pmin.X = slice_offset.X;
+ pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
+ }
+
+ if (slice_offset.Y > 0) {
+ slice_offset.Y--;
+ pmin.Y = slice_offset.Y;
+ pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
+ }
+
+ if (slice_offset.Z > 0) {
+ slice_offset.Z--;
+ pmin.Z = slice_offset.Z;
+ pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
+ }
+
+ const u32 ystride = data_size.X;
+ const u32 zstride = data_size.X * data_size.Y;
+
+ u32 elem_index = 1;
+ for (u32 z = pmin.Z; z != pmax.Z; z++)
+ for (u32 y = pmin.Y; y != pmax.Y; y++)
+ for (u32 x = pmin.X; x != pmax.X; x++) {
+ u32 i = z * zstride + y * ystride + x;
+ lua_pushinteger(L, data[i]);
+ lua_rawseti(L, table_index, elem_index);
+ elem_index++;
+ }
+
+ return elem_index - 1;
+}
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index 3b7eb6f7d..18a045d2a 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -48,11 +48,17 @@ int getintfield_default (lua_State *L, int table,
bool getstringfield(lua_State *L, int table,
const char *fieldname, std::string &result);
-bool getstringlistfield(lua_State *L, int table,
+size_t getstringlistfield(lua_State *L, int table,
const char *fieldname,
- std::vector<const char *> &result);
+ std::vector<std::string> *result);
bool getintfield(lua_State *L, int table,
const char *fieldname, int &result);
+bool getintfield(lua_State *L, int table,
+ const char *fieldname, u8 &result);
+bool getintfield(lua_State *L, int table,
+ const char *fieldname, u16 &result);
+bool getintfield(lua_State *L, int table,
+ const char *fieldname, u32 &result);
void read_groups(lua_State *L, int index,
std::map<std::string, int> &result);
bool getboolfield(lua_State *L, int table,
@@ -70,8 +76,9 @@ void setfloatfield(lua_State *L, int table,
void setboolfield(lua_State *L, int table,
const char *fieldname, bool value);
-
v3f checkFloatPos (lua_State *L, int index);
+v2f check_v2f (lua_State *L, int index);
+v2s16 check_v2s16 (lua_State *L, int index);
v3f check_v3f (lua_State *L, int index);
v3s16 check_v3s16 (lua_State *L, int index);
@@ -79,23 +86,32 @@ v3f read_v3f (lua_State *L, int index);
v2f read_v2f (lua_State *L, int index);
v2s16 read_v2s16 (lua_State *L, int index);
v2s32 read_v2s32 (lua_State *L, int index);
-video::SColor readARGB8 (lua_State *L, int index);
+video::SColor read_ARGB8 (lua_State *L, int index);
+bool read_color (lua_State *L, int index,
+ video::SColor *color);
+
aabb3f read_aabb3f (lua_State *L, int index, f32 scale);
v3s16 read_v3s16 (lua_State *L, int index);
std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale);
-bool read_stringlist (lua_State *L, int index,
- std::vector<const char *> &result);
+size_t read_stringlist (lua_State *L, int index,
+ std::vector<std::string> *result);
+void push_v2s16 (lua_State *L, v2s16 p);
+void push_v2s32 (lua_State *L, v2s32 p);
void push_v3s16 (lua_State *L, v3s16 p);
+void push_aabb3f (lua_State *L, aabb3f box);
+void push_ARGB8 (lua_State *L, video::SColor color);
void pushFloatPos (lua_State *L, v3f p);
void push_v3f (lua_State *L, v3f p);
void push_v2f (lua_State *L, v2f p);
+void warn_if_field_exists(lua_State *L, int table,
+ const char *fieldname,
+ const std::string &message);
-
-void warn_if_field_exists (lua_State *L,
- int table,
- const char *fieldname,
- const std::string &message);
+size_t write_array_slice_float(lua_State *L, int table_index, float *data,
+ v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);
+size_t write_array_slice_u16(lua_State *L, int table_index, u16 *data,
+ v3u16 data_size, v3u16 slice_offset, v3u16 slice_size);
#endif /* C_CONVERTER_H_ */
diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp
index f811dd5d3..2a10ce0f2 100644
--- a/src/script/common/c_internal.cpp
+++ b/src/script/common/c_internal.cpp
@@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_internal.h"
#include "debug.h"
#include "log.h"
-#include "main.h"
#include "settings.h"
std::string script_get_backtrace(lua_State *L)
@@ -64,19 +63,65 @@ int script_exception_wrapper(lua_State *L, lua_CFunction f)
return f(L); // Call wrapped function and return result.
} catch (const char *s) { // Catch and convert exceptions.
lua_pushstring(L, s);
- } catch (std::exception& e) {
+ } catch (std::exception &e) {
lua_pushstring(L, e.what());
- } catch (...) {
- lua_pushliteral(L, "caught (...)");
}
return lua_error(L); // Rethrow as a Lua error.
}
-void script_error(lua_State *L)
+/*
+ * Note that we can't get tracebacks for LUA_ERRMEM or LUA_ERRERR (without
+ * hacking Lua internals). For LUA_ERRMEM, this is because memory errors will
+ * not execute the the error handler, and by the time lua_pcall returns the
+ * execution stack will have already been unwound. For LUA_ERRERR, there was
+ * another error while trying to generate a backtrace from a LUA_ERRRUN. It is
+ * presumed there is an error with the internal Lua state and thus not possible
+ * to gather a coherent backtrace. Realistically, the best we can do here is
+ * print which C function performed the failing pcall.
+ */
+void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn)
{
- const char *s = lua_tostring(L, -1);
- std::string str(s ? s : "");
- throw LuaError(str);
+ if (pcall_result == 0)
+ return;
+
+ const char *err_type;
+ switch (pcall_result) {
+ case LUA_ERRRUN:
+ err_type = "Runtime";
+ break;
+ case LUA_ERRMEM:
+ err_type = "OOM";
+ break;
+ case LUA_ERRERR:
+ err_type = "Double fault";
+ break;
+ default:
+ err_type = "Unknown";
+ }
+
+ if (!mod)
+ mod = "??";
+
+ if (!fxn)
+ fxn = "??";
+
+ const char *err_descr = lua_tostring(L, -1);
+ if (!err_descr)
+ err_descr = "<no description>";
+
+ char buf[256];
+ snprintf(buf, sizeof(buf), "%s error from mod '%s' in callback %s(): ",
+ err_type, mod, fxn);
+
+ std::string err_msg(buf);
+ err_msg += err_descr;
+
+ if (pcall_result == LUA_ERRMEM) {
+ err_msg += "\nCurrent Lua memory usage: "
+ + itos(lua_gc(L, LUA_GCCOUNT, 0) >> 10) + " MB";
+ }
+
+ throw LuaError(err_msg);
}
// Push the list of callbacks (a lua table).
@@ -85,9 +130,10 @@ void script_error(lua_State *L)
// - runs the callbacks
// - replaces the table and arguments with the return value,
// computed depending on mode
-void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode)
+void script_run_callbacks_f(lua_State *L, int nargs,
+ RunCallbacksMode mode, const char *fxn)
{
- assert(lua_gettop(L) >= nargs + 1);
+ FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
// Insert error handler
lua_pushcfunction(L, script_error_handler);
@@ -107,14 +153,14 @@ void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode)
// Stack now looks like this:
// ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
- if (lua_pcall(L, nargs + 2, 1, errorhandler)) {
- script_error(L);
- }
+ int result = lua_pcall(L, nargs + 2, 1, errorhandler);
+ if (result != 0)
+ script_error(L, result, NULL, fxn);
lua_remove(L, -2); // Remove error handler
}
-void log_deprecated(lua_State *L, std::string message)
+void log_deprecated(lua_State *L, const std::string &message)
{
static bool configured = false;
static bool dolog = false;
@@ -125,8 +171,7 @@ void log_deprecated(lua_State *L, std::string message)
std::string value = g_settings->get("deprecated_lua_api_handling");
if (value == "log") {
dolog = true;
- }
- if (value == "error") {
+ } else if (value == "error") {
dolog = true;
doerror = true;
}
@@ -134,11 +179,10 @@ void log_deprecated(lua_State *L, std::string message)
if (doerror) {
if (L != NULL) {
- script_error(L);
+ script_error(L, LUA_ERRRUN, NULL, NULL);
} else {
- /* As of april 2014 assert is not optimized to nop in release builds
- * therefore this is correct. */
- assert("Can't do a scripterror for this deprecated message, so exit completely!");
+ FATAL_ERROR("Can't do a scripterror for this deprecated message, "
+ "so exit completely!");
}
}
diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h
index eb9181b09..ecb514c8f 100644
--- a/src/script/common/c_internal.h
+++ b/src/script/common/c_internal.h
@@ -34,6 +34,16 @@ extern "C" {
#include "common/c_types.h"
+#define PCALL_RESL(L, RES) do { \
+ int result_ = (RES); \
+ if (result_ != 0) { \
+ script_error((L), result_, NULL, __FUNCTION__); \
+ } \
+} while (0)
+
+#define script_run_callbacks(L, nargs, mode) \
+ script_run_callbacks_f((L), (nargs), (mode), __FUNCTION__)
+
// What script_run_callbacks does with the return values of callbacks.
// Regardless of the mode, if only one callback is defined,
// its return value is the total return value.
@@ -67,8 +77,9 @@ enum RunCallbacksMode
std::string script_get_backtrace(lua_State *L);
int script_error_handler(lua_State *L);
int script_exception_wrapper(lua_State *L, lua_CFunction f);
-void script_error(lua_State *L);
-void script_run_callbacks(lua_State *L, int nargs, RunCallbacksMode mode);
-void log_deprecated(lua_State *L, std::string message);
+void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn);
+void script_run_callbacks_f(lua_State *L, int nargs,
+ RunCallbacksMode mode, const char *fxn);
+void log_deprecated(lua_State *L, const std::string &message);
#endif /* C_INTERNAL_H_ */
diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt
index c45020055..be4d0131e 100644
--- a/src/script/cpp_api/CMakeLists.txt
+++ b/src/script/cpp_api/CMakeLists.txt
@@ -1,5 +1,5 @@
-# Used by server and client
set(common_SCRIPT_CPP_API_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_base.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_entity.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp
@@ -8,11 +8,11 @@ set(common_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_node.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_player.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/s_security.cpp
${CMAKE_CURRENT_SOURCE_DIR}/s_server.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/s_async.cpp
PARENT_SCOPE)
-# Used by client only
-set(minetest_SCRIPT_CPP_API_SRCS
+set(client_SCRIPT_CPP_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp
PARENT_SCOPE)
+
diff --git a/src/script/cpp_api/s_async.cpp b/src/script/cpp_api/s_async.cpp
index de1ebc07b..c00b22f98 100644
--- a/src/script/cpp_api/s_async.cpp
+++ b/src/script/cpp_api/s_async.cpp
@@ -155,7 +155,7 @@ void AsyncEngine::step(lua_State *L, int errorhandler)
lua_getfield(L, -1, "async_event_handler");
if (lua_isnil(L, -1)) {
- assert("Async event handler does not exist!" == 0);
+ FATAL_ERROR("Async event handler does not exist!");
}
luaL_checktype(L, -1, LUA_TFUNCTION);
@@ -164,9 +164,7 @@ void AsyncEngine::step(lua_State *L, int errorhandler)
lua_pushlstring(L, jobDone.serializedResult.data(),
jobDone.serializedResult.size());
- if (lua_pcall(L, 2, 0, errorhandler)) {
- script_error(L);
- }
+ PCALL_RESL(L, lua_pcall(L, 2, 0, errorhandler));
}
resultQueueMutex.Unlock();
lua_pop(L, 1); // Pop core
@@ -237,7 +235,7 @@ AsyncWorkerThread::AsyncWorkerThread(AsyncEngine* jobDispatcher,
/******************************************************************************/
AsyncWorkerThread::~AsyncWorkerThread()
{
- assert(IsRunning() == false);
+ sanity_check(IsRunning() == false);
}
/******************************************************************************/
@@ -257,7 +255,7 @@ void* AsyncWorkerThread::Thread()
std::string script = getServer()->getBuiltinLuaPath() + DIR_DELIM + "init.lua";
if (!loadScript(script)) {
errorstream
- << "AsyncWorkderThread execution of async base environment failed!"
+ << "AsyncWorkerThread execution of async base environment failed!"
<< std::endl;
abort();
}
@@ -293,8 +291,9 @@ void* AsyncWorkerThread::Thread()
toProcess.serializedParams.data(),
toProcess.serializedParams.size());
- if (lua_pcall(L, 2, 1, m_errorhandler)) {
- scriptError();
+ int result = lua_pcall(L, 2, 1, m_errorhandler);
+ if (result) {
+ PCALL_RES(result);
toProcess.serializedResult = "";
} else {
// Fetch result
diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp
index 1f96373dc..dcfbac4bf 100644
--- a/src/script/cpp_api/s_base.cpp
+++ b/src/script/cpp_api/s_base.cpp
@@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_base.h"
#include "cpp_api/s_internal.h"
+#include "cpp_api/s_security.h"
#include "lua_api/l_object.h"
+#include "common/c_converter.h"
#include "serverobject.h"
#include "debug.h"
#include "filesys.h"
@@ -45,18 +47,18 @@ class ModNameStorer
private:
lua_State *L;
public:
- ModNameStorer(lua_State *L_, const std::string &modname):
+ ModNameStorer(lua_State *L_, const std::string &mod_name):
L(L_)
{
- // Store current modname in registry
- lua_pushstring(L, modname.c_str());
- lua_setfield(L, LUA_REGISTRYINDEX, "current_modname");
+ // Store current mod name in registry
+ lua_pushstring(L, mod_name.c_str());
+ lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
}
~ModNameStorer()
{
- // Clear current modname in registry
+ // Clear current mod name from registry
lua_pushnil(L);
- lua_setfield(L, LUA_REGISTRYINDEX, "current_modname");
+ lua_setfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
}
};
@@ -67,12 +69,12 @@ public:
ScriptApiBase::ScriptApiBase()
{
- #ifdef SCRIPTAPI_LOCK_DEBUG
+#ifdef SCRIPTAPI_LOCK_DEBUG
m_locked = false;
- #endif
+#endif
m_luastack = luaL_newstate();
- assert(m_luastack);
+ FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed");
luaL_openlibs(m_luastack);
@@ -102,6 +104,11 @@ ScriptApiBase::ScriptApiBase()
lua_pushstring(m_luastack, porting::getPlatformName());
lua_setglobal(m_luastack, "PLATFORM");
+ // m_secure gets set to true inside
+ // ScriptApiSecurity::initializeSecurity(), if neccessary.
+ // Default to false otherwise
+ m_secure = false;
+
m_server = NULL;
m_environment = NULL;
m_guiengine = NULL;
@@ -112,88 +119,136 @@ ScriptApiBase::~ScriptApiBase()
lua_close(m_luastack);
}
-bool ScriptApiBase::loadMod(const std::string &scriptpath,
- const std::string &modname)
+bool ScriptApiBase::loadMod(const std::string &script_path,
+ const std::string &mod_name, std::string *error)
{
- ModNameStorer modnamestorer(getStack(), modname);
-
- if (!string_allowed(modname, MODNAME_ALLOWED_CHARS)) {
- errorstream<<"Error loading mod \""<<modname
- <<"\": modname does not follow naming conventions: "
- <<"Only chararacters [a-z0-9_] are allowed."<<std::endl;
- return false;
- }
+ ModNameStorer mod_name_storer(getStack(), mod_name);
- return loadScript(scriptpath);
+ return loadScript(script_path, error);
}
-bool ScriptApiBase::loadScript(const std::string &scriptpath)
+bool ScriptApiBase::loadScript(const std::string &script_path, std::string *error)
{
- verbosestream<<"Loading and running script from "<<scriptpath<<std::endl;
+ verbosestream << "Loading and running script from " << script_path << std::endl;
lua_State *L = getStack();
- int ret = luaL_loadfile(L, scriptpath.c_str()) || lua_pcall(L, 0, 0, m_errorhandler);
- if (ret) {
- errorstream << "========== ERROR FROM LUA ===========" << std::endl;
- errorstream << "Failed to load and run script from " << std::endl;
- errorstream << scriptpath << ":" << std::endl;
- errorstream << std::endl;
- errorstream << lua_tostring(L, -1) << std::endl;
- errorstream << std::endl;
- errorstream << "======= END OF ERROR FROM LUA ========" << std::endl;
+ bool ok;
+ if (m_secure) {
+ ok = ScriptApiSecurity::safeLoadFile(L, script_path.c_str());
+ } else {
+ ok = !luaL_loadfile(L, script_path.c_str());
+ }
+ ok = ok && !lua_pcall(L, 0, 0, m_errorhandler);
+ if (!ok) {
+ std::string error_msg = lua_tostring(L, -1);
+ if (error)
+ *error = error_msg;
+ errorstream << "========== ERROR FROM LUA ===========" << std::endl
+ << "Failed to load and run script from " << std::endl
+ << script_path << ":" << std::endl << std::endl
+ << error_msg << std::endl << std::endl
+ << "======= END OF ERROR FROM LUA ========" << std::endl;
lua_pop(L, 1); // Pop error message from stack
return false;
}
return true;
}
+// Push the list of callbacks (a lua table).
+// Then push nargs arguments.
+// Then call this function, which
+// - runs the callbacks
+// - replaces the table and arguments with the return value,
+// computed depending on mode
+void ScriptApiBase::runCallbacksRaw(int nargs,
+ RunCallbacksMode mode, const char *fxn)
+{
+ lua_State *L = getStack();
+ FATAL_ERROR_IF(lua_gettop(L) < nargs + 1, "Not enough arguments");
+
+ // Insert error handler
+ lua_pushcfunction(L, script_error_handler);
+ int errorhandler = lua_gettop(L) - nargs - 1;
+ lua_insert(L, errorhandler);
+
+ // Insert run_callbacks between error handler and table
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "run_callbacks");
+ lua_remove(L, -2);
+ lua_insert(L, errorhandler + 1);
+
+ // Insert mode after table
+ lua_pushnumber(L, (int)mode);
+ lua_insert(L, errorhandler + 3);
+
+ // Stack now looks like this:
+ // ... <error handler> <run_callbacks> <table> <mode> <arg#1> <arg#2> ... <arg#n>
+
+ int result = lua_pcall(L, nargs + 2, 1, errorhandler);
+ if (result != 0)
+ scriptError(result, fxn);
+
+ lua_remove(L, -2); // Remove error handler
+}
+
void ScriptApiBase::realityCheck()
{
int top = lua_gettop(m_luastack);
- if(top >= 30){
- dstream<<"Stack is over 30:"<<std::endl;
+ if (top >= 30) {
+ dstream << "Stack is over 30:" << std::endl;
stackDump(dstream);
std::string traceback = script_get_backtrace(m_luastack);
throw LuaError("Stack is over 30 (reality check)\n" + traceback);
}
}
-void ScriptApiBase::scriptError()
+void ScriptApiBase::scriptError(int result, const char *fxn)
{
- throw LuaError(lua_tostring(m_luastack, -1));
+ script_error(getStack(), result, m_last_run_mod.c_str(), fxn);
}
void ScriptApiBase::stackDump(std::ostream &o)
{
- int i;
int top = lua_gettop(m_luastack);
- for (i = 1; i <= top; i++) { /* repeat for each level */
+ for (int i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(m_luastack, i);
switch (t) {
-
case LUA_TSTRING: /* strings */
- o<<"\""<<lua_tostring(m_luastack, i)<<"\"";
+ o << "\"" << lua_tostring(m_luastack, i) << "\"";
break;
-
case LUA_TBOOLEAN: /* booleans */
- o<<(lua_toboolean(m_luastack, i) ? "true" : "false");
+ o << (lua_toboolean(m_luastack, i) ? "true" : "false");
break;
-
case LUA_TNUMBER: /* numbers */ {
char buf[10];
snprintf(buf, 10, "%g", lua_tonumber(m_luastack, i));
- o<<buf;
- break; }
-
+ o << buf;
+ break;
+ }
default: /* other values */
- o<<lua_typename(m_luastack, t);
+ o << lua_typename(m_luastack, t);
break;
-
}
- o<<" ";
+ o << " ";
}
- o<<std::endl;
+ o << std::endl;
+}
+
+void ScriptApiBase::setOriginDirect(const char *origin)
+{
+ m_last_run_mod = origin ? origin : "??";
+}
+
+void ScriptApiBase::setOriginFromTableRaw(int index, const char *fxn)
+{
+#ifdef SCRIPTAPI_DEBUG
+ lua_State *L = getStack();
+
+ m_last_run_mod = lua_istable(L, index) ?
+ getstringfield_default(L, index, "mod_origin", "") : "";
+ //printf(">>>> running %s for mod: %s\n", fxn, m_last_run_mod.c_str());
+#endif
}
void ScriptApiBase::addObjectReference(ServerActiveObject *cobj)
@@ -245,7 +300,7 @@ void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj)
void ScriptApiBase::objectrefGetOrCreate(lua_State *L,
ServerActiveObject *cobj)
{
- if(cobj == NULL || cobj->getId() == 0){
+ if (cobj == NULL || cobj->getId() == 0) {
ObjectRef::create(L, cobj);
} else {
objectrefGet(L, cobj->getId());
@@ -263,4 +318,3 @@ void ScriptApiBase::objectrefGet(lua_State *L, u16 id)
lua_remove(L, -2); // object_refs
lua_remove(L, -2); // core
}
-
diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h
index 4ea3677a9..d653b5bac 100644
--- a/src/script/cpp_api/s_base.h
+++ b/src/script/cpp_api/s_base.h
@@ -34,6 +34,25 @@ extern "C" {
#include "common/c_internal.h"
#define SCRIPTAPI_LOCK_DEBUG
+#define SCRIPTAPI_DEBUG
+
+#define SCRIPT_MOD_NAME_FIELD "current_mod_name"
+// MUST be an invalid mod name so that mods can't
+// use that name to bypass security!
+#define BUILTIN_MOD_NAME "*builtin*"
+
+#define PCALL_RES(RES) do { \
+ int result_ = (RES); \
+ if (result_ != 0) { \
+ scriptError(result_, __FUNCTION__); \
+ } \
+} while (0)
+
+#define runCallbacks(nargs, mode) \
+ runCallbacksRaw((nargs), (mode), __FUNCTION__)
+
+#define setOriginFromTable(index) \
+ setOriginFromTableRaw(index, __FUNCTION__)
class Server;
class Environment;
@@ -42,17 +61,26 @@ class ServerActiveObject;
class ScriptApiBase {
public:
-
ScriptApiBase();
virtual ~ScriptApiBase();
- bool loadMod(const std::string &scriptpath, const std::string &modname);
- bool loadScript(const std::string &scriptpath);
+ bool loadMod(const std::string &script_path, const std::string &mod_name,
+ std::string *error=NULL);
+ bool loadScript(const std::string &script_path, std::string *error=NULL);
+
+ void runCallbacksRaw(int nargs,
+ RunCallbacksMode mode, const char *fxn);
/* object */
void addObjectReference(ServerActiveObject *cobj);
void removeObjectReference(ServerActiveObject *cobj);
+ Server* getServer() { return m_server; }
+
+ std::string getOrigin() { return m_last_run_mod; }
+ void setOriginDirect(const char *origin);
+ void setOriginFromTableRaw(int index, const char *fxn);
+
protected:
friend class LuaABM;
friend class InvRef;
@@ -66,10 +94,9 @@ protected:
{ return m_luastack; }
void realityCheck();
- void scriptError();
+ void scriptError(int result, const char *fxn);
void stackDump(std::ostream &o);
- Server* getServer() { return m_server; }
void setServer(Server* server) { m_server = server; }
Environment* getEnv() { return m_environment; }
@@ -82,8 +109,10 @@ protected:
void objectrefGet(lua_State *L, u16 id);
JMutex m_luastackmutex;
+ std::string m_last_run_mod;
// Stack index of Lua error handler
int m_errorhandler;
+ bool m_secure;
#ifdef SCRIPTAPI_LOCK_DEBUG
bool m_locked;
#endif
diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp
index b52bde18a..0d159846a 100644
--- a/src/script/cpp_api/s_entity.cpp
+++ b/src/script/cpp_api/s_entity.cpp
@@ -91,9 +91,9 @@ void ScriptApiEntity::luaentity_Activate(u16 id,
lua_pushvalue(L, object); // self
lua_pushlstring(L, staticdata.c_str(), staticdata.size());
lua_pushinteger(L, dtime_s);
- // Call with 3 arguments, 0 results
- if (lua_pcall(L, 3, 0, m_errorhandler))
- scriptError();
+
+ setOriginFromTable(object);
+ PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler));
} else {
lua_pop(L, 1);
}
@@ -136,12 +136,12 @@ std::string ScriptApiEntity::luaentity_GetStaticdata(u16 id)
lua_pop(L, 2); // Pop entity and get_staticdata
return "";
}
-
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
- // Call with 1 arguments, 1 results
- if (lua_pcall(L, 1, 1, m_errorhandler))
- scriptError();
+
+ setOriginFromTable(object);
+ PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler));
+
lua_remove(L, object); // Remove object
size_t len = 0;
@@ -209,9 +209,10 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime)
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
lua_pushnumber(L, dtime); // dtime
- // Call with 2 arguments, 0 results
- if (lua_pcall(L, 2, 0, m_errorhandler))
- scriptError();
+
+ setOriginFromTable(object);
+ PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
+
lua_pop(L, 1); // Pop object
}
@@ -241,9 +242,10 @@ void ScriptApiEntity::luaentity_Punch(u16 id,
lua_pushnumber(L, time_from_last_punch);
push_tool_capabilities(L, *toolcap);
push_v3f(L, dir);
- // Call with 5 arguments, 0 results
- if (lua_pcall(L, 5, 0, m_errorhandler))
- scriptError();
+
+ setOriginFromTable(object);
+ PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
+
lua_pop(L, 1); // Pop object
}
@@ -268,9 +270,10 @@ void ScriptApiEntity::luaentity_Rightclick(u16 id,
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, object); // self
objectrefGetOrCreate(L, clicker); // Clicker reference
- // Call with 2 arguments, 0 results
- if (lua_pcall(L, 2, 0, m_errorhandler))
- scriptError();
+
+ setOriginFromTable(object);
+ PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
+
lua_pop(L, 1); // Pop object
}
diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp
index c171bbf02..9c733773a 100644
--- a/src/script/cpp_api/s_env.cpp
+++ b/src/script/cpp_api/s_env.cpp
@@ -38,7 +38,7 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp,
push_v3s16(L, minp);
push_v3s16(L, maxp);
lua_pushnumber(L, blockseed);
- script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(3, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiEnv::environment_Step(float dtime)
@@ -52,7 +52,7 @@ void ScriptApiEnv::environment_Step(float dtime)
// Call callbacks
lua_pushnumber(L, dtime);
try {
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getServer()->setAsyncFatalError(e.what());
}
@@ -73,7 +73,7 @@ void ScriptApiEnv::player_event(ServerActiveObject* player, std::string type)
objectrefGetOrCreate(L, player); // player
lua_pushstring(L,type.c_str()); // event type
try {
- script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
} catch (LuaError &e) {
getServer()->setAsyncFatalError(e.what());
}
diff --git a/src/script/cpp_api/s_internal.h b/src/script/cpp_api/s_internal.h
index 10ee1a7de..9999a584a 100644
--- a/src/script/cpp_api/s_internal.h
+++ b/src/script/cpp_api/s_internal.h
@@ -61,3 +61,4 @@ bool* m_variable;
StackUnroller stack_unroller(L);
#endif /* S_INTERNAL_H_ */
+
diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp
index 422faf150..019d1ccc0 100644
--- a/src/script/cpp_api/s_inventory.cpp
+++ b/src/script/cpp_api/s_inventory.cpp
@@ -48,8 +48,7 @@ int ScriptApiDetached::detached_inventory_AllowMove(
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 7, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler));
if(!lua_isnumber(L, -1))
throw LuaError("allow_move should return a number. name=" + name);
int ret = luaL_checkinteger(L, -1);
@@ -77,8 +76,7 @@ int ScriptApiDetached::detached_inventory_AllowPut(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_put should return a number. name=" + name);
int ret = luaL_checkinteger(L, -1);
@@ -106,8 +104,7 @@ int ScriptApiDetached::detached_inventory_AllowTake(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_take should return a number. name=" + name);
int ret = luaL_checkinteger(L, -1);
@@ -139,8 +136,7 @@ void ScriptApiDetached::detached_inventory_OnMove(
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 7, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler));
}
// Report put items
@@ -164,8 +160,7 @@ void ScriptApiDetached::detached_inventory_OnPut(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
}
// Report taken items
@@ -189,8 +184,7 @@ void ScriptApiDetached::detached_inventory_OnTake(
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
}
// Retrieves core.detached_inventories[name][callbackname]
@@ -215,6 +209,9 @@ bool ScriptApiDetached::getDetachedInventoryCallback(
lua_pop(L, 1);
return false;
}
+
+ setOriginFromTable(-1);
+
lua_getfield(L, -1, callbackname);
lua_remove(L, -2);
// Should be a function or nil
diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp
index e3a9ac7aa..4d4d416ec 100644
--- a/src/script/cpp_api/s_item.cpp
+++ b/src/script/cpp_api/s_item.cpp
@@ -42,8 +42,7 @@ bool ScriptApiItem::item_OnDrop(ItemStack &item,
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, dropper);
pushFloatPos(L, pos);
- if (lua_pcall(L, 3, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -68,8 +67,7 @@ bool ScriptApiItem::item_OnPlace(ItemStack &item,
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, placer);
pushPointedThing(pointed);
- if (lua_pcall(L, 3, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -94,8 +92,7 @@ bool ScriptApiItem::item_OnUse(ItemStack &item,
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
pushPointedThing(pointed);
- if (lua_pcall(L, 3, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 3, 1, m_errorhandler));
if(!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -116,7 +113,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
lua_getfield(L, -1, "on_craft");
LuaItemStack::create(L, item);
objectrefGetOrCreate(L, user);
-
+
// Push inventory list
std::vector<ItemStack> items;
for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
@@ -125,8 +122,7 @@ bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
push_items(L, items);
InvRef::create(L, craft_inv);
- if (lua_pcall(L, 4, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -156,8 +152,7 @@ bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
push_items(L, items);
InvRef::create(L, craft_inv);
- if (lua_pcall(L, 4, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 4, 1, m_errorhandler));
if (!lua_isnil(L, -1)) {
try {
item = read_item(L,-1, getServer());
@@ -198,6 +193,9 @@ bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname)
lua_remove(L, -2);
luaL_checktype(L, -1, LUA_TTABLE);
}
+
+ setOriginFromTable(-1);
+
lua_getfield(L, -1, callbackname);
lua_remove(L, -2); // Remove item def
// Should be a function or nil
diff --git a/src/script/cpp_api/s_mainmenu.cpp b/src/script/cpp_api/s_mainmenu.cpp
index ef8cea6c9..17ceff082 100644
--- a/src/script/cpp_api/s_mainmenu.cpp
+++ b/src/script/cpp_api/s_mainmenu.cpp
@@ -21,15 +21,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_internal.h"
#include "common/c_converter.h"
-void ScriptApiMainMenu::setMainMenuErrorMessage(std::string errormessage)
+void ScriptApiMainMenu::setMainMenuData(MainMenuDataForScript *data)
{
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "gamedata");
int gamedata_idx = lua_gettop(L);
lua_pushstring(L, "errormessage");
- lua_pushstring(L, errormessage.c_str());
+ if (!data->errormessage.empty()) {
+ lua_pushstring(L, data->errormessage.c_str());
+ } else {
+ lua_pushnil(L);
+ }
lua_settable(L, gamedata_idx);
+ setboolfield(L, gamedata_idx, "reconnect_requested",
+ data->reconnect_requested);
lua_pop(L, 1);
}
@@ -49,11 +55,10 @@ void ScriptApiMainMenu::handleMainMenuEvent(std::string text)
// Call it
lua_pushstring(L, text.c_str());
- if (lua_pcall(L, 1, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
}
-void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string> fields)
+void ScriptApiMainMenu::handleMainMenuButtons(const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
@@ -69,8 +74,8 @@ void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string>
// Convert fields to a Lua table
lua_newtable(L);
- std::map<std::string, std::string>::const_iterator it;
- for (it = fields.begin(); it != fields.end(); it++){
+ StringMap::const_iterator it;
+ for (it = fields.begin(); it != fields.end(); ++it) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
@@ -79,7 +84,6 @@ void ScriptApiMainMenu::handleMainMenuButtons(std::map<std::string, std::string>
}
// Call it
- if (lua_pcall(L, 1, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
}
diff --git a/src/script/cpp_api/s_mainmenu.h b/src/script/cpp_api/s_mainmenu.h
index 53dcd37e9..8d5895817 100644
--- a/src/script/cpp_api/s_mainmenu.h
+++ b/src/script/cpp_api/s_mainmenu.h
@@ -21,17 +21,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define S_MAINMENU_H_
#include "cpp_api/s_base.h"
-#include <map>
+#include "util/string.h"
+#include "../guiMainMenu.h"
-class ScriptApiMainMenu
- : virtual public ScriptApiBase
-{
+class ScriptApiMainMenu : virtual public ScriptApiBase {
public:
/**
- * set gamedata.errormessage to inform lua of an error
- * @param errormessage the error message
+ * Hand over MainMenuDataForScript to lua to inform lua of the content
+ * @param data the data
*/
- void setMainMenuErrorMessage(std::string errormessage);
+ void setMainMenuData(MainMenuDataForScript *data);
/**
* process events received from formspec
@@ -43,7 +42,7 @@ public:
* process field data recieved from formspec
* @param fields data in field format
*/
- void handleMainMenuButtons(std::map<std::string, std::string> fields);
+ void handleMainMenuButtons(const StringMap &fields);
};
#endif /* S_MAINMENU_H_ */
diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp
index e3d3fb58b..dac058b13 100644
--- a/src/script/cpp_api/s_node.cpp
+++ b/src/script/cpp_api/s_node.cpp
@@ -106,8 +106,7 @@ bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node,
pushnode(L, node, ndef);
objectrefGetOrCreate(L, puncher);
pushPointedThing(pointed);
- if (lua_pcall(L, 4, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler));
return true;
}
@@ -126,8 +125,7 @@ bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node,
push_v3s16(L, p);
pushnode(L, node, ndef);
objectrefGetOrCreate(L, digger);
- if (lua_pcall(L, 3, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 3, 0, m_errorhandler));
return true;
}
@@ -143,8 +141,7 @@ void ScriptApiNode::node_on_construct(v3s16 p, MapNode node)
// Call function
push_v3s16(L, p);
- if (lua_pcall(L, 1, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
}
void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
@@ -159,8 +156,7 @@ void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node)
// Call function
push_v3s16(L, p);
- if (lua_pcall(L, 1, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
}
void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
@@ -176,8 +172,7 @@ void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node)
// Call function
push_v3s16(L, p);
pushnode(L, node, ndef);
- if (lua_pcall(L, 2, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
}
bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
@@ -193,14 +188,13 @@ bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime)
// Call function
push_v3s16(L, p);
lua_pushnumber(L,dtime);
- if (lua_pcall(L, 2, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler));
return (bool) lua_isboolean(L, -1) && (bool) lua_toboolean(L, -1) == true;
}
void ScriptApiNode::node_on_receive_fields(v3s16 p,
const std::string &formname,
- const std::map<std::string, std::string> &fields,
+ const StringMap &fields,
ServerActiveObject *sender)
{
SCRIPTAPI_PRECHECKHEADER
@@ -220,8 +214,8 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
push_v3s16(L, p); // pos
lua_pushstring(L, formname.c_str()); // formname
lua_newtable(L); // fields
- std::map<std::string, std::string>::const_iterator it;
- for (it = fields.begin(); it != fields.end(); it++){
+ StringMap::const_iterator it;
+ for (it = fields.begin(); it != fields.end(); it++) {
const std::string &name = it->first;
const std::string &value = it->second;
lua_pushstring(L, name.c_str());
@@ -229,8 +223,7 @@ void ScriptApiNode::node_on_receive_fields(v3s16 p,
lua_settable(L, -3);
}
objectrefGetOrCreate(L, sender); // player
- if (lua_pcall(L, 4, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 4, 0, m_errorhandler));
}
void ScriptApiNode::node_falling_update(v3s16 p)
@@ -239,8 +232,7 @@ void ScriptApiNode::node_falling_update(v3s16 p)
lua_getglobal(L, "nodeupdate");
push_v3s16(L, p);
- if (lua_pcall(L, 1, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
}
void ScriptApiNode::node_falling_update_single(v3s16 p)
@@ -249,7 +241,5 @@ void ScriptApiNode::node_falling_update_single(v3s16 p)
lua_getglobal(L, "nodeupdate_single");
push_v3s16(L, p);
- if (lua_pcall(L, 1, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 0, m_errorhandler));
}
-
diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h
index b3a6c83b5..fe1180cb3 100644
--- a/src/script/cpp_api/s_node.h
+++ b/src/script/cpp_api/s_node.h
@@ -20,11 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef S_NODE_H_
#define S_NODE_H_
-#include <map>
-
#include "irr_v3d.h"
#include "cpp_api/s_base.h"
#include "cpp_api/s_nodemeta.h"
+#include "util/string.h"
struct MapNode;
class ServerActiveObject;
@@ -47,7 +46,7 @@ public:
bool node_on_timer(v3s16 p, MapNode node, f32 dtime);
void node_on_receive_fields(v3s16 p,
const std::string &formname,
- const std::map<std::string, std::string> &fields,
+ const StringMap &fields,
ServerActiveObject *sender);
void node_falling_update(v3s16 p);
void node_falling_update_single(v3s16 p);
diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp
index 01eee337c..638750b0e 100644
--- a/src/script/cpp_api/s_nodemeta.cpp
+++ b/src/script/cpp_api/s_nodemeta.cpp
@@ -54,8 +54,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p,
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 7, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 7, 1, m_errorhandler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_move should"
" return a number, guilty node: " + nodename);
@@ -89,8 +88,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
if(!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_put should"
" return a number, guilty node: " + nodename);
@@ -124,8 +122,7 @@ int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 1, m_errorhandler));
if (!lua_isnumber(L, -1))
throw LuaError("allow_metadata_inventory_take should"
" return a number, guilty node: " + nodename);
@@ -162,8 +159,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p,
lua_pushinteger(L, to_index + 1); // to_index
lua_pushinteger(L, count); // count
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 7, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 7, 0, m_errorhandler));
}
// Report put items
@@ -191,8 +187,7 @@ void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
}
// Report taken items
@@ -220,13 +215,14 @@ void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p,
lua_pushinteger(L, index + 1); // index
LuaItemStack::create(L, stack); // stack
objectrefGetOrCreate(L, player); // player
- if (lua_pcall(L, 5, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 5, 0, m_errorhandler));
}
-ScriptApiNodemeta::ScriptApiNodemeta() {
+ScriptApiNodemeta::ScriptApiNodemeta()
+{
}
-ScriptApiNodemeta::~ScriptApiNodemeta() {
+ScriptApiNodemeta::~ScriptApiNodemeta()
+{
}
diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp
index 81bfd4505..ef3c31cfd 100644
--- a/src/script/cpp_api/s_player.cpp
+++ b/src/script/cpp_api/s_player.cpp
@@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_player.h"
#include "cpp_api/s_internal.h"
+#include "common/c_converter.h"
+#include "common/c_content.h"
#include "util/string.h"
void ScriptApiPlayer::on_newplayer(ServerActiveObject *player)
@@ -30,7 +32,7 @@ void ScriptApiPlayer::on_newplayer(ServerActiveObject *player)
lua_getfield(L, -1, "registered_on_newplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player)
@@ -42,7 +44,47 @@ void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player)
lua_getfield(L, -1, "registered_on_dieplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
+}
+
+bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player,
+ ServerActiveObject *hitter,
+ float time_from_last_punch,
+ const ToolCapabilities *toolcap,
+ v3f dir,
+ s16 damage)
+{
+ SCRIPTAPI_PRECHECKHEADER
+ // Get core.registered_on_punchplayers
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_punchplayers");
+ // Call callbacks
+ objectrefGetOrCreate(L, player);
+ objectrefGetOrCreate(L, hitter);
+ lua_pushnumber(L, time_from_last_punch);
+ push_tool_capabilities(L, *toolcap);
+ push_v3f(L, dir);
+ lua_pushnumber(L, damage);
+ runCallbacks(6, RUN_CALLBACKS_MODE_OR);
+ return lua_toboolean(L, -1);
+}
+
+s16 ScriptApiPlayer::on_player_hpchange(ServerActiveObject *player,
+ s16 hp_change)
+{
+ SCRIPTAPI_PRECHECKHEADER
+
+ // Get core.registered_on_player_hpchange
+ lua_getglobal(L, "core");
+ lua_getfield(L, -1, "registered_on_player_hpchange");
+ lua_remove(L, -2);
+
+ objectrefGetOrCreate(L, player);
+ lua_pushnumber(L, hp_change);
+ PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler));
+ hp_change = lua_tointeger(L, -1);
+ lua_pop(L, -1);
+ return hp_change;
}
bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player)
@@ -54,12 +96,15 @@ bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player)
lua_getfield(L, -1, "registered_on_respawnplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR);
+ runCallbacks(1, RUN_CALLBACKS_MODE_OR);
bool positioning_handled_by_some = lua_toboolean(L, -1);
return positioning_handled_by_some;
}
-bool ScriptApiPlayer::on_prejoinplayer(std::string name, std::string ip, std::string &reason)
+bool ScriptApiPlayer::on_prejoinplayer(
+ const std::string &name,
+ const std::string &ip,
+ std::string *reason)
{
SCRIPTAPI_PRECHECKHEADER
@@ -68,9 +113,9 @@ bool ScriptApiPlayer::on_prejoinplayer(std::string name, std::string ip, std::st
lua_getfield(L, -1, "registered_on_prejoinplayers");
lua_pushstring(L, name.c_str());
lua_pushstring(L, ip.c_str());
- script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR);
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR);
if (lua_isstring(L, -1)) {
- reason.assign(lua_tostring(L, -1));
+ reason->assign(lua_tostring(L, -1));
return true;
}
return false;
@@ -85,7 +130,7 @@ void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player)
lua_getfield(L, -1, "registered_on_joinplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player)
@@ -97,7 +142,7 @@ void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player)
lua_getfield(L, -1, "registered_on_leaveplayers");
// Call callbacks
objectrefGetOrCreate(L, player);
- script_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(1, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_cheat(ServerActiveObject *player,
@@ -113,12 +158,12 @@ void ScriptApiPlayer::on_cheat(ServerActiveObject *player,
lua_newtable(L);
lua_pushlstring(L, cheat_type.c_str(), cheat_type.size());
lua_setfield(L, -2, "type");
- script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(2, RUN_CALLBACKS_MODE_FIRST);
}
void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
const std::string &formname,
- const std::map<std::string, std::string> &fields)
+ const StringMap &fields)
{
SCRIPTAPI_PRECHECKHEADER
@@ -132,17 +177,19 @@ void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player,
lua_pushstring(L, formname.c_str());
// param 3
lua_newtable(L);
- for(std::map<std::string, std::string>::const_iterator
- i = fields.begin(); i != fields.end(); i++){
- const std::string &name = i->first;
- const std::string &value = i->second;
+ StringMap::const_iterator it;
+ for (it = fields.begin(); it != fields.end(); ++it) {
+ const std::string &name = it->first;
+ const std::string &value = it->second;
lua_pushstring(L, name.c_str());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
- script_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC);
+ runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC);
}
-ScriptApiPlayer::~ScriptApiPlayer() {
+
+ScriptApiPlayer::~ScriptApiPlayer()
+{
}
diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h
index c77d397c4..2e4dc2222 100644
--- a/src/script/cpp_api/s_player.h
+++ b/src/script/cpp_api/s_player.h
@@ -20,10 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef S_PLAYER_H_
#define S_PLAYER_H_
-#include <map>
-
#include "cpp_api/s_base.h"
+#include "irr_v3d.h"
+#include "util/string.h"
+struct ToolCapabilities;
class ScriptApiPlayer
: virtual public ScriptApiBase
@@ -34,14 +35,17 @@ public:
void on_newplayer(ServerActiveObject *player);
void on_dieplayer(ServerActiveObject *player);
bool on_respawnplayer(ServerActiveObject *player);
- bool on_prejoinplayer(std::string name, std::string ip, std::string &reason);
+ bool on_prejoinplayer(const std::string &name, const std::string &ip,
+ std::string *reason);
void on_joinplayer(ServerActiveObject *player);
void on_leaveplayer(ServerActiveObject *player);
void on_cheat(ServerActiveObject *player, const std::string &cheat_type);
-
+ bool on_punchplayer(ServerActiveObject *player,
+ ServerActiveObject *hitter, float time_from_last_punch,
+ const ToolCapabilities *toolcap, v3f dir, s16 damage);
+ s16 on_player_hpchange(ServerActiveObject *player, s16 hp_change);
void on_playerReceiveFields(ServerActiveObject *player,
- const std::string &formname,
- const std::map<std::string, std::string> &fields);
+ const std::string &formname, const StringMap &fields);
};
diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp
new file mode 100644
index 000000000..6a6d40307
--- /dev/null
+++ b/src/script/cpp_api/s_security.cpp
@@ -0,0 +1,604 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "cpp_api/s_security.h"
+
+#include "filesys.h"
+#include "porting.h"
+#include "server.h"
+#include "settings.h"
+
+#include <cerrno>
+#include <string>
+#include <iostream>
+
+
+#define SECURE_API(lib, name) \
+ lua_pushcfunction(L, sl_##lib##_##name); \
+ lua_setfield(L, -2, #name);
+
+
+static inline void copy_safe(lua_State *L, const char *list[], unsigned len, int from=-2, int to=-1)
+{
+ if (from < 0) from = lua_gettop(L) + from + 1;
+ if (to < 0) to = lua_gettop(L) + to + 1;
+ for (unsigned i = 0; i < (len / sizeof(list[0])); i++) {
+ lua_getfield(L, from, list[i]);
+ lua_setfield(L, to, list[i]);
+ }
+}
+
+// Pushes the original version of a library function on the stack, from the old version
+static inline void push_original(lua_State *L, const char *lib, const char *func)
+{
+ lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ lua_getfield(L, -1, lib);
+ lua_remove(L, -2); // Remove globals_backup
+ lua_getfield(L, -1, func);
+ lua_remove(L, -2); // Remove lib
+}
+
+
+void ScriptApiSecurity::initializeSecurity()
+{
+ static const char *whitelist[] = {
+ "assert",
+ "core",
+ "collectgarbage",
+ "DIR_DELIM",
+ "error",
+ "getfenv",
+ "getmetatable",
+ "ipairs",
+ "next",
+ "pairs",
+ "pcall",
+ "print",
+ "rawequal",
+ "rawget",
+ "rawset",
+ "select",
+ "setfenv",
+ "setmetatable",
+ "tonumber",
+ "tostring",
+ "type",
+ "unpack",
+ "_VERSION",
+ "xpcall",
+ // Completely safe libraries
+ "coroutine",
+ "string",
+ "table",
+ "math",
+ };
+ static const char *io_whitelist[] = {
+ "close",
+ "flush",
+ "read",
+ "type",
+ "write",
+ };
+ static const char *os_whitelist[] = {
+ "clock",
+ "date",
+ "difftime",
+ "exit",
+ "getenv",
+ "setlocale",
+ "time",
+ "tmpname",
+ };
+ static const char *debug_whitelist[] = {
+ "gethook",
+ "traceback",
+ "getinfo",
+ "getmetatable",
+ "setupvalue",
+ "setmetatable",
+ "upvalueid",
+ "upvaluejoin",
+ "sethook",
+ "debug",
+ "getupvalue",
+ "setlocal",
+ };
+ static const char *package_whitelist[] = {
+ "config",
+ "cpath",
+ "path",
+ "searchpath",
+ };
+ static const char *jit_whitelist[] = {
+ "arch",
+ "flush",
+ "off",
+ "on",
+ "opt",
+ "os",
+ "status",
+ "version",
+ "version_num",
+ };
+
+ m_secure = true;
+
+ lua_State *L = getStack();
+
+ // Backup globals to the registry
+ lua_getglobal(L, "_G");
+ lua_setfield(L, LUA_REGISTRYINDEX, "globals_backup");
+
+ // Replace the global environment with an empty one
+#if LUA_VERSION_NUM <= 501
+ int is_main = lua_pushthread(L); // Push the main thread
+ FATAL_ERROR_IF(!is_main, "Security: ScriptApi's Lua state "
+ "isn't the main Lua thread!");
+#endif
+ lua_newtable(L); // Create new environment
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "_G"); // Set _G of new environment
+#if LUA_VERSION_NUM >= 502 // Lua >= 5.2
+ // Set the global environment
+ lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);
+#else // Lua <= 5.1
+ // Set the environment of the main thread
+ FATAL_ERROR_IF(!lua_setfenv(L, -2), "Security: Unable to set "
+ "environment of the main Lua thread!");
+ lua_pop(L, 1); // Pop thread
+#endif
+
+ // Get old globals
+ lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ int old_globals = lua_gettop(L);
+
+
+ // Copy safe base functions
+ lua_getglobal(L, "_G");
+ copy_safe(L, whitelist, sizeof(whitelist));
+
+ // And replace unsafe ones
+ SECURE_API(g, dofile);
+ SECURE_API(g, load);
+ SECURE_API(g, loadfile);
+ SECURE_API(g, loadstring);
+ SECURE_API(g, require);
+ lua_pop(L, 1);
+
+
+ // Copy safe IO functions
+ lua_getfield(L, old_globals, "io");
+ lua_newtable(L);
+ copy_safe(L, io_whitelist, sizeof(io_whitelist));
+
+ // And replace unsafe ones
+ SECURE_API(io, open);
+ SECURE_API(io, input);
+ SECURE_API(io, output);
+ SECURE_API(io, lines);
+
+ lua_setglobal(L, "io");
+ lua_pop(L, 1); // Pop old IO
+
+
+ // Copy safe OS functions
+ lua_getfield(L, old_globals, "os");
+ lua_newtable(L);
+ copy_safe(L, os_whitelist, sizeof(os_whitelist));
+
+ // And replace unsafe ones
+ SECURE_API(os, remove);
+ SECURE_API(os, rename);
+
+ lua_setglobal(L, "os");
+ lua_pop(L, 1); // Pop old OS
+
+
+ // Copy safe debug functions
+ lua_getfield(L, old_globals, "debug");
+ lua_newtable(L);
+ copy_safe(L, debug_whitelist, sizeof(debug_whitelist));
+ lua_setglobal(L, "debug");
+ lua_pop(L, 1); // Pop old debug
+
+
+ // Copy safe package fields
+ lua_getfield(L, old_globals, "package");
+ lua_newtable(L);
+ copy_safe(L, package_whitelist, sizeof(package_whitelist));
+ lua_setglobal(L, "package");
+ lua_pop(L, 1); // Pop old package
+
+
+ // Copy safe jit functions, if they exist
+ lua_getfield(L, -1, "jit");
+ if (!lua_isnil(L, -1)) {
+ lua_newtable(L);
+ copy_safe(L, jit_whitelist, sizeof(jit_whitelist));
+ lua_setglobal(L, "jit");
+ }
+ lua_pop(L, 1); // Pop old jit
+
+ lua_pop(L, 1); // Pop globals_backup
+}
+
+
+bool ScriptApiSecurity::isSecure(lua_State *L)
+{
+ lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ bool secure = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+ return secure;
+}
+
+
+#define CHECK_FILE_ERR(ret, fp) \
+ if (ret) { \
+ if (fp) std::fclose(fp); \
+ lua_pushfstring(L, "%s: %s", path, strerror(errno)); \
+ return false; \
+ }
+
+
+bool ScriptApiSecurity::safeLoadFile(lua_State *L, const char *path)
+{
+ FILE *fp;
+ char *chunk_name;
+ if (path == NULL) {
+ fp = stdin;
+ chunk_name = const_cast<char *>("=stdin");
+ } else {
+ fp = fopen(path, "rb");
+ if (!fp) {
+ lua_pushfstring(L, "%s: %s", path, strerror(errno));
+ return false;
+ }
+ chunk_name = new char[strlen(path) + 2];
+ chunk_name[0] = '@';
+ chunk_name[1] = '\0';
+ strcat(chunk_name, path);
+ }
+
+ size_t start = 0;
+ int c = std::getc(fp);
+ if (c == '#') {
+ // Skip the first line
+ while ((c = std::getc(fp)) != EOF && c != '\n');
+ if (c == '\n') c = std::getc(fp);
+ start = std::ftell(fp);
+ }
+
+ if (c == LUA_SIGNATURE[0]) {
+ lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
+ return false;
+ }
+
+ // Read the file
+ int ret = std::fseek(fp, 0, SEEK_END);
+ CHECK_FILE_ERR(ret, fp);
+ if (ret) {
+ std::fclose(fp);
+ lua_pushfstring(L, "%s: %s", path, strerror(errno));
+ return false;
+ }
+ size_t size = std::ftell(fp) - start;
+ char *code = new char[size];
+ ret = std::fseek(fp, start, SEEK_SET);
+ CHECK_FILE_ERR(ret, fp);
+ if (ret) {
+ std::fclose(fp);
+ lua_pushfstring(L, "%s: %s", path, strerror(errno));
+ return false;
+ }
+ size_t num_read = std::fread(code, 1, size, fp);
+ if (path) {
+ std::fclose(fp);
+ }
+ if (num_read != size) {
+ lua_pushliteral(L, "Error reading file to load.");
+ return false;
+ }
+
+ if (luaL_loadbuffer(L, code, size, chunk_name)) {
+ return false;
+ }
+
+ if (path) {
+ delete [] chunk_name;
+ }
+ return true;
+}
+
+
+bool ScriptApiSecurity::checkPath(lua_State *L, const char *path)
+{
+ std::string str; // Transient
+
+ std::string norel_path = fs::RemoveRelativePathComponents(path);
+ std::string abs_path = fs::AbsolutePath(norel_path);
+
+ if (!abs_path.empty()) {
+ // Don't allow accessing the settings file
+ str = fs::AbsolutePath(g_settings_path);
+ if (str == abs_path) return false;
+ }
+
+ // If we couldn't find the absolute path (path doesn't exist) then
+ // try removing the last components until it works (to allow
+ // non-existent files/folders for mkdir).
+ std::string cur_path = norel_path;
+ std::string removed;
+ while (abs_path.empty() && !cur_path.empty()) {
+ std::string tmp_rmed;
+ cur_path = fs::RemoveLastPathComponent(cur_path, &tmp_rmed);
+ removed = tmp_rmed + (removed.empty() ? "" : DIR_DELIM + removed);
+ abs_path = fs::AbsolutePath(cur_path);
+ }
+ if (abs_path.empty()) return false;
+ // Add the removed parts back so that you can't, eg, create a
+ // directory in worldmods if worldmods doesn't exist.
+ if (!removed.empty()) abs_path += DIR_DELIM + removed;
+
+ // Get server from registry
+ lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi");
+ ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1);
+ lua_pop(L, 1);
+ const Server *server = script->getServer();
+
+ if (!server) return false;
+
+ // Get mod name
+ lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ if (lua_isstring(L, -1)) {
+ std::string mod_name = lua_tostring(L, -1);
+
+ // Builtin can access anything
+ if (mod_name == BUILTIN_MOD_NAME) {
+ return true;
+ }
+
+ // Allow paths in mod path
+ const ModSpec *mod = server->getModSpec(mod_name);
+ if (mod) {
+ str = fs::AbsolutePath(mod->path);
+ if (!str.empty() && fs::PathStartsWith(abs_path, str)) {
+ return true;
+ }
+ }
+ }
+ lua_pop(L, 1); // Pop mod name
+
+ str = fs::AbsolutePath(server->getWorldPath());
+ if (str.empty()) return false;
+ // Don't allow access to world mods. We add to the absolute path
+ // of the world instead of getting the absolute paths directly
+ // because that won't work if they don't exist.
+ if (fs::PathStartsWith(abs_path, str + DIR_DELIM + "worldmods") ||
+ fs::PathStartsWith(abs_path, str + DIR_DELIM + "game")) {
+ return false;
+ }
+ // Allow all other paths in world path
+ if (fs::PathStartsWith(abs_path, str)) {
+ return true;
+ }
+
+ // Default to disallowing
+ return false;
+}
+
+
+int ScriptApiSecurity::sl_g_dofile(lua_State *L)
+{
+ int nret = sl_g_loadfile(L);
+ if (nret != 1) {
+ lua_error(L);
+ // code after this function isn't executed
+ }
+ int top_precall = lua_gettop(L);
+ lua_call(L, 0, LUA_MULTRET);
+ // Return number of arguments returned by the function,
+ // adjusting for the function being poped.
+ return lua_gettop(L) - (top_precall - 1);
+}
+
+
+int ScriptApiSecurity::sl_g_load(lua_State *L)
+{
+ size_t len;
+ const char *buf;
+ std::string code;
+ const char *chunk_name = "=(load)";
+
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ if (!lua_isnone(L, 2)) {
+ luaL_checktype(L, 2, LUA_TSTRING);
+ chunk_name = lua_tostring(L, 2);
+ }
+
+ while (true) {
+ lua_pushvalue(L, 1);
+ lua_call(L, 0, 1);
+ int t = lua_type(L, -1);
+ if (t == LUA_TNIL) {
+ break;
+ } else if (t != LUA_TSTRING) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "Loader didn't return a string");
+ return 2;
+ }
+ buf = lua_tolstring(L, -1, &len);
+ code += std::string(buf, len);
+ lua_pop(L, 1); // Pop return value
+ }
+ if (code[0] == LUA_SIGNATURE[0]) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
+ return 2;
+ }
+ if (luaL_loadbuffer(L, code.data(), code.size(), chunk_name)) {
+ lua_pushnil(L);
+ lua_insert(L, lua_gettop(L) - 1);
+ return 2;
+ }
+ return 1;
+}
+
+
+int ScriptApiSecurity::sl_g_loadfile(lua_State *L)
+{
+ const char *path = NULL;
+
+ if (lua_isstring(L, 1)) {
+ path = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path);
+ }
+
+ if (!safeLoadFile(L, path)) {
+ lua_pushnil(L);
+ lua_insert(L, -2);
+ return 2;
+ }
+
+ return 1;
+}
+
+
+int ScriptApiSecurity::sl_g_loadstring(lua_State *L)
+{
+ const char *chunk_name = "=(load)";
+
+ luaL_checktype(L, 1, LUA_TSTRING);
+ if (!lua_isnone(L, 2)) {
+ luaL_checktype(L, 2, LUA_TSTRING);
+ chunk_name = lua_tostring(L, 2);
+ }
+
+ size_t size;
+ const char *code = lua_tolstring(L, 1, &size);
+
+ if (size > 0 && code[0] == LUA_SIGNATURE[0]) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "Bytecode prohibited when mod security is enabled.");
+ return 2;
+ }
+ if (luaL_loadbuffer(L, code, size, chunk_name)) {
+ lua_pushnil(L);
+ lua_insert(L, lua_gettop(L) - 1);
+ return 2;
+ }
+ return 1;
+}
+
+
+int ScriptApiSecurity::sl_g_require(lua_State *L)
+{
+ lua_pushliteral(L, "require() is disabled when mod security is on.");
+ return lua_error(L);
+}
+
+
+int ScriptApiSecurity::sl_io_open(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TSTRING);
+ const char *path = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path);
+
+ push_original(L, "io", "open");
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_call(L, 2, 2);
+ return 2;
+}
+
+
+int ScriptApiSecurity::sl_io_input(lua_State *L)
+{
+ if (lua_isstring(L, 1)) {
+ const char *path = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path);
+ }
+
+ push_original(L, "io", "input");
+ lua_pushvalue(L, 1);
+ lua_call(L, 1, 1);
+ return 1;
+}
+
+
+int ScriptApiSecurity::sl_io_output(lua_State *L)
+{
+ if (lua_isstring(L, 1)) {
+ const char *path = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path);
+ }
+
+ push_original(L, "io", "output");
+ lua_pushvalue(L, 1);
+ lua_call(L, 1, 1);
+ return 1;
+}
+
+
+int ScriptApiSecurity::sl_io_lines(lua_State *L)
+{
+ if (lua_isstring(L, 1)) {
+ const char *path = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path);
+ }
+
+ push_original(L, "io", "lines");
+ lua_pushvalue(L, 1);
+ int top_precall = lua_gettop(L);
+ lua_call(L, 1, LUA_MULTRET);
+ // Return number of arguments returned by the function,
+ // adjusting for the function being poped.
+ return lua_gettop(L) - (top_precall - 1);
+}
+
+
+int ScriptApiSecurity::sl_os_rename(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TSTRING);
+ const char *path1 = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path1);
+
+ luaL_checktype(L, 2, LUA_TSTRING);
+ const char *path2 = lua_tostring(L, 2);
+ CHECK_SECURE_PATH(L, path2);
+
+ push_original(L, "os", "rename");
+ lua_pushvalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_call(L, 2, 2);
+ return 2;
+}
+
+
+int ScriptApiSecurity::sl_os_remove(lua_State *L)
+{
+ luaL_checktype(L, 1, LUA_TSTRING);
+ const char *path = lua_tostring(L, 1);
+ CHECK_SECURE_PATH(L, path);
+
+ push_original(L, "os", "remove");
+ lua_pushvalue(L, 1);
+ lua_call(L, 1, 2);
+ return 2;
+}
+
diff --git a/src/script/cpp_api/s_security.h b/src/script/cpp_api/s_security.h
new file mode 100644
index 000000000..4a4389cf5
--- /dev/null
+++ b/src/script/cpp_api/s_security.h
@@ -0,0 +1,70 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef S_SECURITY_H
+#define S_SECURITY_H
+
+#include "cpp_api/s_base.h"
+
+
+#define CHECK_SECURE_PATH(L, path) \
+ if (!ScriptApiSecurity::checkPath(L, path)) { \
+ lua_pushstring(L, (std::string("Attempt to access external file ") + \
+ path + " with mod security on.").c_str()); \
+ lua_error(L); \
+ }
+#define CHECK_SECURE_PATH_OPTIONAL(L, path) \
+ if (ScriptApiSecurity::isSecure(L)) { \
+ CHECK_SECURE_PATH(L, path); \
+ }
+
+
+class ScriptApiSecurity : virtual public ScriptApiBase
+{
+public:
+ // Sets up security on the ScriptApi's Lua state
+ void initializeSecurity();
+ // Checks if the Lua state has been secured
+ static bool isSecure(lua_State *L);
+ // Loads a file as Lua code safely (doesn't allow bytecode).
+ static bool safeLoadFile(lua_State *L, const char *path);
+ // Checks if mods are allowed to read and write to the path
+ static bool checkPath(lua_State *L, const char *path);
+
+private:
+ // Syntax: "sl_" <Library name or 'g' (global)> '_' <Function name>
+ // (sl stands for Secure Lua)
+
+ static int sl_g_dofile(lua_State *L);
+ static int sl_g_load(lua_State *L);
+ static int sl_g_loadfile(lua_State *L);
+ static int sl_g_loadstring(lua_State *L);
+ static int sl_g_require(lua_State *L);
+
+ static int sl_io_open(lua_State *L);
+ static int sl_io_input(lua_State *L);
+ static int sl_io_output(lua_State *L);
+ static int sl_io_lines(lua_State *L);
+
+ static int sl_os_rename(lua_State *L);
+ static int sl_os_remove(lua_State *L);
+};
+
+#endif
+
diff --git a/src/script/cpp_api/s_server.cpp b/src/script/cpp_api/s_server.cpp
index 21fe164aa..ec2f9c0af 100644
--- a/src/script/cpp_api/s_server.cpp
+++ b/src/script/cpp_api/s_server.cpp
@@ -32,8 +32,7 @@ bool ScriptApiServer::getAuth(const std::string &playername,
if (lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError("Authentication handler missing get_auth");
lua_pushstring(L, playername.c_str());
- if (lua_pcall(L, 1, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 1, 1, m_errorhandler));
lua_remove(L, -2); // Remove auth handler
// nil = login not allowed
@@ -68,6 +67,9 @@ void ScriptApiServer::getAuthHandler()
lua_pop(L, 1);
lua_getfield(L, -1, "builtin_auth_handler");
}
+
+ setOriginFromTable(-1);
+
lua_remove(L, -2); // Remove core
if (lua_type(L, -1) != LUA_TTABLE)
throw LuaError("Authentication handler table not valid");
@@ -104,8 +106,7 @@ void ScriptApiServer::createAuth(const std::string &playername,
throw LuaError("Authentication handler missing create_auth");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
- if (lua_pcall(L, 2, 0, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 2, 0, m_errorhandler));
}
bool ScriptApiServer::setPassword(const std::string &playername,
@@ -120,8 +121,7 @@ bool ScriptApiServer::setPassword(const std::string &playername,
throw LuaError("Authentication handler missing set_password");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
- if (lua_pcall(L, 2, 1, m_errorhandler))
- scriptError();
+ PCALL_RES(lua_pcall(L, 2, 1, m_errorhandler));
return lua_toboolean(L, -1);
}
@@ -136,7 +136,7 @@ bool ScriptApiServer::on_chat_message(const std::string &name,
// Call callbacks
lua_pushstring(L, name.c_str());
lua_pushstring(L, message.c_str());
- script_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC);
+ runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC);
bool ate = lua_toboolean(L, -1);
return ate;
}
@@ -149,6 +149,6 @@ void ScriptApiServer::on_shutdown()
lua_getglobal(L, "core");
lua_getfield(L, -1, "registered_on_shutdown");
// Call callbacks
- script_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST);
+ runCallbacks(0, RUN_CALLBACKS_MODE_FIRST);
}
diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt
index 08960d2ad..2501ce6d6 100644
--- a/src/script/lua_api/CMakeLists.txt
+++ b/src/script/lua_api/CMakeLists.txt
@@ -1,5 +1,5 @@
-# Used by server and client
set(common_SCRIPT_LUA_API_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/l_areastore.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_base.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp
${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp
@@ -18,7 +18,7 @@ set(common_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp
PARENT_SCOPE)
-# Used by client only
-set(minetest_SCRIPT_LUA_API_SRCS
+set(client_SCRIPT_LUA_API_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp
PARENT_SCOPE)
+
diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp
new file mode 100644
index 000000000..1e9075119
--- /dev/null
+++ b/src/script/lua_api/l_areastore.cpp
@@ -0,0 +1,401 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <mtest31@outlook.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.
+*/
+
+
+#include "lua_api/l_areastore.h"
+#include "lua_api/l_internal.h"
+#include "common/c_converter.h"
+#include "cpp_api/s_security.h"
+#include "areastore.h"
+#include "filesys.h"
+#ifndef ANDROID
+ #include "cmake_config.h"
+#endif
+#include <fstream>
+
+static inline void get_data_and_border_flags(lua_State *L, u8 start_i,
+ bool *borders, bool *data)
+{
+ if (!lua_isboolean(L, start_i))
+ return;
+ *borders = lua_toboolean(L, start_i);
+ if (!lua_isboolean(L, start_i + 1))
+ return;
+ *data = lua_toboolean(L, start_i + 1);
+}
+
+static void push_area(lua_State *L, const Area *a,
+ bool include_borders, bool include_data)
+{
+ if (!include_borders && !include_data) {
+ lua_pushboolean(L, true);
+ }
+ lua_newtable(L);
+ if (include_borders) {
+ push_v3s16(L, a->minedge);
+ lua_setfield(L, -2, "min");
+ push_v3s16(L, a->maxedge);
+ lua_setfield(L, -2, "max");
+ }
+ if (include_data) {
+ lua_pushlstring(L, a->data.c_str(), a->data.size());
+ lua_setfield(L, -2, "data");
+ }
+}
+
+static inline void push_areas(lua_State *L, const std::vector<Area *> &areas,
+ bool borders, bool data)
+{
+ lua_newtable(L);
+ size_t cnt = areas.size();
+ for (size_t i = 0; i < cnt; i++) {
+ lua_pushnumber(L, areas[i]->id);
+ push_area(L, areas[i], borders, data);
+ lua_settable(L, -3);
+ }
+}
+
+// garbage collector
+int LuaAreaStore::gc_object(lua_State *L)
+{
+ LuaAreaStore *o = *(LuaAreaStore **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+// get_area(id, include_borders, include_data)
+int LuaAreaStore::l_get_area(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ u32 id = luaL_checknumber(L, 2);
+
+ bool include_borders = true;
+ bool include_data = false;
+ get_data_and_border_flags(L, 3, &include_borders, &include_data);
+
+ const Area *res;
+
+ res = ast->getArea(id);
+ push_area(L, res, include_borders, include_data);
+
+ return 1;
+}
+
+// get_areas_for_pos(pos, include_borders, include_data)
+int LuaAreaStore::l_get_areas_for_pos(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ v3s16 pos = check_v3s16(L, 2);
+
+ bool include_borders = true;
+ bool include_data = false;
+ get_data_and_border_flags(L, 3, &include_borders, &include_data);
+
+ std::vector<Area *> res;
+
+ ast->getAreasForPos(&res, pos);
+ push_areas(L, res, include_borders, include_data);
+
+ return 1;
+}
+
+// get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)
+int LuaAreaStore::l_get_areas_in_area(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ v3s16 minedge = check_v3s16(L, 2);
+ v3s16 maxedge = check_v3s16(L, 3);
+
+ bool include_borders = true;
+ bool include_data = false;
+ bool accept_overlap = false;
+ if (lua_isboolean(L, 4)) {
+ accept_overlap = lua_toboolean(L, 4);
+ get_data_and_border_flags(L, 5, &include_borders, &include_data);
+ }
+ std::vector<Area *> res;
+
+ ast->getAreasInArea(&res, minedge, maxedge, accept_overlap);
+ push_areas(L, res, include_borders, include_data);
+
+ return 1;
+}
+
+// insert_area(edge1, edge2, data)
+int LuaAreaStore::l_insert_area(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ Area a;
+
+ a.minedge = check_v3s16(L, 2);
+ a.maxedge = check_v3s16(L, 3);
+
+ a.extremifyEdges();
+ a.id = ast->getFreeId(a.minedge, a.maxedge);
+
+ if (a.id == AREA_ID_INVALID) {
+ // couldn't get free id
+ lua_pushnil(L);
+ return 1;
+ }
+
+ size_t d_len;
+ const char *data = luaL_checklstring(L, 4, &d_len);
+
+ a.data = std::string(data, d_len);
+
+ ast->insertArea(a);
+
+ lua_pushnumber(L, a.id);
+ return 1;
+}
+
+// reserve(count)
+int LuaAreaStore::l_reserve(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ size_t count = luaL_checknumber(L, 2);
+ ast->reserve(count);
+ return 0;
+}
+
+// remove_area(id)
+int LuaAreaStore::l_remove_area(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ u32 id = luaL_checknumber(L, 2);
+ bool success = ast->removeArea(id);
+
+ lua_pushboolean(L, success);
+ return 1;
+}
+
+// set_cache_params(params)
+int LuaAreaStore::l_set_cache_params(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ bool enabled = getboolfield_default(L, 2, "enabled", true);
+ u8 block_radius = getintfield_default(L, 2, "block_radius", 64);
+ size_t limit = getintfield_default(L, 2, "block_radius", 1000);
+
+ ast->setCacheParams(enabled, block_radius, limit);
+
+ return 0;
+}
+
+#if 0
+// to_string()
+int LuaAreaStore::l_to_string(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ std::ostringstream os(std::ios_base::binary);
+ ast->serialize(os);
+ std::string str = os.str();
+
+ lua_pushlstring(L, str.c_str(), str.length());
+ return 1;
+}
+
+// to_file(filename)
+int LuaAreaStore::l_to_file(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ const char *filename = luaL_checkstring(L, 2);
+ CHECK_SECURE_PATH_OPTIONAL(L, filename);
+
+ std::ostringstream os(std::ios_base::binary);
+ ast->serialize(os);
+
+ lua_pushboolean(L, fs::safeWriteToFile(filename, os.str()));
+ return 1;
+}
+
+// from_string(str)
+int LuaAreaStore::l_from_string(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ size_t len;
+ const char *str = luaL_checklstring(L, 2, &len);
+
+ std::istringstream is(std::string(str, len), std::ios::binary);
+ bool success = ast->deserialize(is);
+
+ lua_pushboolean(L, success);
+ return 1;
+}
+
+// from_file(filename)
+int LuaAreaStore::l_from_file(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = checkobject(L, 1);
+ AreaStore *ast = o->as;
+
+ const char *filename = luaL_checkstring(L, 2);
+ CHECK_SECURE_PATH_OPTIONAL(L, filename);
+
+ std::ifstream is(filename, std::ios::binary);
+ bool success = ast->deserialize(is);
+
+ lua_pushboolean(L, success);
+ return 1;
+}
+#endif
+
+LuaAreaStore::LuaAreaStore()
+{
+#if USE_SPATIAL
+ this->as = new SpatialAreaStore();
+#else
+ this->as = new VectorAreaStore();
+#endif
+}
+
+LuaAreaStore::LuaAreaStore(const std::string &type)
+{
+#if USE_SPATIAL
+ if (type == "LibSpatial") {
+ this->as = new SpatialAreaStore();
+ } else
+#endif
+ {
+ this->as = new VectorAreaStore();
+ }
+}
+
+LuaAreaStore::~LuaAreaStore()
+{
+ delete as;
+}
+
+// LuaAreaStore()
+// Creates an LuaAreaStore and leaves it on top of stack
+int LuaAreaStore::create_object(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaAreaStore *o = (lua_isstring(L, 1)) ?
+ new LuaAreaStore(lua_tostring(L, 1)) :
+ new LuaAreaStore();
+
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+ return 1;
+}
+
+LuaAreaStore *LuaAreaStore::checkobject(lua_State *L, int narg)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ luaL_checktype(L, narg, LUA_TUSERDATA);
+
+ void *ud = luaL_checkudata(L, narg, className);
+ if (!ud)
+ luaL_typerror(L, narg, className);
+
+ return *(LuaAreaStore **)ud; // unbox pointer
+}
+
+void LuaAreaStore::Register(lua_State *L)
+{
+ lua_newtable(L);
+ int methodtable = lua_gettop(L);
+ luaL_newmetatable(L, className);
+ int metatable = lua_gettop(L);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable); // hide metatable from Lua getmetatable()
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, gc_object);
+ lua_settable(L, metatable);
+
+ lua_pop(L, 1); // drop metatable
+
+ luaL_openlib(L, 0, methods, 0); // fill methodtable
+ lua_pop(L, 1); // drop methodtable
+
+ // Can be created from Lua (AreaStore())
+ lua_register(L, className, create_object);
+}
+
+const char LuaAreaStore::className[] = "AreaStore";
+const luaL_reg LuaAreaStore::methods[] = {
+ luamethod(LuaAreaStore, get_area),
+ luamethod(LuaAreaStore, get_areas_for_pos),
+ luamethod(LuaAreaStore, get_areas_in_area),
+ luamethod(LuaAreaStore, insert_area),
+ luamethod(LuaAreaStore, reserve),
+ luamethod(LuaAreaStore, remove_area),
+ luamethod(LuaAreaStore, set_cache_params),
+ /* luamethod(LuaAreaStore, to_string),
+ luamethod(LuaAreaStore, to_file),
+ luamethod(LuaAreaStore, from_string),
+ luamethod(LuaAreaStore, from_file),*/
+ {0,0}
+};
diff --git a/src/script/lua_api/l_areastore.h b/src/script/lua_api/l_areastore.h
new file mode 100644
index 000000000..a25529627
--- /dev/null
+++ b/src/script/lua_api/l_areastore.h
@@ -0,0 +1,70 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <mtest31@outlook.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.
+*/
+
+#ifndef L_AREASTORE_H_
+#define L_AREASTORE_H_
+
+#include "lua_api/l_base.h"
+#include "irr_v3d.h"
+#include "areastore.h"
+
+/*
+ AreaStore
+ */
+class LuaAreaStore : public ModApiBase {
+private:
+
+ static const char className[];
+ static const luaL_reg methods[];
+
+ static int gc_object(lua_State *L);
+
+ static int l_get_area(lua_State *L);
+
+ static int l_get_areas_for_pos(lua_State *L);
+ static int l_get_areas_in_area(lua_State *L);
+ static int l_insert_area(lua_State *L);
+ static int l_reserve(lua_State *L);
+ static int l_remove_area(lua_State *L);
+
+ static int l_set_cache_params(lua_State *L);
+
+ /* static int l_to_string(lua_State *L);
+ static int l_to_file(lua_State *L);
+
+ static int l_from_string(lua_State *L);
+ static int l_from_file(lua_State *L); */
+
+public:
+ AreaStore *as;
+
+ LuaAreaStore();
+ LuaAreaStore(const std::string &type);
+ ~LuaAreaStore();
+
+ // AreaStore()
+ // Creates a AreaStore and leaves it on top of stack
+ static int create_object(lua_State *L);
+
+ static LuaAreaStore *checkobject(lua_State *L, int narg);
+
+ static void Register(lua_State *L);
+};
+
+#endif /* L_AREASTORE_H_ */
diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp
index b8d673ee4..6ad3e4ba2 100644
--- a/src/script/lua_api/l_base.cpp
+++ b/src/script/lua_api/l_base.cpp
@@ -20,8 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_base.h"
#include "lua_api/l_internal.h"
#include "cpp_api/s_base.h"
+#include <mods.h>
+#include <server.h>
-ScriptApiBase* ModApiBase::getScriptApiBase(lua_State *L) {
+ScriptApiBase *ModApiBase::getScriptApiBase(lua_State *L)
+{
// Get server from registry
lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi");
ScriptApiBase *sapi_ptr = (ScriptApiBase*) lua_touserdata(L, -1);
@@ -29,23 +32,42 @@ ScriptApiBase* ModApiBase::getScriptApiBase(lua_State *L) {
return sapi_ptr;
}
-Server* ModApiBase::getServer(lua_State *L) {
+Server *ModApiBase::getServer(lua_State *L)
+{
return getScriptApiBase(L)->getServer();
}
-Environment* ModApiBase::getEnv(lua_State *L) {
+Environment *ModApiBase::getEnv(lua_State *L)
+{
return getScriptApiBase(L)->getEnv();
}
-GUIEngine* ModApiBase::getGuiEngine(lua_State *L) {
+GUIEngine *ModApiBase::getGuiEngine(lua_State *L)
+{
return getScriptApiBase(L)->getGuiEngine();
}
-bool ModApiBase::registerFunction(lua_State *L,
- const char *name,
- lua_CFunction fct,
- int top
- ) {
+std::string ModApiBase::getCurrentModPath(lua_State *L)
+{
+ lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ const char *current_mod_name = lua_tostring(L, -1);
+ if (!current_mod_name)
+ return ".";
+
+ const ModSpec *mod = getServer(L)->getModSpec(current_mod_name);
+ if (!mod)
+ return ".";
+
+ return mod->path;
+}
+
+
+bool ModApiBase::registerFunction(
+ lua_State *L,
+ const char *name,
+ lua_CFunction fct,
+ int top)
+{
//TODO check presence first!
lua_pushstring(L,name);
diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h
index debbcd09b..641013dfd 100644
--- a/src/script/lua_api/l_base.h
+++ b/src/script/lua_api/l_base.h
@@ -35,11 +35,13 @@ class GUIEngine;
class ModApiBase {
-protected:
+public:
static ScriptApiBase* getScriptApiBase(lua_State *L);
static Server* getServer(lua_State *L);
static Environment* getEnv(lua_State *L);
static GUIEngine* getGuiEngine(lua_State *L);
+ // When we are not loading the mod, this function returns "."
+ static std::string getCurrentModPath(lua_State *L);
// Get an arbitrary subclass of ScriptApiBase
// by using dynamic_cast<> on getScriptApiBase()
diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp
index 8f8efbfbc..391a0133d 100644
--- a/src/script/lua_api/l_craft.cpp
+++ b/src/script/lua_api/l_craft.cpp
@@ -173,7 +173,7 @@ int ModApiCraft::l_register_craft(lua_State *L)
CraftDefinition *def = new CraftDefinitionShaped(
output, width, recipe, replacements);
- craftdef->registerCraft(def);
+ craftdef->registerCraft(def, getServer(L));
}
/*
CraftDefinitionShapeless
@@ -205,7 +205,7 @@ int ModApiCraft::l_register_craft(lua_State *L)
CraftDefinition *def = new CraftDefinitionShapeless(
output, recipe, replacements);
- craftdef->registerCraft(def);
+ craftdef->registerCraft(def, getServer(L));
}
/*
CraftDefinitionToolRepair
@@ -216,7 +216,7 @@ int ModApiCraft::l_register_craft(lua_State *L)
CraftDefinition *def = new CraftDefinitionToolRepair(
additional_wear);
- craftdef->registerCraft(def);
+ craftdef->registerCraft(def, getServer(L));
}
/*
CraftDefinitionCooking
@@ -246,7 +246,7 @@ int ModApiCraft::l_register_craft(lua_State *L)
CraftDefinition *def = new CraftDefinitionCooking(
output, recipe, cooktime, replacements);
- craftdef->registerCraft(def);
+ craftdef->registerCraft(def, getServer(L));
}
/*
CraftDefinitionFuel
@@ -270,7 +270,7 @@ int ModApiCraft::l_register_craft(lua_State *L)
CraftDefinition *def = new CraftDefinitionFuel(
recipe, burntime, replacements);
- craftdef->registerCraft(def);
+ craftdef->registerCraft(def, getServer(L));
}
else
{
@@ -303,18 +303,23 @@ int ModApiCraft::l_get_craft_result(lua_State *L)
ICraftDefManager *cdef = gdef->cdef();
CraftInput input(method, width, items);
CraftOutput output;
- bool got = cdef->getCraftResult(input, output, true, gdef);
+ std::vector<ItemStack> output_replacements;
+ bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
lua_newtable(L); // output table
- if(got){
+ if (got) {
ItemStack item;
item.deSerialize(output.item, gdef->idef());
LuaItemStack::create(L, item);
lua_setfield(L, -2, "item");
setintfield(L, -1, "time", output.time);
+ push_items(L, output_replacements);
+ lua_setfield(L, -2, "replacements");
} else {
LuaItemStack::create(L, ItemStack());
lua_setfield(L, -2, "item");
setintfield(L, -1, "time", 0);
+ lua_newtable(L);
+ lua_setfield(L, -2, "replacements");
}
lua_newtable(L); // decremented input table
lua_pushstring(L, method_s.c_str());
@@ -326,56 +331,82 @@ int ModApiCraft::l_get_craft_result(lua_State *L)
return 2;
}
+
+static void push_craft_recipe(lua_State *L, IGameDef *gdef,
+ const CraftDefinition *recipe,
+ const CraftOutput &tmpout)
+{
+ CraftInput input = recipe->getInput(tmpout, gdef);
+ CraftOutput output = recipe->getOutput(input, gdef);
+
+ lua_newtable(L); // items
+ std::vector<ItemStack>::const_iterator iter = input.items.begin();
+ for (u16 j = 1; iter != input.items.end(); iter++, j++) {
+ if (iter->empty())
+ continue;
+ lua_pushstring(L, iter->name.c_str());
+ lua_rawseti(L, -2, j);
+ }
+ lua_setfield(L, -2, "items");
+ setintfield(L, -1, "width", input.width);
+ switch (input.method) {
+ case CRAFT_METHOD_NORMAL:
+ lua_pushstring(L, "normal");
+ break;
+ case CRAFT_METHOD_COOKING:
+ lua_pushstring(L, "cooking");
+ break;
+ case CRAFT_METHOD_FUEL:
+ lua_pushstring(L, "fuel");
+ break;
+ default:
+ lua_pushstring(L, "unknown");
+ }
+ lua_setfield(L, -2, "type");
+ lua_pushstring(L, output.item.c_str());
+ lua_setfield(L, -2, "output");
+}
+
+static void push_craft_recipes(lua_State *L, IGameDef *gdef,
+ const std::vector<CraftDefinition*> &recipes,
+ const CraftOutput &output)
+{
+ lua_createtable(L, recipes.size(), 0);
+
+ if (recipes.empty()) {
+ lua_pushnil(L);
+ return;
+ }
+
+ std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
+ for (unsigned i = 0; it != recipes.end(); ++it) {
+ lua_newtable(L);
+ push_craft_recipe(L, gdef, *it, output);
+ lua_rawseti(L, -2, ++i);
+ }
+}
+
+
// get_craft_recipe(result item)
int ModApiCraft::l_get_craft_recipe(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- int k = 1;
- int input_i = 1;
- std::string o_item = luaL_checkstring(L,input_i);
+ std::string item = luaL_checkstring(L, 1);
+ Server *server = getServer(L);
+ CraftOutput output(item, 0);
+ std::vector<CraftDefinition*> recipes = server->cdef()
+ ->getCraftRecipes(output, server, 1);
- IGameDef *gdef = getServer(L);
- ICraftDefManager *cdef = gdef->cdef();
- CraftInput input;
- CraftOutput output(o_item,0);
- bool got = cdef->getCraftRecipe(input, output, gdef);
- lua_newtable(L); // output table
- if(got){
- lua_newtable(L);
- for(std::vector<ItemStack>::const_iterator
- i = input.items.begin();
- i != input.items.end(); i++, k++)
- {
- if (i->empty())
- {
- continue;
- }
- lua_pushinteger(L,k);
- lua_pushstring(L,i->name.c_str());
- lua_settable(L, -3);
- }
- lua_setfield(L, -2, "items");
- setintfield(L, -1, "width", input.width);
- switch (input.method) {
- case CRAFT_METHOD_NORMAL:
- lua_pushstring(L,"normal");
- break;
- case CRAFT_METHOD_COOKING:
- lua_pushstring(L,"cooking");
- break;
- case CRAFT_METHOD_FUEL:
- lua_pushstring(L,"fuel");
- break;
- default:
- lua_pushstring(L,"unknown");
- }
- lua_setfield(L, -2, "type");
- } else {
+ lua_createtable(L, 1, 0);
+
+ if (recipes.empty()) {
lua_pushnil(L);
lua_setfield(L, -2, "items");
setintfield(L, -1, "width", 0);
+ return 1;
}
+ push_craft_recipe(L, server, recipes[0], output);
return 1;
}
@@ -384,59 +415,13 @@ int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- std::string o_item = luaL_checkstring(L,1);
- IGameDef *gdef = getServer(L);
- ICraftDefManager *cdef = gdef->cdef();
- CraftInput input;
- CraftOutput output(o_item,0);
- std::vector<CraftDefinition*> recipes_list;
- recipes_list = cdef->getCraftRecipes(output, gdef);
- if (recipes_list.empty()) {
- lua_pushnil(L);
- return 1;
- }
+ std::string item = luaL_checkstring(L, 1);
+ Server *server = getServer(L);
+ CraftOutput output(item, 0);
+ std::vector<CraftDefinition*> recipes = server->cdef()
+ ->getCraftRecipes(output, server);
- lua_createtable(L, recipes_list.size(), 0);
- std::vector<CraftDefinition*>::const_iterator iter = recipes_list.begin();
- for (u16 i = 0; iter != recipes_list.end(); iter++) {
- CraftOutput tmpout;
- tmpout.item = "";
- tmpout.time = 0;
- tmpout = (*iter)->getOutput(input, gdef);
- std::string query = tmpout.item;
- char *fmtpos, *fmt = &query[0];
- if (strtok_r(fmt, " ", &fmtpos) == output.item) {
- input = (*iter)->getInput(output, gdef);
- lua_newtable(L);
- lua_newtable(L); // items
- std::vector<ItemStack>::const_iterator iter = input.items.begin();
- for (u16 j = 1; iter != input.items.end(); iter++, j++) {
- if (iter->empty())
- continue;
- lua_pushstring(L, iter->name.c_str());
- lua_rawseti(L, -2, j);
- }
- lua_setfield(L, -2, "items");
- setintfield(L, -1, "width", input.width);
- switch (input.method) {
- case CRAFT_METHOD_NORMAL:
- lua_pushstring(L, "normal");
- break;
- case CRAFT_METHOD_COOKING:
- lua_pushstring(L, "cooking");
- break;
- case CRAFT_METHOD_FUEL:
- lua_pushstring(L, "fuel");
- break;
- default:
- lua_pushstring(L, "unknown");
- }
- lua_setfield(L, -2, "type");
- lua_pushstring(L, &tmpout.item[0]);
- lua_setfield(L, -2, "output");
- lua_rawseti(L, -2, ++i);
- }
- }
+ push_craft_recipes(L, server, recipes, output);
return 1;
}
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index cd5d253ac..28afdd071 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -49,7 +49,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
scriptIface->realityCheck();
lua_State *L = scriptIface->getStack();
- assert(lua_checkstack(L, 20));
+ sanity_check(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
lua_pushcfunction(L, script_error_handler);
@@ -65,9 +65,11 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
lua_pushnumber(L, m_id);
lua_gettable(L, -2);
if(lua_isnil(L, -1))
- assert(0);
+ FATAL_ERROR("");
lua_remove(L, -2); // Remove registered_abms
+ scriptIface->setOriginFromTable(-1);
+
// Call action
luaL_checktype(L, -1, LUA_TTABLE);
lua_getfield(L, -1, "action");
@@ -77,8 +79,11 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n,
pushnode(L, n, env->getGameDef()->ndef());
lua_pushnumber(L, active_object_count);
lua_pushnumber(L, active_object_count_wider);
- if(lua_pcall(L, 4, 0, errorhandler))
- script_error(L);
+
+ int result = lua_pcall(L, 4, 0, errorhandler);
+ if (result)
+ scriptIface->scriptError(result, "LuaABM::trigger");
+
lua_pop(L, 1); // Pop error handler
}
@@ -334,6 +339,22 @@ int ModApiEnvMod::l_add_node_level(lua_State *L)
return 1;
}
+// find_nodes_with_meta(pos1, pos2)
+int ModApiEnvMod::l_find_nodes_with_meta(lua_State *L)
+{
+ GET_ENV_PTR;
+
+ std::vector<v3s16> positions = env->getMap().findNodesWithMetadata(
+ check_v3s16(L, 1), check_v3s16(L, 2));
+
+ lua_newtable(L);
+ for (size_t i = 0; i != positions.size(); i++) {
+ push_v3s16(L, positions[i]);
+ lua_rawseti(L, -2, i + 1);
+ }
+
+ return 1;
+}
// get_meta(pos)
int ModApiEnvMod::l_get_meta(lua_State *L)
@@ -402,23 +423,11 @@ int ModApiEnvMod::l_add_item(lua_State *L)
return 0;
lua_pushvalue(L, 1);
lua_pushstring(L, item.getItemString().c_str());
- if(lua_pcall(L, 2, 1, errorhandler))
- script_error(L);
+
+ PCALL_RESL(L, lua_pcall(L, 2, 1, errorhandler));
+
lua_remove(L, errorhandler); // Remove error handler
return 1;
- /*lua_pushvalue(L, 1);
- lua_pushstring(L, "__builtin:item");
- lua_pushstring(L, item.getItemString().c_str());
- return l_add_entity(L);*/
- /*// Do it
- ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString());
- int objectid = env->addActiveObject(obj);
- // If failed to add, return nothing (reads as nil)
- if(objectid == 0)
- return 0;
- // Return ObjectRef
- objectrefGetOrCreate(L, obj);
- return 1;*/
}
// get_player_by_name(name)
@@ -451,10 +460,11 @@ int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L)
// Do it
v3f pos = checkFloatPos(L, 1);
float radius = luaL_checknumber(L, 2) * BS;
- std::set<u16> ids = env->getObjectsInsideRadius(pos, radius);
+ std::vector<u16> ids;
+ env->getObjectsInsideRadius(ids, pos, radius);
ScriptApiBase *script = getScriptApiBase(L);
lua_createtable(L, ids.size(), 0);
- std::set<u16>::const_iterator iter = ids.begin();
+ std::vector<u16>::const_iterator iter = ids.begin();
for(u32 i = 0; iter != ids.end(); iter++) {
ServerActiveObject *obj = env->getActiveObject(*iter);
// Insert object reference into table
@@ -472,7 +482,7 @@ int ModApiEnvMod::l_set_timeofday(lua_State *L)
// Do it
float timeofday_f = luaL_checknumber(L, 1);
- assert(timeofday_f >= 0.0 && timeofday_f <= 1.0);
+ sanity_check(timeofday_f >= 0.0 && timeofday_f <= 1.0);
int timeofday_mh = (int)(timeofday_f * 24000.0);
// This should be set directly in the environment but currently
// such changes aren't immediately sent to the clients, so call
@@ -530,9 +540,8 @@ int ModApiEnvMod::l_find_node_near(lua_State *L)
}
for(int d=1; d<=radius; d++){
- std::list<v3s16> list;
- getFacePositions(list, d);
- for(std::list<v3s16>::iterator i = list.begin();
+ std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
+ for(std::vector<v3s16>::iterator i = list.begin();
i != list.end(); ++i){
v3s16 p = pos + (*i);
content_t c = env->getMap().getNodeNoEx(p).getContent();
@@ -555,30 +564,92 @@ int ModApiEnvMod::l_find_nodes_in_area(lua_State *L)
v3s16 minp = read_v3s16(L, 1);
v3s16 maxp = read_v3s16(L, 2);
std::set<content_t> filter;
- if(lua_istable(L, 3)){
+ if(lua_istable(L, 3)) {
int table = 3;
lua_pushnil(L);
- while(lua_next(L, table) != 0){
+ while(lua_next(L, table) != 0) {
// key at index -2 and value at index -1
luaL_checktype(L, -1, LUA_TSTRING);
ndef->getIds(lua_tostring(L, -1), filter);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
- } else if(lua_isstring(L, 3)){
+ } else if(lua_isstring(L, 3)) {
+ ndef->getIds(lua_tostring(L, 3), filter);
+ }
+
+ std::map<content_t, u16> individual_count;
+
+ lua_newtable(L);
+ u64 i = 0;
+ for (s16 x = minp.X; x <= maxp.X; x++)
+ for (s16 y = minp.Y; y <= maxp.Y; y++)
+ for (s16 z = minp.Z; z <= maxp.Z; z++) {
+ v3s16 p(x, y, z);
+ content_t c = env->getMap().getNodeNoEx(p).getContent();
+ if (filter.count(c) != 0) {
+ push_v3s16(L, p);
+ lua_rawseti(L, -2, ++i);
+ individual_count[c]++;
+ }
+ }
+ lua_newtable(L);
+ for (std::set<content_t>::iterator it = filter.begin();
+ it != filter.end(); ++it) {
+ lua_pushnumber(L, individual_count[*it]);
+ lua_setfield(L, -2, ndef->get(*it).name.c_str());
+ }
+ return 2;
+}
+
+// find_nodes_in_area_under_air(minp, maxp, nodenames) -> list of positions
+// nodenames: e.g. {"ignore", "group:tree"} or "default:dirt"
+int ModApiEnvMod::l_find_nodes_in_area_under_air(lua_State *L)
+{
+ /* Note: A similar but generalized (and therefore slower) version of this
+ * function could be created -- e.g. find_nodes_in_area_under -- which
+ * would accept a node name (or ID?) or list of names that the "above node"
+ * should be.
+ * TODO
+ */
+
+ GET_ENV_PTR;
+
+ INodeDefManager *ndef = getServer(L)->ndef();
+ v3s16 minp = read_v3s16(L, 1);
+ v3s16 maxp = read_v3s16(L, 2);
+ std::set<content_t> filter;
+
+ if (lua_istable(L, 3)) {
+ int table = 3;
+ lua_pushnil(L);
+ while(lua_next(L, table) != 0) {
+ // key at index -2 and value at index -1
+ luaL_checktype(L, -1, LUA_TSTRING);
+ ndef->getIds(lua_tostring(L, -1), filter);
+ // removes value, keeps key for next iteration
+ lua_pop(L, 1);
+ }
+ } else if (lua_isstring(L, 3)) {
ndef->getIds(lua_tostring(L, 3), filter);
}
lua_newtable(L);
u64 i = 0;
- for(s16 x = minp.X; x <= maxp.X; x++)
- for(s16 y = minp.Y; y <= maxp.Y; y++)
- for(s16 z = minp.Z; z <= maxp.Z; z++) {
+ for (s16 x = minp.X; x <= maxp.X; x++)
+ for (s16 z = minp.Z; z <= maxp.Z; z++) {
+ s16 y = minp.Y;
v3s16 p(x, y, z);
content_t c = env->getMap().getNodeNoEx(p).getContent();
- if(filter.count(c) != 0) {
- push_v3s16(L, p);
- lua_rawseti(L, -2, ++i);
+ for (; y <= maxp.Y; y++) {
+ v3s16 psurf(x, y + 1, z);
+ content_t csurf = env->getMap().getNodeNoEx(psurf).getContent();
+ if(c != CONTENT_AIR && csurf == CONTENT_AIR &&
+ filter.count(c) != 0) {
+ push_v3s16(L, v3s16(x, y, z));
+ lua_rawseti(L, -2, ++i);
+ }
+ c = csurf;
}
}
return 1;
@@ -702,10 +773,12 @@ int ModApiEnvMod::l_delete_area(lua_State *L)
for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
for (s16 x = bpmin.X; x <= bpmax.X; x++) {
v3s16 bp(x, y, z);
- if (map.deleteBlock(bp))
+ if (map.deleteBlock(bp)) {
+ env->setStaticForActiveObjectsInBlock(bp, false);
event.modified_blocks.insert(bp);
- else
+ } else {
success = false;
+ }
}
map.dispatchEvent(&event);
@@ -872,6 +945,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(set_node_level);
API_FCT(add_node_level);
API_FCT(add_entity);
+ API_FCT(find_nodes_with_meta);
API_FCT(get_meta);
API_FCT(get_node_timer);
API_FCT(get_player_by_name);
@@ -881,6 +955,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
API_FCT(get_gametime);
API_FCT(find_node_near);
API_FCT(find_nodes_in_area);
+ API_FCT(find_nodes_in_area_under_air);
API_FCT(delete_area);
API_FCT(get_perlin);
API_FCT(get_perlin_map);
diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h
index bfaea1c4d..0d4ca788e 100644
--- a/src/script/lua_api/l_env.h
+++ b/src/script/lua_api/l_env.h
@@ -64,7 +64,6 @@ private:
// pos = {x=num, y=num, z=num}
static int l_punch_node(lua_State *L);
-
// get_node_max_level(pos)
// pos = {x=num, y=num, z=num}
static int l_get_node_max_level(lua_State *L);
@@ -81,6 +80,9 @@ private:
// pos = {x=num, y=num, z=num}
static int l_add_node_level(lua_State *L);
+ // find_nodes_with_meta(pos1, pos2)
+ static int l_find_nodes_with_meta(lua_State *L);
+
// get_meta(pos)
static int l_get_meta(lua_State *L);
@@ -119,6 +121,10 @@ private:
// nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
static int l_find_nodes_in_area(lua_State *L);
+ // find_surface_nodes_in_area(minp, maxp, nodenames) -> list of positions
+ // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
+ static int l_find_nodes_in_area_under_air(lua_State *L);
+
// delete_area(p1, p2) -> true/false
static int l_delete_area(lua_State *L);
diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h
index 5936ac046..1e40c5c4a 100644
--- a/src/script/lua_api/l_internal.h
+++ b/src/script/lua_api/l_internal.h
@@ -33,13 +33,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define API_FCT(name) registerFunction(L, #name, l_##name,top)
#define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name)
-#if (defined(WIN32) || defined(_WIN32_WCE))
#define NO_MAP_LOCK_REQUIRED
+
+/*
+#if (defined(WIN32) || defined(_WIN32_WCE))
+ #define NO_MAP_LOCK_REQUIRED
#else
-#include "main.h"
-#include "profiler.h"
-#define NO_MAP_LOCK_REQUIRED \
- ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD)
+ #include "profiler.h"
+ #define NO_MAP_LOCK_REQUIRED \
+ ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD)
#endif
+*/
#endif /* L_INTERNAL_H_ */
diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp
index 2bed2a255..92311d6fc 100644
--- a/src/script/lua_api/l_mainmenu.cpp
+++ b/src/script/lua_api/l_mainmenu.cpp
@@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "emerge.h"
#include "sound.h"
#include "settings.h"
-#include "main.h" // for g_settings
#include "log.h"
#include "EDriverTypes.h"
@@ -90,7 +89,7 @@ int ModApiMainMenu::getBoolData(lua_State *L, std::string name,bool& valid)
int ModApiMainMenu::l_update_formspec(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
if (engine->m_startgame)
return 0;
@@ -109,21 +108,25 @@ int ModApiMainMenu::l_update_formspec(lua_State *L)
int ModApiMainMenu::l_start(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
//update c++ gamedata from lua table
bool valid = false;
+ MainMenuData *data = engine->m_data;
- engine->m_data->selected_world = getIntegerData(L, "selected_world",valid) -1;
- engine->m_data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
- engine->m_data->name = getTextData(L,"playername");
- engine->m_data->password = getTextData(L,"password");
- engine->m_data->address = getTextData(L,"address");
- engine->m_data->port = getTextData(L,"port");
- engine->m_data->serverdescription = getTextData(L,"serverdescription");
- engine->m_data->servername = getTextData(L,"servername");
+ data->selected_world = getIntegerData(L, "selected_world",valid) -1;
+ data->simple_singleplayer_mode = getBoolData(L,"singleplayer",valid);
+ data->do_reconnect = getBoolData(L, "do_reconnect", valid);
+ if (!data->do_reconnect) {
+ data->name = getTextData(L,"playername");
+ data->password = getTextData(L,"password");
+ data->address = getTextData(L,"address");
+ data->port = getTextData(L,"port");
+ }
+ data->serverdescription = getTextData(L,"serverdescription");
+ data->servername = getTextData(L,"servername");
//close menu next time
engine->m_startgame = true;
@@ -134,7 +137,7 @@ int ModApiMainMenu::l_start(lua_State *L)
int ModApiMainMenu::l_close(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
engine->m_kill = true;
return 0;
@@ -144,7 +147,7 @@ int ModApiMainMenu::l_close(lua_State *L)
int ModApiMainMenu::l_set_background(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
std::string backgroundlevel(luaL_checkstring(L, 1));
std::string texturename(luaL_checkstring(L, 2));
@@ -189,7 +192,7 @@ int ModApiMainMenu::l_set_background(lua_State *L)
int ModApiMainMenu::l_set_clouds(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
bool value = lua_toboolean(L,1);
@@ -209,9 +212,9 @@ int ModApiMainMenu::l_get_textlist_index(lua_State *L)
int ModApiMainMenu::l_get_table_index(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
- std::wstring tablename(narrow_to_wide(luaL_checkstring(L, 1)));
+ std::string tablename(luaL_checkstring(L, 1));
GUITable *table = engine->m_menu->getTable(tablename);
s32 selection = table ? table->getSelected() : 0;
@@ -617,7 +620,7 @@ int ModApiMainMenu::l_delete_favorite(lua_State *L)
int ModApiMainMenu::l_show_keys_menu(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
GUIKeyChangeMenu *kmenu
= new GUIKeyChangeMenu( engine->m_device->getGUIEnvironment(),
@@ -644,15 +647,12 @@ int ModApiMainMenu::l_create_world(lua_State *L)
(gameidx < (int) games.size())) {
// Create world if it doesn't exist
- if(!initializeWorld(path, games[gameidx].id)){
+ if (!loadGameConfAndInitWorld(path, games[gameidx])) {
lua_pushstring(L, "Failed to initialize world");
-
- }
- else {
- lua_pushnil(L);
+ } else {
+ lua_pushnil(L);
}
- }
- else {
+ } else {
lua_pushstring(L, "Invalid game index");
}
return 1;
@@ -692,7 +692,7 @@ int ModApiMainMenu::l_delete_world(lua_State *L)
int ModApiMainMenu::l_set_topleft_text(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
std::string text = "";
@@ -758,30 +758,6 @@ int ModApiMainMenu::l_get_texturepath_share(lua_State *L)
}
/******************************************************************************/
-int ModApiMainMenu::l_get_dirlist(lua_State *L)
-{
- const char *path = luaL_checkstring(L, 1);
- bool dironly = lua_toboolean(L, 2);
-
- std::vector<fs::DirListNode> dirlist = fs::GetDirListing(path);
-
- unsigned int index = 1;
- lua_newtable(L);
- int table = lua_gettop(L);
-
- for (unsigned int i=0;i< dirlist.size(); i++) {
- if ((dirlist[i].dir) || (dironly == false)) {
- lua_pushnumber(L,index);
- lua_pushstring(L,dirlist[i].name.c_str());
- lua_settable(L, table);
- index++;
- }
- }
-
- return 1;
-}
-
-/******************************************************************************/
int ModApiMainMenu::l_create_dir(lua_State *L) {
const char *path = luaL_checkstring(L, 1);
@@ -843,7 +819,7 @@ int ModApiMainMenu::l_copy_dir(lua_State *L)
int ModApiMainMenu::l_extract_zip(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine);
const char *zipfile = luaL_checkstring(L, 1);
const char *destination = luaL_checkstring(L, 2);
@@ -860,7 +836,7 @@ int ModApiMainMenu::l_extract_zip(lua_State *L)
return 1;
}
- assert(fs->getFileArchiveCount() > 0);
+ sanity_check(fs->getFileArchiveCount() > 0);
/**********************************************************************/
/* WARNING this is not threadsafe!! */
@@ -931,7 +907,7 @@ int ModApiMainMenu::l_extract_zip(lua_State *L)
int ModApiMainMenu::l_get_mainmenu_path(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
lua_pushstring(L,engine->getScriptDir().c_str());
return 1;
@@ -963,7 +939,7 @@ bool ModApiMainMenu::isMinetestPath(std::string path)
int ModApiMainMenu::l_show_file_open_dialog(lua_State *L)
{
GUIEngine* engine = getGuiEngine(L);
- assert(engine != 0);
+ sanity_check(engine != NULL);
const char *formname= luaL_checkstring(L, 1);
const char *title = luaL_checkstring(L, 2);
@@ -983,7 +959,7 @@ int ModApiMainMenu::l_show_file_open_dialog(lua_State *L)
/******************************************************************************/
int ModApiMainMenu::l_get_version(lua_State *L)
{
- lua_pushstring(L, minetest_version_simple);
+ lua_pushstring(L, g_version_string);
return 1;
}
@@ -1060,10 +1036,32 @@ int ModApiMainMenu::l_get_video_drivers(lua_State *L)
}
/******************************************************************************/
+int ModApiMainMenu::l_get_video_modes(lua_State *L)
+{
+ std::vector<core::vector3d<u32> > videomodes
+ = porting::getSupportedVideoModes();
+
+ lua_newtable(L);
+ for (u32 i = 0; i != videomodes.size(); i++) {
+ lua_newtable(L);
+ lua_pushnumber(L, videomodes[i].X);
+ lua_setfield(L, -2, "w");
+ lua_pushnumber(L, videomodes[i].Y);
+ lua_setfield(L, -2, "h");
+ lua_pushnumber(L, videomodes[i].Z);
+ lua_setfield(L, -2, "depth");
+
+ lua_rawseti(L, -2, i + 1);
+ }
+
+ return 1;
+}
+
+/******************************************************************************/
int ModApiMainMenu::l_gettext(lua_State *L)
{
std::wstring wtext = wstrgettext((std::string) luaL_checkstring(L, 1));
- lua_pushstring(L, wide_to_narrow(wtext).c_str());
+ lua_pushstring(L, wide_to_utf8(wtext).c_str());
return 1;
}
@@ -1118,8 +1116,8 @@ int ModApiMainMenu::l_do_async_callback(lua_State *L)
const char* serialized_param_raw = luaL_checklstring(L, 2, &param_length);
- assert(serialized_func_raw != NULL);
- assert(serialized_param_raw != NULL);
+ sanity_check(serialized_func_raw != NULL);
+ sanity_check(serialized_param_raw != NULL);
std::string serialized_func = std::string(serialized_func_raw, func_length);
std::string serialized_param = std::string(serialized_param_raw, param_length);
@@ -1152,7 +1150,6 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
API_FCT(get_gamepath);
API_FCT(get_texturepath);
API_FCT(get_texturepath_share);
- API_FCT(get_dirlist);
API_FCT(create_dir);
API_FCT(delete_dir);
API_FCT(copy_dir);
@@ -1167,6 +1164,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
API_FCT(sound_stop);
API_FCT(gettext);
API_FCT(get_video_drivers);
+ API_FCT(get_video_modes);
API_FCT(get_screen_info);
API_FCT(get_min_supp_proto);
API_FCT(get_max_supp_proto);
@@ -1185,7 +1183,6 @@ void ModApiMainMenu::InitializeAsync(AsyncEngine& engine)
ASYNC_API_FCT(get_gamepath);
ASYNC_API_FCT(get_texturepath);
ASYNC_API_FCT(get_texturepath_share);
- ASYNC_API_FCT(get_dirlist);
ASYNC_API_FCT(create_dir);
ASYNC_API_FCT(delete_dir);
ASYNC_API_FCT(copy_dir);
diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h
index 8b21a93aa..9c1fed272 100644
--- a/src/script/lua_api/l_mainmenu.h
+++ b/src/script/lua_api/l_mainmenu.h
@@ -137,6 +137,8 @@ private:
static int l_get_video_drivers(lua_State *L);
+ static int l_get_video_modes(lua_State *L);
+
//version compatibility
static int l_get_min_supp_proto(lua_State *L);
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index d470cef88..d30b68054 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_vmanip.h"
#include "common/c_converter.h"
#include "common/c_content.h"
+#include "cpp_api/s_security.h"
#include "util/serialize.h"
#include "server.h"
#include "environment.h"
@@ -32,18 +33,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mg_schematic.h"
#include "mapgen_v5.h"
#include "mapgen_v7.h"
+#include "filesys.h"
#include "settings.h"
-#include "main.h"
#include "log.h"
-
struct EnumString ModApiMapgen::es_BiomeTerrainType[] =
{
- {BIOME_TYPE_NORMAL, "normal"},
- {BIOME_TYPE_LIQUID, "liquid"},
- {BIOME_TYPE_NETHER, "nether"},
- {BIOME_TYPE_AETHER, "aether"},
- {BIOME_TYPE_FLAT, "flat"},
+ {BIOME_NORMAL, "normal"},
+ {BIOME_LIQUID, "liquid"},
+ {BIOME_NETHER, "nether"},
+ {BIOME_AETHER, "aether"},
+ {BIOME_FLAT, "flat"},
{0, NULL},
};
@@ -68,10 +68,10 @@ struct EnumString ModApiMapgen::es_MapgenObject[] =
struct EnumString ModApiMapgen::es_OreType[] =
{
- {ORE_TYPE_SCATTER, "scatter"},
- {ORE_TYPE_SHEET, "sheet"},
- {ORE_TYPE_BLOB, "blob"},
- {ORE_TYPE_VEIN, "vein"},
+ {ORE_SCATTER, "scatter"},
+ {ORE_SHEET, "sheet"},
+ {ORE_BLOB, "blob"},
+ {ORE_VEIN, "vein"},
{0, NULL},
};
@@ -85,116 +85,238 @@ struct EnumString ModApiMapgen::es_Rotation[] =
{0, NULL},
};
+struct EnumString ModApiMapgen::es_SchematicFormatType[] =
+{
+ {SCHEM_FMT_HANDLE, "handle"},
+ {SCHEM_FMT_MTS, "mts"},
+ {SCHEM_FMT_LUA, "lua"},
+ {0, NULL},
+};
+
+ObjDef *get_objdef(lua_State *L, int index, ObjDefManager *objmgr);
+
+Biome *get_or_load_biome(lua_State *L, int index,
+ BiomeManager *biomemgr);
+Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef);
+size_t get_biome_list(lua_State *L, int index,
+ BiomeManager *biomemgr, std::set<u8> *biome_id_list);
+
+Schematic *get_or_load_schematic(lua_State *L, int index,
+ SchematicManager *schemmgr, StringMap *replace_names);
+Schematic *load_schematic(lua_State *L, int index, INodeDefManager *ndef,
+ StringMap *replace_names);
+Schematic *load_schematic_from_def(lua_State *L, int index,
+ INodeDefManager *ndef, StringMap *replace_names);
+bool read_schematic_def(lua_State *L, int index,
+ Schematic *schem, std::vector<std::string> *names);
+
+bool read_deco_simple(lua_State *L, DecoSimple *deco);
+bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco);
+
///////////////////////////////////////////////////////////////////////////////
+ObjDef *get_objdef(lua_State *L, int index, ObjDefManager *objmgr)
+{
+ if (index < 0)
+ index = lua_gettop(L) + 1 + index;
-bool read_schematic(lua_State *L, int index, Schematic *schem,
- INodeDefManager *ndef, std::map<std::string, std::string> &replace_names)
+ // If a number, assume this is a handle to an object def
+ if (lua_isnumber(L, index))
+ return objmgr->get(lua_tointeger(L, index));
+
+ // If a string, assume a name is given instead
+ if (lua_isstring(L, index))
+ return objmgr->getByName(lua_tostring(L, index));
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Schematic *get_or_load_schematic(lua_State *L, int index,
+ SchematicManager *schemmgr, StringMap *replace_names)
{
+ if (index < 0)
+ index = lua_gettop(L) + 1 + index;
+
+ Schematic *schem = (Schematic *)get_objdef(L, index, schemmgr);
+ if (schem)
+ return schem;
+
+ schem = load_schematic(L, index, schemmgr->getNodeDef(),
+ replace_names);
+ if (!schem)
+ return NULL;
+
+ if (schemmgr->add(schem) == OBJDEF_INVALID_HANDLE) {
+ delete schem;
+ return NULL;
+ }
+
+ return schem;
+}
+
+
+Schematic *load_schematic(lua_State *L, int index, INodeDefManager *ndef,
+ StringMap *replace_names)
+{
+ if (index < 0)
+ index = lua_gettop(L) + 1 + index;
+
+ Schematic *schem = NULL;
+
+ if (lua_istable(L, index)) {
+ schem = load_schematic_from_def(L, index, ndef,
+ replace_names);
+ if (!schem) {
+ delete schem;
+ return NULL;
+ }
+ } else if (lua_isnumber(L, index)) {
+ return NULL;
+ } else if (lua_isstring(L, index)) {
+ schem = SchematicManager::create(SCHEMATIC_NORMAL);
+
+ std::string filepath = lua_tostring(L, index);
+ if (!fs::IsPathAbsolute(filepath))
+ filepath = ModApiBase::getCurrentModPath(L) + DIR_DELIM + filepath;
+
+ if (!schem->loadSchematicFromFile(filepath, ndef,
+ replace_names)) {
+ delete schem;
+ return NULL;
+ }
+ }
+
+ return schem;
+}
+
+
+Schematic *load_schematic_from_def(lua_State *L, int index,
+ INodeDefManager *ndef, StringMap *replace_names)
+{
+ Schematic *schem = SchematicManager::create(SCHEMATIC_NORMAL);
+
+ if (!read_schematic_def(L, index, schem, &schem->m_nodenames)) {
+ delete schem;
+ return NULL;
+ }
+
+ size_t num_nodes = schem->m_nodenames.size();
+
+ schem->m_nnlistsizes.push_back(num_nodes);
+
+ if (replace_names) {
+ for (size_t i = 0; i != num_nodes; i++) {
+ StringMap::iterator it = replace_names->find(schem->m_nodenames[i]);
+ if (it != replace_names->end())
+ schem->m_nodenames[i] = it->second;
+ }
+ }
+
+ if (ndef)
+ ndef->pendNodeResolve(schem);
+
+ return schem;
+}
+
+
+bool read_schematic_def(lua_State *L, int index,
+ Schematic *schem, std::vector<std::string> *names)
+{
+ if (!lua_istable(L, index))
+ return false;
+
//// Get schematic size
lua_getfield(L, index, "size");
- v3s16 size = read_v3s16(L, -1);
+ v3s16 size = check_v3s16(L, -1);
lua_pop(L, 1);
+ schem->size = size;
+
//// Get schematic data
lua_getfield(L, index, "data");
luaL_checktype(L, -1, LUA_TTABLE);
- int numnodes = size.X * size.Y * size.Z;
- MapNode *schemdata = new MapNode[numnodes];
- int i = 0;
+ u32 numnodes = size.X * size.Y * size.Z;
+ schem->schemdata = new MapNode[numnodes];
- lua_pushnil(L);
- while (lua_next(L, -2)) {
- if (i >= numnodes) {
- i++;
- lua_pop(L, 1);
+ size_t names_base = names->size();
+ std::map<std::string, content_t> name_id_map;
+
+ u32 i = 0;
+ for (lua_pushnil(L); lua_next(L, -2); i++, lua_pop(L, 1)) {
+ if (i >= numnodes)
continue;
- }
- // same as readnode, except param1 default is MTSCHEM_PROB_CONST
- lua_getfield(L, -1, "name");
- std::string name = luaL_checkstring(L, -1);
- lua_pop(L, 1);
+ //// Read name
+ std::string name;
+ if (!getstringfield(L, -1, "name", name))
+ throw LuaError("Schematic data definition with missing name field");
+ //// Read param1/prob
u8 param1;
- lua_getfield(L, -1, "param1");
- param1 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : MTSCHEM_PROB_ALWAYS;
- lua_pop(L, 1);
-
- u8 param2;
- lua_getfield(L, -1, "param2");
- param2 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : 0;
- lua_pop(L, 1);
-
- std::map<std::string, std::string>::iterator it;
- it = replace_names.find(name);
- if (it != replace_names.end())
- name = it->second;
+ if (!getintfield(L, -1, "param1", param1) &&
+ !getintfield(L, -1, "prob", param1))
+ param1 = MTSCHEM_PROB_ALWAYS_OLD;
+
+ //// Read param2
+ u8 param2 = getintfield_default(L, -1, "param2", 0);
+
+ //// Find or add new nodename-to-ID mapping
+ std::map<std::string, content_t>::iterator it = name_id_map.find(name);
+ content_t name_index;
+ if (it != name_id_map.end()) {
+ name_index = it->second;
+ } else {
+ name_index = names->size() - names_base;
+ name_id_map[name] = name_index;
+ names->push_back(name);
+ }
- schemdata[i] = MapNode(ndef, name, param1, param2);
+ //// Perform probability/force_place fixup on param1
+ param1 >>= 1;
+ if (getboolfield_default(L, -1, "force_place", false))
+ param1 |= MTSCHEM_FORCE_PLACE;
- i++;
- lua_pop(L, 1);
+ //// Actually set the node in the schematic
+ schem->schemdata[i] = MapNode(name_index, param1, param2);
}
if (i != numnodes) {
- errorstream << "read_schematic: incorrect number of "
+ errorstream << "read_schematic_def: incorrect number of "
"nodes provided in raw schematic data (got " << i <<
", expected " << numnodes << ")." << std::endl;
- delete schemdata;
return false;
}
//// Get Y-slice probability values (if present)
- u8 *slice_probs = new u8[size.Y];
- for (i = 0; i != size.Y; i++)
- slice_probs[i] = MTSCHEM_PROB_ALWAYS;
+ schem->slice_probs = new u8[size.Y];
+ for (i = 0; i != (u32) size.Y; i++)
+ schem->slice_probs[i] = MTSCHEM_PROB_ALWAYS;
lua_getfield(L, index, "yslice_prob");
if (lua_istable(L, -1)) {
- lua_pushnil(L);
- while (lua_next(L, -2)) {
- if (getintfield(L, -1, "ypos", i) && i >= 0 && i < size.Y) {
- slice_probs[i] = getintfield_default(L, -1,
- "prob", MTSCHEM_PROB_ALWAYS);
- }
- lua_pop(L, 1);
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ u16 ypos;
+ if (!getintfield(L, -1, "ypos", ypos) || (ypos >= size.Y) ||
+ !getintfield(L, -1, "prob", schem->slice_probs[ypos]))
+ continue;
+
+ schem->slice_probs[ypos] >>= 1;
}
}
- // Here, we read the nodes directly from the INodeDefManager - there is no
- // need for pending node resolutions so we'll mark this schematic as updated
- schem->flags = SCHEM_CIDS_UPDATED;
-
- schem->size = size;
- schem->schemdata = schemdata;
- schem->slice_probs = slice_probs;
return true;
}
-bool get_schematic(lua_State *L, int index, Schematic *schem,
- INodeDefManager *ndef, std::map<std::string, std::string> &replace_names)
+void read_schematic_replacements(lua_State *L, int index, StringMap *replace_names)
{
if (index < 0)
index = lua_gettop(L) + 1 + index;
- if (lua_istable(L, index)) {
- return read_schematic(L, index, schem, ndef, replace_names);
- } else if (lua_isstring(L, index)) {
- const char *filename = lua_tostring(L, index);
- return schem->loadSchematicFromFile(filename, ndef, replace_names);
- } else {
- return false;
- }
-}
-
-
-void read_schematic_replacements(lua_State *L,
- std::map<std::string, std::string> &replace_names, int index)
-{
lua_pushnil(L);
while (lua_next(L, index)) {
std::string replace_from;
@@ -213,11 +335,119 @@ void read_schematic_replacements(lua_State *L,
replace_to = lua_tostring(L, -1);
}
- replace_names[replace_from] = replace_to;
+ replace_names->insert(std::make_pair(replace_from, replace_to));
lua_pop(L, 1);
}
}
+///////////////////////////////////////////////////////////////////////////////
+
+Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr)
+{
+ if (index < 0)
+ index = lua_gettop(L) + 1 + index;
+
+ Biome *biome = (Biome *)get_objdef(L, index, biomemgr);
+ if (biome)
+ return biome;
+
+ biome = read_biome_def(L, index, biomemgr->getNodeDef());
+ if (!biome)
+ return NULL;
+
+ if (biomemgr->add(biome) == OBJDEF_INVALID_HANDLE) {
+ delete biome;
+ return NULL;
+ }
+
+ return biome;
+}
+
+
+Biome *read_biome_def(lua_State *L, int index, INodeDefManager *ndef)
+{
+ if (!lua_istable(L, index))
+ return NULL;
+
+ BiomeType biometype = (BiomeType)getenumfield(L, index, "type",
+ ModApiMapgen::es_BiomeTerrainType, BIOME_NORMAL);
+ Biome *b = BiomeManager::create(biometype);
+
+ b->name = getstringfield_default(L, index, "name", "");
+ b->depth_top = getintfield_default(L, index, "depth_top", 0);
+ b->depth_filler = getintfield_default(L, index, "depth_filler", -31000);
+ b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0);
+ b->y_min = getintfield_default(L, index, "y_min", -31000);
+ b->y_max = getintfield_default(L, index, "y_max", 31000);
+ b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f);
+ b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.f);
+ b->flags = 0; //reserved
+
+ std::vector<std::string> &nn = b->m_nodenames;
+ nn.push_back(getstringfield_default(L, index, "node_top", ""));
+ nn.push_back(getstringfield_default(L, index, "node_filler", ""));
+ nn.push_back(getstringfield_default(L, index, "node_stone", ""));
+ nn.push_back(getstringfield_default(L, index, "node_water_top", ""));
+ nn.push_back(getstringfield_default(L, index, "node_water", ""));
+ nn.push_back(getstringfield_default(L, index, "node_river_water", ""));
+ nn.push_back(getstringfield_default(L, index, "node_dust", ""));
+ ndef->pendNodeResolve(b);
+
+ return b;
+}
+
+
+size_t get_biome_list(lua_State *L, int index,
+ BiomeManager *biomemgr, std::set<u8> *biome_id_list)
+{
+ if (index < 0)
+ index = lua_gettop(L) + 1 + index;
+
+ if (lua_isnil(L, index))
+ return 0;
+
+ bool is_single = true;
+ if (lua_istable(L, index)) {
+ lua_getfield(L, index, "name");
+ is_single = !lua_isnil(L, -1);
+ lua_pop(L, 1);
+ }
+
+ if (is_single) {
+ Biome *biome = get_or_load_biome(L, index, biomemgr);
+ if (!biome) {
+ errorstream << "get_biome_list: failed to get biome '"
+ << (lua_isstring(L, index) ? lua_tostring(L, index) : "")
+ << "'." << std::endl;
+ return 1;
+ }
+
+ biome_id_list->insert(biome->index);
+ return 0;
+ }
+
+ // returns number of failed resolutions
+ size_t fail_count = 0;
+ size_t count = 0;
+
+ for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1)) {
+ count++;
+ Biome *biome = get_or_load_biome(L, -1, biomemgr);
+ if (!biome) {
+ fail_count++;
+ errorstream << "get_biome_list: failed to get biome '"
+ << (lua_isstring(L, -1) ? lua_tostring(L, -1) : "")
+ << "'" << std::endl;
+ continue;
+ }
+
+ biome_id_list->insert(biome->index);
+ }
+
+ return fail_count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// get_mapgen_object(objectname)
// returns the requested object used during map generation
@@ -239,92 +469,98 @@ int ModApiMapgen::l_get_mapgen_object(lua_State *L)
size_t maplen = mg->csize.X * mg->csize.Z;
switch (mgobj) {
- case MGOBJ_VMANIP: {
- MMVManip *vm = mg->vm;
+ case MGOBJ_VMANIP: {
+ MMVManip *vm = mg->vm;
- // VoxelManip object
- LuaVoxelManip *o = new LuaVoxelManip(vm, true);
- *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
- luaL_getmetatable(L, "VoxelManip");
- lua_setmetatable(L, -2);
+ // VoxelManip object
+ LuaVoxelManip *o = new LuaVoxelManip(vm, true);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, "VoxelManip");
+ lua_setmetatable(L, -2);
- // emerged min pos
- push_v3s16(L, vm->m_area.MinEdge);
+ // emerged min pos
+ push_v3s16(L, vm->m_area.MinEdge);
- // emerged max pos
- push_v3s16(L, vm->m_area.MaxEdge);
+ // emerged max pos
+ push_v3s16(L, vm->m_area.MaxEdge);
- return 3;
+ return 3;
+ }
+ case MGOBJ_HEIGHTMAP: {
+ if (!mg->heightmap)
+ return 0;
+
+ lua_newtable(L);
+ for (size_t i = 0; i != maplen; i++) {
+ lua_pushinteger(L, mg->heightmap[i]);
+ lua_rawseti(L, -2, i + 1);
}
- case MGOBJ_HEIGHTMAP: {
- if (!mg->heightmap)
- return 0;
-
- lua_newtable(L);
- for (size_t i = 0; i != maplen; i++) {
- lua_pushinteger(L, mg->heightmap[i]);
- lua_rawseti(L, -2, i + 1);
- }
- return 1;
+ return 1;
+ }
+ case MGOBJ_BIOMEMAP: {
+ if (!mg->biomemap)
+ return 0;
+
+ lua_newtable(L);
+ for (size_t i = 0; i != maplen; i++) {
+ lua_pushinteger(L, mg->biomemap[i]);
+ lua_rawseti(L, -2, i + 1);
}
- case MGOBJ_BIOMEMAP: {
- if (!mg->biomemap)
- return 0;
- lua_newtable(L);
- for (size_t i = 0; i != maplen; i++) {
- lua_pushinteger(L, mg->biomemap[i]);
- lua_rawseti(L, -2, i + 1);
- }
-
- return 1;
+ return 1;
+ }
+ case MGOBJ_HEATMAP: {
+ if (!mg->heatmap)
+ return 0;
+
+ lua_newtable(L);
+ for (size_t i = 0; i != maplen; i++) {
+ lua_pushnumber(L, mg->heatmap[i]);
+ lua_rawseti(L, -2, i + 1);
}
- case MGOBJ_HEATMAP: { // Mapgen V7 specific objects
- case MGOBJ_HUMIDMAP:
- if (strcmp(emerge->params.mg_name.c_str(), "v7"))
- return 0;
-
- MapgenV7 *mgv7 = (MapgenV7 *)mg;
- float *arr = (mgobj == MGOBJ_HEATMAP) ?
- mgv7->noise_heat->result : mgv7->noise_humidity->result;
- if (!arr)
- return 0;
+ return 1;
+ }
- lua_newtable(L);
- for (size_t i = 0; i != maplen; i++) {
- lua_pushnumber(L, arr[i]);
- lua_rawseti(L, -2, i + 1);
- }
+ case MGOBJ_HUMIDMAP: {
+ if (!mg->humidmap)
+ return 0;
- return 1;
+ lua_newtable(L);
+ for (size_t i = 0; i != maplen; i++) {
+ lua_pushnumber(L, mg->humidmap[i]);
+ lua_rawseti(L, -2, i + 1);
}
- case MGOBJ_GENNOTIFY: {
- std::map<std::string, std::vector<v3s16> >event_map;
- std::map<std::string, std::vector<v3s16> >::iterator it;
- mg->gennotify.getEvents(event_map);
+ return 1;
+ }
+ case MGOBJ_GENNOTIFY: {
+ std::map<std::string, std::vector<v3s16> >event_map;
+ std::map<std::string, std::vector<v3s16> >::iterator it;
- lua_newtable(L);
- for (it = event_map.begin(); it != event_map.end(); ++it) {
- lua_newtable(L);
+ mg->gennotify.getEvents(event_map);
- for (size_t j = 0; j != it->second.size(); j++) {
- push_v3s16(L, it->second[j]);
- lua_rawseti(L, -2, j + 1);
- }
+ lua_newtable(L);
+ for (it = event_map.begin(); it != event_map.end(); ++it) {
+ lua_newtable(L);
- lua_setfield(L, -2, it->first.c_str());
+ for (size_t j = 0; j != it->second.size(); j++) {
+ push_v3s16(L, it->second[j]);
+ lua_rawseti(L, -2, j + 1);
}
- return 1;
+ lua_setfield(L, -2, it->first.c_str());
}
+
+ return 1;
+ }
}
return 0;
}
+
int ModApiMapgen::l_get_mapgen_params(lua_State *L)
{
MapgenParams *params = &getServer(L)->getEmergeManager()->params;
@@ -350,6 +586,7 @@ int ModApiMapgen::l_get_mapgen_params(lua_State *L)
return 1;
}
+
// set_mapgen_params(params)
// set mapgen parameters
int ModApiMapgen::l_set_mapgen_params(lua_State *L)
@@ -389,6 +626,7 @@ int ModApiMapgen::l_set_mapgen_params(lua_State *L)
return 0;
}
+
// set_noiseparams(name, noiseparams, set_default)
// set global config values for noise parameters
int ModApiMapgen::l_set_noiseparams(lua_State *L)
@@ -406,6 +644,21 @@ int ModApiMapgen::l_set_noiseparams(lua_State *L)
return 0;
}
+
+// get_noiseparams(name)
+int ModApiMapgen::l_get_noiseparams(lua_State *L)
+{
+ std::string name = luaL_checkstring(L, 1);
+
+ NoiseParams np;
+ if (!g_settings->getNoiseParams(name, np))
+ return 0;
+
+ push_noiseparams(L, &np);
+ return 1;
+}
+
+
// set_gen_notify(flags, {deco_id_table})
int ModApiMapgen::l_set_gen_notify(lua_State *L)
{
@@ -421,7 +674,7 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L)
lua_pushnil(L);
while (lua_next(L, 2)) {
if (lua_isnumber(L, -1))
- emerge->gen_notify_on_deco_ids.insert(lua_tonumber(L, -1));
+ emerge->gen_notify_on_deco_ids.insert((u32)lua_tonumber(L, -1));
lua_pop(L, 1);
}
}
@@ -429,6 +682,26 @@ int ModApiMapgen::l_set_gen_notify(lua_State *L)
return 0;
}
+
+// get_gen_notify()
+int ModApiMapgen::l_get_gen_notify(lua_State *L)
+{
+ EmergeManager *emerge = getServer(L)->getEmergeManager();
+ push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on,
+ emerge->gen_notify_on);
+
+ lua_newtable(L);
+ int i = 1;
+ for (std::set<u32>::iterator it = emerge->gen_notify_on_deco_ids.begin();
+ it != emerge->gen_notify_on_deco_ids.end(); ++it) {
+ lua_pushnumber(L, *it);
+ lua_rawseti(L, -2, i);
+ i++;
+ }
+ return 2;
+}
+
+
// register_biome({lots of stuff})
int ModApiMapgen::l_register_biome(lua_State *L)
{
@@ -438,66 +711,20 @@ int ModApiMapgen::l_register_biome(lua_State *L)
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
- enum BiomeType biometype = (BiomeType)getenumfield(L, index, "type",
- es_BiomeTerrainType, BIOME_TYPE_NORMAL);
- Biome *b = bmgr->create(biometype);
-
- b->name = getstringfield_default(L, index, "name", "");
- b->depth_top = getintfield_default(L, index, "depth_top", 1);
- b->depth_filler = getintfield_default(L, index, "depth_filler", 3);
- b->height_shore = getintfield_default(L, index, "height_shore", 3);
- b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0);
- b->y_min = getintfield_default(L, index, "y_min", -31000);
- b->y_max = getintfield_default(L, index, "y_max", 31000);
- b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f);
- b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.f);
- b->flags = 0; //reserved
+ Biome *biome = read_biome_def(L, index, ndef);
+ if (!biome)
+ return 0;
- u32 id = bmgr->add(b);
- if (id == (u32)-1) {
- delete b;
+ ObjDefHandle handle = bmgr->add(biome);
+ if (handle == OBJDEF_INVALID_HANDLE) {
+ delete biome;
return 0;
}
- NodeResolveInfo *nri = new NodeResolveInfo(b);
- std::list<std::string> &nnames = nri->nodenames;
- nnames.push_back(getstringfield_default(L, index, "node_top", ""));
- nnames.push_back(getstringfield_default(L, index, "node_filler", ""));
- nnames.push_back(getstringfield_default(L, index, "node_shore_top", ""));
- nnames.push_back(getstringfield_default(L, index, "node_shore_filler", ""));
- nnames.push_back(getstringfield_default(L, index, "node_underwater", ""));
- nnames.push_back(getstringfield_default(L, index, "node_stone", ""));
- nnames.push_back(getstringfield_default(L, index, "node_water_top", ""));
- nnames.push_back(getstringfield_default(L, index, "node_water", ""));
- nnames.push_back(getstringfield_default(L, index, "node_dust", ""));
- ndef->pendNodeResolve(nri);
-
- verbosestream << "register_biome: " << b->name << std::endl;
-
- lua_pushinteger(L, id);
+ lua_pushinteger(L, handle);
return 1;
}
-int ModApiMapgen::l_clear_registered_biomes(lua_State *L)
-{
- BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
- bmgr->clear();
- return 0;
-}
-
-int ModApiMapgen::l_clear_registered_decorations(lua_State *L)
-{
- DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr;
- dmgr->clear();
- return 0;
-}
-
-int ModApiMapgen::l_clear_registered_ores(lua_State *L)
-{
- OreManager *omgr = getServer(L)->getEmergeManager()->oremgr;
- omgr->clear();
- return 0;
-}
// register_decoration({lots of stuff})
int ModApiMapgen::l_register_decoration(lua_State *L)
@@ -508,6 +735,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
DecorationManager *decomgr = getServer(L)->getEmergeManager()->decomgr;
BiomeManager *biomemgr = getServer(L)->getEmergeManager()->biomemgr;
+ SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
enum DecorationType decotype = (DecorationType)getenumfield(L, index,
"deco_type", es_DecorationType, -1);
@@ -515,7 +743,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
Decoration *deco = decomgr->create(decotype);
if (!deco) {
errorstream << "register_decoration: decoration placement type "
- << decotype << " not implemented";
+ << decotype << " not implemented" << std::endl;
return 0;
}
@@ -531,15 +759,11 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
return 0;
}
- NodeResolveInfo *nri = new NodeResolveInfo(deco);
-
//// Get node name(s) to place decoration on
- std::vector<const char *> place_on_names;
- getstringlistfield(L, index, "place_on", place_on_names);
- nri->nodelistinfo.push_back(NodeListInfo(place_on_names.size()));
- for (size_t i = 0; i != place_on_names.size(); i++)
- nri->nodenames.push_back(place_on_names[i]);
+ size_t nread = getstringlistfield(L, index, "place_on", &deco->m_nodenames);
+ deco->m_nnlistsizes.push_back(nread);
+ //// Get decoration flags
getflagsfield(L, index, "flags", flagdesc_deco, &deco->flags, NULL);
//// Get NoiseParams to define how decoration is placed
@@ -549,51 +773,45 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
lua_pop(L, 1);
//// Get biomes associated with this decoration (if any)
- std::vector<const char *> biome_list;
- getstringlistfield(L, index, "biomes", biome_list);
- for (size_t i = 0; i != biome_list.size(); i++) {
- Biome *b = (Biome *)biomemgr->getByName(biome_list[i]);
- if (!b)
- continue;
-
- deco->biomes.insert(b->id);
- }
+ lua_getfield(L, index, "biomes");
+ if (get_biome_list(L, -1, biomemgr, &deco->biomes))
+ errorstream << "register_decoration: couldn't get all biomes " << std::endl;
+ lua_pop(L, 1);
//// Handle decoration type-specific parameters
bool success = false;
switch (decotype) {
- case DECO_SIMPLE:
- success = regDecoSimple(L, nri, (DecoSimple *)deco);
- break;
- case DECO_SCHEMATIC:
- success = regDecoSchematic(L, ndef, (DecoSchematic *)deco);
- break;
- case DECO_LSYSTEM:
- break;
+ case DECO_SIMPLE:
+ success = read_deco_simple(L, (DecoSimple *)deco);
+ break;
+ case DECO_SCHEMATIC:
+ success = read_deco_schematic(L, schemmgr, (DecoSchematic *)deco);
+ break;
+ case DECO_LSYSTEM:
+ break;
}
- ndef->pendNodeResolve(nri);
-
if (!success) {
delete deco;
return 0;
}
- u32 id = decomgr->add(deco);
- if (id == (u32)-1) {
+ ndef->pendNodeResolve(deco);
+
+ ObjDefHandle handle = decomgr->add(deco);
+ if (handle == OBJDEF_INVALID_HANDLE) {
delete deco;
return 0;
}
- verbosestream << "register_decoration: " << deco->name << std::endl;
-
- lua_pushinteger(L, id);
+ lua_pushinteger(L, handle);
return 1;
}
-bool ModApiMapgen::regDecoSimple(lua_State *L,
- NodeResolveInfo *nri, DecoSimple *deco)
+
+bool read_deco_simple(lua_State *L, DecoSimple *deco)
{
+ size_t nnames;
int index = 1;
deco->deco_height = getintfield_default(L, index, "height", 1);
@@ -606,60 +824,48 @@ bool ModApiMapgen::regDecoSimple(lua_State *L,
return false;
}
- std::vector<const char *> deco_names;
- getstringlistfield(L, index, "decoration", deco_names);
- if (deco_names.size() == 0) {
+ nnames = getstringlistfield(L, index, "decoration", &deco->m_nodenames);
+ deco->m_nnlistsizes.push_back(nnames);
+ if (nnames == 0) {
errorstream << "register_decoration: no decoration nodes "
"defined" << std::endl;
return false;
}
- nri->nodelistinfo.push_back(NodeListInfo(deco_names.size()));
- for (size_t i = 0; i != deco_names.size(); i++)
- nri->nodenames.push_back(deco_names[i]);
- std::vector<const char *> spawnby_names;
- getstringlistfield(L, index, "spawn_by", spawnby_names);
- if (deco->nspawnby != -1 && spawnby_names.size() == 0) {
+ nnames = getstringlistfield(L, index, "spawn_by", &deco->m_nodenames);
+ deco->m_nnlistsizes.push_back(nnames);
+ if (nnames == 0 && deco->nspawnby != -1) {
errorstream << "register_decoration: no spawn_by nodes defined,"
" but num_spawn_by specified" << std::endl;
return false;
}
- nri->nodelistinfo.push_back(NodeListInfo(spawnby_names.size()));
- for (size_t i = 0; i != spawnby_names.size(); i++)
- nri->nodenames.push_back(spawnby_names[i]);
return true;
}
-bool ModApiMapgen::regDecoSchematic(lua_State *L, INodeDefManager *ndef,
- DecoSchematic *deco)
+
+bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco)
{
int index = 1;
deco->rotation = (Rotation)getenumfield(L, index, "rotation",
- es_Rotation, ROTATE_0);
+ ModApiMapgen::es_Rotation, ROTATE_0);
- std::map<std::string, std::string> replace_names;
+ StringMap replace_names;
lua_getfield(L, index, "replacements");
if (lua_istable(L, -1))
- read_schematic_replacements(L, replace_names, lua_gettop(L));
+ read_schematic_replacements(L, -1, &replace_names);
lua_pop(L, 1);
- // TODO(hmmmm): get a ref from registered schematics
- Schematic *schem = new Schematic;
lua_getfield(L, index, "schematic");
- if (!get_schematic(L, -1, schem, ndef, replace_names)) {
- lua_pop(L, 1);
- delete schem;
- return false;
- }
+ Schematic *schem = get_or_load_schematic(L, -1, schemmgr, &replace_names);
lua_pop(L, 1);
deco->schematic = schem;
-
- return true;
+ return schem != NULL;
}
+
// register_ore({lots of stuff})
int ModApiMapgen::l_register_ore(lua_State *L)
{
@@ -667,10 +873,11 @@ int ModApiMapgen::l_register_ore(lua_State *L)
luaL_checktype(L, index, LUA_TTABLE);
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
+ BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
OreManager *oremgr = getServer(L)->getEmergeManager()->oremgr;
enum OreType oretype = (OreType)getenumfield(L, index,
- "ore_type", es_OreType, ORE_TYPE_SCATTER);
+ "ore_type", es_OreType, ORE_SCATTER);
Ore *ore = oremgr->create(oretype);
if (!ore) {
errorstream << "register_ore: ore_type " << oretype << " not implemented";
@@ -686,6 +893,7 @@ int ModApiMapgen::l_register_ore(lua_State *L)
ore->noise = NULL;
ore->flags = 0;
+ //// Get y_min/y_max
warn_if_field_exists(L, index, "height_min",
"Deprecated: new name is \"y_min\".");
warn_if_field_exists(L, index, "height_max",
@@ -708,8 +916,16 @@ int ModApiMapgen::l_register_ore(lua_State *L)
return 0;
}
+ //// Get flags
getflagsfield(L, index, "flags", flagdesc_ore, &ore->flags, NULL);
+ //// Get biomes associated with this decoration (if any)
+ lua_getfield(L, index, "biomes");
+ if (get_biome_list(L, -1, bmgr, &ore->biomes))
+ errorstream << "register_ore: couldn't get all biomes " << std::endl;
+ lua_pop(L, 1);
+
+ //// Get noise parameters if needed
lua_getfield(L, index, "noise_params");
if (read_noiseparams(L, -1, &ore->np)) {
ore->flags |= OREFLAG_USE_NOISE;
@@ -721,45 +937,152 @@ int ModApiMapgen::l_register_ore(lua_State *L)
}
lua_pop(L, 1);
- if (oretype == ORE_TYPE_VEIN) {
+ if (oretype == ORE_VEIN) {
OreVein *orevein = (OreVein *)ore;
orevein->random_factor = getfloatfield_default(L, index,
"random_factor", 1.f);
}
- u32 id = oremgr->add(ore);
- if (id == (u32)-1) {
+ ObjDefHandle handle = oremgr->add(ore);
+ if (handle == OBJDEF_INVALID_HANDLE) {
delete ore;
return 0;
}
- NodeResolveInfo *nri = new NodeResolveInfo(ore);
- nri->nodenames.push_back(getstringfield_default(L, index, "ore", ""));
+ ore->m_nodenames.push_back(getstringfield_default(L, index, "ore", ""));
+
+ size_t nnames = getstringlistfield(L, index, "wherein", &ore->m_nodenames);
+ ore->m_nnlistsizes.push_back(nnames);
+
+ ndef->pendNodeResolve(ore);
+
+ lua_pushinteger(L, handle);
+ return 1;
+}
+
- std::vector<const char *> wherein_names;
- getstringlistfield(L, index, "wherein", wherein_names);
- nri->nodelistinfo.push_back(NodeListInfo(wherein_names.size()));
- for (size_t i = 0; i != wherein_names.size(); i++)
- nri->nodenames.push_back(wherein_names[i]);
+// register_schematic({schematic}, replacements={})
+int ModApiMapgen::l_register_schematic(lua_State *L)
+{
+ SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
+
+ StringMap replace_names;
+ if (lua_istable(L, 2))
+ read_schematic_replacements(L, 2, &replace_names);
- ndef->pendNodeResolve(nri);
+ Schematic *schem = load_schematic(L, 1, schemmgr->getNodeDef(),
+ &replace_names);
+ if (!schem)
+ return 0;
- verbosestream << "register_ore: " << ore->name << std::endl;
+ ObjDefHandle handle = schemmgr->add(schem);
+ if (handle == OBJDEF_INVALID_HANDLE) {
+ delete schem;
+ return 0;
+ }
- lua_pushinteger(L, id);
+ lua_pushinteger(L, handle);
return 1;
}
-// create_schematic(p1, p2, probability_list, filename)
+
+// clear_registered_biomes()
+int ModApiMapgen::l_clear_registered_biomes(lua_State *L)
+{
+ BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr;
+ bmgr->clear();
+ return 0;
+}
+
+
+// clear_registered_decorations()
+int ModApiMapgen::l_clear_registered_decorations(lua_State *L)
+{
+ DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr;
+ dmgr->clear();
+ return 0;
+}
+
+
+// clear_registered_ores()
+int ModApiMapgen::l_clear_registered_ores(lua_State *L)
+{
+ OreManager *omgr = getServer(L)->getEmergeManager()->oremgr;
+ omgr->clear();
+ return 0;
+}
+
+
+// clear_registered_schematics()
+int ModApiMapgen::l_clear_registered_schematics(lua_State *L)
+{
+ SchematicManager *smgr = getServer(L)->getEmergeManager()->schemmgr;
+ smgr->clear();
+ return 0;
+}
+
+
+// generate_ores(vm, p1, p2, [ore_id])
+int ModApiMapgen::l_generate_ores(lua_State *L)
+{
+ EmergeManager *emerge = getServer(L)->getEmergeManager();
+
+ Mapgen mg;
+ mg.seed = emerge->params.seed;
+ mg.vm = LuaVoxelManip::checkobject(L, 1)->vm;
+ mg.ndef = getServer(L)->getNodeDefManager();
+
+ v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) :
+ mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE;
+ v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) :
+ mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE;
+ sortBoxVerticies(pmin, pmax);
+
+ u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed);
+
+ emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax);
+
+ return 0;
+}
+
+
+// generate_decorations(vm, p1, p2, [deco_id])
+int ModApiMapgen::l_generate_decorations(lua_State *L)
+{
+ EmergeManager *emerge = getServer(L)->getEmergeManager();
+
+ Mapgen mg;
+ mg.seed = emerge->params.seed;
+ mg.vm = LuaVoxelManip::checkobject(L, 1)->vm;
+ mg.ndef = getServer(L)->getNodeDefManager();
+
+ v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) :
+ mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE;
+ v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) :
+ mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE;
+ sortBoxVerticies(pmin, pmax);
+
+ u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed);
+
+ emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax);
+
+ return 0;
+}
+
+
+// create_schematic(p1, p2, probability_list, filename, y_slice_prob_list)
int ModApiMapgen::l_create_schematic(lua_State *L)
{
- Schematic schem;
+ INodeDefManager *ndef = getServer(L)->getNodeDefManager();
+
+ const char *filename = luaL_checkstring(L, 4);
+ CHECK_SECURE_PATH_OPTIONAL(L, filename);
Map *map = &(getEnv(L)->getMap());
- INodeDefManager *ndef = getServer(L)->getNodeDefManager();
+ Schematic schem;
- v3s16 p1 = read_v3s16(L, 1);
- v3s16 p2 = read_v3s16(L, 2);
+ v3s16 p1 = check_v3s16(L, 1);
+ v3s16 p2 = check_v3s16(L, 2);
sortBoxVerticies(p1, p2);
std::vector<std::pair<v3s16, u8> > prob_list;
@@ -768,7 +1091,7 @@ int ModApiMapgen::l_create_schematic(lua_State *L)
while (lua_next(L, 3)) {
if (lua_istable(L, -1)) {
lua_getfield(L, -1, "pos");
- v3s16 pos = read_v3s16(L, -1);
+ v3s16 pos = check_v3s16(L, -1);
lua_pop(L, 1);
u8 prob = getintfield_default(L, -1, "prob", MTSCHEM_PROB_ALWAYS);
@@ -793,8 +1116,6 @@ int ModApiMapgen::l_create_schematic(lua_State *L)
}
}
- const char *filename = luaL_checkstring(L, 4);
-
if (!schem.getSchematicFromMap(map, p1, p2)) {
errorstream << "create_schematic: failed to get schematic "
"from map" << std::endl;
@@ -807,60 +1128,25 @@ int ModApiMapgen::l_create_schematic(lua_State *L)
actionstream << "create_schematic: saved schematic file '"
<< filename << "'." << std::endl;
+ lua_pushboolean(L, true);
return 1;
}
-// generate_ores(vm, [ore_id])
-int ModApiMapgen::l_generate_ores(lua_State *L)
-{
- EmergeManager *emerge = getServer(L)->getEmergeManager();
-
- Mapgen mg;
- mg.seed = emerge->params.seed;
- mg.vm = LuaVoxelManip::checkobject(L, 1)->vm;
- mg.ndef = getServer(L)->getNodeDefManager();
-
- u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed);
-
- emerge->oremgr->placeAllOres(&mg, blockseed,
- mg.vm->m_area.MinEdge, mg.vm->m_area.MaxEdge);
-
- return 0;
-}
-
-// generate_decorations(vm, [deco_id])
-int ModApiMapgen::l_generate_decorations(lua_State *L)
-{
- EmergeManager *emerge = getServer(L)->getEmergeManager();
-
- Mapgen mg;
- mg.seed = emerge->params.seed;
- mg.vm = LuaVoxelManip::checkobject(L, 1)->vm;
- mg.ndef = getServer(L)->getNodeDefManager();
-
- u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed);
-
- emerge->decomgr->placeAllDecos(&mg, blockseed,
- mg.vm->m_area.MinEdge, mg.vm->m_area.MaxEdge);
-
- return 0;
-}
// place_schematic(p, schematic, rotation, replacement)
int ModApiMapgen::l_place_schematic(lua_State *L)
{
- Schematic schem;
-
Map *map = &(getEnv(L)->getMap());
- INodeDefManager *ndef = getServer(L)->getNodeDefManager();
+ SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
//// Read position
- v3s16 p = read_v3s16(L, 1);
+ v3s16 p = check_v3s16(L, 1);
//// Read rotation
int rot = ROTATE_0;
- if (lua_isstring(L, 3))
- string_to_enum(es_Rotation, rot, std::string(lua_tostring(L, 3)));
+ const char *enumstr = lua_tostring(L, 3);
+ if (enumstr)
+ string_to_enum(es_Rotation, rot, std::string(enumstr));
//// Read force placement
bool force_placement = true;
@@ -868,21 +1154,73 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
force_placement = lua_toboolean(L, 5);
//// Read node replacements
- std::map<std::string, std::string> replace_names;
+ StringMap replace_names;
if (lua_istable(L, 4))
- read_schematic_replacements(L, replace_names, 4);
+ read_schematic_replacements(L, 4, &replace_names);
//// Read schematic
- if (!get_schematic(L, 2, &schem, ndef, replace_names)) {
+ Schematic *schem = get_or_load_schematic(L, 2, schemmgr, &replace_names);
+ if (!schem) {
errorstream << "place_schematic: failed to get schematic" << std::endl;
return 0;
}
- schem.placeStructure(map, p, 0, (Rotation)rot, force_placement, ndef);
+ schem->placeStructure(map, p, 0, (Rotation)rot, force_placement);
+
+ lua_pushboolean(L, true);
+ return 1;
+}
+
+// serialize_schematic(schematic, format, options={...})
+int ModApiMapgen::l_serialize_schematic(lua_State *L)
+{
+ SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
+
+ //// Read options
+ bool use_comments = getboolfield_default(L, 3, "lua_use_comments", false);
+ u32 indent_spaces = getintfield_default(L, 3, "lua_num_indent_spaces", 0);
+
+ //// Get schematic
+ bool was_loaded = false;
+ Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr);
+ if (!schem) {
+ schem = load_schematic(L, 1, NULL, NULL);
+ was_loaded = true;
+ }
+ if (!schem) {
+ errorstream << "serialize_schematic: failed to get schematic" << std::endl;
+ return 0;
+ }
+
+ //// Read format of definition to save as
+ int schem_format = SCHEM_FMT_MTS;
+ const char *enumstr = lua_tostring(L, 2);
+ if (enumstr)
+ string_to_enum(es_SchematicFormatType, schem_format, std::string(enumstr));
+
+ //// Serialize to binary string
+ std::ostringstream os(std::ios_base::binary);
+ switch (schem_format) {
+ case SCHEM_FMT_MTS:
+ schem->serializeToMts(&os, schem->m_nodenames);
+ break;
+ case SCHEM_FMT_LUA:
+ schem->serializeToLua(&os, schem->m_nodenames,
+ use_comments, indent_spaces);
+ break;
+ default:
+ return 0;
+ }
+
+ if (was_loaded)
+ delete schem;
+ std::string ser = os.str();
+ lua_pushlstring(L, ser.c_str(), ser.length());
return 1;
}
+
void ModApiMapgen::Initialize(lua_State *L, int top)
{
API_FCT(get_mapgen_object);
@@ -890,19 +1228,23 @@ void ModApiMapgen::Initialize(lua_State *L, int top)
API_FCT(get_mapgen_params);
API_FCT(set_mapgen_params);
API_FCT(set_noiseparams);
+ API_FCT(get_noiseparams);
API_FCT(set_gen_notify);
+ API_FCT(get_gen_notify);
API_FCT(register_biome);
API_FCT(register_decoration);
API_FCT(register_ore);
+ API_FCT(register_schematic);
API_FCT(clear_registered_biomes);
API_FCT(clear_registered_decorations);
API_FCT(clear_registered_ores);
+ API_FCT(clear_registered_schematics);
API_FCT(generate_ores);
API_FCT(generate_decorations);
-
API_FCT(create_schematic);
API_FCT(place_schematic);
+ API_FCT(serialize_schematic);
}
diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h
index e17d1b85a..7440d1285 100644
--- a/src/script/lua_api/l_mapgen.h
+++ b/src/script/lua_api/l_mapgen.h
@@ -22,11 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_base.h"
-class INodeDefManager;
-struct NodeResolveInfo;
-class DecoSimple;
-class DecoSchematic;
-
class ModApiMapgen : public ModApiBase {
private:
// get_mapgen_object(objectname)
@@ -44,9 +39,15 @@ private:
// set_noiseparam_defaults(name, noiseparams, set_default)
static int l_set_noiseparams(lua_State *L);
+ // get_noiseparam_defaults(name)
+ static int l_get_noiseparams(lua_State *L);
+
// set_gen_notify(flagstring)
static int l_set_gen_notify(lua_State *L);
+ // set_gen_notify(flagstring)
+ static int l_get_gen_notify(lua_State *L);
+
// register_biome({lots of stuff})
static int l_register_biome(lua_State *L);
@@ -56,16 +57,22 @@ private:
// register_ore({lots of stuff})
static int l_register_ore(lua_State *L);
+ // register_schematic({schematic}, replacements={})
+ static int l_register_schematic(lua_State *L);
+
// clear_registered_biomes()
static int l_clear_registered_biomes(lua_State *L);
// clear_registered_decorations()
static int l_clear_registered_decorations(lua_State *L);
- // generate_ores(vm)
+ // clear_registered_schematics()
+ static int l_clear_registered_schematics(lua_State *L);
+
+ // generate_ores(vm, p1, p2)
static int l_generate_ores(lua_State *L);
- // generate_decorations(vm)
+ // generate_decorations(vm, p1, p2)
static int l_generate_decorations(lua_State *L);
// clear_registered_ores
@@ -77,19 +84,19 @@ private:
// place_schematic(p, schematic, rotation, replacement)
static int l_place_schematic(lua_State *L);
- static bool regDecoSimple(lua_State *L,
- NodeResolveInfo *nri, DecoSimple *deco);
- static bool regDecoSchematic(lua_State *L,
- INodeDefManager *ndef, DecoSchematic *deco);
+ // serialize_schematic(schematic, format, options={...})
+ static int l_serialize_schematic(lua_State *L);
+
+public:
+ static void Initialize(lua_State *L, int top);
static struct EnumString es_BiomeTerrainType[];
static struct EnumString es_DecorationType[];
static struct EnumString es_MapgenObject[];
static struct EnumString es_OreType[];
static struct EnumString es_Rotation[];
-
-public:
- static void Initialize(lua_State *L, int top);
+ static struct EnumString es_SchematicFormatType[];
+ static struct EnumString es_NodeResolveMethod[];
};
#endif /* L_MAPGEN_H_ */
diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp
index 4f20e56f9..6cdbe5c68 100644
--- a/src/script/lua_api/l_nodemeta.cpp
+++ b/src/script/lua_api/l_nodemeta.cpp
@@ -63,9 +63,10 @@ void NodeMetaRef::reportMetadataChange(NodeMetaRef *ref)
ref->m_env->getMap().dispatchEvent(&event);
// Set the block to be saved
MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos);
- if(block)
+ if (block) {
block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "NodeMetaRef::reportMetadataChange");
+ MOD_REASON_REPORT_META_CHANGE);
+ }
}
// Exported functions
@@ -189,32 +190,34 @@ int NodeMetaRef::l_to_table(lua_State *L)
NodeMetaRef *ref = checkobject(L, 1);
NodeMetadata *meta = getmeta(ref, true);
- if(meta == NULL){
+ if (meta == NULL) {
lua_pushnil(L);
return 1;
}
lua_newtable(L);
+
// fields
lua_newtable(L);
{
- std::map<std::string, std::string> fields = meta->getStrings();
- for(std::map<std::string, std::string>::const_iterator
- i = fields.begin(); i != fields.end(); i++){
- const std::string &name = i->first;
- const std::string &value = i->second;
+ StringMap fields = meta->getStrings();
+ for (StringMap::const_iterator
+ it = fields.begin(); it != fields.end(); ++it) {
+ const std::string &name = it->first;
+ const std::string &value = it->second;
lua_pushlstring(L, name.c_str(), name.size());
lua_pushlstring(L, value.c_str(), value.size());
lua_settable(L, -3);
}
}
lua_setfield(L, -2, "fields");
+
// inventory
lua_newtable(L);
Inventory *inv = meta->getInventory();
- if(inv){
- std::vector<const InventoryList*> lists = inv->getLists();
- for(std::vector<const InventoryList*>::const_iterator
- i = lists.begin(); i != lists.end(); i++){
+ if (inv) {
+ std::vector<const InventoryList *> lists = inv->getLists();
+ for(std::vector<const InventoryList *>::const_iterator
+ i = lists.begin(); i != lists.end(); i++) {
push_inventory_list(L, inv, (*i)->getName().c_str());
lua_setfield(L, -2, (*i)->getName().c_str());
}
diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp
index 5a82b6485..c8dc2d2dc 100644
--- a/src/script/lua_api/l_noise.cpp
+++ b/src/script/lua_api/l_noise.cpp
@@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common/c_content.h"
#include "log.h"
-// garbage collector
-int LuaPerlinNoise::gc_object(lua_State *L)
+///////////////////////////////////////
+/*
+ LuaPerlinNoise
+*/
+
+LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
+ np(*params)
+{
+}
+
+
+LuaPerlinNoise::~LuaPerlinNoise()
{
- LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
- delete o;
- return 0;
}
@@ -36,7 +43,7 @@ int LuaPerlinNoise::l_get2d(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaPerlinNoise *o = checkobject(L, 1);
- v2f p = read_v2f(L, 2);
+ v2f p = check_v2f(L, 2);
lua_Number val = NoisePerlin2D(&o->np, p.X, p.Y, 0);
lua_pushnumber(L, val);
return 1;
@@ -47,26 +54,13 @@ int LuaPerlinNoise::l_get3d(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaPerlinNoise *o = checkobject(L, 1);
- v3f p = read_v3f(L, 2);
+ v3f p = check_v3f(L, 2);
lua_Number val = NoisePerlin3D(&o->np, p.X, p.Y, p.Z, 0);
lua_pushnumber(L, val);
return 1;
}
-LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) :
- np(*params)
-{
-}
-
-
-LuaPerlinNoise::~LuaPerlinNoise()
-{
-}
-
-
-// LuaPerlinNoise(seed, octaves, persistence, scale)
-// Creates an LuaPerlinNoise and leaves it on top of stack
int LuaPerlinNoise::create_object(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
@@ -91,14 +85,22 @@ int LuaPerlinNoise::create_object(lua_State *L)
}
-LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg)
+int LuaPerlinNoise::gc_object(lua_State *L)
+{
+ LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+
+LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg)
{
NO_MAP_LOCK_REQUIRED;
luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className);
if (!ud)
luaL_typerror(L, narg, className);
- return *(LuaPerlinNoise**)ud; // unbox pointer
+ return *(LuaPerlinNoise **)ud;
}
@@ -111,7 +113,7 @@ void LuaPerlinNoise::Register(lua_State *L)
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable);
- lua_settable(L, metatable); // hide metatable from Lua getmetatable()
+ lua_settable(L, metatable);
lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable);
@@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L)
lua_pushcfunction(L, gc_object);
lua_settable(L, metatable);
- lua_pop(L, 1); // drop metatable
+ lua_pop(L, 1);
- luaL_openlib(L, 0, methods, 0); // fill methodtable
- lua_pop(L, 1); // drop methodtable
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
- // Can be created from Lua (PerlinNoise(seed, octaves, persistence)
lua_register(L, className, create_object);
}
@@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = {
{0,0}
};
-
+///////////////////////////////////////
/*
- PerlinNoiseMap
- */
+ LuaPerlinNoiseMap
+*/
-int LuaPerlinNoiseMap::gc_object(lua_State *L)
+LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
{
- LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
- delete o;
- return 0;
+ m_is3d = size.Z > 1;
+ np = *params;
+ try {
+ noise = new Noise(&np, seed, size.X, size.Y, size.Z);
+ } catch (InvalidNoiseParamsException &e) {
+ throw LuaError(e.what());
+ }
+}
+
+
+LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
+{
+ delete noise;
}
@@ -157,15 +168,15 @@ int LuaPerlinNoiseMap::l_get2dMap(lua_State *L)
size_t i = 0;
LuaPerlinNoiseMap *o = checkobject(L, 1);
- v2f p = read_v2f(L, 2);
+ v2f p = check_v2f(L, 2);
Noise *n = o->noise;
n->perlinMap2D(p.X, p.Y);
lua_newtable(L);
- for (int y = 0; y != n->sy; y++) {
+ for (u32 y = 0; y != n->sy; y++) {
lua_newtable(L);
- for (int x = 0; x != n->sx; x++) {
+ for (u32 x = 0; x != n->sx; x++) {
lua_pushnumber(L, n->result[i++]);
lua_rawseti(L, -2, x + 1);
}
@@ -180,14 +191,19 @@ int LuaPerlinNoiseMap::l_get2dMap_flat(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaPerlinNoiseMap *o = checkobject(L, 1);
- v2f p = read_v2f(L, 2);
+ v2f p = check_v2f(L, 2);
+ bool use_buffer = lua_istable(L, 3);
Noise *n = o->noise;
n->perlinMap2D(p.X, p.Y);
size_t maplen = n->sx * n->sy;
- lua_newtable(L);
+ if (use_buffer)
+ lua_pushvalue(L, 3);
+ else
+ lua_newtable(L);
+
for (size_t i = 0; i != maplen; i++) {
lua_pushnumber(L, n->result[i]);
lua_rawseti(L, -2, i + 1);
@@ -202,7 +218,7 @@ int LuaPerlinNoiseMap::l_get3dMap(lua_State *L)
size_t i = 0;
LuaPerlinNoiseMap *o = checkobject(L, 1);
- v3f p = read_v3f(L, 2);
+ v3f p = check_v3f(L, 2);
if (!o->m_is3d)
return 0;
@@ -211,11 +227,11 @@ int LuaPerlinNoiseMap::l_get3dMap(lua_State *L)
n->perlinMap3D(p.X, p.Y, p.Z);
lua_newtable(L);
- for (int z = 0; z != n->sz; z++) {
+ for (u32 z = 0; z != n->sz; z++) {
lua_newtable(L);
- for (int y = 0; y != n->sy; y++) {
+ for (u32 y = 0; y != n->sy; y++) {
lua_newtable(L);
- for (int x = 0; x != n->sx; x++) {
+ for (u32 x = 0; x != n->sx; x++) {
lua_pushnumber(L, n->result[i++]);
lua_rawseti(L, -2, x + 1);
}
@@ -232,7 +248,8 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaPerlinNoiseMap *o = checkobject(L, 1);
- v3f p = read_v3f(L, 2);
+ v3f p = check_v3f(L, 2);
+ bool use_buffer = lua_istable(L, 3);
if (!o->m_is3d)
return 0;
@@ -242,7 +259,11 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L)
size_t maplen = n->sx * n->sy * n->sz;
- lua_newtable(L);
+ if (use_buffer)
+ lua_pushvalue(L, 3);
+ else
+ lua_newtable(L);
+
for (size_t i = 0; i != maplen; i++) {
lua_pushnumber(L, n->result[i]);
lua_rawseti(L, -2, i + 1);
@@ -251,26 +272,61 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L)
}
-LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size)
+int LuaPerlinNoiseMap::l_calc2dMap(lua_State *L)
{
- m_is3d = size.Z > 1;
- np = *params;
- try {
- noise = new Noise(&np, seed, size.X, size.Y, size.Z);
- } catch (InvalidNoiseParamsException &e) {
- throw LuaError(e.what());
- }
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaPerlinNoiseMap *o = checkobject(L, 1);
+ v2f p = check_v2f(L, 2);
+
+ Noise *n = o->noise;
+ n->perlinMap2D(p.X, p.Y);
+
+ return 0;
}
+int LuaPerlinNoiseMap::l_calc3dMap(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
-LuaPerlinNoiseMap::~LuaPerlinNoiseMap()
+ LuaPerlinNoiseMap *o = checkobject(L, 1);
+ v3f p = check_v3f(L, 2);
+
+ if (!o->m_is3d)
+ return 0;
+
+ Noise *n = o->noise;
+ n->perlinMap3D(p.X, p.Y, p.Z);
+
+ return 0;
+}
+
+
+int LuaPerlinNoiseMap::l_getMapSlice(lua_State *L)
{
- delete noise;
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaPerlinNoiseMap *o = checkobject(L, 1);
+ v3s16 slice_offset = read_v3s16(L, 2);
+ v3s16 slice_size = read_v3s16(L, 3);
+ bool use_buffer = lua_istable(L, 4);
+
+ Noise *n = o->noise;
+
+ if (use_buffer)
+ lua_pushvalue(L, 3);
+ else
+ lua_newtable(L);
+
+ write_array_slice_float(L, lua_gettop(L), n->result,
+ v3u16(n->sx, n->sy, n->sz),
+ v3u16(slice_offset.X, slice_offset.Y, slice_offset.Z),
+ v3u16(slice_size.X, slice_size.Y, slice_size.Z));
+
+ return 1;
}
-// LuaPerlinNoiseMap(np, size)
-// Creates an LuaPerlinNoiseMap and leaves it on top of stack
int LuaPerlinNoiseMap::create_object(lua_State *L)
{
NoiseParams np;
@@ -286,6 +342,14 @@ int LuaPerlinNoiseMap::create_object(lua_State *L)
}
+int LuaPerlinNoiseMap::gc_object(lua_State *L)
+{
+ LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+
LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
{
luaL_checktype(L, narg, LUA_TUSERDATA);
@@ -294,7 +358,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg)
if (!ud)
luaL_typerror(L, narg, className);
- return *(LuaPerlinNoiseMap **)ud; // unbox pointer
+ return *(LuaPerlinNoiseMap **)ud;
}
@@ -307,7 +371,7 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable);
- lua_settable(L, metatable); // hide metatable from Lua getmetatable()
+ lua_settable(L, metatable);
lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable);
@@ -317,12 +381,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L)
lua_pushcfunction(L, gc_object);
lua_settable(L, metatable);
- lua_pop(L, 1); // drop metatable
+ lua_pop(L, 1);
- luaL_openlib(L, 0, methods, 0); // fill methodtable
- lua_pop(L, 1); // drop methodtable
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
- // Can be created from Lua (PerlinNoiseMap(np, size)
lua_register(L, className, create_object);
}
@@ -331,37 +394,31 @@ const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap";
const luaL_reg LuaPerlinNoiseMap::methods[] = {
luamethod(LuaPerlinNoiseMap, get2dMap),
luamethod(LuaPerlinNoiseMap, get2dMap_flat),
+ luamethod(LuaPerlinNoiseMap, calc2dMap),
luamethod(LuaPerlinNoiseMap, get3dMap),
luamethod(LuaPerlinNoiseMap, get3dMap_flat),
+ luamethod(LuaPerlinNoiseMap, calc3dMap),
+ luamethod(LuaPerlinNoiseMap, getMapSlice),
{0,0}
};
+///////////////////////////////////////
/*
LuaPseudoRandom
*/
-// garbage collector
-int LuaPseudoRandom::gc_object(lua_State *L)
-{
- LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
- delete o;
- return 0;
-}
-
-
-// next(self, min=0, max=32767) -> get next value
int LuaPseudoRandom::l_next(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
LuaPseudoRandom *o = checkobject(L, 1);
int min = 0;
int max = 32767;
- lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist
- if(!lua_isnil(L, 2))
+ lua_settop(L, 3);
+ if (lua_isnumber(L, 2))
min = luaL_checkinteger(L, 2);
- if(!lua_isnil(L, 3))
+ if (lua_isnumber(L, 3))
max = luaL_checkinteger(L, 3);
- if(max < min){
+ if (max < min) {
errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl;
throw LuaError("PseudoRandom.next(): max < min");
}
@@ -378,34 +435,107 @@ int LuaPseudoRandom::l_next(lua_State *L)
}
-LuaPseudoRandom::LuaPseudoRandom(int seed):
- m_pseudo(seed)
+int LuaPseudoRandom::create_object(lua_State *L)
{
+ int seed = luaL_checknumber(L, 1);
+ LuaPseudoRandom *o = new LuaPseudoRandom(seed);
+ *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+ luaL_getmetatable(L, className);
+ lua_setmetatable(L, -2);
+ return 1;
}
-LuaPseudoRandom::~LuaPseudoRandom()
+int LuaPseudoRandom::gc_object(lua_State *L)
{
+ LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
}
-const PseudoRandom& LuaPseudoRandom::getItem() const
+LuaPseudoRandom *LuaPseudoRandom::checkobject(lua_State *L, int narg)
{
- return m_pseudo;
+ luaL_checktype(L, narg, LUA_TUSERDATA);
+ void *ud = luaL_checkudata(L, narg, className);
+ if (!ud)
+ luaL_typerror(L, narg, className);
+ return *(LuaPseudoRandom **)ud;
}
-PseudoRandom& LuaPseudoRandom::getItem()
+
+void LuaPseudoRandom::Register(lua_State *L)
{
- return m_pseudo;
+ lua_newtable(L);
+ int methodtable = lua_gettop(L);
+ luaL_newmetatable(L, className);
+ int metatable = lua_gettop(L);
+
+ lua_pushliteral(L, "__metatable");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, methodtable);
+ lua_settable(L, metatable);
+
+ lua_pushliteral(L, "__gc");
+ lua_pushcfunction(L, gc_object);
+ lua_settable(L, metatable);
+
+ lua_pop(L, 1);
+
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
+
+ lua_register(L, className, create_object);
}
-// LuaPseudoRandom(seed)
-// Creates an LuaPseudoRandom and leaves it on top of stack
-int LuaPseudoRandom::create_object(lua_State *L)
+const char LuaPseudoRandom::className[] = "PseudoRandom";
+const luaL_reg LuaPseudoRandom::methods[] = {
+ luamethod(LuaPseudoRandom, next),
+ {0,0}
+};
+
+///////////////////////////////////////
+/*
+ LuaPcgRandom
+*/
+
+int LuaPcgRandom::l_next(lua_State *L)
{
- int seed = luaL_checknumber(L, 1);
- LuaPseudoRandom *o = new LuaPseudoRandom(seed);
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaPcgRandom *o = checkobject(L, 1);
+ u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
+ u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
+
+ lua_pushinteger(L, o->m_rnd.range(min, max));
+ return 1;
+}
+
+
+int LuaPcgRandom::l_rand_normal_dist(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+
+ LuaPcgRandom *o = checkobject(L, 1);
+ u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN;
+ u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX;
+ int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6;
+
+ lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials));
+ return 1;
+}
+
+
+int LuaPcgRandom::create_object(lua_State *L)
+{
+ lua_Integer seed = luaL_checknumber(L, 1);
+ LuaPcgRandom *o = lua_isnumber(L, 2) ?
+ new LuaPcgRandom(seed, lua_tointeger(L, 2)) :
+ new LuaPcgRandom(seed);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
luaL_getmetatable(L, className);
lua_setmetatable(L, -2);
@@ -413,17 +543,25 @@ int LuaPseudoRandom::create_object(lua_State *L)
}
-LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg)
+int LuaPcgRandom::gc_object(lua_State *L)
+{
+ LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1));
+ delete o;
+ return 0;
+}
+
+
+LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg)
{
luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className);
if (!ud)
luaL_typerror(L, narg, className);
- return *(LuaPseudoRandom**)ud; // unbox pointer
+ return *(LuaPcgRandom **)ud;
}
-void LuaPseudoRandom::Register(lua_State *L)
+void LuaPcgRandom::Register(lua_State *L)
{
lua_newtable(L);
int methodtable = lua_gettop(L);
@@ -432,7 +570,7 @@ void LuaPseudoRandom::Register(lua_State *L)
lua_pushliteral(L, "__metatable");
lua_pushvalue(L, methodtable);
- lua_settable(L, metatable); // hide metatable from Lua getmetatable()
+ lua_settable(L, metatable);
lua_pushliteral(L, "__index");
lua_pushvalue(L, methodtable);
@@ -442,18 +580,18 @@ void LuaPseudoRandom::Register(lua_State *L)
lua_pushcfunction(L, gc_object);
lua_settable(L, metatable);
- lua_pop(L, 1); // drop metatable
+ lua_pop(L, 1);
- luaL_openlib(L, 0, methods, 0); // fill methodtable
- lua_pop(L, 1); // drop methodtable
+ luaL_openlib(L, 0, methods, 0);
+ lua_pop(L, 1);
- // Can be created from Lua (LuaPseudoRandom(seed))
lua_register(L, className, create_object);
}
-const char LuaPseudoRandom::className[] = "PseudoRandom";
-const luaL_reg LuaPseudoRandom::methods[] = {
- luamethod(LuaPseudoRandom, next),
+const char LuaPcgRandom::className[] = "PcgRandom";
+const luaL_reg LuaPcgRandom::methods[] = {
+ luamethod(LuaPcgRandom, next),
+ luamethod(LuaPcgRandom, rand_normal_dist),
{0,0}
};
diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h
index 3e22ac7a0..e958c5a23 100644
--- a/src/script/lua_api/l_noise.h
+++ b/src/script/lua_api/l_noise.h
@@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase {
static const char className[];
static const luaL_reg methods[];
+ // Exported functions
+
+ // garbage collector
static int gc_object(lua_State *L);
static int l_get2dMap(lua_State *L);
@@ -71,6 +74,10 @@ class LuaPerlinNoiseMap : public ModApiBase {
static int l_get3dMap(lua_State *L);
static int l_get3dMap_flat(lua_State *L);
+ static int l_calc2dMap(lua_State *L);
+ static int l_calc3dMap(lua_State *L);
+ static int l_getMapSlice(lua_State *L);
+
public:
LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size);
@@ -104,18 +111,51 @@ private:
static int l_next(lua_State *L);
public:
- LuaPseudoRandom(int seed);
-
- ~LuaPseudoRandom();
-
- const PseudoRandom& getItem() const;
- PseudoRandom& getItem();
+ LuaPseudoRandom(int seed) :
+ m_pseudo(seed) {}
// LuaPseudoRandom(seed)
// Creates an LuaPseudoRandom and leaves it on top of stack
static int create_object(lua_State *L);
- static LuaPseudoRandom* checkobject(lua_State *L, int narg);
+ static LuaPseudoRandom *checkobject(lua_State *L, int narg);
+
+ static void Register(lua_State *L);
+};
+
+/*
+ LuaPcgRandom
+*/
+class LuaPcgRandom : public ModApiBase {
+private:
+ PcgRandom m_rnd;
+
+ static const char className[];
+ static const luaL_reg methods[];
+
+ // Exported functions
+
+ // garbage collector
+ static int gc_object(lua_State *L);
+
+ // next(self, min=-2147483648, max=2147483647) -> get next value
+ static int l_next(lua_State *L);
+
+ // rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) ->
+ // get next normally distributed random value
+ static int l_rand_normal_dist(lua_State *L);
+
+public:
+ LuaPcgRandom(u64 seed) :
+ m_rnd(seed) {}
+ LuaPcgRandom(u64 seed, u64 seq) :
+ m_rnd(seed, seq) {}
+
+ // LuaPcgRandom(seed)
+ // Creates an LuaPcgRandom and leaves it on top of stack
+ static int create_object(lua_State *L);
+
+ static LuaPcgRandom *checkobject(lua_State *L, int narg);
static void Register(lua_State *L);
};
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index 4286840fe..3ac8eeefb 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -26,11 +26,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "tool.h"
#include "serverobject.h"
-#include "content_object.h"
#include "content_sao.h"
#include "server.h"
#include "hud.h"
+#include "scripting_game.h"
+#define GET_ENV_PTR ServerEnvironment* env = \
+ dynamic_cast<ServerEnvironment*>(getEnv(L)); \
+ if (env == NULL) return 0
struct EnumString es_HudElementType[] =
{
@@ -65,6 +68,7 @@ struct EnumString es_HudBuiltinElement[] =
{HUD_FLAG_CROSSHAIR_VISIBLE, "crosshair"},
{HUD_FLAG_WIELDITEM_VISIBLE, "wielditem"},
{HUD_FLAG_BREATHBAR_VISIBLE, "breathbar"},
+ {HUD_FLAG_MINIMAP_VISIBLE, "minimap"},
{0, NULL},
};
@@ -77,7 +81,7 @@ ObjectRef* ObjectRef::checkobject(lua_State *L, int narg)
{
luaL_checktype(L, narg, LUA_TUSERDATA);
void *ud = luaL_checkudata(L, narg, className);
- if(!ud) luaL_typerror(L, narg, className);
+ if (!ud) luaL_typerror(L, narg, className);
return *(ObjectRef**)ud; // unbox pointer
}
@@ -90,9 +94,9 @@ ServerActiveObject* ObjectRef::getobject(ObjectRef *ref)
LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref)
{
ServerActiveObject *obj = getobject(ref);
- if(obj == NULL)
+ if (obj == NULL)
return NULL;
- if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
+ if (obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY)
return NULL;
return (LuaEntitySAO*)obj;
}
@@ -100,9 +104,9 @@ LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref)
PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref)
{
ServerActiveObject *obj = getobject(ref);
- if(obj == NULL)
+ if (obj == NULL)
return NULL;
- if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER)
+ if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER)
return NULL;
return (PlayerSAO*)obj;
}
@@ -110,7 +114,7 @@ PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref)
Player* ObjectRef::getplayer(ObjectRef *ref)
{
PlayerSAO *playersao = getplayersao(ref);
- if(playersao == NULL)
+ if (playersao == NULL)
return NULL;
return playersao->getPlayer();
}
@@ -129,9 +133,22 @@ int ObjectRef::gc_object(lua_State *L) {
int ObjectRef::l_remove(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
+ GET_ENV_PTR;
+
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL)
+ return 0;
+ if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ return 0;
+
+ std::set<int> child_ids = co->getAttachmentChildIds();
+ std::set<int>::iterator it;
+ for (it = child_ids.begin(); it != child_ids.end(); ++it) {
+ ServerActiveObject *child = env->getActiveObject(*it);
+ child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
+ }
+
verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl;
co->m_removed = true;
return 0;
@@ -144,7 +161,7 @@ int ObjectRef::l_getpos(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
v3f pos = co->getBasePosition() / BS;
lua_newtable(L);
lua_pushnumber(L, pos.X);
@@ -163,7 +180,7 @@ int ObjectRef::l_setpos(lua_State *L)
ObjectRef *ref = checkobject(L, 1);
//LuaEntitySAO *co = getluaobject(ref);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// pos
v3f pos = checkFloatPos(L, 2);
// Do it
@@ -178,7 +195,7 @@ int ObjectRef::l_moveto(lua_State *L)
ObjectRef *ref = checkobject(L, 1);
//LuaEntitySAO *co = getluaobject(ref);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// pos
v3f pos = checkFloatPos(L, 2);
// continuous
@@ -196,20 +213,36 @@ int ObjectRef::l_punch(lua_State *L)
ObjectRef *puncher_ref = checkobject(L, 2);
ServerActiveObject *co = getobject(ref);
ServerActiveObject *puncher = getobject(puncher_ref);
- if(co == NULL) return 0;
- if(puncher == NULL) return 0;
+ if (co == NULL) return 0;
+ if (puncher == NULL) return 0;
v3f dir;
- if(lua_type(L, 5) != LUA_TTABLE)
+ if (lua_type(L, 5) != LUA_TTABLE)
dir = co->getBasePosition() - puncher->getBasePosition();
else
dir = read_v3f(L, 5);
float time_from_last_punch = 1000000;
- if(lua_isnumber(L, 3))
+ if (lua_isnumber(L, 3))
time_from_last_punch = lua_tonumber(L, 3);
ToolCapabilities toolcap = read_tool_capabilities(L, 4);
dir.normalize();
+
+ s16 src_original_hp = co->getHP();
+ s16 dst_origin_hp = puncher->getHP();
+
// Do it
co->punch(dir, &toolcap, puncher, time_from_last_punch);
+
+ // If the punched is a player, and its HP changed
+ if (src_original_hp != co->getHP() &&
+ co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co);
+ }
+
+ // If the puncher is a player, and its HP changed
+ if (dst_origin_hp != puncher->getHP() &&
+ puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ getServer(L)->SendPlayerHPOrDie((PlayerSAO *)puncher);
+ }
return 0;
}
@@ -221,8 +254,8 @@ int ObjectRef::l_right_click(lua_State *L)
ObjectRef *ref2 = checkobject(L, 2);
ServerActiveObject *co = getobject(ref);
ServerActiveObject *co2 = getobject(ref2);
- if(co == NULL) return 0;
- if(co2 == NULL) return 0;
+ if (co == NULL) return 0;
+ if (co2 == NULL) return 0;
// Do it
co->rightClick(co2);
return 0;
@@ -237,12 +270,15 @@ int ObjectRef::l_set_hp(lua_State *L)
ObjectRef *ref = checkobject(L, 1);
luaL_checknumber(L, 2);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
int hp = lua_tonumber(L, 2);
/*infostream<<"ObjectRef::l_set_hp(): id="<<co->getId()
<<" hp="<<hp<<std::endl;*/
// Do it
co->setHP(hp);
+ if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ getServer(L)->SendPlayerHPOrDie((PlayerSAO *)co);
+
// Return
return 0;
}
@@ -255,7 +291,7 @@ int ObjectRef::l_get_hp(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL){
+ if (co == NULL) {
// Default hp is 1
lua_pushnumber(L, 1);
return 1;
@@ -274,10 +310,10 @@ int ObjectRef::l_get_inventory(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
InventoryLocation loc = co->getInventoryLocation();
- if(getServer(L)->getInventory(loc) != NULL)
+ if (getServer(L)->getInventory(loc) != NULL)
InvRef::create(L, loc);
else
lua_pushnil(L); // An object may have no inventory (nil)
@@ -290,7 +326,7 @@ int ObjectRef::l_get_wield_list(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
lua_pushstring(L, co->getWieldList().c_str());
return 1;
@@ -302,7 +338,7 @@ int ObjectRef::l_get_wield_index(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
lua_pushinteger(L, co->getWieldIndex() + 1);
return 1;
@@ -314,7 +350,7 @@ int ObjectRef::l_get_wielded_item(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL){
+ if (co == NULL) {
// Empty ItemStack
LuaItemStack::create(L, ItemStack());
return 1;
@@ -330,10 +366,13 @@ int ObjectRef::l_set_wielded_item(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
ItemStack item = read_item(L, 2, getServer(L));
bool success = co->setWieldedItem(item);
+ if (success && co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+ getServer(L)->SendInventory(((PlayerSAO*)co));
+ }
lua_pushboolean(L, success);
return 1;
}
@@ -344,7 +383,7 @@ int ObjectRef::l_set_armor_groups(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
ItemGroupList groups;
read_groups(L, 2, groups);
@@ -352,13 +391,27 @@ int ObjectRef::l_set_armor_groups(lua_State *L)
return 0;
}
+// get_armor_groups(self)
+int ObjectRef::l_get_armor_groups(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ ServerActiveObject *co = getobject(ref);
+ if (co == NULL)
+ return 0;
+ // Do it
+ ItemGroupList groups = co->getArmorGroups();
+ push_groups(L, groups);
+ return 1;
+}
+
// set_physics_override(self, physics_override_speed, physics_override_jump,
// physics_override_gravity, sneak, sneak_glitch)
int ObjectRef::l_set_physics_override(lua_State *L)
{
ObjectRef *ref = checkobject(L, 1);
PlayerSAO *co = (PlayerSAO *) getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
if (lua_istable(L, 2)) {
co->m_physics_override_speed = getfloatfield_default(L, 2, "speed", co->m_physics_override_speed);
@@ -369,15 +422,15 @@ int ObjectRef::l_set_physics_override(lua_State *L)
co->m_physics_override_sent = false;
} else {
// old, non-table format
- if(!lua_isnil(L, 2)){
+ if (!lua_isnil(L, 2)) {
co->m_physics_override_speed = lua_tonumber(L, 2);
co->m_physics_override_sent = false;
}
- if(!lua_isnil(L, 3)){
+ if (!lua_isnil(L, 3)) {
co->m_physics_override_jump = lua_tonumber(L, 3);
co->m_physics_override_sent = false;
}
- if(!lua_isnil(L, 4)){
+ if (!lua_isnil(L, 4)) {
co->m_physics_override_gravity = lua_tonumber(L, 4);
co->m_physics_override_sent = false;
}
@@ -385,27 +438,74 @@ int ObjectRef::l_set_physics_override(lua_State *L)
return 0;
}
-// set_animation(self, frame_range, frame_speed, frame_blend)
+// get_physics_override(self)
+int ObjectRef::l_get_physics_override(lua_State *L)
+{
+ ObjectRef *ref = checkobject(L, 1);
+ PlayerSAO *co = (PlayerSAO *)getobject(ref);
+ if (co == NULL)
+ return 0;
+ // Do it
+ lua_newtable(L);
+ lua_pushnumber(L, co->m_physics_override_speed);
+ lua_setfield(L, -2, "speed");
+ lua_pushnumber(L, co->m_physics_override_jump);
+ lua_setfield(L, -2, "jump");
+ lua_pushnumber(L, co->m_physics_override_gravity);
+ lua_setfield(L, -2, "gravity");
+ lua_pushboolean(L, co->m_physics_override_sneak);
+ lua_setfield(L, -2, "sneak");
+ lua_pushboolean(L, co->m_physics_override_sneak_glitch);
+ lua_setfield(L, -2, "sneak_glitch");
+ return 1;
+}
+
+// set_animation(self, frame_range, frame_speed, frame_blend, frame_loop)
int ObjectRef::l_set_animation(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
v2f frames = v2f(1, 1);
- if(!lua_isnil(L, 2))
+ if (!lua_isnil(L, 2))
frames = read_v2f(L, 2);
float frame_speed = 15;
- if(!lua_isnil(L, 3))
+ if (!lua_isnil(L, 3))
frame_speed = lua_tonumber(L, 3);
float frame_blend = 0;
- if(!lua_isnil(L, 4))
+ if (!lua_isnil(L, 4))
frame_blend = lua_tonumber(L, 4);
- co->setAnimation(frames, frame_speed, frame_blend);
+ bool frame_loop = true;
+ if (lua_isboolean(L, 5))
+ frame_loop = lua_toboolean(L, 5);
+ co->setAnimation(frames, frame_speed, frame_blend, frame_loop);
return 0;
}
+// get_animation(self)
+int ObjectRef::l_get_animation(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ ServerActiveObject *co = getobject(ref);
+ if (co == NULL)
+ return 0;
+ // Do it
+ v2f frames = v2f(1,1);
+ float frame_speed = 15;
+ float frame_blend = 0;
+ bool frame_loop = true;
+ co->getAnimation(&frames, &frame_speed, &frame_blend, &frame_loop);
+
+ push_v2f(L, frames);
+ lua_pushnumber(L, frame_speed);
+ lua_pushnumber(L, frame_blend);
+ lua_pushboolean(L, frame_loop);
+ return 4;
+}
+
// set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed)
int ObjectRef::l_set_local_animation(lua_State *L)
{
@@ -417,11 +517,11 @@ int ObjectRef::l_set_local_animation(lua_State *L)
// Do it
v2s32 frames[4];
for (int i=0;i<4;i++) {
- if(!lua_isnil(L, 2+1))
+ if (!lua_isnil(L, 2+1))
frames[i] = read_v2s32(L, 2+i);
}
float frame_speed = 30;
- if(!lua_isnil(L, 6))
+ if (!lua_isnil(L, 6))
frame_speed = lua_tonumber(L, 6);
if (!getServer(L)->setLocalPlayerAnimations(player, frames, frame_speed))
@@ -431,6 +531,27 @@ int ObjectRef::l_set_local_animation(lua_State *L)
return 0;
}
+// get_local_animation(self)
+int ObjectRef::l_get_local_animation(lua_State *L)
+{
+ //NO_MAP_LOCK_REQUIRED
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+
+ v2s32 frames[4];
+ float frame_speed;
+ player->getLocalAnimations(frames, &frame_speed);
+
+ for (int i = 0; i < 4; i++) {
+ push_v2s32(L, frames[i]);
+ }
+
+ lua_pushnumber(L, frame_speed);
+ return 5;
+}
+
// set_eye_offset(self, v3f first pv, v3f third pv)
int ObjectRef::l_set_eye_offset(lua_State *L)
{
@@ -443,9 +564,9 @@ int ObjectRef::l_set_eye_offset(lua_State *L)
v3f offset_first = v3f(0, 0, 0);
v3f offset_third = v3f(0, 0, 0);
- if(!lua_isnil(L, 2))
+ if (!lua_isnil(L, 2))
offset_first = read_v3f(L, 2);
- if(!lua_isnil(L, 3))
+ if (!lua_isnil(L, 3))
offset_third = read_v3f(L, 3);
// Prevent abuse of offset values (keep player always visible)
@@ -461,60 +582,154 @@ int ObjectRef::l_set_eye_offset(lua_State *L)
return 0;
}
+// get_eye_offset(self)
+int ObjectRef::l_get_eye_offset(lua_State *L)
+{
+ //NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+ // Do it
+ push_v3f(L, player->eye_offset_first);
+ push_v3f(L, player->eye_offset_third);
+ return 2;
+}
+
// set_bone_position(self, std::string bone, v3f position, v3f rotation)
int ObjectRef::l_set_bone_position(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
std::string bone = "";
- if(!lua_isnil(L, 2))
+ if (!lua_isnil(L, 2))
bone = lua_tostring(L, 2);
v3f position = v3f(0, 0, 0);
- if(!lua_isnil(L, 3))
+ if (!lua_isnil(L, 3))
position = read_v3f(L, 3);
v3f rotation = v3f(0, 0, 0);
- if(!lua_isnil(L, 4))
+ if (!lua_isnil(L, 4))
rotation = read_v3f(L, 4);
co->setBonePosition(bone, position, rotation);
return 0;
}
+// get_bone_position(self, bone)
+int ObjectRef::l_get_bone_position(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ ServerActiveObject *co = getobject(ref);
+ if (co == NULL)
+ return 0;
+ // Do it
+ std::string bone = "";
+ if (!lua_isnil(L, 2))
+ bone = lua_tostring(L, 2);
+
+ v3f position = v3f(0, 0, 0);
+ v3f rotation = v3f(0, 0, 0);
+ co->getBonePosition(bone, &position, &rotation);
+
+ push_v3f(L, position);
+ push_v3f(L, rotation);
+ return 2;
+}
+
// set_attach(self, parent, bone, position, rotation)
int ObjectRef::l_set_attach(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
+ GET_ENV_PTR;
+
ObjectRef *ref = checkobject(L, 1);
ObjectRef *parent_ref = checkobject(L, 2);
ServerActiveObject *co = getobject(ref);
ServerActiveObject *parent = getobject(parent_ref);
- if(co == NULL) return 0;
- if(parent == NULL) return 0;
+ if (co == NULL)
+ return 0;
+ if (parent == NULL)
+ return 0;
// Do it
+ int parent_id = 0;
std::string bone = "";
- if(!lua_isnil(L, 3))
- bone = lua_tostring(L, 3);
v3f position = v3f(0, 0, 0);
- if(!lua_isnil(L, 4))
- position = read_v3f(L, 4);
v3f rotation = v3f(0, 0, 0);
- if(!lua_isnil(L, 5))
+ co->getAttachment(&parent_id, &bone, &position, &rotation);
+ if (parent_id) {
+ ServerActiveObject *old_parent = env->getActiveObject(parent_id);
+ old_parent->removeAttachmentChild(co->getId());
+ }
+
+ bone = "";
+ if (!lua_isnil(L, 3))
+ bone = lua_tostring(L, 3);
+ position = v3f(0, 0, 0);
+ if (!lua_isnil(L, 4))
+ position = read_v3f(L, 4);
+ rotation = v3f(0, 0, 0);
+ if (!lua_isnil(L, 5))
rotation = read_v3f(L, 5);
co->setAttachment(parent->getId(), bone, position, rotation);
+ parent->addAttachmentChild(co->getId());
return 0;
}
+// get_attach(self)
+int ObjectRef::l_get_attach(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ GET_ENV_PTR;
+
+ ObjectRef *ref = checkobject(L, 1);
+ ServerActiveObject *co = getobject(ref);
+ if (co == NULL)
+ return 0;
+
+ // Do it
+ int parent_id = 0;
+ std::string bone = "";
+ v3f position = v3f(0, 0, 0);
+ v3f rotation = v3f(0, 0, 0);
+ co->getAttachment(&parent_id, &bone, &position, &rotation);
+ if (!parent_id)
+ return 0;
+ ServerActiveObject *parent = env->getActiveObject(parent_id);
+
+ getScriptApiBase(L)->objectrefGetOrCreate(L, parent);
+ lua_pushlstring(L, bone.c_str(), bone.size());
+ push_v3f(L, position);
+ push_v3f(L, rotation);
+ return 4;
+}
+
// set_detach(self)
int ObjectRef::l_set_detach(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
+ GET_ENV_PTR;
+
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL)
+ return 0;
+
+ int parent_id = 0;
+ std::string bone = "";
+ v3f position;
+ v3f rotation;
+ co->getAttachment(&parent_id, &bone, &position, &rotation);
+ ServerActiveObject *parent = NULL;
+ if (parent_id)
+ parent = env->getActiveObject(parent_id);
+
// Do it
co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0));
+ if (parent != NULL)
+ parent->removeAttachmentChild(co->getId());
return 0;
}
@@ -524,15 +739,40 @@ int ObjectRef::l_set_properties(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
ServerActiveObject *co = getobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
ObjectProperties *prop = co->accessObjectProperties();
- if(!prop)
+ if (!prop)
return 0;
read_object_properties(L, 2, prop);
co->notifyObjectPropertiesModified();
return 0;
}
+// get_properties(self)
+int ObjectRef::l_get_properties(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ ServerActiveObject *co = getobject(ref);
+ if (co == NULL)
+ return 0;
+ ObjectProperties *prop = co->accessObjectProperties();
+ if (!prop)
+ return 0;
+ push_object_properties(L, prop);
+ return 1;
+}
+
+// is_player(self)
+int ObjectRef::l_is_player(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ lua_pushboolean(L, (player != NULL));
+ return 1;
+}
+
/* LuaEntitySAO-only */
// setvelocity(self, {x=num, y=num, z=num})
@@ -541,7 +781,7 @@ int ObjectRef::l_setvelocity(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
v3f pos = checkFloatPos(L, 2);
// Do it
co->setVelocity(pos);
@@ -554,7 +794,7 @@ int ObjectRef::l_getvelocity(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
v3f v = co->getVelocity();
pushFloatPos(L, v);
@@ -567,7 +807,7 @@ int ObjectRef::l_setacceleration(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// pos
v3f pos = checkFloatPos(L, 2);
// Do it
@@ -581,7 +821,7 @@ int ObjectRef::l_getacceleration(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
v3f v = co->getAcceleration();
pushFloatPos(L, v);
@@ -594,7 +834,7 @@ int ObjectRef::l_setyaw(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
float yaw = luaL_checknumber(L, 2) * core::RADTODEG;
// Do it
co->setYaw(yaw);
@@ -607,7 +847,7 @@ int ObjectRef::l_getyaw(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
float yaw = co->getYaw() * core::DEGTORAD;
lua_pushnumber(L, yaw);
@@ -620,7 +860,7 @@ int ObjectRef::l_settexturemod(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
std::string mod = luaL_checkstring(L, 2);
co->setTextureMod(mod);
@@ -634,19 +874,19 @@ int ObjectRef::l_setsprite(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
v2s16 p(0,0);
- if(!lua_isnil(L, 2))
+ if (!lua_isnil(L, 2))
p = read_v2s16(L, 2);
int num_frames = 1;
- if(!lua_isnil(L, 3))
+ if (!lua_isnil(L, 3))
num_frames = lua_tonumber(L, 3);
float framelength = 0.2;
- if(!lua_isnil(L, 4))
+ if (!lua_isnil(L, 4))
framelength = lua_tonumber(L, 4);
bool select_horiz_by_yawpitch = false;
- if(!lua_isnil(L, 5))
+ if (!lua_isnil(L, 5))
select_horiz_by_yawpitch = lua_toboolean(L, 5);
co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch);
return 0;
@@ -660,7 +900,7 @@ int ObjectRef::l_get_entity_name(lua_State *L)
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
log_deprecated(L,"Deprecated call to \"get_entity_name");
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
std::string name = co->getName();
lua_pushstring(L, name.c_str());
@@ -673,7 +913,7 @@ int ObjectRef::l_get_luaentity(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
LuaEntitySAO *co = getluaobject(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
luaentity_get(L, co->getId());
return 1;
@@ -681,16 +921,6 @@ int ObjectRef::l_get_luaentity(lua_State *L)
/* Player-only */
-// is_player(self)
-int ObjectRef::l_is_player(lua_State *L)
-{
- NO_MAP_LOCK_REQUIRED;
- ObjectRef *ref = checkobject(L, 1);
- Player *player = getplayer(ref);
- lua_pushboolean(L, (player != NULL));
- return 1;
-}
-
// is_player_connected(self)
int ObjectRef::l_is_player_connected(lua_State *L)
{
@@ -707,7 +937,7 @@ int ObjectRef::l_get_player_name(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL){
+ if (player == NULL) {
lua_pushlstring(L, "", 0);
return 1;
}
@@ -716,13 +946,28 @@ int ObjectRef::l_get_player_name(lua_State *L)
return 1;
}
+// get_player_velocity(self)
+int ObjectRef::l_get_player_velocity(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL) {
+ lua_pushnil(L);
+ return 1;
+ }
+ // Do it
+ push_v3f(L, player->getSpeed() / BS);
+ return 1;
+}
+
// get_look_dir(self)
int ObjectRef::l_get_look_dir(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL) return 0;
+ if (player == NULL) return 0;
// Do it
float pitch = player->getRadPitch();
float yaw = player->getRadYaw();
@@ -737,7 +982,7 @@ int ObjectRef::l_get_look_pitch(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL) return 0;
+ if (player == NULL) return 0;
// Do it
lua_pushnumber(L, player->getRadPitch());
return 1;
@@ -749,7 +994,7 @@ int ObjectRef::l_get_look_yaw(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL) return 0;
+ if (player == NULL) return 0;
// Do it
lua_pushnumber(L, player->getRadYaw());
return 1;
@@ -761,7 +1006,7 @@ int ObjectRef::l_set_look_pitch(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
PlayerSAO* co = getplayersao(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
float pitch = luaL_checknumber(L, 2) * core::RADTODEG;
// Do it
co->setPitch(pitch);
@@ -774,7 +1019,7 @@ int ObjectRef::l_set_look_yaw(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
PlayerSAO* co = getplayersao(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
float yaw = luaL_checknumber(L, 2) * core::RADTODEG;
// Do it
co->setYaw(yaw);
@@ -787,11 +1032,15 @@ int ObjectRef::l_set_breath(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
PlayerSAO* co = getplayersao(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
u16 breath = luaL_checknumber(L, 2);
// Do it
co->setBreath(breath);
- co->m_breath_not_sent = true;
+
+ // If the object is a player sent the breath to client
+ if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
+ getServer(L)->SendPlayerBreath(((PlayerSAO*)co)->getPeerID());
+
return 0;
}
@@ -801,7 +1050,7 @@ int ObjectRef::l_get_breath(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
PlayerSAO* co = getplayersao(ref);
- if(co == NULL) return 0;
+ if (co == NULL) return 0;
// Do it
u16 breath = co->getBreath();
lua_pushinteger (L, breath);
@@ -814,7 +1063,7 @@ int ObjectRef::l_set_inventory_formspec(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL) return 0;
+ if (player == NULL) return 0;
std::string formspec = luaL_checkstring(L, 2);
player->inventory_formspec = formspec;
@@ -829,7 +1078,7 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL) return 0;
+ if (player == NULL) return 0;
std::string formspec = player->inventory_formspec;
lua_pushlstring(L, formspec.c_str(), formspec.size());
@@ -842,7 +1091,7 @@ int ObjectRef::l_get_player_control(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL){
+ if (player == NULL) {
lua_pushlstring(L, "", 0);
return 1;
}
@@ -876,7 +1125,7 @@ int ObjectRef::l_get_player_control_bits(lua_State *L)
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
Player *player = getplayer(ref);
- if(player == NULL){
+ if (player == NULL) {
lua_pushlstring(L, "", 0);
return 1;
}
@@ -1136,6 +1385,8 @@ int ObjectRef::l_hud_get_flags(lua_State *L)
lua_setfield(L, -2, "wielditem");
lua_pushboolean(L, player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE);
lua_setfield(L, -2, "breathbar");
+ lua_pushboolean(L, player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE);
+ lua_setfield(L, -2, "minimap");
return 1;
}
@@ -1157,6 +1408,20 @@ int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L)
return 1;
}
+// hud_get_hotbar_itemcount(self)
+int ObjectRef::l_hud_get_hotbar_itemcount(lua_State *L)
+{
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+
+ s32 hotbar_itemcount = getServer(L)->hudGetHotbarItemcount(player);
+
+ lua_pushnumber(L, hotbar_itemcount);
+ return 1;
+}
+
// hud_set_hotbar_image(self, name)
int ObjectRef::l_hud_set_hotbar_image(lua_State *L)
{
@@ -1171,6 +1436,19 @@ int ObjectRef::l_hud_set_hotbar_image(lua_State *L)
return 1;
}
+// hud_get_hotbar_image(self)
+int ObjectRef::l_hud_get_hotbar_image(lua_State *L)
+{
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+
+ std::string name = getServer(L)->hudGetHotbarImage(player);
+ lua_pushlstring(L, name.c_str(), name.size());
+ return 1;
+}
+
// hud_set_hotbar_selected_image(self, name)
int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L)
{
@@ -1185,6 +1463,19 @@ int ObjectRef::l_hud_set_hotbar_selected_image(lua_State *L)
return 1;
}
+// hud_get_hotbar_selected_image(self)
+int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L)
+{
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+
+ std::string name = getServer(L)->hudGetHotbarSelectedImage(player);
+ lua_pushlstring(L, name.c_str(), name.size());
+ return 1;
+}
+
// set_sky(self, bgcolor, type, list)
int ObjectRef::l_set_sky(lua_State *L)
{
@@ -1194,8 +1485,7 @@ int ObjectRef::l_set_sky(lua_State *L)
return 0;
video::SColor bgcolor(255,255,255,255);
- if (!lua_isnil(L, 2))
- bgcolor = readARGB8(L, 2);
+ read_color(L, 2, &bgcolor);
std::string type = luaL_checkstring(L, 3);
@@ -1224,6 +1514,33 @@ int ObjectRef::l_set_sky(lua_State *L)
return 1;
}
+// get_sky(self)
+int ObjectRef::l_get_sky(lua_State *L)
+{
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+ video::SColor bgcolor(255, 255, 255, 255);
+ std::string type;
+ std::vector<std::string> params;
+
+ player->getSky(&bgcolor, &type, &params);
+ type = type == "" ? "regular" : type;
+
+ push_ARGB8(L, bgcolor);
+ lua_pushlstring(L, type.c_str(), type.size());
+ lua_newtable(L);
+ s16 i = 1;
+ for (std::vector<std::string>::iterator it = params.begin();
+ it != params.end(); ++it) {
+ lua_pushlstring(L, it->c_str(), it->size());
+ lua_rawseti(L, -2, i);
+ i++;
+ }
+ return 3;
+}
+
// override_day_night_ratio(self, brightness=0...1)
int ObjectRef::l_override_day_night_ratio(lua_State *L)
{
@@ -1234,7 +1551,7 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L)
bool do_override = false;
float ratio = 0.0f;
- if (!lua_isnil(L, 2)){
+ if (!lua_isnil(L, 2)) {
do_override = true;
ratio = luaL_checknumber(L, 2);
}
@@ -1246,6 +1563,65 @@ int ObjectRef::l_override_day_night_ratio(lua_State *L)
return 1;
}
+// get_day_night_ratio(self)
+int ObjectRef::l_get_day_night_ratio(lua_State *L)
+{
+ ObjectRef *ref = checkobject(L, 1);
+ Player *player = getplayer(ref);
+ if (player == NULL)
+ return 0;
+
+ bool do_override;
+ float ratio;
+ player->getDayNightRatio(&do_override, &ratio);
+
+ if (do_override)
+ lua_pushnumber(L, ratio);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+// set_nametag_attributes(self, attributes)
+int ObjectRef::l_set_nametag_attributes(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ PlayerSAO *playersao = getplayersao(ref);
+ if (playersao == NULL)
+ return 0;
+
+ lua_getfield(L, 2, "color");
+ if (!lua_isnil(L, -1)) {
+ video::SColor color = playersao->getNametagColor();
+ if (!read_color(L, -1, &color))
+ return 0;
+ playersao->setNametagColor(color);
+ }
+
+ lua_pushboolean(L, true);
+ return 1;
+}
+
+// get_nametag_attributes(self)
+int ObjectRef::l_get_nametag_attributes(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ ObjectRef *ref = checkobject(L, 1);
+ PlayerSAO *playersao = getplayersao(ref);
+ if (playersao == NULL)
+ return 0;
+
+ video::SColor color = playersao->getNametagColor();
+
+ lua_newtable(L);
+ push_ARGB8(L, color);
+ lua_setfield(L, -2, "color");
+
+ return 1;
+}
+
ObjectRef::ObjectRef(ServerActiveObject *object):
m_object(object)
{
@@ -1254,7 +1630,7 @@ ObjectRef::ObjectRef(ServerActiveObject *object):
ObjectRef::~ObjectRef()
{
- /*if(m_object)
+ /*if (m_object)
infostream<<"ObjectRef destructing for id="
<<m_object->getId()<<std::endl;
else
@@ -1323,12 +1699,16 @@ const luaL_reg ObjectRef::methods[] = {
luamethod(ObjectRef, get_wielded_item),
luamethod(ObjectRef, set_wielded_item),
luamethod(ObjectRef, set_armor_groups),
- luamethod(ObjectRef, set_physics_override),
+ luamethod(ObjectRef, get_armor_groups),
luamethod(ObjectRef, set_animation),
+ luamethod(ObjectRef, get_animation),
luamethod(ObjectRef, set_bone_position),
+ luamethod(ObjectRef, get_bone_position),
luamethod(ObjectRef, set_attach),
+ luamethod(ObjectRef, get_attach),
luamethod(ObjectRef, set_detach),
luamethod(ObjectRef, set_properties),
+ luamethod(ObjectRef, get_properties),
// LuaEntitySAO-only
luamethod(ObjectRef, setvelocity),
luamethod(ObjectRef, getvelocity),
@@ -1344,6 +1724,7 @@ const luaL_reg ObjectRef::methods[] = {
luamethod(ObjectRef, is_player),
luamethod(ObjectRef, is_player_connected),
luamethod(ObjectRef, get_player_name),
+ luamethod(ObjectRef, get_player_velocity),
luamethod(ObjectRef, get_look_dir),
luamethod(ObjectRef, get_look_pitch),
luamethod(ObjectRef, get_look_yaw),
@@ -1355,6 +1736,8 @@ const luaL_reg ObjectRef::methods[] = {
luamethod(ObjectRef, get_inventory_formspec),
luamethod(ObjectRef, get_player_control),
luamethod(ObjectRef, get_player_control_bits),
+ luamethod(ObjectRef, set_physics_override),
+ luamethod(ObjectRef, get_physics_override),
luamethod(ObjectRef, hud_add),
luamethod(ObjectRef, hud_remove),
luamethod(ObjectRef, hud_change),
@@ -1362,11 +1745,20 @@ const luaL_reg ObjectRef::methods[] = {
luamethod(ObjectRef, hud_set_flags),
luamethod(ObjectRef, hud_get_flags),
luamethod(ObjectRef, hud_set_hotbar_itemcount),
+ luamethod(ObjectRef, hud_get_hotbar_itemcount),
luamethod(ObjectRef, hud_set_hotbar_image),
+ luamethod(ObjectRef, hud_get_hotbar_image),
luamethod(ObjectRef, hud_set_hotbar_selected_image),
+ luamethod(ObjectRef, hud_get_hotbar_selected_image),
luamethod(ObjectRef, set_sky),
+ luamethod(ObjectRef, get_sky),
luamethod(ObjectRef, override_day_night_ratio),
+ luamethod(ObjectRef, get_day_night_ratio),
luamethod(ObjectRef, set_local_animation),
+ luamethod(ObjectRef, get_local_animation),
luamethod(ObjectRef, set_eye_offset),
+ luamethod(ObjectRef, get_eye_offset),
+ luamethod(ObjectRef, set_nametag_attributes),
+ luamethod(ObjectRef, get_nametag_attributes),
{0,0}
};
diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h
index d51ca379f..a4457cc05 100644
--- a/src/script/lua_api/l_object.h
+++ b/src/script/lua_api/l_object.h
@@ -101,25 +101,46 @@ private:
// set_armor_groups(self, groups)
static int l_set_armor_groups(lua_State *L);
+ // get_armor_groups(self)
+ static int l_get_armor_groups(lua_State *L);
+
// set_physics_override(self, physics_override_speed, physics_override_jump,
// physics_override_gravity, sneak, sneak_glitch)
static int l_set_physics_override(lua_State *L);
- // set_animation(self, frame_range, frame_speed, frame_blend)
+ // get_physics_override(self)
+ static int l_get_physics_override(lua_State *L);
+
+ // set_animation(self, frame_range, frame_speed, frame_blend, frame_loop)
static int l_set_animation(lua_State *L);
+ // get_animation(self)
+ static int l_get_animation(lua_State *L);
+
// set_bone_position(self, std::string bone, v3f position, v3f rotation)
static int l_set_bone_position(lua_State *L);
+ // get_bone_position(self, bone)
+ static int l_get_bone_position(lua_State *L);
+
// set_attach(self, parent, bone, position, rotation)
static int l_set_attach(lua_State *L);
+ // get_attach(self)
+ static int l_get_attach(lua_State *L);
+
// set_detach(self)
static int l_set_detach(lua_State *L);
// set_properties(self, properties)
static int l_set_properties(lua_State *L);
+ // get_properties(self)
+ static int l_get_properties(lua_State *L);
+
+ // is_player(self)
+ static int l_is_player(lua_State *L);
+
/* LuaEntitySAO-only */
// setvelocity(self, {x=num, y=num, z=num})
@@ -156,15 +177,15 @@ private:
/* Player-only */
- // is_player(self)
- static int l_is_player(lua_State *L);
-
// is_player_connected(self)
static int l_is_player_connected(lua_State *L);
// get_player_name(self)
static int l_get_player_name(lua_State *L);
+ // get_player_velocity(self)
+ static int l_get_player_velocity(lua_State *L);
+
// get_look_dir(self)
static int l_get_look_dir(lua_State *L);
@@ -222,24 +243,51 @@ private:
// hud_set_hotbar_itemcount(self, hotbar_itemcount)
static int l_hud_set_hotbar_itemcount(lua_State *L);
+ // hud_get_hotbar_itemcount(self)
+ static int l_hud_get_hotbar_itemcount(lua_State *L);
+
// hud_set_hotbar_image(self, name)
static int l_hud_set_hotbar_image(lua_State *L);
+ // hud_get_hotbar_image(self)
+ static int l_hud_get_hotbar_image(lua_State *L);
+
// hud_set_hotbar_selected_image(self, name)
static int l_hud_set_hotbar_selected_image(lua_State *L);
+ // hud_get_hotbar_selected_image(self)
+ static int l_hud_get_hotbar_selected_image(lua_State *L);
+
// set_sky(self, type, list)
static int l_set_sky(lua_State *L);
- // override_day_night_ratio(self, type, list)
+ // get_sky(self, type, list)
+ static int l_get_sky(lua_State *L);
+
+ // override_day_night_ratio(self, type)
static int l_override_day_night_ratio(lua_State *L);
+ // get_day_night_ratio(self)
+ static int l_get_day_night_ratio(lua_State *L);
+
// set_local_animation(self, {stand/idle}, {walk}, {dig}, {walk+dig}, frame_speed)
static int l_set_local_animation(lua_State *L);
+ // get_local_animation(self)
+ static int l_get_local_animation(lua_State *L);
+
// set_eye_offset(self, v3f first pv, v3f third pv)
static int l_set_eye_offset(lua_State *L);
+ // get_eye_offset(self)
+ static int l_get_eye_offset(lua_State *L);
+
+ // set_nametag_attributes(self, attributes)
+ static int l_set_nametag_attributes(lua_State *L);
+
+ // get_nametag_attributes(self)
+ static int l_get_nametag_attributes(lua_State *L);
+
public:
ObjectRef(ServerActiveObject *object);
diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp
index 6769f5c23..2532b2b08 100644
--- a/src/script/lua_api/l_particles.cpp
+++ b/src/script/lua_api/l_particles.cpp
@@ -34,17 +34,20 @@ int ModApiParticles::l_add_particle(lua_State *L)
{
// Get parameters
v3f pos, vel, acc;
- pos= vel= acc= v3f(0, 0, 0);
+ pos = vel = acc = v3f(0, 0, 0);
+
float expirationtime, size;
- expirationtime= size= 1;
+ expirationtime = size = 1;
+
bool collisiondetection, vertical;
- collisiondetection= vertical= false;
+ collisiondetection = vertical = false;
+
std::string texture = "";
- const char *playername = "";
+ std::string playername = "";
if (lua_gettop(L) > 1) // deprecated
{
- log_deprecated(L,"Deprecated add_particle call with individual parameters instead of definition");
+ log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition");
pos = check_v3f(L, 1);
vel = check_v3f(L, 2);
acc = check_v3f(L, 3);
@@ -57,44 +60,44 @@ int ModApiParticles::l_add_particle(lua_State *L)
}
else if (lua_istable(L, 1))
{
- int table = lua_gettop(L);
- lua_pushnil(L);
- while (lua_next(L, table) != 0)
- {
- const char *key = lua_tostring(L, -2);
- if(strcmp(key,"pos")==0){
- pos=check_v3f(L, -1);
- }else if(strcmp(key,"vel")==0){
- vel=check_v3f(L, -1);
- }else if(strcmp(key,"acc")==0){
- acc=check_v3f(L, -1);
- }else if(strcmp(key,"expirationtime")==0){
- expirationtime=luaL_checknumber(L, -1);
- }else if(strcmp(key,"size")==0){
- size=luaL_checknumber(L, -1);
- }else if(strcmp(key,"collisiondetection")==0){
- collisiondetection=lua_toboolean(L, -1);
- }else if(strcmp(key,"vertical")==0){
- vertical=lua_toboolean(L, -1);
- }else if(strcmp(key,"texture")==0){
- texture=luaL_checkstring(L, -1);
- }else if(strcmp(key,"playername")==0){
- playername=luaL_checkstring(L, -1);
- }
- lua_pop(L, 1);
+ lua_getfield(L, 1, "pos");
+ pos = lua_istable(L, -1) ? check_v3f(L, -1) : v3f();
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "vel");
+ if (lua_istable(L, -1)) {
+ vel = check_v3f(L, -1);
+ log_deprecated(L, "The use of vel is deprecated. "
+ "Use velocity instead");
}
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "velocity");
+ vel = lua_istable(L, -1) ? check_v3f(L, -1) : vel;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "acc");
+ if (lua_istable(L, -1)) {
+ acc = check_v3f(L, -1);
+ log_deprecated(L, "The use of acc is deprecated. "
+ "Use acceleration instead");
+ }
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "acceleration");
+ acc = lua_istable(L, -1) ? check_v3f(L, -1) : acc;
+ lua_pop(L, 1);
+
+ expirationtime = getfloatfield_default(L, 1, "expirationtime", 1);
+ size = getfloatfield_default(L, 1, "size", 1);
+ collisiondetection = getboolfield_default(L, 1,
+ "collisiondetection", collisiondetection);
+ vertical = getboolfield_default(L, 1, "vertical", vertical);
+ texture = getstringfield_default(L, 1, "texture", "");
+ playername = getstringfield_default(L, 1, "playername", "");
}
- if (strcmp(playername, "")==0) // spawn for all players
- {
- getServer(L)->spawnParticleAll(pos, vel, acc,
+ getServer(L)->spawnParticle(playername, pos, vel, acc,
expirationtime, size, collisiondetection, vertical, texture);
- }
- else
- {
- getServer(L)->spawnParticle(playername,
- pos, vel, acc, expirationtime,
- size, collisiondetection, vertical, texture);
- }
return 1;
}
@@ -125,7 +128,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
bool collisiondetection, vertical;
collisiondetection= vertical= false;
std::string texture = "";
- const char *playername = "";
+ std::string playername = "";
if (lua_gettop(L) > 1) //deprecated
{
@@ -149,74 +152,55 @@ int ModApiParticles::l_add_particlespawner(lua_State *L)
}
else if (lua_istable(L, 1))
{
- int table = lua_gettop(L);
- lua_pushnil(L);
- while (lua_next(L, table) != 0)
- {
- const char *key = lua_tostring(L, -2);
- if(strcmp(key,"amount")==0){
- amount=luaL_checknumber(L, -1);
- }else if(strcmp(key,"time")==0){
- time=luaL_checknumber(L, -1);
- }else if(strcmp(key,"minpos")==0){
- minpos=check_v3f(L, -1);
- }else if(strcmp(key,"maxpos")==0){
- maxpos=check_v3f(L, -1);
- }else if(strcmp(key,"minvel")==0){
- minvel=check_v3f(L, -1);
- }else if(strcmp(key,"maxvel")==0){
- maxvel=check_v3f(L, -1);
- }else if(strcmp(key,"minacc")==0){
- minacc=check_v3f(L, -1);
- }else if(strcmp(key,"maxacc")==0){
- maxacc=check_v3f(L, -1);
- }else if(strcmp(key,"minexptime")==0){
- minexptime=luaL_checknumber(L, -1);
- }else if(strcmp(key,"maxexptime")==0){
- maxexptime=luaL_checknumber(L, -1);
- }else if(strcmp(key,"minsize")==0){
- minsize=luaL_checknumber(L, -1);
- }else if(strcmp(key,"maxsize")==0){
- maxsize=luaL_checknumber(L, -1);
- }else if(strcmp(key,"collisiondetection")==0){
- collisiondetection=lua_toboolean(L, -1);
- }else if(strcmp(key,"vertical")==0){
- vertical=lua_toboolean(L, -1);
- }else if(strcmp(key,"texture")==0){
- texture=luaL_checkstring(L, -1);
- }else if(strcmp(key,"playername")==0){
- playername=luaL_checkstring(L, -1);
- }
- lua_pop(L, 1);
- }
- }
- if (strcmp(playername, "")==0) //spawn for all players
- {
- u32 id = getServer(L)->addParticleSpawnerAll( amount, time,
- minpos, maxpos,
- minvel, maxvel,
- minacc, maxacc,
- minexptime, maxexptime,
- minsize, maxsize,
- collisiondetection,
- vertical,
- texture);
- lua_pushnumber(L, id);
- }
- else
- {
- u32 id = getServer(L)->addParticleSpawner(playername,
- amount, time,
- minpos, maxpos,
- minvel, maxvel,
- minacc, maxacc,
- minexptime, maxexptime,
- minsize, maxsize,
- collisiondetection,
- vertical,
- texture);
- lua_pushnumber(L, id);
+ amount = getintfield_default(L, 1, "amount", amount);
+ time = getfloatfield_default(L, 1, "time", time);
+
+ lua_getfield(L, 1, "minpos");
+ minpos = lua_istable(L, -1) ? check_v3f(L, -1) : minpos;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "maxpos");
+ maxpos = lua_istable(L, -1) ? check_v3f(L, -1) : maxpos;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "minvel");
+ minvel = lua_istable(L, -1) ? check_v3f(L, -1) : minvel;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "maxvel");
+ maxvel = lua_istable(L, -1) ? check_v3f(L, -1) : maxvel;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "minacc");
+ minacc = lua_istable(L, -1) ? check_v3f(L, -1) : minacc;
+ lua_pop(L, 1);
+
+ lua_getfield(L, 1, "maxacc");
+ maxacc = lua_istable(L, -1) ? check_v3f(L, -1) : maxacc;
+ lua_pop(L, 1);
+
+ minexptime = getfloatfield_default(L, 1, "minexptime", minexptime);
+ maxexptime = getfloatfield_default(L, 1, "maxexptime", maxexptime);
+ minsize = getfloatfield_default(L, 1, "minsize", minsize);
+ maxsize = getfloatfield_default(L, 1, "maxsize", maxsize);
+ collisiondetection = getboolfield_default(L, 1,
+ "collisiondetection", collisiondetection);
+ vertical = getboolfield_default(L, 1, "vertical", vertical);
+ texture = getstringfield_default(L, 1, "texture", "");
+ playername = getstringfield_default(L, 1, "playername", "");
}
+
+ u32 id = getServer(L)->addParticleSpawner(amount, time,
+ minpos, maxpos,
+ minvel, maxvel,
+ minacc, maxacc,
+ minexptime, maxexptime,
+ minsize, maxsize,
+ collisiondetection,
+ vertical,
+ texture, playername);
+ lua_pushnumber(L, id);
+
return 1;
}
@@ -226,16 +210,12 @@ int ModApiParticles::l_delete_particlespawner(lua_State *L)
{
// Get parameters
u32 id = luaL_checknumber(L, 1);
-
- if (lua_gettop(L) == 2) // only delete for one player
- {
- const char *playername = luaL_checkstring(L, 2);
- getServer(L)->deleteParticleSpawner(playername, id);
- }
- else // delete for all players
- {
- getServer(L)->deleteParticleSpawnerAll(id);
+ std::string playername = "";
+ if (lua_gettop(L) == 2) {
+ playername = luaL_checkstring(L, 2);
}
+
+ getServer(L)->deleteParticleSpawner(playername, id);
return 1;
}
diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp
index 8d7f6512e..73eca9d60 100644
--- a/src/script/lua_api/l_server.cpp
+++ b/src/script/lua_api/l_server.cpp
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_internal.h"
#include "common/c_converter.h"
#include "common/c_content.h"
+#include "cpp_api/s_base.h"
#include "server.h"
#include "environment.h"
#include "player.h"
@@ -29,7 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// request_shutdown()
int ModApiServer::l_request_shutdown(lua_State *L)
{
- getServer(L)->requestShutdown();
+ const char *msg = lua_tolstring(L, 1, NULL);
+ bool reconnect = lua_toboolean(L, 2);
+ getServer(L)->requestShutdown(msg ? msg : "", reconnect);
return 0;
}
@@ -306,7 +309,7 @@ int ModApiServer::l_kick_player(lua_State *L)
lua_pushboolean(L, false); // No such player
return 1;
}
- getServer(L)->DenyAccess(player->peer_id, narrow_to_wide(message));
+ getServer(L)->DenyAccess_Legacy(player->peer_id, utf8_to_wide(message));
lua_pushboolean(L, true);
return 1;
}
@@ -342,7 +345,7 @@ int ModApiServer::l_show_formspec(lua_State *L)
int ModApiServer::l_get_current_modname(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- lua_getfield(L, LUA_REGISTRYINDEX, "current_modname");
+ lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
return 1;
}
@@ -367,33 +370,18 @@ int ModApiServer::l_get_modnames(lua_State *L)
NO_MAP_LOCK_REQUIRED;
// Get a list of mods
- std::list<std::string> mods_unsorted, mods_sorted;
- getServer(L)->getModNames(mods_unsorted);
+ std::vector<std::string> modlist;
+ getServer(L)->getModNames(modlist);
// Take unsorted items from mods_unsorted and sort them into
// mods_sorted; not great performance but the number of mods on a
// server will likely be small.
- for(std::list<std::string>::iterator i = mods_unsorted.begin();
- i != mods_unsorted.end(); ++i) {
- bool added = false;
- for(std::list<std::string>::iterator x = mods_sorted.begin();
- x != mods_sorted.end(); ++x) {
- // I doubt anybody using Minetest will be using
- // anything not ASCII based :)
- if(i->compare(*x) <= 0) {
- mods_sorted.insert(x, *i);
- added = true;
- break;
- }
- }
- if(!added)
- mods_sorted.push_back(*i);
- }
+ std::sort(modlist.begin(), modlist.end());
// Package them up for Lua
- lua_createtable(L, mods_sorted.size(), 0);
- std::list<std::string>::iterator iter = mods_sorted.begin();
- for (u16 i = 0; iter != mods_sorted.end(); iter++) {
+ lua_createtable(L, modlist.size(), 0);
+ std::vector<std::string>::iterator iter = modlist.begin();
+ for (u16 i = 0; iter != modlist.end(); iter++) {
lua_pushstring(L, iter->c_str());
lua_rawseti(L, -2, ++i);
}
@@ -450,6 +438,31 @@ int ModApiServer::l_notify_authentication_modified(lua_State *L)
return 0;
}
+// get_last_run_mod()
+int ModApiServer::l_get_last_run_mod(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ const char *current_mod = lua_tostring(L, -1);
+ if (current_mod == NULL || current_mod[0] == '\0') {
+ lua_pop(L, 1);
+ lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str());
+ }
+ return 1;
+}
+
+// set_last_run_mod(modname)
+int ModApiServer::l_set_last_run_mod(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+#ifdef SCRIPTAPI_DEBUG
+ const char *mod = lua_tostring(L, 1);
+ getScriptApiBase(L)->setOriginDirect(mod);
+ //printf(">>>> last mod set from Lua: %s\n", mod);
+#endif
+ return 0;
+}
+
#ifndef NDEBUG
// cause_error(type_of_error)
int ModApiServer::l_cause_error(lua_State *L)
@@ -507,6 +520,8 @@ void ModApiServer::Initialize(lua_State *L, int top)
API_FCT(unban_player_or_ip);
API_FCT(notify_authentication_modified);
+ API_FCT(get_last_run_mod);
+ API_FCT(set_last_run_mod);
#ifndef NDEBUG
API_FCT(cause_error);
#endif
diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h
index fd85a8975..df31f325f 100644
--- a/src/script/lua_api/l_server.h
+++ b/src/script/lua_api/l_server.h
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class ModApiServer : public ModApiBase {
private:
- // request_shutdown()
+ // request_shutdown([message], [reconnect])
static int l_request_shutdown(lua_State *L);
// get_server_status()
@@ -88,6 +88,12 @@ private:
// notify_authentication_modified(name)
static int l_notify_authentication_modified(lua_State *L);
+ // get_last_run_mod()
+ static int l_get_last_run_mod(lua_State *L);
+
+ // set_last_run_mod(modname)
+ static int l_set_last_run_mod(lua_State *L);
+
#ifndef NDEBUG
// cause_error(type_of_error)
static int l_cause_error(lua_State *L);
diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp
index 9c88a3e05..35b82b435 100644
--- a/src/script/lua_api/l_settings.cpp
+++ b/src/script/lua_api/l_settings.cpp
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_settings.h"
#include "lua_api/l_internal.h"
+#include "cpp_api/s_security.h"
#include "settings.h"
#include "log.h"
@@ -188,6 +189,7 @@ int LuaSettings::create_object(lua_State* L)
{
NO_MAP_LOCK_REQUIRED;
const char* filename = luaL_checkstring(L, 1);
+ CHECK_SECURE_PATH_OPTIONAL(L, filename);
LuaSettings* o = new LuaSettings(filename);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
luaL_getmetatable(L, className);
diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp
index eb6c1835d..12146e80a 100644
--- a/src/script/lua_api/l_util.cpp
+++ b/src/script/lua_api/l_util.cpp
@@ -24,13 +24,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_async.h"
#include "serialization.h"
#include "json/json.h"
+#include "cpp_api/s_security.h"
+#include "areastore.h"
#include "debug.h"
#include "porting.h"
#include "log.h"
#include "tool.h"
#include "filesys.h"
#include "settings.h"
-#include "main.h" //required for g_settings, g_settings_path
+#include "util/auth.h"
+#include <algorithm>
// debug(...)
// Writes a line to dstream
@@ -92,12 +95,19 @@ int ModApiUtil::l_log(lua_State *L)
return 0;
}
+#define CHECK_SECURE_SETTING(L, name) \
+ if (name.compare(0, 7, "secure.") == 0) {\
+ lua_pushliteral(L, "Attempt to set secure setting.");\
+ lua_error(L);\
+ }
+
// setting_set(name, value)
int ModApiUtil::l_setting_set(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- const char *name = luaL_checkstring(L, 1);
- const char *value = luaL_checkstring(L, 2);
+ std::string name = luaL_checkstring(L, 1);
+ std::string value = luaL_checkstring(L, 2);
+ CHECK_SECURE_SETTING(L, name);
g_settings->set(name, value);
return 0;
}
@@ -120,8 +130,9 @@ int ModApiUtil::l_setting_get(lua_State *L)
int ModApiUtil::l_setting_setbool(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
- const char *name = luaL_checkstring(L, 1);
+ std::string name = luaL_checkstring(L, 1);
bool value = lua_toboolean(L, 2);
+ CHECK_SECURE_SETTING(L, name);
g_settings->setBool(name, value);
return 0;
}
@@ -256,8 +267,7 @@ int ModApiUtil::l_get_password_hash(lua_State *L)
NO_MAP_LOCK_REQUIRED;
std::string name = luaL_checkstring(L, 1);
std::string raw_password = luaL_checkstring(L, 2);
- std::string hash = translatePassword(name,
- narrow_to_wide(raw_password));
+ std::string hash = translatePassword(name, raw_password);
lua_pushstring(L, hash.c_str());
return 1;
}
@@ -308,7 +318,7 @@ int ModApiUtil::l_compress(lua_State *L)
int ModApiUtil::l_decompress(lua_State *L)
{
size_t size;
- const char * data = luaL_checklstring(L, 1, &size);
+ const char *data = luaL_checklstring(L, 1, &size);
std::istringstream is(std::string(data, size));
std::ostringstream os;
@@ -320,6 +330,64 @@ int ModApiUtil::l_decompress(lua_State *L)
return 1;
}
+// mkdir(path)
+int ModApiUtil::l_mkdir(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ const char *path = luaL_checkstring(L, 1);
+ CHECK_SECURE_PATH_OPTIONAL(L, path);
+ lua_pushboolean(L, fs::CreateAllDirs(path));
+ return 1;
+}
+
+// get_dir_list(path, is_dir)
+int ModApiUtil::l_get_dir_list(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ const char *path = luaL_checkstring(L, 1);
+ short is_dir = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : -1;
+
+ CHECK_SECURE_PATH_OPTIONAL(L, path);
+
+ std::vector<fs::DirListNode> list = fs::GetDirListing(path);
+
+ int index = 0;
+ lua_newtable(L);
+
+ for (size_t i = 0; i < list.size(); i++) {
+ if (is_dir == -1 || is_dir == list[i].dir) {
+ lua_pushstring(L, list[i].name.c_str());
+ lua_rawseti(L, -2, ++index);
+ }
+ }
+
+ return 1;
+}
+
+int ModApiUtil::l_request_insecure_environment(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ if (!ScriptApiSecurity::isSecure(L)) {
+ lua_getglobal(L, "_G");
+ return 1;
+ }
+ lua_getfield(L, LUA_REGISTRYINDEX, SCRIPT_MOD_NAME_FIELD);
+ if (!lua_isstring(L, -1)) {
+ lua_pushnil(L);
+ return 1;
+ }
+ const char *mod_name = lua_tostring(L, -1);
+ std::string trusted_mods = g_settings->get("secure.trusted_mods");
+ std::vector<std::string> mod_list = str_split(trusted_mods, ',');
+ if (std::find(mod_list.begin(), mod_list.end(), mod_name) == mod_list.end()) {
+ lua_pushnil(L);
+ return 1;
+ }
+ lua_getfield(L, LUA_REGISTRYINDEX, "globals_backup");
+ return 1;
+}
+
+
void ModApiUtil::Initialize(lua_State *L, int top)
{
API_FCT(debug);
@@ -345,6 +413,11 @@ void ModApiUtil::Initialize(lua_State *L, int top)
API_FCT(compress);
API_FCT(decompress);
+
+ API_FCT(mkdir);
+ API_FCT(get_dir_list);
+
+ API_FCT(request_insecure_environment);
}
void ModApiUtil::InitializeAsync(AsyncEngine& engine)
@@ -367,5 +440,8 @@ void ModApiUtil::InitializeAsync(AsyncEngine& engine)
ASYNC_API_FCT(compress);
ASYNC_API_FCT(decompress);
+
+ ASYNC_API_FCT(mkdir);
+ ASYNC_API_FCT(get_dir_list);
}
diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h
index e82432381..e75aa28cb 100644
--- a/src/script/lua_api/l_util.h
+++ b/src/script/lua_api/l_util.h
@@ -78,7 +78,7 @@ private:
// is_yes(arg)
static int l_is_yes(lua_State *L);
- // get_scriptdir()
+ // get_builtin_path()
static int l_get_builtin_path(lua_State *L);
// compress(data, method, ...)
@@ -87,6 +87,15 @@ private:
// decompress(data, method, ...)
static int l_decompress(lua_State *L);
+ // mkdir(path)
+ static int l_mkdir(lua_State *L);
+
+ // get_dir_list(path, is_dir)
+ static int l_get_dir_list(lua_State *L);
+
+ // request_insecure_environment()
+ static int l_request_insecure_environment(lua_State *L);
+
public:
static void Initialize(lua_State *L, int top);
@@ -95,3 +104,4 @@ public:
};
#endif /* L_UTIL_H_ */
+
diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp
index de7612115..ac6c10303 100644
--- a/src/script/lua_api/l_vmanip.cpp
+++ b/src/script/lua_api/l_vmanip.cpp
@@ -46,8 +46,8 @@ int LuaVoxelManip::l_read_from_map(lua_State *L)
LuaVoxelManip *o = checkobject(L, 1);
MMVManip *vm = o->vm;
- v3s16 bp1 = getNodeBlockPos(read_v3s16(L, 2));
- v3s16 bp2 = getNodeBlockPos(read_v3s16(L, 3));
+ v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2));
+ v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3));
sortBoxVerticies(bp1, bp2);
vm->initialEmerge(bp1, bp2);
@@ -63,12 +63,18 @@ int LuaVoxelManip::l_get_data(lua_State *L)
NO_MAP_LOCK_REQUIRED;
LuaVoxelManip *o = checkobject(L, 1);
+ bool use_buffer = lua_istable(L, 2);
+
MMVManip *vm = o->vm;
- int volume = vm->m_area.getVolume();
+ u32 volume = vm->m_area.getVolume();
- lua_newtable(L);
- for (int i = 0; i != volume; i++) {
+ if (use_buffer)
+ lua_pushvalue(L, 2);
+ else
+ lua_newtable(L);
+
+ for (u32 i = 0; i != volume; i++) {
lua_Integer cid = vm->m_data[i].getContent();
lua_pushinteger(L, cid);
lua_rawseti(L, -2, i + 1);
@@ -87,8 +93,8 @@ int LuaVoxelManip::l_set_data(lua_State *L)
if (!lua_istable(L, 2))
return 0;
- int volume = vm->m_area.getVolume();
- for (int i = 0; i != volume; i++) {
+ u32 volume = vm->m_area.getVolume();
+ for (u32 i = 0; i != volume; i++) {
lua_rawgeti(L, 2, i + 1);
content_t c = lua_tointeger(L, -1);
@@ -116,7 +122,7 @@ int LuaVoxelManip::l_get_node_at(lua_State *L)
GET_ENV_PTR;
LuaVoxelManip *o = checkobject(L, 1);
- v3s16 pos = read_v3s16(L, 2);
+ v3s16 pos = check_v3s16(L, 2);
pushnode(L, o->vm->getNodeNoExNoEmerge(pos), env->getGameDef()->ndef());
return 1;
@@ -128,7 +134,7 @@ int LuaVoxelManip::l_set_node_at(lua_State *L)
GET_ENV_PTR;
LuaVoxelManip *o = checkobject(L, 1);
- v3s16 pos = read_v3s16(L, 2);
+ v3s16 pos = check_v3s16(L, 2);
MapNode n = readnode(L, 3, env->getGameDef()->ndef());
o->vm->setNodeNoEmerge(pos, n);
@@ -171,8 +177,8 @@ int LuaVoxelManip::l_calc_lighting(lua_State *L)
v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE;
v3s16 fpmin = vm->m_area.MinEdge;
v3s16 fpmax = vm->m_area.MaxEdge;
- v3s16 pmin = lua_istable(L, 2) ? read_v3s16(L, 2) : fpmin + yblock;
- v3s16 pmax = lua_istable(L, 3) ? read_v3s16(L, 3) : fpmax - yblock;
+ v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) : fpmin + yblock;
+ v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) : fpmax - yblock;
sortBoxVerticies(pmin, pmax);
if (!vm->m_area.contains(VoxelArea(pmin, pmax)))
@@ -206,8 +212,8 @@ int LuaVoxelManip::l_set_lighting(lua_State *L)
MMVManip *vm = o->vm;
v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE;
- v3s16 pmin = lua_istable(L, 3) ? read_v3s16(L, 3) : vm->m_area.MinEdge + yblock;
- v3s16 pmax = lua_istable(L, 4) ? read_v3s16(L, 4) : vm->m_area.MaxEdge - yblock;
+ v3s16 pmin = lua_istable(L, 3) ? check_v3s16(L, 3) : vm->m_area.MinEdge + yblock;
+ v3s16 pmax = lua_istable(L, 4) ? check_v3s16(L, 4) : vm->m_area.MaxEdge - yblock;
sortBoxVerticies(pmin, pmax);
if (!vm->m_area.contains(VoxelArea(pmin, pmax)))
@@ -228,10 +234,10 @@ int LuaVoxelManip::l_get_light_data(lua_State *L)
LuaVoxelManip *o = checkobject(L, 1);
MMVManip *vm = o->vm;
- int volume = vm->m_area.getVolume();
+ u32 volume = vm->m_area.getVolume();
lua_newtable(L);
- for (int i = 0; i != volume; i++) {
+ for (u32 i = 0; i != volume; i++) {
lua_Integer light = vm->m_data[i].param1;
lua_pushinteger(L, light);
lua_rawseti(L, -2, i + 1);
@@ -250,8 +256,8 @@ int LuaVoxelManip::l_set_light_data(lua_State *L)
if (!lua_istable(L, 2))
return 0;
- int volume = vm->m_area.getVolume();
- for (int i = 0; i != volume; i++) {
+ u32 volume = vm->m_area.getVolume();
+ for (u32 i = 0; i != volume; i++) {
lua_rawgeti(L, 2, i + 1);
u8 light = lua_tointeger(L, -1);
@@ -270,10 +276,10 @@ int LuaVoxelManip::l_get_param2_data(lua_State *L)
LuaVoxelManip *o = checkobject(L, 1);
MMVManip *vm = o->vm;
- int volume = vm->m_area.getVolume();
+ u32 volume = vm->m_area.getVolume();
lua_newtable(L);
- for (int i = 0; i != volume; i++) {
+ for (u32 i = 0; i != volume; i++) {
lua_Integer param2 = vm->m_data[i].param2;
lua_pushinteger(L, param2);
lua_rawseti(L, -2, i + 1);
@@ -292,8 +298,8 @@ int LuaVoxelManip::l_set_param2_data(lua_State *L)
if (!lua_istable(L, 2))
return 0;
- int volume = vm->m_area.getVolume();
- for (int i = 0; i != volume; i++) {
+ u32 volume = vm->m_area.getVolume();
+ for (u32 i = 0; i != volume; i++) {
lua_rawgeti(L, 2, i + 1);
u8 param2 = lua_tointeger(L, -1);
@@ -402,7 +408,7 @@ int LuaVoxelManip::create_object(lua_State *L)
Map *map = &(env->getMap());
LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ?
- new LuaVoxelManip(map, read_v3s16(L, 1), read_v3s16(L, 2)) :
+ new LuaVoxelManip(map, check_v3s16(L, 1), check_v3s16(L, 2)) :
new LuaVoxelManip(map);
*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp
index e716bc979..4f0350d41 100644
--- a/src/script/scripting_game.cpp
+++ b/src/script/scripting_game.cpp
@@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "scripting_game.h"
#include "server.h"
#include "log.h"
+#include "settings.h"
#include "cpp_api/s_internal.h"
+#include "lua_api/l_areastore.h"
#include "lua_api/l_base.h"
#include "lua_api/l_craft.h"
#include "lua_api/l_env.h"
@@ -49,10 +51,12 @@ GameScripting::GameScripting(Server* server)
// setEnv(env) is called by ScriptApiEnv::initializeEnvironment()
// once the environment has been created
- //TODO add security
-
SCRIPTAPI_PRECHECKHEADER
+ if (g_settings->getBool("secure.enable_security")) {
+ initializeSecurity();
+ }
+
lua_getglobal(L, "core");
int top = lua_gettop(L);
@@ -88,10 +92,12 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
// Register reference classes (userdata)
InvRef::Register(L);
+ LuaAreaStore::Register(L);
LuaItemStack::Register(L);
LuaPerlinNoise::Register(L);
LuaPerlinNoiseMap::Register(L);
LuaPseudoRandom::Register(L);
+ LuaPcgRandom::Register(L);
LuaVoxelManip::Register(L);
NodeMetaRef::Register(L);
NodeTimerRef::Register(L);
@@ -99,7 +105,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
LuaSettings::Register(L);
}
-void log_deprecated(std::string message)
+void log_deprecated(const std::string &message)
{
- log_deprecated(NULL,message);
+ log_deprecated(NULL, message);
}
diff --git a/src/script/scripting_game.h b/src/script/scripting_game.h
index 14dbd9170..970b3e80d 100644
--- a/src/script/scripting_game.h
+++ b/src/script/scripting_game.h
@@ -27,19 +27,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "cpp_api/s_node.h"
#include "cpp_api/s_player.h"
#include "cpp_api/s_server.h"
+#include "cpp_api/s_security.h"
/*****************************************************************************/
/* Scripting <-> Game Interface */
/*****************************************************************************/
-class GameScripting
- : virtual public ScriptApiBase,
- public ScriptApiDetached,
- public ScriptApiEntity,
- public ScriptApiEnv,
- public ScriptApiNode,
- public ScriptApiPlayer,
- public ScriptApiServer
+class GameScripting :
+ virtual public ScriptApiBase,
+ public ScriptApiDetached,
+ public ScriptApiEntity,
+ public ScriptApiEnv,
+ public ScriptApiNode,
+ public ScriptApiPlayer,
+ public ScriptApiServer,
+ public ScriptApiSecurity
{
public:
GameScripting(Server* server);
@@ -50,6 +52,6 @@ private:
void InitializeModApi(lua_State *L, int top);
};
-void log_deprecated(std::string message);
+void log_deprecated(const std::string &message);
#endif /* SCRIPTING_GAME_H_ */
diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp
index 54b3133c5..c74c18edc 100644
--- a/src/script/scripting_mainmenu.cpp
+++ b/src/script/scripting_mainmenu.cpp
@@ -38,8 +38,6 @@ MainMenuScripting::MainMenuScripting(GUIEngine* guiengine)
{
setGuiEngine(guiengine);
- //TODO add security
-
SCRIPTAPI_PRECHECKHEADER
lua_getglobal(L, "core");
diff --git a/src/server.cpp b/src/server.cpp
index 2d84ad032..dc7b101a6 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -21,12 +21,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iostream>
#include <queue>
#include <algorithm>
-#include "clientserver.h"
+#include "network/networkprotocol.h"
+#include "network/serveropcodes.h"
#include "ban.h"
#include "environment.h"
#include "map.h"
#include "jthread/jmutexautolock.h"
-#include "main.h"
#include "constants.h"
#include "voxel.h"
#include "config.h"
@@ -50,20 +50,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content_abm.h"
#include "content_sao.h"
#include "mods.h"
-#include "sha1.h"
-#include "base64.h"
-#include "tool.h"
#include "sound.h" // dummySoundManager
#include "event_manager.h"
-#include "hex.h"
#include "serverlist.h"
#include "util/string.h"
-#include "util/pointedthing.h"
#include "util/mathconstants.h"
#include "rollback.h"
#include "util/serialize.h"
#include "util/thread.h"
#include "defaultsettings.h"
+#include "util/base64.h"
+#include "util/sha1.h"
+#include "util/hex.h"
class ClientNotFoundException : public BaseException
{
@@ -88,7 +86,7 @@ public:
void * Thread();
};
-void * ServerThread::Thread()
+void *ServerThread::Thread()
{
log_register_thread("ServerThread");
@@ -101,33 +99,22 @@ void * ServerThread::Thread()
porting::setThreadName("ServerThread");
- while(!StopRequested())
- {
- try{
+ while (!StopRequested()) {
+ try {
//TimeTaker timer("AsyncRunStep() + Receive()");
m_server->AsyncRunStep();
m_server->Receive();
- }
- catch(con::NoIncomingDataException &e)
- {
- }
- catch(con::PeerNotFoundException &e)
- {
+ } catch (con::NoIncomingDataException &e) {
+ } catch (con::PeerNotFoundException &e) {
infostream<<"Server: PeerNotFoundException"<<std::endl;
- }
- catch(ClientNotFoundException &e)
- {
- }
- catch(con::ConnectionBindFailed &e)
- {
- m_server->setAsyncFatalError(e.what());
- }
- catch(LuaError &e)
- {
+ } catch (ClientNotFoundException &e) {
+ } catch (con::ConnectionBindFailed &e) {
m_server->setAsyncFatalError(e.what());
+ } catch (LuaError &e) {
+ m_server->setAsyncFatalError("Lua: " + std::string(e.what()));
}
}
@@ -193,6 +180,7 @@ Server::Server(
m_uptime(0),
m_clients(&m_con),
m_shutdown_requested(false),
+ m_shutdown_ask_reconnect(false),
m_ignore_map_edit_events(false),
m_ignore_map_edit_events_peer_id(0),
m_next_sound_id(0)
@@ -223,12 +211,9 @@ Server::Server(
infostream<<"- world: "<<m_path_world<<std::endl;
infostream<<"- game: "<<m_gamespec.path<<std::endl;
- // Initialize default settings and override defaults with those provided
- // by the game
- set_default_settings(g_settings);
- Settings gamedefaults;
- getGameMinetestConfig(gamespec.path, gamedefaults);
- override_default_settings(g_settings, &gamedefaults);
+ // Create world if it doesn't exist
+ if(!loadGameConfAndInitWorld(m_path_world, m_gamespec))
+ throw ServerError("Failed to initialize world");
// Create server thread
m_thread = new ServerThread(this);
@@ -236,10 +221,6 @@ Server::Server(
// Create emerge manager
m_emerge = new EmergeManager(this);
- // Create world if it doesn't exist
- if(!initializeWorld(m_path_world, m_gamespec.id))
- throw ServerError("Failed to initialize world");
-
// Create ban manager
std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
m_banmanager = new BanManager(ban_path);
@@ -248,11 +229,9 @@ Server::Server(
m_mods = modconf.getMods();
std::vector<ModSpec> unsatisfied_mods = modconf.getUnsatisfiedMods();
// complain about mods with unsatisfied dependencies
- if(!modconf.isConsistent())
- {
+ if(!modconf.isConsistent()) {
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
- it != unsatisfied_mods.end(); ++it)
- {
+ it != unsatisfied_mods.end(); ++it) {
ModSpec mod = *it;
errorstream << "mod \"" << mod.name << "\" has unsatisfied dependencies: ";
for(std::set<std::string>::iterator dep_it = mod.unsatisfied_depends.begin();
@@ -268,8 +247,7 @@ Server::Server(
std::vector<std::string> names = worldmt_settings.getNames();
std::set<std::string> load_mod_names;
for(std::vector<std::string>::iterator it = names.begin();
- it != names.end(); ++it)
- {
+ it != names.end(); ++it) {
std::string name = *it;
if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name))
load_mod_names.insert(name.substr(9));
@@ -281,8 +259,7 @@ Server::Server(
for(std::vector<ModSpec>::iterator it = unsatisfied_mods.begin();
it != unsatisfied_mods.end(); ++it)
load_mod_names.erase((*it).name);
- if(!load_mod_names.empty())
- {
+ if(!load_mod_names.empty()) {
errorstream << "The following mods could not be found:";
for(std::set<std::string>::iterator it = load_mod_names.begin();
it != load_mod_names.end(); ++it)
@@ -304,31 +281,41 @@ Server::Server(
m_script = new GameScripting(this);
- std::string scriptpath = getBuiltinLuaPath() + DIR_DELIM "init.lua";
+ std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua";
+ std::string error_msg;
- if (!m_script->loadScript(scriptpath))
- throw ModError("Failed to load and run " + scriptpath);
+ if (!m_script->loadMod(script_path, BUILTIN_MOD_NAME, &error_msg))
+ throw ModError("Failed to load and run " + script_path
+ + "\nError from Lua:\n" + error_msg);
- // Print 'em
- infostream<<"Server: Loading mods: ";
+ // Print mods
+ infostream << "Server: Loading mods: ";
for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++){
+ i != m_mods.end(); i++) {
const ModSpec &mod = *i;
- infostream<<mod.name<<" ";
+ infostream << mod.name << " ";
}
- infostream<<std::endl;
+ infostream << std::endl;
// Load and run "mod" scripts
- for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++){
+ for (std::vector<ModSpec>::iterator i = m_mods.begin();
+ i != m_mods.end(); i++) {
const ModSpec &mod = *i;
- std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
- infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
- <<scriptpath<<"\"]"<<std::endl;
- bool success = m_script->loadMod(scriptpath, mod.name);
- if(!success){
- errorstream<<"Server: Failed to load and run "
- <<scriptpath<<std::endl;
- throw ModError("Failed to load and run "+scriptpath);
+ if (!string_allowed(mod.name, MODNAME_ALLOWED_CHARS)) {
+ std::ostringstream err;
+ err << "Error loading mod \"" << mod.name
+ << "\": mod_name does not follow naming conventions: "
+ << "Only chararacters [a-z0-9_] are allowed." << std::endl;
+ errorstream << err.str().c_str();
+ throw ModError(err.str());
+ }
+ std::string script_path = mod.path + DIR_DELIM "init.lua";
+ infostream << " [" << padStringRight(mod.name, 12) << "] [\""
+ << script_path << "\"]" << std::endl;
+ if (!m_script->loadMod(script_path, mod.name, &error_msg)) {
+ errorstream << "Server: Failed to load and run "
+ << script_path << std::endl;
+ throw ModError("Failed to load and run " + script_path
+ + "\nError from Lua:\n" + error_msg);
}
}
@@ -338,10 +325,18 @@ Server::Server(
// Apply item aliases in the node definition manager
m_nodedef->updateAliases(m_itemdef);
+ // Apply texture overrides from texturepack/override.txt
+ std::string texture_path = g_settings->get("texture_path");
+ if (texture_path != "" && fs::IsDir(texture_path))
+ m_nodedef->applyTextureOverrides(texture_path + DIR_DELIM + "override.txt");
+
m_nodedef->setNodeRegistrationStatus(true);
// Perform pending node name resolutions
- m_nodedef->runNodeResolverCallbacks();
+ m_nodedef->runNodeResolveCallbacks();
+
+ // init the recipe hashes to speed up crafting
+ m_craftdef->initHashes(this);
// Initialize Environment
m_env = new ServerEnvironment(servermap, m_script, this, m_path_world);
@@ -389,10 +384,23 @@ Server::~Server()
// Execute script shutdown hooks
m_script->on_shutdown();
- infostream<<"Server: Saving players"<<std::endl;
+ infostream << "Server: Saving players" << std::endl;
m_env->saveLoadedPlayers();
- infostream<<"Server: Saving environment metadata"<<std::endl;
+ infostream << "Server: Kicking players" << std::endl;
+ std::string kick_msg;
+ bool reconnect = false;
+ if (getShutdownRequested()) {
+ reconnect = m_shutdown_ask_reconnect;
+ kick_msg = m_shutdown_msg;
+ }
+ if (kick_msg == "") {
+ kick_msg = g_settings->get("kick_msg_shutdown");
+ }
+ m_env->kickAllPlayers(SERVER_ACCESSDENIED_SHUTDOWN,
+ kick_msg, reconnect);
+
+ infostream << "Server: Saving environment metadata" << std::endl;
m_env->saveMeta();
}
@@ -489,8 +497,19 @@ void Server::step(float dtime)
}
// Throw if fatal error occurred in thread
std::string async_err = m_async_fatal_error.get();
- if(async_err != ""){
- throw ServerError(async_err);
+ if(async_err != "") {
+ if (m_simple_singleplayer_mode) {
+ throw ServerError(async_err);
+ }
+ else {
+ m_env->kickAllPlayers(SERVER_ACCESSDENIED_CRASH,
+ g_settings->get("kick_msg_crash"),
+ g_settings->getBool("ask_reconnect_on_crash"));
+ errorstream << "UNRECOVERABLE error occurred. Stopping server. "
+ << "Please fix the following error:" << std::endl
+ << async_err << std::endl;
+ FATAL_ERROR(async_err.c_str());
+ }
}
}
@@ -536,23 +555,18 @@ void Server::AsyncRunStep(bool initial_step)
/*
Update time of day and overall game time
*/
- {
- JMutexAutoLock envlock(m_env_mutex);
+ m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
- m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
-
- /*
- Send to clients at constant intervals
- */
+ /*
+ Send to clients at constant intervals
+ */
- m_time_of_day_send_timer -= dtime;
- if(m_time_of_day_send_timer < 0.0)
- {
- m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
- u16 time = m_env->getTimeOfDay();
- float time_speed = g_settings->getFloat("time_speed");
- SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
- }
+ m_time_of_day_send_timer -= dtime;
+ if(m_time_of_day_send_timer < 0.0) {
+ m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
+ u16 time = m_env->getTimeOfDay();
+ float time_speed = g_settings->getFloat("time_speed");
+ SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed);
}
{
@@ -580,63 +594,14 @@ void Server::AsyncRunStep(bool initial_step)
// Run Map's timers and unload unused data
ScopeProfiler sp(g_profiler, "Server: map timer and unload");
m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
- g_settings->getFloat("server_unload_unused_data_timeout"));
+ g_settings->getFloat("server_unload_unused_data_timeout"),
+ (u32)-1);
}
/*
Do background stuff
*/
- /*
- Handle players
- */
- {
- JMutexAutoLock lock(m_env_mutex);
-
- std::list<u16> clientids = m_clients.getClientIDs();
-
- ScopeProfiler sp(g_profiler, "Server: handle players");
-
- for(std::list<u16>::iterator
- i = clientids.begin();
- i != clientids.end(); ++i)
- {
- PlayerSAO *playersao = getPlayerSAO(*i);
- if(playersao == NULL)
- continue;
-
- /*
- Handle player HPs (die if hp=0)
- */
- if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage"))
- {
- if(playersao->getHP() == 0)
- DiePlayer(*i);
- else
- SendPlayerHP(*i);
- }
-
- /*
- Send player breath if changed
- */
- if(playersao->m_breath_not_sent) {
- SendPlayerBreath(*i);
- }
-
- /*
- Send player inventories if necessary
- */
- if(playersao->m_moved){
- SendMovePlayer(*i);
- playersao->m_moved = false;
- }
- if(playersao->m_inventory_not_sent){
- UpdateCrafting(*i);
- SendInventory(*i);
- }
- }
- }
-
/* Transform liquids */
m_liquid_transform_timer += dtime;
if(m_liquid_transform_timer >= m_liquid_transform_every)
@@ -821,46 +786,13 @@ void Server::AsyncRunStep(bool initial_step)
obj->m_known_by_count++;
}
- // Send packet
- SharedBuffer<u8> reply(2 + data_buffer.size());
- writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
- memcpy((char*)&reply[2], data_buffer.c_str(),
- data_buffer.size());
- // Send as reliable
- m_clients.send(client->peer_id, 0, reply, true);
-
- verbosestream<<"Server: Sent object remove/add: "
- <<removed_objects.size()<<" removed, "
- <<added_objects.size()<<" added, "
- <<"packet size is "<<reply.getSize()<<std::endl;
+ u32 pktSize = SendActiveObjectRemoveAdd(client->peer_id, data_buffer);
+ verbosestream << "Server: Sent object remove/add: "
+ << removed_objects.size() << " removed, "
+ << added_objects.size() << " added, "
+ << "packet size is " << pktSize << std::endl;
}
m_clients.Unlock();
-#if 0
- /*
- Collect a list of all the objects known by the clients
- and report it back to the environment.
- */
-
- core::map<u16, bool> all_known_objects;
-
- for(core::map<u16, RemoteClient*>::Iterator
- i = m_clients.getIterator();
- i.atEnd() == false; i++)
- {
- RemoteClient *client = i.getNode()->getValue();
- // Go through all known objects of client
- for(core::map<u16, bool>::Iterator
- i = client->m_known_objects.getIterator();
- i.atEnd()==false; i++)
- {
- u16 id = i.getNode()->getKey();
- all_known_objects[id] = true;
- }
- }
-
- m_env->setKnownActiveObjects(whatever);
-#endif
-
}
/*
@@ -872,25 +804,22 @@ void Server::AsyncRunStep(bool initial_step)
// Key = object id
// Value = data sent by object
- std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages;
+ std::map<u16, std::vector<ActiveObjectMessage>* > buffered_messages;
// Get active object messages from environment
- for(;;)
- {
+ for(;;) {
ActiveObjectMessage aom = m_env->getActiveObjectMessage();
- if(aom.id == 0)
+ if (aom.id == 0)
break;
- std::list<ActiveObjectMessage>* message_list = NULL;
- std::map<u16, std::list<ActiveObjectMessage>* >::iterator n;
+ std::vector<ActiveObjectMessage>* message_list = NULL;
+ std::map<u16, std::vector<ActiveObjectMessage>* >::iterator n;
n = buffered_messages.find(aom.id);
- if(n == buffered_messages.end())
- {
- message_list = new std::list<ActiveObjectMessage>;
+ if (n == buffered_messages.end()) {
+ message_list = new std::vector<ActiveObjectMessage>;
buffered_messages[aom.id] = message_list;
}
- else
- {
+ else {
message_list = n->second;
}
message_list->push_back(aom);
@@ -899,28 +828,26 @@ void Server::AsyncRunStep(bool initial_step)
m_clients.Lock();
std::map<u16, RemoteClient*> clients = m_clients.getClientList();
// Route data to every client
- for(std::map<u16, RemoteClient*>::iterator
+ for (std::map<u16, RemoteClient*>::iterator
i = clients.begin();
- i != clients.end(); ++i)
- {
+ i != clients.end(); ++i) {
RemoteClient *client = i->second;
std::string reliable_data;
std::string unreliable_data;
// Go through all objects in message buffer
- for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+ for (std::map<u16, std::vector<ActiveObjectMessage>* >::iterator
j = buffered_messages.begin();
- j != buffered_messages.end(); ++j)
- {
+ j != buffered_messages.end(); ++j) {
// If object is not known by client, skip it
u16 id = j->first;
- if(client->m_known_objects.find(id) == client->m_known_objects.end())
+ if (client->m_known_objects.find(id) == client->m_known_objects.end())
continue;
+
// Get message list of object
- std::list<ActiveObjectMessage>* list = j->second;
+ std::vector<ActiveObjectMessage>* list = j->second;
// Go through every message
- for(std::list<ActiveObjectMessage>::iterator
- k = list->begin(); k != list->end(); ++k)
- {
+ for (std::vector<ActiveObjectMessage>::iterator
+ k = list->begin(); k != list->end(); ++k) {
// Compose the full new data with header
ActiveObjectMessage aom = *k;
std::string new_data;
@@ -941,40 +868,20 @@ void Server::AsyncRunStep(bool initial_step)
reliable_data and unreliable_data are now ready.
Send them.
*/
- if(reliable_data.size() > 0)
- {
- SharedBuffer<u8> reply(2 + reliable_data.size());
- writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
- memcpy((char*)&reply[2], reliable_data.c_str(),
- reliable_data.size());
- // Send as reliable
- m_clients.send(client->peer_id, 0, reply, true);
- }
- if(unreliable_data.size() > 0)
- {
- SharedBuffer<u8> reply(2 + unreliable_data.size());
- writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
- memcpy((char*)&reply[2], unreliable_data.c_str(),
- unreliable_data.size());
- // Send as unreliable
- m_clients.send(client->peer_id, 1, reply, false);
+ if(reliable_data.size() > 0) {
+ SendActiveObjectMessages(client->peer_id, reliable_data);
}
- /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
- {
- infostream<<"Server: Size of object message data: "
- <<"reliable: "<<reliable_data.size()
- <<", unreliable: "<<unreliable_data.size()
- <<std::endl;
- }*/
+ if(unreliable_data.size() > 0) {
+ SendActiveObjectMessages(client->peer_id, unreliable_data, false);
+ }
}
m_clients.Unlock();
// Clear buffered_messages
- for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator
+ for(std::map<u16, std::vector<ActiveObjectMessage>* >::iterator
i = buffered_messages.begin();
- i != buffered_messages.end(); ++i)
- {
+ i != buffered_messages.end(); ++i) {
delete i->second;
}
}
@@ -1001,83 +908,67 @@ void Server::AsyncRunStep(bool initial_step)
while(m_unsent_map_edit_queue.size() != 0)
{
- MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
+ MapEditEvent* event = m_unsent_map_edit_queue.front();
+ m_unsent_map_edit_queue.pop();
// Players far away from the change are stored here.
// Instead of sending the changes, MapBlocks are set not sent
// for them.
- std::list<u16> far_players;
+ std::vector<u16> far_players;
- if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE)
- {
- //infostream<<"Server: MEET_ADDNODE"<<std::endl;
+ switch (event->type) {
+ case MEET_ADDNODE:
+ case MEET_SWAPNODE:
prof.add("MEET_ADDNODE", 1);
- if(disable_single_change_sending)
- sendAddNode(event->p, event->n, event->already_known_by_peer,
- &far_players, 5, event->type == MEET_ADDNODE);
- else
- sendAddNode(event->p, event->n, event->already_known_by_peer,
- &far_players, 30, event->type == MEET_ADDNODE);
- }
- else if(event->type == MEET_REMOVENODE)
- {
- //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
+ sendAddNode(event->p, event->n, event->already_known_by_peer,
+ &far_players, disable_single_change_sending ? 5 : 30,
+ event->type == MEET_ADDNODE);
+ break;
+ case MEET_REMOVENODE:
prof.add("MEET_REMOVENODE", 1);
- if(disable_single_change_sending)
- sendRemoveNode(event->p, event->already_known_by_peer,
- &far_players, 5);
- else
- sendRemoveNode(event->p, event->already_known_by_peer,
- &far_players, 30);
- }
- else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
- {
- infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
- prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
- setBlockNotSent(event->p);
- }
- else if(event->type == MEET_OTHER)
- {
- infostream<<"Server: MEET_OTHER"<<std::endl;
+ sendRemoveNode(event->p, event->already_known_by_peer,
+ &far_players, disable_single_change_sending ? 5 : 30);
+ break;
+ case MEET_BLOCK_NODE_METADATA_CHANGED:
+ infostream << "Server: MEET_BLOCK_NODE_METADATA_CHANGED" << std::endl;
+ prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
+ setBlockNotSent(event->p);
+ break;
+ case MEET_OTHER:
+ infostream << "Server: MEET_OTHER" << std::endl;
prof.add("MEET_OTHER", 1);
for(std::set<v3s16>::iterator
i = event->modified_blocks.begin();
- i != event->modified_blocks.end(); ++i)
- {
+ i != event->modified_blocks.end(); ++i) {
setBlockNotSent(*i);
}
- }
- else
- {
+ break;
+ default:
prof.add("unknown", 1);
- infostream<<"WARNING: Server: Unknown MapEditEvent "
- <<((u32)event->type)<<std::endl;
+ infostream << "WARNING: Server: Unknown MapEditEvent "
+ << ((u32)event->type) << std::endl;
+ break;
}
/*
Set blocks not sent to far players
*/
- if(!far_players.empty())
- {
+ if(!far_players.empty()) {
// Convert list format to that wanted by SetBlocksNotSent
std::map<v3s16, MapBlock*> modified_blocks2;
for(std::set<v3s16>::iterator
i = event->modified_blocks.begin();
- i != event->modified_blocks.end(); ++i)
- {
+ i != event->modified_blocks.end(); ++i) {
modified_blocks2[*i] =
m_env->getMap().getBlockNoCreateNoEx(*i);
}
+
// Set blocks not sent
- for(std::list<u16>::iterator
+ for(std::vector<u16>::iterator
i = far_players.begin();
- i != far_players.end(); ++i)
- {
- u16 peer_id = *i;
- RemoteClient *client = getClient(peer_id);
- if(client==NULL)
- continue;
- client->SetBlocksNotSent(modified_blocks2);
+ i != far_players.end(); ++i) {
+ if(RemoteClient *client = getClient(*i))
+ client->SetBlocksNotSent(modified_blocks2);
}
}
@@ -1147,13 +1038,13 @@ void Server::Receive()
DSTACK(__FUNCTION_NAME);
SharedBuffer<u8> data;
u16 peer_id;
- u32 datasize;
- try{
- datasize = m_con.Receive(peer_id,data);
- ProcessData(*data, datasize, peer_id);
+ try {
+ NetworkPacket pkt;
+ m_con.Receive(&pkt);
+ peer_id = pkt.getPeerId();
+ ProcessData(&pkt);
}
- catch(con::InvalidIncomingDataException &e)
- {
+ catch(con::InvalidIncomingDataException &e) {
infostream<<"Server::Receive(): "
"InvalidIncomingDataException: what()="
<<e.what()<<std::endl;
@@ -1163,14 +1054,12 @@ void Server::Receive()
"SerializationError: what()="
<<e.what()<<std::endl;
}
- catch(ClientStateError &e)
- {
+ catch(ClientStateError &e) {
errorstream << "ProcessData: peer=" << peer_id << e.what() << std::endl;
- DenyAccess(peer_id, L"Your client sent something server didn't expect."
+ DenyAccess_Legacy(peer_id, L"Your client sent something server didn't expect."
L"Try reconnecting or updating your client");
}
- catch(con::PeerNotFoundException &e)
- {
+ catch(con::PeerNotFoundException &e) {
// Do nothing
}
}
@@ -1184,7 +1073,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id, CS_InitDone);
if (client != NULL) {
playername = client->getName();
- playersao = emergePlayer(playername.c_str(), peer_id);
+ playersao = emergePlayer(playername.c_str(), peer_id, client->net_proto_version);
}
} catch (std::exception &e) {
m_clients.Unlock();
@@ -1196,18 +1085,17 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str()));
// If failed, cancel
- if((playersao == NULL) || (player == NULL))
- {
- if(player && player->peer_id != 0){
- errorstream<<"Server: "<<playername<<": Failed to emerge player"
- <<" (player allocated to an another client)"<<std::endl;
- DenyAccess(peer_id, L"Another client is connected with this "
+ if ((playersao == NULL) || (player == NULL)) {
+ if (player && player->peer_id != 0) {
+ actionstream << "Server: Failed to emerge player \"" << playername
+ << "\" (player allocated to an another client)" << std::endl;
+ DenyAccess_Legacy(peer_id, L"Another client is connected with this "
L"name. If your client closed unexpectedly, try again in "
L"a minute.");
} else {
- errorstream<<"Server: "<<playername<<": Failed to emerge player"
- <<std::endl;
- DenyAccess(peer_id, L"Could not allocate player.");
+ errorstream << "Server: " << playername << ": Failed to emerge player"
+ << std::endl;
+ DenyAccess_Legacy(peer_id, L"Could not allocate player.");
}
return NULL;
}
@@ -1224,23 +1112,20 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
SendPlayerInventoryFormspec(peer_id);
// Send inventory
- UpdateCrafting(peer_id);
- SendInventory(peer_id);
+ SendInventory(playersao);
// Send HP
- if(g_settings->getBool("enable_damage"))
- SendPlayerHP(peer_id);
+ SendPlayerHPOrDie(playersao);
// Send Breath
SendPlayerBreath(peer_id);
// Show death screen if necessary
- if(player->hp == 0)
+ if(player->isDead())
SendDeathscreen(peer_id, false, v3f(0,0,0));
// Note things in chat if not in simple singleplayer mode
- if(!m_simple_singleplayer_mode)
- {
+ if(!m_simple_singleplayer_mode) {
// Send information about server to player in chat
SendChatMessage(peer_id, getStatusString());
@@ -1270,8 +1155,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
actionstream<<player->getName() <<" joins game. List of players: ";
for (std::vector<std::string>::iterator i = names.begin();
- i != names.end(); i++)
- {
+ i != names.end(); i++) {
actionstream << *i << " ";
}
@@ -1280,1467 +1164,96 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
return playersao;
}
-void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
+inline void Server::handleCommand(NetworkPacket* pkt)
+{
+ const ToServerCommandHandler& opHandle = toServerCommandTable[pkt->getCommand()];
+ (this->*opHandle.handler)(pkt);
+}
+
+void Server::ProcessData(NetworkPacket *pkt)
{
DSTACK(__FUNCTION_NAME);
// Environment is locked first.
JMutexAutoLock envlock(m_env_mutex);
ScopeProfiler sp(g_profiler, "Server::ProcessData");
+ u32 peer_id = pkt->getPeerId();
- std::string addr_s;
- try{
+ try {
Address address = getPeerAddress(peer_id);
- addr_s = address.serializeString();
+ std::string addr_s = address.serializeString();
- // drop player if is ip is banned
- if(m_banmanager->isIpBanned(addr_s)){
+ if(m_banmanager->isIpBanned(addr_s)) {
std::string ban_name = m_banmanager->getBanName(addr_s);
- infostream<<"Server: A banned client tried to connect from "
- <<addr_s<<"; banned name was "
- <<ban_name<<std::endl;
+ infostream << "Server: A banned client tried to connect from "
+ << addr_s << "; banned name was "
+ << ban_name << std::endl;
// This actually doesn't seem to transfer to the client
- DenyAccess(peer_id, L"Your ip is banned. Banned name was "
- +narrow_to_wide(ban_name));
+ DenyAccess_Legacy(peer_id, L"Your ip is banned. Banned name was "
+ + utf8_to_wide(ban_name));
return;
}
}
- catch(con::PeerNotFoundException &e)
- {
+ catch(con::PeerNotFoundException &e) {
/*
* no peer for this packet found
* most common reason is peer timeout, e.g. peer didn't
* respond for some time, your server was overloaded or
* things like that.
*/
- infostream<<"Server::ProcessData(): Cancelling: peer "
- <<peer_id<<" not found"<<std::endl;
- return;
- }
-
- try
- {
-
- if(datasize < 2)
- return;
-
- ToServerCommand command = (ToServerCommand)readU16(&data[0]);
-
- if(command == TOSERVER_INIT)
- {
- // [0] u16 TOSERVER_INIT
- // [2] u8 SER_FMT_VER_HIGHEST_READ
- // [3] u8[20] player_name
- // [23] u8[28] password <--- can be sent without this, from old versions
-
- if(datasize < 2+1+PLAYERNAME_SIZE)
- return;
-
- RemoteClient* client = getClient(peer_id, CS_Created);
-
- // If net_proto_version is set, this client has already been handled
- if(client->getState() > CS_Created)
- {
- verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from "
- <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
- return;
- }
-
- verbosestream<<"Server: Got TOSERVER_INIT from "<<addr_s<<" (peer_id="
- <<peer_id<<")"<<std::endl;
-
- // Do not allow multiple players in simple singleplayer mode.
- // This isn't a perfect way to do it, but will suffice for now
- if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){
- infostream<<"Server: Not allowing another client ("<<addr_s
- <<") to connect in simple singleplayer mode"<<std::endl;
- DenyAccess(peer_id, L"Running in simple singleplayer mode.");
- return;
- }
-
- // First byte after command is maximum supported
- // serialization version
- u8 client_max = data[2];
- u8 our_max = SER_FMT_VER_HIGHEST_READ;
- // Use the highest version supported by both
- int deployed = std::min(client_max, our_max);
- // If it's lower than the lowest supported, give up.
- if(deployed < SER_FMT_CLIENT_VER_LOWEST)
- deployed = SER_FMT_VER_INVALID;
-
- if(deployed == SER_FMT_VER_INVALID)
- {
- actionstream<<"Server: A mismatched client tried to connect from "
- <<addr_s<<std::endl;
- infostream<<"Server: Cannot negotiate serialization version with "
- <<addr_s<<std::endl;
- DenyAccess(peer_id, std::wstring(
- L"Your client's version is not supported.\n"
- L"Server version is ")
- + narrow_to_wide(minetest_version_simple) + L"."
- );
- return;
- }
-
- client->setPendingSerializationVersion(deployed);
-
- /*
- Read and check network protocol version
- */
-
- u16 min_net_proto_version = 0;
- if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
- min_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
-
- // Use same version as minimum and maximum if maximum version field
- // doesn't exist (backwards compatibility)
- u16 max_net_proto_version = min_net_proto_version;
- if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2+2)
- max_net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2]);
-
- // Start with client's maximum version
- u16 net_proto_version = max_net_proto_version;
-
- // Figure out a working version if it is possible at all
- if(max_net_proto_version >= SERVER_PROTOCOL_VERSION_MIN ||
- min_net_proto_version <= SERVER_PROTOCOL_VERSION_MAX)
- {
- // If maximum is larger than our maximum, go with our maximum
- if(max_net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
- net_proto_version = SERVER_PROTOCOL_VERSION_MAX;
- // Else go with client's maximum
- else
- net_proto_version = max_net_proto_version;
- }
-
- verbosestream<<"Server: "<<addr_s<<": Protocol version: min: "
- <<min_net_proto_version<<", max: "<<max_net_proto_version
- <<", chosen: "<<net_proto_version<<std::endl;
-
- client->net_proto_version = net_proto_version;
-
- if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
- net_proto_version > SERVER_PROTOCOL_VERSION_MAX)
- {
- actionstream<<"Server: A mismatched client tried to connect from "
- <<addr_s<<std::endl;
- DenyAccess(peer_id, std::wstring(
- L"Your client's version is not supported.\n"
- L"Server version is ")
- + narrow_to_wide(minetest_version_simple) + L",\n"
- + L"server's PROTOCOL_VERSION is "
- + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MIN))
- + L"..."
- + narrow_to_wide(itos(SERVER_PROTOCOL_VERSION_MAX))
- + L", client's PROTOCOL_VERSION is "
- + narrow_to_wide(itos(min_net_proto_version))
- + L"..."
- + narrow_to_wide(itos(max_net_proto_version))
- );
- return;
- }
-
- if(g_settings->getBool("strict_protocol_version_checking"))
- {
- if(net_proto_version != LATEST_PROTOCOL_VERSION)
- {
- actionstream<<"Server: A mismatched (strict) client tried to "
- <<"connect from "<<addr_s<<std::endl;
- DenyAccess(peer_id, std::wstring(
- L"Your client's version is not supported.\n"
- L"Server version is ")
- + narrow_to_wide(minetest_version_simple) + L",\n"
- + L"server's PROTOCOL_VERSION (strict) is "
- + narrow_to_wide(itos(LATEST_PROTOCOL_VERSION))
- + L", client's PROTOCOL_VERSION is "
- + narrow_to_wide(itos(min_net_proto_version))
- + L"..."
- + narrow_to_wide(itos(max_net_proto_version))
- );
- return;
- }
- }
-
- /*
- Set up player
- */
- char playername[PLAYERNAME_SIZE];
- unsigned int playername_length = 0;
- for (; playername_length < PLAYERNAME_SIZE; playername_length++ ) {
- playername[playername_length] = data[3+playername_length];
- if (data[3+playername_length] == 0)
- break;
- }
-
- if (playername_length == PLAYERNAME_SIZE) {
- actionstream<<"Server: Player with name exceeding max length "
- <<"tried to connect from "<<addr_s<<std::endl;
- DenyAccess(peer_id, L"Name too long");
- return;
- }
-
-
- if(playername[0]=='\0')
- {
- actionstream<<"Server: Player with an empty name "
- <<"tried to connect from "<<addr_s<<std::endl;
- DenyAccess(peer_id, L"Empty name");
- return;
- }
-
- if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
- {
- actionstream<<"Server: Player with an invalid name "
- <<"tried to connect from "<<addr_s<<std::endl;
- DenyAccess(peer_id, L"Name contains unallowed characters");
- return;
- }
-
- if(!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0)
- {
- actionstream<<"Server: Player with the name \"singleplayer\" "
- <<"tried to connect from "<<addr_s<<std::endl;
- DenyAccess(peer_id, L"Name is not allowed");
- return;
- }
-
- {
- std::string reason;
- if(m_script->on_prejoinplayer(playername, addr_s, reason))
- {
- actionstream<<"Server: Player with the name \""<<playername<<"\" "
- <<"tried to connect from "<<addr_s<<" "
- <<"but it was disallowed for the following reason: "
- <<reason<<std::endl;
- DenyAccess(peer_id, narrow_to_wide(reason));
- return;
- }
- }
-
- infostream<<"Server: New connection: \""<<playername<<"\" from "
- <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl;
-
- // Get password
- char given_password[PASSWORD_SIZE];
- if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
- {
- // old version - assume blank password
- given_password[0] = 0;
- }
- else
- {
- for(u32 i=0; i<PASSWORD_SIZE-1; i++)
- {
- given_password[i] = data[23+i];
- }
- given_password[PASSWORD_SIZE-1] = 0;
- }
-
- if(!base64_is_valid(given_password)){
- actionstream<<"Server: "<<playername
- <<" supplied invalid password hash"<<std::endl;
- DenyAccess(peer_id, L"Invalid password hash");
- return;
- }
-
- // Enforce user limit.
- // Don't enforce for users that have some admin right
- if(m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
- !checkPriv(playername, "server") &&
- !checkPriv(playername, "ban") &&
- !checkPriv(playername, "privs") &&
- !checkPriv(playername, "password") &&
- playername != g_settings->get("name"))
- {
- actionstream<<"Server: "<<playername<<" tried to join, but there"
- <<" are already max_users="
- <<g_settings->getU16("max_users")<<" players."<<std::endl;
- DenyAccess(peer_id, L"Too many users.");
- return;
- }
-
- std::string checkpwd; // Password hash to check against
- bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
-
- // If no authentication info exists for user, create it
- if(!has_auth){
- if(!isSingleplayer() &&
- g_settings->getBool("disallow_empty_password") &&
- std::string(given_password) == ""){
- actionstream<<"Server: "<<playername
- <<" supplied empty password"<<std::endl;
- DenyAccess(peer_id, L"Empty passwords are "
- L"disallowed. Set a password and try again.");
- return;
- }
- std::wstring raw_default_password =
- narrow_to_wide(g_settings->get("default_password"));
- std::string initial_password =
- translatePassword(playername, raw_default_password);
-
- // If default_password is empty, allow any initial password
- if (raw_default_password.length() == 0)
- initial_password = given_password;
-
- m_script->createAuth(playername, initial_password);
- }
-
- has_auth = m_script->getAuth(playername, &checkpwd, NULL);
-
- if(!has_auth){
- actionstream<<"Server: "<<playername<<" cannot be authenticated"
- <<" (auth handler does not work?)"<<std::endl;
- DenyAccess(peer_id, L"Not allowed to login");
- return;
- }
-
- if(given_password != checkpwd){
- actionstream<<"Server: "<<playername<<" supplied wrong password"
- <<std::endl;
- DenyAccess(peer_id, L"Wrong password");
- return;
- }
-
- RemotePlayer *player =
- static_cast<RemotePlayer*>(m_env->getPlayer(playername));
-
- if(player && player->peer_id != 0){
- errorstream<<"Server: "<<playername<<": Failed to emerge player"
- <<" (player allocated to an another client)"<<std::endl;
- DenyAccess(peer_id, L"Another client is connected with this "
- L"name. If your client closed unexpectedly, try again in "
- L"a minute.");
- }
-
- m_clients.setPlayerName(peer_id,playername);
-
- /*
- Answer with a TOCLIENT_INIT
- */
- {
- SharedBuffer<u8> reply(2+1+6+8+4);
- writeU16(&reply[0], TOCLIENT_INIT);
- writeU8(&reply[2], deployed);
- //send dummy pos for legacy reasons only
- writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS));
- writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
- writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step"));
-
- // Send as reliable
- m_clients.send(peer_id, 0, reply, true);
- m_clients.event(peer_id, CSE_Init);
- }
-
- return;
- }
-
- if(command == TOSERVER_INIT2)
- {
-
- verbosestream<<"Server: Got TOSERVER_INIT2 from "
- <<peer_id<<std::endl;
-
- m_clients.event(peer_id, CSE_GotInit2);
- u16 protocol_version = m_clients.getProtocolVersion(peer_id);
-
-
- ///// begin compatibility code
- PlayerSAO* playersao = NULL;
- if (protocol_version <= 22) {
- playersao = StageTwoClientInit(peer_id);
-
- if (playersao == NULL) {
- errorstream
- << "TOSERVER_INIT2 stage 2 client init failed for peer "
- << peer_id << std::endl;
- return;
- }
- }
- ///// end compatibility code
-
- /*
- Send some initialization data
- */
-
- infostream<<"Server: Sending content to "
- <<getPlayerName(peer_id)<<std::endl;
-
- // Send player movement settings
- SendMovement(peer_id);
-
- // Send item definitions
- SendItemDef(peer_id, m_itemdef, protocol_version);
-
- // Send node definitions
- SendNodeDef(peer_id, m_nodedef, protocol_version);
-
- m_clients.event(peer_id, CSE_SetDefinitionsSent);
-
- // Send media announcement
- sendMediaAnnouncement(peer_id);
-
- // Send detached inventories
- sendDetachedInventories(peer_id);
-
- // Send time of day
- u16 time = m_env->getTimeOfDay();
- float time_speed = g_settings->getFloat("time_speed");
- SendTimeOfDay(peer_id, time, time_speed);
-
- ///// begin compatibility code
- if (protocol_version <= 22) {
- m_clients.event(peer_id, CSE_SetClientReady);
- m_script->on_joinplayer(playersao);
- }
- ///// end compatibility code
-
- // Warnings about protocol version can be issued here
- if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION)
- {
- SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S "
- L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!");
- }
-
- return;
- }
-
- u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
- u16 peer_proto_ver = getClient(peer_id, CS_InitDone)->net_proto_version;
-
- if(peer_ser_ver == SER_FMT_VER_INVALID)
- {
- errorstream<<"Server::ProcessData(): Cancelling: Peer"
- " serialization format invalid or not initialized."
- " Skipping incoming command="<<command<<std::endl;
+ infostream << "Server::ProcessData(): Canceling: peer "
+ << peer_id << " not found" << std::endl;
return;
}
- /* Handle commands relate to client startup */
- if(command == TOSERVER_REQUEST_MEDIA) {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- std::list<std::string> tosend;
- u16 numfiles = readU16(is);
-
- infostream<<"Sending "<<numfiles<<" files to "
- <<getPlayerName(peer_id)<<std::endl;
- verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
-
- for(int i = 0; i < numfiles; i++) {
- std::string name = deSerializeString(is);
- tosend.push_back(name);
- verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
- <<name<<std::endl;
- }
-
- sendRequestedMedia(peer_id, tosend);
- return;
- }
- else if(command == TOSERVER_RECEIVED_MEDIA) {
- return;
- }
- else if(command == TOSERVER_CLIENT_READY) {
- // clients <= protocol version 22 did not send ready message,
- // they're already initialized
- if (peer_proto_ver <= 22) {
- infostream << "Client sent message not expected by a "
- << "client using protocol version <= 22,"
- << "disconnecing peer_id: " << peer_id << std::endl;
- m_con.DisconnectPeer(peer_id);
- return;
- }
-
- PlayerSAO* playersao = StageTwoClientInit(peer_id);
-
- if (playersao == NULL) {
- errorstream
- << "TOSERVER_CLIENT_READY stage 2 client init failed for peer_id: "
- << peer_id << std::endl;
- m_con.DisconnectPeer(peer_id);
- return;
- }
-
-
- if(datasize < 2+8) {
- errorstream
- << "TOSERVER_CLIENT_READY client sent inconsistent data, disconnecting peer_id: "
- << peer_id << std::endl;
- m_con.DisconnectPeer(peer_id);
- return;
- }
-
- m_clients.setClientVersion(
- peer_id,
- data[2], data[3], data[4],
- std::string((char*) &data[8],(u16) data[6]));
-
- m_clients.event(peer_id, CSE_SetClientReady);
- m_script->on_joinplayer(playersao);
-
- }
- else if(command == TOSERVER_GOTBLOCKS)
- {
- if(datasize < 2+1)
- return;
-
- /*
- [0] u16 command
- [2] u8 count
- [3] v3s16 pos_0
- [3+6] v3s16 pos_1
- ...
- */
-
- u16 count = data[2];
- for(u16 i=0; i<count; i++)
- {
- if((s16)datasize < 2+1+(i+1)*6)
- throw con::InvalidIncomingDataException
- ("GOTBLOCKS length is too short");
- v3s16 p = readV3S16(&data[2+1+i*6]);
- /*infostream<<"Server: GOTBLOCKS ("
- <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
- RemoteClient *client = getClient(peer_id);
- client->GotBlock(p);
- }
- return;
- }
-
- if (m_clients.getClientState(peer_id) < CS_Active)
- {
- if (command == TOSERVER_PLAYERPOS) return;
-
- errorstream<<"Got packet command: " << command << " for peer id "
- << peer_id << " but client isn't active yet. Dropping packet "
- <<std::endl;
- return;
- }
-
- Player *player = m_env->getPlayer(peer_id);
- if(player == NULL) {
- errorstream<<"Server::ProcessData(): Cancelling: "
- "No player for peer_id="<<peer_id
- << " disconnecting peer!" <<std::endl;
- m_con.DisconnectPeer(peer_id);
- return;
- }
-
- PlayerSAO *playersao = player->getPlayerSAO();
- if(playersao == NULL) {
- errorstream<<"Server::ProcessData(): Cancelling: "
- "No player object for peer_id="<<peer_id
- << " disconnecting peer!" <<std::endl;
- m_con.DisconnectPeer(peer_id);
- return;
- }
-
- if(command == TOSERVER_PLAYERPOS)
- {
- if(datasize < 2+12+12+4+4)
- return;
-
- u32 start = 0;
- v3s32 ps = readV3S32(&data[start+2]);
- v3s32 ss = readV3S32(&data[start+2+12]);
- f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
- f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
- u32 keyPressed = 0;
- if(datasize >= 2+12+12+4+4+4)
- keyPressed = (u32)readU32(&data[2+12+12+4+4]);
- v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
- v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
- pitch = wrapDegrees(pitch);
- yaw = wrapDegrees(yaw);
-
- player->setPosition(position);
- player->setSpeed(speed);
- player->setPitch(pitch);
- player->setYaw(yaw);
- player->keyPressed=keyPressed;
- player->control.up = (bool)(keyPressed&1);
- player->control.down = (bool)(keyPressed&2);
- player->control.left = (bool)(keyPressed&4);
- player->control.right = (bool)(keyPressed&8);
- player->control.jump = (bool)(keyPressed&16);
- player->control.aux1 = (bool)(keyPressed&32);
- player->control.sneak = (bool)(keyPressed&64);
- player->control.LMB = (bool)(keyPressed&128);
- player->control.RMB = (bool)(keyPressed&256);
-
- bool cheated = playersao->checkMovementCheat();
- if(cheated){
- // Call callbacks
- m_script->on_cheat(playersao, "moved_too_fast");
- }
-
- /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
- <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
- <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
- }
- else if(command == TOSERVER_DELETEDBLOCKS)
- {
- if(datasize < 2+1)
- return;
-
- /*
- [0] u16 command
- [2] u8 count
- [3] v3s16 pos_0
- [3+6] v3s16 pos_1
- ...
- */
-
- u16 count = data[2];
- for(u16 i=0; i<count; i++)
- {
- if((s16)datasize < 2+1+(i+1)*6)
- throw con::InvalidIncomingDataException
- ("DELETEDBLOCKS length is too short");
- v3s16 p = readV3S16(&data[2+1+i*6]);
- /*infostream<<"Server: DELETEDBLOCKS ("
- <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
- RemoteClient *client = getClient(peer_id);
- client->SetBlockNotSent(p);
- }
- }
- else if(command == TOSERVER_CLICK_OBJECT)
- {
- infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
- return;
- }
- else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
- {
- infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
- return;
- }
- else if(command == TOSERVER_GROUND_ACTION)
- {
- infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
- return;
-
- }
- else if(command == TOSERVER_RELEASE)
- {
- infostream<<"Server: RELEASE not supported anymore"<<std::endl;
- return;
- }
- else if(command == TOSERVER_SIGNTEXT)
- {
- infostream<<"Server: SIGNTEXT not supported anymore"
- <<std::endl;
- return;
- }
- else if(command == TOSERVER_SIGNNODETEXT)
- {
- infostream<<"Server: SIGNNODETEXT not supported anymore"
- <<std::endl;
- return;
- }
- else if(command == TOSERVER_INVENTORY_ACTION)
- {
- // Strip command and create a stream
- std::string datastring((char*)&data[2], datasize-2);
- verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
- std::istringstream is(datastring, std::ios_base::binary);
- // Create an action
- InventoryAction *a = InventoryAction::deSerialize(is);
- if(a == NULL)
- {
- infostream<<"TOSERVER_INVENTORY_ACTION: "
- <<"InventoryAction::deSerialize() returned NULL"
- <<std::endl;
- return;
- }
-
- // If something goes wrong, this player is to blame
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- /*
- Note: Always set inventory not sent, to repair cases
- where the client made a bad prediction.
- */
-
- /*
- Handle restrictions and special cases of the move action
- */
- if(a->getType() == IACTION_MOVE)
- {
- IMoveAction *ma = (IMoveAction*)a;
-
- ma->from_inv.applyCurrentPlayer(player->getName());
- ma->to_inv.applyCurrentPlayer(player->getName());
-
- setInventoryModified(ma->from_inv);
- setInventoryModified(ma->to_inv);
-
- bool from_inv_is_current_player =
- (ma->from_inv.type == InventoryLocation::PLAYER) &&
- (ma->from_inv.name == player->getName());
-
- bool to_inv_is_current_player =
- (ma->to_inv.type == InventoryLocation::PLAYER) &&
- (ma->to_inv.name == player->getName());
-
- /*
- Disable moving items out of craftpreview
- */
- if(ma->from_list == "craftpreview")
- {
- infostream<<"Ignoring IMoveAction from "
- <<(ma->from_inv.dump())<<":"<<ma->from_list
- <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
- <<" because src is "<<ma->from_list<<std::endl;
- delete a;
- return;
- }
-
- /*
- Disable moving items into craftresult and craftpreview
- */
- if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
- {
- infostream<<"Ignoring IMoveAction from "
- <<(ma->from_inv.dump())<<":"<<ma->from_list
- <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
- <<" because dst is "<<ma->to_list<<std::endl;
- delete a;
- return;
- }
-
- // Disallow moving items in elsewhere than player's inventory
- // if not allowed to interact
- if(!checkPriv(player->getName(), "interact") &&
- (!from_inv_is_current_player ||
- !to_inv_is_current_player))
- {
- infostream<<"Cannot move outside of player's inventory: "
- <<"No interact privilege"<<std::endl;
- delete a;
- return;
- }
- }
- /*
- Handle restrictions and special cases of the drop action
- */
- else if(a->getType() == IACTION_DROP)
- {
- IDropAction *da = (IDropAction*)a;
-
- da->from_inv.applyCurrentPlayer(player->getName());
-
- setInventoryModified(da->from_inv);
-
- /*
- Disable dropping items out of craftpreview
- */
- if(da->from_list == "craftpreview")
- {
- infostream<<"Ignoring IDropAction from "
- <<(da->from_inv.dump())<<":"<<da->from_list
- <<" because src is "<<da->from_list<<std::endl;
- delete a;
- return;
- }
-
- // Disallow dropping items if not allowed to interact
- if(!checkPriv(player->getName(), "interact"))
- {
- delete a;
- return;
- }
- }
- /*
- Handle restrictions and special cases of the craft action
- */
- else if(a->getType() == IACTION_CRAFT)
- {
- ICraftAction *ca = (ICraftAction*)a;
-
- ca->craft_inv.applyCurrentPlayer(player->getName());
-
- setInventoryModified(ca->craft_inv);
-
- //bool craft_inv_is_current_player =
- // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
- // (ca->craft_inv.name == player->getName());
-
- // Disallow crafting if not allowed to interact
- if(!checkPriv(player->getName(), "interact"))
- {
- infostream<<"Cannot craft: "
- <<"No interact privilege"<<std::endl;
- delete a;
- return;
- }
- }
-
- // Do the action
- a->apply(this, playersao, this);
- // Eat the action
- delete a;
- }
- else if(command == TOSERVER_CHAT_MESSAGE)
- {
- /*
- u16 command
- u16 length
- wstring message
- */
- u8 buf[6];
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- // Read stuff
- is.read((char*)buf, 2);
- u16 len = readU16(buf);
-
- std::wstring message;
- for(u16 i=0; i<len; i++)
- {
- is.read((char*)buf, 2);
- message += (wchar_t)readU16(buf);
- }
-
- // If something goes wrong, this player is to blame
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- // Get player name of this client
- std::wstring name = narrow_to_wide(player->getName());
-
- // Run script hook
- bool ate = m_script->on_chat_message(player->getName(),
- wide_to_narrow(message));
- // If script ate the message, don't proceed
- if(ate)
- return;
-
- // Line to send to players
- std::wstring line;
- // Whether to send to the player that sent the line
- bool send_to_sender_only = false;
-
- // Commands are implemented in Lua, so only catch invalid
- // commands that were not "eaten" and send an error back
- if(message[0] == L'/')
- {
- message = message.substr(1);
- send_to_sender_only = true;
- if(message.length() == 0)
- line += L"-!- Empty command";
- else
- line += L"-!- Invalid command: " + str_split(message, L' ')[0];
- }
- else
- {
- if(checkPriv(player->getName(), "shout")){
- line += L"<";
- line += name;
- line += L"> ";
- line += message;
- } else {
- line += L"-!- You don't have permission to shout.";
- send_to_sender_only = true;
- }
- }
-
- if(line != L"")
- {
- /*
- Send the message to sender
- */
- if (send_to_sender_only)
- {
- SendChatMessage(peer_id, line);
- }
- /*
- Send the message to others
- */
- else
- {
- actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
-
- std::list<u16> clients = m_clients.getClientIDs();
-
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
- if (*i != peer_id)
- SendChatMessage(*i, line);
- }
- }
- }
- }
- else if(command == TOSERVER_DAMAGE)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- u8 damage = readU8(is);
-
- if(g_settings->getBool("enable_damage"))
- {
- actionstream<<player->getName()<<" damaged by "
- <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
- <<std::endl;
-
- playersao->setHP(playersao->getHP() - damage);
-
- if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
- DiePlayer(peer_id);
-
- if(playersao->m_hp_not_sent)
- SendPlayerHP(peer_id);
- }
- }
- else if(command == TOSERVER_BREATH)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
- u16 breath = readU16(is);
- playersao->setBreath(breath);
- m_script->player_event(playersao,"breath_changed");
- }
- else if(command == TOSERVER_PASSWORD)
- {
- /*
- [0] u16 TOSERVER_PASSWORD
- [2] u8[28] old password
- [30] u8[28] new password
- */
+ try {
+ ToServerCommand command = (ToServerCommand) pkt->getCommand();
- if(datasize != 2+PASSWORD_SIZE*2)
+ // Command must be handled into ToServerCommandHandler
+ if (command >= TOSERVER_NUM_MSG_TYPES) {
+ infostream << "Server: Ignoring unknown command "
+ << command << std::endl;
return;
- /*char password[PASSWORD_SIZE];
- for(u32 i=0; i<PASSWORD_SIZE-1; i++)
- password[i] = data[2+i];
- password[PASSWORD_SIZE-1] = 0;*/
- std::string oldpwd;
- for(u32 i=0; i<PASSWORD_SIZE-1; i++)
- {
- char c = data[2+i];
- if(c == 0)
- break;
- oldpwd += c;
- }
- std::string newpwd;
- for(u32 i=0; i<PASSWORD_SIZE-1; i++)
- {
- char c = data[2+PASSWORD_SIZE+i];
- if(c == 0)
- break;
- newpwd += c;
}
- if(!base64_is_valid(newpwd)){
- infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
- // Wrong old password supplied!!
- SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
+ if (toServerCommandTable[command].state == TOSERVER_STATE_NOT_CONNECTED) {
+ handleCommand(pkt);
return;
}
- infostream<<"Server: Client requests a password change from "
- <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
+ u8 peer_ser_ver = getClient(peer_id, CS_InitDone)->serialization_version;
- std::string playername = player->getName();
-
- std::string checkpwd;
- m_script->getAuth(playername, &checkpwd, NULL);
-
- if(oldpwd != checkpwd)
- {
- infostream<<"Server: invalid old password"<<std::endl;
- // Wrong old password supplied!!
- SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
+ if(peer_ser_ver == SER_FMT_VER_INVALID) {
+ errorstream << "Server::ProcessData(): Cancelling: Peer"
+ " serialization format invalid or not initialized."
+ " Skipping incoming command=" << command << std::endl;
return;
}
- bool success = m_script->setPassword(playername, newpwd);
- if(success){
- actionstream<<player->getName()<<" changes password"<<std::endl;
- SendChatMessage(peer_id, L"Password change successful.");
- } else {
- actionstream<<player->getName()<<" tries to change password but "
- <<"it fails"<<std::endl;
- SendChatMessage(peer_id, L"Password change failed or inavailable.");
- }
- }
- else if(command == TOSERVER_PLAYERITEM)
- {
- if (datasize < 2+2)
- return;
-
- u16 item = readU16(&data[2]);
- playersao->setWieldIndex(item);
- }
- else if(command == TOSERVER_RESPAWN)
- {
- if(player->hp != 0 || !g_settings->getBool("enable_damage"))
+ /* Handle commands related to client startup */
+ if (toServerCommandTable[command].state == TOSERVER_STATE_STARTUP) {
+ handleCommand(pkt);
return;
-
- RespawnPlayer(peer_id);
-
- actionstream<<player->getName()<<" respawns at "
- <<PP(player->getPosition()/BS)<<std::endl;
-
- // ActiveObject is added to environment in AsyncRunStep after
- // the previous addition has been succesfully removed
- }
- else if(command == TOSERVER_INTERACT)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- /*
- [0] u16 command
- [2] u8 action
- [3] u16 item
- [5] u32 length of the next item
- [9] serialized PointedThing
- actions:
- 0: start digging (from undersurface) or use
- 1: stop digging (all parameters ignored)
- 2: digging completed
- 3: place block or item (to abovesurface)
- 4: use item
- */
- u8 action = readU8(is);
- u16 item_i = readU16(is);
- std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
- PointedThing pointed;
- pointed.deSerialize(tmp_is);
-
- verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
- <<item_i<<", pointed="<<pointed.dump()<<std::endl;
-
- if(player->hp == 0)
- {
- verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
- <<" tried to interact, but is dead!"<<std::endl;
- return;
- }
-
- v3f player_pos = playersao->getLastGoodPosition();
-
- // Update wielded item
- playersao->setWieldIndex(item_i);
-
- // Get pointed to node (undefined if not POINTEDTYPE_NODE)
- v3s16 p_under = pointed.node_undersurface;
- v3s16 p_above = pointed.node_abovesurface;
-
- // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
- ServerActiveObject *pointed_object = NULL;
- if(pointed.type == POINTEDTHING_OBJECT)
- {
- pointed_object = m_env->getActiveObject(pointed.object_id);
- if(pointed_object == NULL)
- {
- verbosestream<<"TOSERVER_INTERACT: "
- "pointed object is NULL"<<std::endl;
- return;
- }
-
- }
-
- v3f pointed_pos_under = player_pos;
- v3f pointed_pos_above = player_pos;
- if(pointed.type == POINTEDTHING_NODE)
- {
- pointed_pos_under = intToFloat(p_under, BS);
- pointed_pos_above = intToFloat(p_above, BS);
- }
- else if(pointed.type == POINTEDTHING_OBJECT)
- {
- pointed_pos_under = pointed_object->getBasePosition();
- pointed_pos_above = pointed_pos_under;
}
- /*
- Check that target is reasonably close
- (only when digging or placing things)
- */
- if(action == 0 || action == 2 || action == 3)
- {
- float d = player_pos.getDistanceFrom(pointed_pos_under);
- float max_d = BS * 14; // Just some large enough value
- if(d > max_d){
- actionstream<<"Player "<<player->getName()
- <<" tried to access "<<pointed.dump()
- <<" from too far: "
- <<"d="<<d<<", max_d="<<max_d
- <<". ignoring."<<std::endl;
- // Re-send block to revert change on client-side
- RemoteClient *client = getClient(peer_id);
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- client->SetBlockNotSent(blockpos);
- // Call callbacks
- m_script->on_cheat(playersao, "interacted_too_far");
- // Do nothing else
- return;
- }
- }
+ if (m_clients.getClientState(peer_id) < CS_Active) {
+ if (command == TOSERVER_PLAYERPOS) return;
- /*
- Make sure the player is allowed to do it
- */
- if(!checkPriv(player->getName(), "interact"))
- {
- actionstream<<player->getName()<<" attempted to interact with "
- <<pointed.dump()<<" without 'interact' privilege"
- <<std::endl;
- // Re-send block to revert change on client-side
- RemoteClient *client = getClient(peer_id);
- // Digging completed -> under
- if(action == 2){
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- client->SetBlockNotSent(blockpos);
- }
- // Placement -> above
- if(action == 3){
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
- client->SetBlockNotSent(blockpos);
- }
+ errorstream << "Got packet command: " << command << " for peer id "
+ << peer_id << " but client isn't active yet. Dropping packet "
+ << std::endl;
return;
}
- /*
- If something goes wrong, this player is to blame
- */
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- /*
- 0: start digging or punch object
- */
- if(action == 0)
- {
- if(pointed.type == POINTEDTHING_NODE)
- {
- /*
- NOTE: This can be used in the future to check if
- somebody is cheating, by checking the timing.
- */
- MapNode n(CONTENT_IGNORE);
- bool pos_ok;
- n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
- if (pos_ok)
- n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
-
- if (!pos_ok) {
- infostream<<"Server: Not punching: Node not found."
- <<" Adding block to emerge queue."
- <<std::endl;
- m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
- }
-
- if(n.getContent() != CONTENT_IGNORE)
- m_script->node_on_punch(p_under, n, playersao, pointed);
- // Cheat prevention
- playersao->noCheatDigStart(p_under);
- }
- else if(pointed.type == POINTEDTHING_OBJECT)
- {
- // Skip if object has been removed
- if(pointed_object->m_removed)
- return;
-
- actionstream<<player->getName()<<" punches object "
- <<pointed.object_id<<": "
- <<pointed_object->getDescription()<<std::endl;
-
- ItemStack punchitem = playersao->getWieldedItem();
- ToolCapabilities toolcap =
- punchitem.getToolCapabilities(m_itemdef);
- v3f dir = (pointed_object->getBasePosition() -
- (player->getPosition() + player->getEyeOffset())
- ).normalize();
- float time_from_last_punch =
- playersao->resetTimeFromLastPunch();
- pointed_object->punch(dir, &toolcap, playersao,
- time_from_last_punch);
- }
-
- } // action == 0
-
- /*
- 1: stop digging
- */
- else if(action == 1)
- {
- } // action == 1
-
- /*
- 2: Digging completed
- */
- else if(action == 2)
- {
- // Only digging of nodes
- if(pointed.type == POINTEDTHING_NODE)
- {
- bool pos_ok;
- MapNode n = m_env->getMap().getNodeNoEx(p_under, &pos_ok);
- if (!pos_ok) {
- infostream << "Server: Not finishing digging: Node not found."
- << " Adding block to emerge queue."
- << std::endl;
- m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false);
- }
-
- /* Cheat prevention */
- bool is_valid_dig = true;
- if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
- {
- v3s16 nocheat_p = playersao->getNoCheatDigPos();
- float nocheat_t = playersao->getNoCheatDigTime();
- playersao->noCheatDigEnd();
- // If player didn't start digging this, ignore dig
- if(nocheat_p != p_under){
- infostream<<"Server: NoCheat: "<<player->getName()
- <<" started digging "
- <<PP(nocheat_p)<<" and completed digging "
- <<PP(p_under)<<"; not digging."<<std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "finished_unknown_dig");
- }
- // Get player's wielded item
- ItemStack playeritem;
- InventoryList *mlist = playersao->getInventory()->getList("main");
- if(mlist != NULL)
- playeritem = mlist->getItem(playersao->getWieldIndex());
- ToolCapabilities playeritem_toolcap =
- playeritem.getToolCapabilities(m_itemdef);
- // Get diggability and expected digging time
- DigParams params = getDigParams(m_nodedef->get(n).groups,
- &playeritem_toolcap);
- // If can't dig, try hand
- if(!params.diggable){
- const ItemDefinition &hand = m_itemdef->get("");
- const ToolCapabilities *tp = hand.tool_capabilities;
- if(tp)
- params = getDigParams(m_nodedef->get(n).groups, tp);
- }
- // If can't dig, ignore dig
- if(!params.diggable){
- infostream<<"Server: NoCheat: "<<player->getName()
- <<" completed digging "<<PP(p_under)
- <<", which is not diggable with tool. not digging."
- <<std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "dug_unbreakable");
- }
- // Check digging time
- // If already invalidated, we don't have to
- if(!is_valid_dig){
- // Well not our problem then
- }
- // Clean and long dig
- else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){
- // All is good, but grab time from pool; don't care if
- // it's actually available
- playersao->getDigPool().grab(params.time);
- }
- // Short or laggy dig
- // Try getting the time from pool
- else if(playersao->getDigPool().grab(params.time)){
- // All is good
- }
- // Dig not possible
- else{
- infostream<<"Server: NoCheat: "<<player->getName()
- <<" completed digging "<<PP(p_under)
- <<"too fast; not digging."<<std::endl;
- is_valid_dig = false;
- // Call callbacks
- m_script->on_cheat(playersao, "dug_too_fast");
- }
- }
-
- /* Actually dig node */
-
- if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
- m_script->node_on_dig(p_under, n, playersao);
-
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- RemoteClient *client = getClient(peer_id);
- // Send unusual result (that is, node not being removed)
- if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
- {
- // Re-send block to revert change on client-side
- client->SetBlockNotSent(blockpos);
- }
- else {
- client->ResendBlockIfOnWire(blockpos);
- }
- }
- } // action == 2
-
- /*
- 3: place block or right-click object
- */
- else if(action == 3)
- {
- ItemStack item = playersao->getWieldedItem();
-
- // Reset build time counter
- if(pointed.type == POINTEDTHING_NODE &&
- item.getDefinition(m_itemdef).type == ITEM_NODE)
- getClient(peer_id)->m_time_from_building = 0.0;
-
- if(pointed.type == POINTEDTHING_OBJECT)
- {
- // Right click object
-
- // Skip if object has been removed
- if(pointed_object->m_removed)
- return;
-
- actionstream<<player->getName()<<" right-clicks object "
- <<pointed.object_id<<": "
- <<pointed_object->getDescription()<<std::endl;
-
- // Do stuff
- pointed_object->rightClick(playersao);
- }
- else if(m_script->item_OnPlace(
- item, playersao, pointed))
- {
- // Placement was handled in lua
-
- // Apply returned ItemStack
- playersao->setWieldedItem(item);
- }
-
- // If item has node placement prediction, always send the
- // blocks to make sure the client knows what exactly happened
- RemoteClient *client = getClient(peer_id);
- v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
- v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
- if(item.getDefinition(m_itemdef).node_placement_prediction != "") {
- client->SetBlockNotSent(blockpos);
- if(blockpos2 != blockpos) {
- client->SetBlockNotSent(blockpos2);
- }
- }
- else {
- client->ResendBlockIfOnWire(blockpos);
- if(blockpos2 != blockpos) {
- client->ResendBlockIfOnWire(blockpos2);
- }
- }
- } // action == 3
-
- /*
- 4: use
- */
- else if(action == 4)
- {
- ItemStack item = playersao->getWieldedItem();
-
- actionstream<<player->getName()<<" uses "<<item.name
- <<", pointing at "<<pointed.dump()<<std::endl;
-
- if(m_script->item_OnUse(
- item, playersao, pointed))
- {
- // Apply returned ItemStack
- playersao->setWieldedItem(item);
- }
-
- } // action == 4
-
-
- /*
- Catch invalid actions
- */
- else
- {
- infostream<<"WARNING: Server: Invalid action "
- <<action<<std::endl;
- }
- }
- else if(command == TOSERVER_REMOVED_SOUNDS)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- int num = readU16(is);
- for(int k=0; k<num; k++){
- s32 id = readS32(is);
- std::map<s32, ServerPlayingSound>::iterator i =
- m_playing_sounds.find(id);
- if(i == m_playing_sounds.end())
- continue;
- ServerPlayingSound &psound = i->second;
- psound.clients.erase(peer_id);
- if(psound.clients.empty())
- m_playing_sounds.erase(i++);
- }
- }
- else if(command == TOSERVER_NODEMETA_FIELDS)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- v3s16 p = readV3S16(is);
- std::string formname = deSerializeString(is);
- int num = readU16(is);
- std::map<std::string, std::string> fields;
- for(int k=0; k<num; k++){
- std::string fieldname = deSerializeString(is);
- std::string fieldvalue = deSerializeLongString(is);
- fields[fieldname] = fieldvalue;
- }
-
- // If something goes wrong, this player is to blame
- RollbackScopeActor rollback_scope(m_rollback,
- std::string("player:")+player->getName());
-
- // Check the target node for rollback data; leave others unnoticed
- RollbackNode rn_old(&m_env->getMap(), p, this);
-
- m_script->node_on_receive_fields(p, formname, fields,playersao);
-
- // Report rollback data
- RollbackNode rn_new(&m_env->getMap(), p, this);
- if(rollback() && rn_new != rn_old){
- RollbackAction action;
- action.setSetNode(p, rn_old, rn_new);
- rollback()->reportAction(action);
- }
- }
- else if(command == TOSERVER_INVENTORY_FIELDS)
- {
- std::string datastring((char*)&data[2], datasize-2);
- std::istringstream is(datastring, std::ios_base::binary);
-
- std::string formname = deSerializeString(is);
- int num = readU16(is);
- std::map<std::string, std::string> fields;
- for(int k=0; k<num; k++){
- std::string fieldname = deSerializeString(is);
- std::string fieldvalue = deSerializeLongString(is);
- fields[fieldname] = fieldvalue;
- }
-
- m_script->on_playerReceiveFields(playersao, formname, fields);
- }
- else
- {
- infostream<<"Server::ProcessData(): Ignoring "
- "unknown command "<<command<<std::endl;
- }
-
- } //try
- catch(SendFailedException &e)
- {
- errorstream<<"Server::ProcessData(): SendFailedException: "
- <<"what="<<e.what()
- <<std::endl;
+ handleCommand(pkt);
+ } catch (SendFailedException &e) {
+ errorstream << "Server::ProcessData(): SendFailedException: "
+ << "what=" << e.what()
+ << std::endl;
+ } catch (PacketError &e) {
+ actionstream << "Server::ProcessData(): PacketError: "
+ << "what=" << e.what()
+ << std::endl;
}
}
@@ -2752,24 +1265,20 @@ void Server::setTimeOfDay(u32 time)
void Server::onMapEditEvent(MapEditEvent *event)
{
- //infostream<<"Server::onMapEditEvent()"<<std::endl;
if(m_ignore_map_edit_events)
return;
if(m_ignore_map_edit_events_area.contains(event->getArea()))
return;
MapEditEvent *e = event->clone();
- m_unsent_map_edit_queue.push_back(e);
+ m_unsent_map_edit_queue.push(e);
}
Inventory* Server::getInventory(const InventoryLocation &loc)
{
- switch(loc.type){
+ switch (loc.type) {
case InventoryLocation::UNDEFINED:
- {}
- break;
case InventoryLocation::CURRENT_PLAYER:
- {}
- break;
+ break;
case InventoryLocation::PLAYER:
{
Player *player = m_env->getPlayer(loc.name.c_str());
@@ -2780,7 +1289,7 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
return NULL;
return playersao->getInventory();
}
- break;
+ break;
case InventoryLocation::NODEMETA:
{
NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
@@ -2788,37 +1297,40 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
return NULL;
return meta->getInventory();
}
- break;
+ break;
case InventoryLocation::DETACHED:
{
if(m_detached_inventories.count(loc.name) == 0)
return NULL;
return m_detached_inventories[loc.name];
}
- break;
+ break;
default:
- assert(0);
+ sanity_check(false); // abort
+ break;
}
return NULL;
}
-void Server::setInventoryModified(const InventoryLocation &loc)
+void Server::setInventoryModified(const InventoryLocation &loc, bool playerSend)
{
switch(loc.type){
case InventoryLocation::UNDEFINED:
- {}
- break;
+ break;
case InventoryLocation::PLAYER:
{
+ if (!playerSend)
+ return;
+
Player *player = m_env->getPlayer(loc.name.c_str());
if(!player)
return;
PlayerSAO *playersao = player->getPlayerSAO();
if(!playersao)
return;
- playersao->m_inventory_not_sent = true;
- playersao->m_wielded_item_not_sent = true;
+
+ SendInventory(playersao);
}
- break;
+ break;
case InventoryLocation::NODEMETA:
{
v3s16 blockpos = getNodeBlockPos(loc.p);
@@ -2829,29 +1341,28 @@ void Server::setInventoryModified(const InventoryLocation &loc)
setBlockNotSent(blockpos);
}
- break;
+ break;
case InventoryLocation::DETACHED:
{
sendDetachedInventory(loc.name,PEER_ID_INEXISTENT);
}
- break;
+ break;
default:
- assert(0);
+ sanity_check(false); // abort
+ break;
}
}
void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block)
{
- std::list<u16> clients = m_clients.getClientIDs();
+ std::vector<u16> clients = m_clients.getClientIDs();
m_clients.Lock();
// Set the modified blocks unsent for all the clients
- for (std::list<u16>::iterator
- i = clients.begin();
+ for (std::vector<u16>::iterator i = clients.begin();
i != clients.end(); ++i) {
- RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
- if (client != NULL)
+ if (RemoteClient *client = m_clients.lockedGetClientNoEx(*i))
client->SetBlocksNotSent(block);
- }
+ }
m_clients.Unlock();
}
@@ -2865,7 +1376,7 @@ void Server::peerAdded(con::Peer *peer)
c.type = con::PEER_ADDED;
c.peer_id = peer->id;
c.timeout = false;
- m_peer_change_queue.push_back(c);
+ m_peer_change_queue.push(c);
}
void Server::deletingPeer(con::Peer *peer, bool timeout)
@@ -2879,7 +1390,7 @@ void Server::deletingPeer(con::Peer *peer, bool timeout)
c.type = con::PEER_REMOVED;
c.peer_id = peer->id;
c.timeout = timeout;
- m_peer_change_queue.push_back(c);
+ m_peer_change_queue.push(c);
}
bool Server::getClientConInfo(u16 peer_id, con::rtt_stat_type type, float* retval)
@@ -2928,7 +1439,8 @@ void Server::handlePeerChanges()
{
while(m_peer_change_queue.size() > 0)
{
- con::PeerChange c = m_peer_change_queue.pop_front();
+ con::PeerChange c = m_peer_change_queue.front();
+ m_peer_change_queue.pop();
verbosestream<<"Server: Handling peer change: "
<<"id="<<c.peer_id<<", timeout="<<c.timeout
@@ -2945,219 +1457,197 @@ void Server::handlePeerChanges()
break;
default:
- assert("Invalid peer change event received!" == 0);
+ FATAL_ERROR("Invalid peer change event received!");
break;
}
}
}
+void Server::Send(NetworkPacket* pkt)
+{
+ m_clients.send(pkt->getPeerId(),
+ clientCommandFactoryTable[pkt->getCommand()].channel,
+ pkt,
+ clientCommandFactoryTable[pkt->getCommand()].reliable);
+}
+
void Server::SendMovement(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_MOVEMENT);
- writeF1000(os, g_settings->getFloat("movement_acceleration_default"));
- writeF1000(os, g_settings->getFloat("movement_acceleration_air"));
- writeF1000(os, g_settings->getFloat("movement_acceleration_fast"));
- writeF1000(os, g_settings->getFloat("movement_speed_walk"));
- writeF1000(os, g_settings->getFloat("movement_speed_crouch"));
- writeF1000(os, g_settings->getFloat("movement_speed_fast"));
- writeF1000(os, g_settings->getFloat("movement_speed_climb"));
- writeF1000(os, g_settings->getFloat("movement_speed_jump"));
- writeF1000(os, g_settings->getFloat("movement_liquid_fluidity"));
- writeF1000(os, g_settings->getFloat("movement_liquid_fluidity_smooth"));
- writeF1000(os, g_settings->getFloat("movement_liquid_sink"));
- writeF1000(os, g_settings->getFloat("movement_gravity"));
+ NetworkPacket pkt(TOCLIENT_MOVEMENT, 12 * sizeof(float), peer_id);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ pkt << g_settings->getFloat("movement_acceleration_default");
+ pkt << g_settings->getFloat("movement_acceleration_air");
+ pkt << g_settings->getFloat("movement_acceleration_fast");
+ pkt << g_settings->getFloat("movement_speed_walk");
+ pkt << g_settings->getFloat("movement_speed_crouch");
+ pkt << g_settings->getFloat("movement_speed_fast");
+ pkt << g_settings->getFloat("movement_speed_climb");
+ pkt << g_settings->getFloat("movement_speed_jump");
+ pkt << g_settings->getFloat("movement_liquid_fluidity");
+ pkt << g_settings->getFloat("movement_liquid_fluidity_smooth");
+ pkt << g_settings->getFloat("movement_liquid_sink");
+ pkt << g_settings->getFloat("movement_gravity");
+
+ Send(&pkt);
+}
+
+void Server::SendPlayerHPOrDie(PlayerSAO *playersao)
+{
+ if (!g_settings->getBool("enable_damage"))
+ return;
+
+ u16 peer_id = playersao->getPeerID();
+ bool is_alive = playersao->getHP() > 0;
+
+ if (is_alive)
+ SendPlayerHP(peer_id);
+ else
+ DiePlayer(peer_id);
}
void Server::SendHP(u16 peer_id, u8 hp)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOCLIENT_HP);
- writeU8(os, hp);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ NetworkPacket pkt(TOCLIENT_HP, 1, peer_id);
+ pkt << hp;
+ Send(&pkt);
}
void Server::SendBreath(u16 peer_id, u16 breath)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_BREATH);
- writeU16(os, breath);
+ NetworkPacket pkt(TOCLIENT_BREATH, 2, peer_id);
+ pkt << (u16) breath;
+ Send(&pkt);
+}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+void Server::SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
+ const std::string &custom_reason, bool reconnect)
+{
+ assert(reason < SERVER_ACCESSDENIED_MAX);
+
+ NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id);
+ pkt << (u8)reason;
+ if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING)
+ pkt << custom_reason;
+ else if (reason == SERVER_ACCESSDENIED_SHUTDOWN ||
+ reason == SERVER_ACCESSDENIED_CRASH)
+ pkt << custom_reason << (u8)reconnect;
+ Send(&pkt);
}
-void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason)
+void Server::SendAccessDenied_Legacy(u16 peer_id,const std::wstring &reason)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOCLIENT_ACCESS_DENIED);
- os<<serializeWideString(reason);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ NetworkPacket pkt(TOCLIENT_ACCESS_DENIED_LEGACY, 0, peer_id);
+ pkt << reason;
+ Send(&pkt);
}
void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target,
v3f camera_point_target)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOCLIENT_DEATHSCREEN);
- writeU8(os, set_camera_point_target);
- writeV3F1000(os, camera_point_target);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id);
+ pkt << set_camera_point_target << camera_point_target;
+ Send(&pkt);
}
void Server::SendItemDef(u16 peer_id,
IItemDefManager *itemdef, u16 protocol_version)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
+
+ NetworkPacket pkt(TOCLIENT_ITEMDEF, 0, peer_id);
/*
u16 command
u32 length of the next item
zlib-compressed serialized ItemDefManager
*/
- writeU16(os, TOCLIENT_ITEMDEF);
std::ostringstream tmp_os(std::ios::binary);
itemdef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
- os<<serializeLongString(tmp_os2.str());
+ pkt.putLongString(tmp_os2.str());
// Make data buffer
- std::string s = os.str();
- verbosestream<<"Server: Sending item definitions to id("<<peer_id
- <<"): size="<<s.size()<<std::endl;
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ verbosestream << "Server: Sending item definitions to id(" << peer_id
+ << "): size=" << pkt.getSize() << std::endl;
+
+ Send(&pkt);
}
void Server::SendNodeDef(u16 peer_id,
INodeDefManager *nodedef, u16 protocol_version)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
+
+ NetworkPacket pkt(TOCLIENT_NODEDEF, 0, peer_id);
/*
u16 command
u32 length of the next item
zlib-compressed serialized NodeDefManager
*/
- writeU16(os, TOCLIENT_NODEDEF);
std::ostringstream tmp_os(std::ios::binary);
nodedef->serialize(tmp_os, protocol_version);
std::ostringstream tmp_os2(std::ios::binary);
compressZlib(tmp_os.str(), tmp_os2);
- os<<serializeLongString(tmp_os2.str());
+
+ pkt.putLongString(tmp_os2.str());
// Make data buffer
- std::string s = os.str();
- verbosestream<<"Server: Sending node definitions to id("<<peer_id
- <<"): size="<<s.size()<<std::endl;
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ verbosestream << "Server: Sending node definitions to id(" << peer_id
+ << "): size=" << pkt.getSize() << std::endl;
+
+ Send(&pkt);
}
/*
Non-static send methods
*/
-void Server::SendInventory(u16 peer_id)
+void Server::SendInventory(PlayerSAO* playerSAO)
{
DSTACK(__FUNCTION_NAME);
- PlayerSAO *playersao = getPlayerSAO(peer_id);
- assert(playersao);
-
- playersao->m_inventory_not_sent = false;
+ UpdateCrafting(playerSAO->getPlayer());
/*
Serialize it
*/
+ NetworkPacket pkt(TOCLIENT_INVENTORY, 0, playerSAO->getPeerID());
+
std::ostringstream os;
- playersao->getInventory()->serialize(os);
+ playerSAO->getInventory()->serialize(os);
std::string s = os.str();
- SharedBuffer<u8> data(s.size()+2);
- writeU16(&data[0], TOCLIENT_INVENTORY);
- memcpy(&data[2], s.c_str(), s.size());
-
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ pkt.putRawString(s.c_str(), s.size());
+ Send(&pkt);
}
void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- u8 buf[12];
-
- // Write command
- writeU16(buf, TOCLIENT_CHAT_MESSAGE);
- os.write((char*)buf, 2);
+ NetworkPacket pkt(TOCLIENT_CHAT_MESSAGE, 0, peer_id);
+ pkt << message;
- // Write length
- writeU16(buf, message.size());
- os.write((char*)buf, 2);
-
- // Write string
- for(u32 i=0; i<message.size(); i++)
- {
- u16 w = message[i];
- writeU16(buf, w);
- os.write((char*)buf, 2);
- }
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
-
- if (peer_id != PEER_ID_INEXISTENT)
- {
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ if (peer_id != PEER_ID_INEXISTENT) {
+ Send(&pkt);
}
- else
- {
- m_clients.sendToAll(0,data,true);
+ else {
+ m_clients.sendToAll(0, &pkt, true);
}
}
@@ -3166,21 +1656,12 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- u8 buf[12];
+ NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
+ pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
+ pkt << formname;
- // Write command
- writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
- os.write((char*)buf, 2);
- os<<serializeLongString(FORMSPEC_VERSION_STRING + formspec);
- os<<serializeString(formname);
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
// Spawns a particle on peer with peer_id
@@ -3190,29 +1671,18 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_SPAWN_PARTICLE);
- writeV3F1000(os, pos);
- writeV3F1000(os, velocity);
- writeV3F1000(os, acceleration);
- writeF1000(os, expirationtime);
- writeF1000(os, size);
- writeU8(os, collisiondetection);
- os<<serializeLongString(texture);
- writeU8(os, vertical);
+ NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ pkt << pos << velocity << acceleration << expirationtime
+ << size << collisiondetection;
+ pkt.putLongString(texture);
+ pkt << vertical;
- if (peer_id != PEER_ID_INEXISTENT)
- {
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ if (peer_id != PEER_ID_INEXISTENT) {
+ Send(&pkt);
}
- else
- {
- m_clients.sendToAll(0,data,true);
+ else {
+ m_clients.sendToAll(0, &pkt, true);
}
}
@@ -3223,37 +1693,21 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER);
-
- writeU16(os, amount);
- writeF1000(os, spawntime);
- writeV3F1000(os, minpos);
- writeV3F1000(os, maxpos);
- writeV3F1000(os, minvel);
- writeV3F1000(os, maxvel);
- writeV3F1000(os, minacc);
- writeV3F1000(os, maxacc);
- writeF1000(os, minexptime);
- writeF1000(os, maxexptime);
- writeF1000(os, minsize);
- writeF1000(os, maxsize);
- writeU8(os, collisiondetection);
- os<<serializeLongString(texture);
- writeU32(os, id);
- writeU8(os, vertical);
+ NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ pkt << amount << spawntime << minpos << maxpos << minvel << maxvel
+ << minacc << maxacc << minexptime << maxexptime << minsize
+ << maxsize << collisiondetection;
- if (peer_id != PEER_ID_INEXISTENT)
- {
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ pkt.putLongString(texture);
+
+ pkt << id << vertical;
+
+ if (peer_id != PEER_ID_INEXISTENT) {
+ Send(&pkt);
}
else {
- m_clients.sendToAll(0,data,true);
+ m_clients.sendToAll(0, &pkt, true);
}
}
@@ -3261,197 +1715,124 @@ void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id)
{
DSTACK(__FUNCTION_NAME);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER);
-
- writeU16(os, id);
+ NetworkPacket pkt(TOCLIENT_DELETE_PARTICLESPAWNER_LEGACY, 2, peer_id);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Ugly error in this packet
+ pkt << (u16) id;
if (peer_id != PEER_ID_INEXISTENT) {
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
else {
- m_clients.sendToAll(0,data,true);
+ m_clients.sendToAll(0, &pkt, true);
}
}
void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form)
{
- std::ostringstream os(std::ios_base::binary);
+ NetworkPacket pkt(TOCLIENT_HUDADD, 0 , peer_id);
- // Write command
- writeU16(os, TOCLIENT_HUDADD);
- writeU32(os, id);
- writeU8(os, (u8)form->type);
- writeV2F1000(os, form->pos);
- os << serializeString(form->name);
- writeV2F1000(os, form->scale);
- os << serializeString(form->text);
- writeU32(os, form->number);
- writeU32(os, form->item);
- writeU32(os, form->dir);
- writeV2F1000(os, form->align);
- writeV2F1000(os, form->offset);
- writeV3F1000(os, form->world_pos);
- writeV2S32(os,form->size);
+ pkt << id << (u8) form->type << form->pos << form->name << form->scale
+ << form->text << form->number << form->item << form->dir
+ << form->align << form->offset << form->world_pos << form->size;
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 1, data, true);
+ Send(&pkt);
}
void Server::SendHUDRemove(u16 peer_id, u32 id)
{
- std::ostringstream os(std::ios_base::binary);
-
- // Write command
- writeU16(os, TOCLIENT_HUDRM);
- writeU32(os, id);
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
-
- m_clients.send(peer_id, 1, data, true);
+ NetworkPacket pkt(TOCLIENT_HUDRM, 4, peer_id);
+ pkt << id;
+ Send(&pkt);
}
void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value)
{
- std::ostringstream os(std::ios_base::binary);
+ NetworkPacket pkt(TOCLIENT_HUDCHANGE, 0, peer_id);
+ pkt << id << (u8) stat;
- // Write command
- writeU16(os, TOCLIENT_HUDCHANGE);
- writeU32(os, id);
- writeU8(os, (u8)stat);
switch (stat) {
case HUD_STAT_POS:
case HUD_STAT_SCALE:
case HUD_STAT_ALIGN:
case HUD_STAT_OFFSET:
- writeV2F1000(os, *(v2f *)value);
+ pkt << *(v2f *) value;
break;
case HUD_STAT_NAME:
case HUD_STAT_TEXT:
- os << serializeString(*(std::string *)value);
+ pkt << *(std::string *) value;
break;
case HUD_STAT_WORLD_POS:
- writeV3F1000(os, *(v3f *)value);
+ pkt << *(v3f *) value;
break;
case HUD_STAT_SIZE:
- writeV2S32(os,*(v2s32 *)value);
+ pkt << *(v2s32 *) value;
break;
case HUD_STAT_NUMBER:
case HUD_STAT_ITEM:
case HUD_STAT_DIR:
default:
- writeU32(os, *(u32 *)value);
+ pkt << *(u32 *) value;
break;
}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask)
{
- std::ostringstream os(std::ios_base::binary);
-
- // Write command
- writeU16(os, TOCLIENT_HUD_SET_FLAGS);
+ NetworkPacket pkt(TOCLIENT_HUD_SET_FLAGS, 4 + 4, peer_id);
- //////////////////////////// compatibility code to be removed //////////////
flags &= ~(HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_BREATHBAR_VISIBLE);
- ////////////////////////////////////////////////////////////////////////////
- writeU32(os, flags);
- writeU32(os, mask);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ pkt << flags << mask;
+
+ Send(&pkt);
}
void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value)
{
- std::ostringstream os(std::ios_base::binary);
-
- // Write command
- writeU16(os, TOCLIENT_HUD_SET_PARAM);
- writeU16(os, param);
- os<<serializeString(value);
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ NetworkPacket pkt(TOCLIENT_HUD_SET_PARAM, 0, peer_id);
+ pkt << param << value;
+ Send(&pkt);
}
void Server::SendSetSky(u16 peer_id, const video::SColor &bgcolor,
const std::string &type, const std::vector<std::string> &params)
{
- std::ostringstream os(std::ios_base::binary);
+ NetworkPacket pkt(TOCLIENT_SET_SKY, 0, peer_id);
+ pkt << bgcolor << type << (u16) params.size();
- // Write command
- writeU16(os, TOCLIENT_SET_SKY);
- writeARGB8(os, bgcolor);
- os<<serializeString(type);
- writeU16(os, params.size());
for(size_t i=0; i<params.size(); i++)
- os<<serializeString(params[i]);
+ pkt << params[i];
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
void Server::SendOverrideDayNightRatio(u16 peer_id, bool do_override,
float ratio)
{
- std::ostringstream os(std::ios_base::binary);
+ NetworkPacket pkt(TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO,
+ 1 + 2, peer_id);
- // Write command
- writeU16(os, TOCLIENT_OVERRIDE_DAY_NIGHT_RATIO);
- writeU8(os, do_override);
- writeU16(os, ratio*65535);
+ pkt << do_override << (u16) (ratio * 65535);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed)
{
DSTACK(__FUNCTION_NAME);
- // Make packet
- SharedBuffer<u8> data(2+2+4);
- writeU16(&data[0], TOCLIENT_TIME_OF_DAY);
- writeU16(&data[2], time);
- writeF1000(&data[4], time_speed);
+ NetworkPacket pkt(TOCLIENT_TIME_OF_DAY, 0, peer_id);
+ pkt << time << time_speed;
if (peer_id == PEER_ID_INEXISTENT) {
- m_clients.sendToAll(0,data,true);
+ m_clients.sendToAll(0, &pkt, true);
}
else {
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
}
@@ -3459,15 +1840,18 @@ void Server::SendPlayerHP(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
- assert(playersao);
- playersao->m_hp_not_sent = false;
+ // In some rare case, if the player is disconnected
+ // while Lua call l_punch, for example, this can be NULL
+ if (!playersao)
+ return;
+
SendHP(peer_id, playersao->getHP());
m_script->player_event(playersao,"health_changed");
// Send to other clients
std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP());
ActiveObjectMessage aom(playersao->getId(), true, str);
- playersao->m_messages_out.push_back(aom);
+ playersao->m_messages_out.push(aom);
}
void Server::SendPlayerBreath(u16 peer_id)
@@ -3475,8 +1859,8 @@ void Server::SendPlayerBreath(u16 peer_id)
DSTACK(__FUNCTION_NAME);
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
- playersao->m_breath_not_sent = false;
- m_script->player_event(playersao,"breath_changed");
+
+ m_script->player_event(playersao, "breath_changed");
SendBreath(peer_id, playersao->getBreath());
}
@@ -3486,61 +1870,39 @@ void Server::SendMovePlayer(u16 peer_id)
Player *player = m_env->getPlayer(peer_id);
assert(player);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_MOVE_PLAYER);
- writeV3F1000(os, player->getPosition());
- writeF1000(os, player->getPitch());
- writeF1000(os, player->getYaw());
+ NetworkPacket pkt(TOCLIENT_MOVE_PLAYER, sizeof(v3f) + sizeof(f32) * 2, peer_id);
+ pkt << player->getPosition() << player->getPitch() << player->getYaw();
{
v3f pos = player->getPosition();
f32 pitch = player->getPitch();
f32 yaw = player->getYaw();
- verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
- <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
- <<" pitch="<<pitch
- <<" yaw="<<yaw
- <<std::endl;
+ verbosestream << "Server: Sending TOCLIENT_MOVE_PLAYER"
+ << " pos=(" << pos.X << "," << pos.Y << "," << pos.Z << ")"
+ << " pitch=" << pitch
+ << " yaw=" << yaw
+ << std::endl;
}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
void Server::SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed)
{
- std::ostringstream os(std::ios_base::binary);
+ NetworkPacket pkt(TOCLIENT_LOCAL_PLAYER_ANIMATIONS, 0,
+ peer_id);
- writeU16(os, TOCLIENT_LOCAL_PLAYER_ANIMATIONS);
- writeV2S32(os, animation_frames[0]);
- writeV2S32(os, animation_frames[1]);
- writeV2S32(os, animation_frames[2]);
- writeV2S32(os, animation_frames[3]);
- writeF1000(os, animation_speed);
+ pkt << animation_frames[0] << animation_frames[1] << animation_frames[2]
+ << animation_frames[3] << animation_speed;
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
void Server::SendEyeOffset(u16 peer_id, v3f first, v3f third)
{
- std::ostringstream os(std::ios_base::binary);
-
- writeU16(os, TOCLIENT_EYE_OFFSET);
- writeV3F1000(os, first);
- writeV3F1000(os, third);
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8 *)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ NetworkPacket pkt(TOCLIENT_EYE_OFFSET, 0, peer_id);
+ pkt << first << third;
+ Send(&pkt);
}
void Server::SendPlayerPrivileges(u16 peer_id)
{
@@ -3552,19 +1914,15 @@ void Server::SendPlayerPrivileges(u16 peer_id)
std::set<std::string> privs;
m_script->getAuth(player->getName(), NULL, &privs);
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_PRIVILEGES);
- writeU16(os, privs.size());
+ NetworkPacket pkt(TOCLIENT_PRIVILEGES, 0, peer_id);
+ pkt << (u16) privs.size();
+
for(std::set<std::string>::const_iterator i = privs.begin();
- i != privs.end(); i++){
- os<<serializeString(*i);
+ i != privs.end(); i++) {
+ pkt << (*i);
}
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ Send(&pkt);
}
void Server::SendPlayerInventoryFormspec(u16 peer_id)
@@ -3574,15 +1932,30 @@ void Server::SendPlayerInventoryFormspec(u16 peer_id)
if(player->peer_id == PEER_ID_INEXISTENT)
return;
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
- os<<serializeLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
+ NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
+ pkt.putLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
+ Send(&pkt);
+}
+
+u32 Server::SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas)
+{
+ NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
+ pkt.putRawString(datas.c_str(), datas.size());
+ Send(&pkt);
+ return pkt.getSize();
+}
+
+void Server::SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable)
+{
+ NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_MESSAGES,
+ datas.size(), peer_id);
+
+ pkt.putRawString(datas.c_str(), datas.size());
+
+ m_clients.send(pkt.getPeerId(),
+ reliable ? clientCommandFactoryTable[pkt.getCommand()].channel : 1,
+ &pkt, reliable);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
}
s32 Server::playSound(const SimpleSoundSpec &spec,
@@ -3596,7 +1969,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
return -1;
// Filter destination clients
- std::list<u16> dst_clients;
+ std::vector<u16> dst_clients;
if(params.to_player != "")
{
Player *player = m_env->getPlayer(params.to_player.c_str());
@@ -3612,17 +1985,16 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
}
dst_clients.push_back(player->peer_id);
}
- else
- {
- std::list<u16> clients = m_clients.getClientIDs();
+ else {
+ std::vector<u16> clients = m_clients.getClientIDs();
- for(std::list<u16>::iterator
- i = clients.begin(); i != clients.end(); ++i)
- {
+ for(std::vector<u16>::iterator
+ i = clients.begin(); i != clients.end(); ++i) {
Player *player = m_env->getPlayer(*i);
if(!player)
continue;
- if(pos_exists){
+
+ if(pos_exists) {
if(player->getPosition().getDistanceFrom(pos) >
params.max_hear_distance)
continue;
@@ -3630,6 +2002,7 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
dst_clients.push_back(*i);
}
}
+
if(dst_clients.empty())
return -1;
@@ -3639,27 +2012,15 @@ s32 Server::playSound(const SimpleSoundSpec &spec,
m_playing_sounds[id] = ServerPlayingSound();
ServerPlayingSound &psound = m_playing_sounds[id];
psound.params = params;
- for(std::list<u16>::iterator i = dst_clients.begin();
- i != dst_clients.end(); i++)
+
+ NetworkPacket pkt(TOCLIENT_PLAY_SOUND, 0);
+ pkt << id << spec.name << (float) (spec.gain * params.gain)
+ << (u8) params.type << pos << params.object << params.loop;
+
+ for(std::vector<u16>::iterator i = dst_clients.begin();
+ i != dst_clients.end(); i++) {
psound.clients.insert(*i);
- // Create packet
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_PLAY_SOUND);
- writeS32(os, id);
- os<<serializeString(spec.name);
- writeF1000(os, spec.gain * params.gain);
- writeU8(os, params.type);
- writeV3F1000(os, pos);
- writeU16(os, params.object);
- writeU8(os, params.loop);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send
- for(std::list<u16>::iterator i = dst_clients.begin();
- i != dst_clients.end(); i++){
- // Send as reliable
- m_clients.send(*i, 0, data, true);
+ m_clients.send(*i, 0, &pkt, true);
}
return id;
}
@@ -3671,52 +2032,37 @@ void Server::stopSound(s32 handle)
if(i == m_playing_sounds.end())
return;
ServerPlayingSound &psound = i->second;
- // Create packet
- std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_STOP_SOUND);
- writeS32(os, handle);
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send
+
+ NetworkPacket pkt(TOCLIENT_STOP_SOUND, 4);
+ pkt << handle;
+
for(std::set<u16>::iterator i = psound.clients.begin();
- i != psound.clients.end(); i++){
+ i != psound.clients.end(); i++) {
// Send as reliable
- m_clients.send(*i, 0, data, true);
+ m_clients.send(*i, 0, &pkt, true);
}
// Remove sound reference
m_playing_sounds.erase(i);
}
void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
- std::list<u16> *far_players, float far_d_nodes)
+ std::vector<u16> *far_players, float far_d_nodes)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
- // Create packet
- u32 replysize = 8;
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_REMOVENODE);
- writeS16(&reply[2], p.X);
- writeS16(&reply[4], p.Y);
- writeS16(&reply[6], p.Z);
-
- std::list<u16> clients = m_clients.getClientIDs();
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
- if(far_players)
- {
+ NetworkPacket pkt(TOCLIENT_REMOVENODE, 6);
+ pkt << p;
+
+ std::vector<u16> clients = m_clients.getClientIDs();
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
+ if (far_players) {
// Get player
- Player *player = m_env->getPlayer(*i);
- if(player)
- {
+ if(Player *player = m_env->getPlayer(*i)) {
// If player is far away, only set modified blocks not sent
v3f player_pos = player->getPosition();
- if(player_pos.getDistanceFrom(p_f) > maxd)
- {
+ if(player_pos.getDistanceFrom(p_f) > maxd) {
far_players->push_back(*i);
continue;
}
@@ -3724,53 +2070,39 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
}
// Send as reliable
- m_clients.send(*i, 0, reply, true);
+ m_clients.send(*i, 0, &pkt, true);
}
}
void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
- std::list<u16> *far_players, float far_d_nodes,
+ std::vector<u16> *far_players, float far_d_nodes,
bool remove_metadata)
{
float maxd = far_d_nodes*BS;
v3f p_f = intToFloat(p, BS);
- std::list<u16> clients = m_clients.getClientIDs();
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
+ std::vector<u16> clients = m_clients.getClientIDs();
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
- if(far_players)
- {
+ if(far_players) {
// Get player
- Player *player = m_env->getPlayer(*i);
- if(player)
- {
+ if(Player *player = m_env->getPlayer(*i)) {
// If player is far away, only set modified blocks not sent
v3f player_pos = player->getPosition();
- if(player_pos.getDistanceFrom(p_f) > maxd)
- {
+ if(player_pos.getDistanceFrom(p_f) > maxd) {
far_players->push_back(*i);
continue;
}
}
}
- SharedBuffer<u8> reply(0);
+
+ NetworkPacket pkt(TOCLIENT_ADDNODE, 6 + 2 + 1 + 1 + 1);
m_clients.Lock();
RemoteClient* client = m_clients.lockedGetClientNoEx(*i);
- if (client != 0)
- {
- // Create packet
- u32 replysize = 9 + MapNode::serializedLength(client->serialization_version);
- reply = SharedBuffer<u8>(replysize);
- writeU16(&reply[0], TOCLIENT_ADDNODE);
- writeS16(&reply[2], p.X);
- writeS16(&reply[4], p.Y);
- writeS16(&reply[6], p.Z);
- n.serialize(&reply[8], client->serialization_version);
- u32 index = 8 + MapNode::serializedLength(client->serialization_version);
- writeU8(&reply[index], remove_metadata ? 0 : 1);
+ if (client != 0) {
+ pkt << p << n.param0 << n.param1 << n.param2
+ << (u8) (remove_metadata ? 0 : 1);
if (!remove_metadata) {
if (client->net_proto_version <= 21) {
@@ -3783,19 +2115,17 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
m_clients.Unlock();
// Send as reliable
- if (reply.getSize() > 0)
- m_clients.send(*i, 0, reply, true);
+ if (pkt.getSize() > 0)
+ m_clients.send(*i, 0, &pkt, true);
}
}
void Server::setBlockNotSent(v3s16 p)
{
- std::list<u16> clients = m_clients.getClientIDs();
+ std::vector<u16> clients = m_clients.getClientIDs();
m_clients.Lock();
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
RemoteClient *client = m_clients.lockedGetClientNoEx(*i);
client->SetBlockNotSent(p);
}
@@ -3808,27 +2138,6 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto
v3s16 p = block->getPos();
-#if 0
- // Analyze it a bit
- bool completely_air = true;
- for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
- for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
- for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
- {
- if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
- {
- completely_air = false;
- x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
- }
- }
-
- // Print result
- infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
- if(completely_air)
- infostream<<"[completely air] ";
- infostream<<std::endl;
-#endif
-
/*
Create a packet with the block in the right format
*/
@@ -3837,23 +2146,12 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto
block->serialize(os, ver, false);
block->serializeNetworkSpecific(os, net_proto_version);
std::string s = os.str();
- SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
-
- u32 replysize = 8 + blockdata.getSize();
- SharedBuffer<u8> reply(replysize);
- writeU16(&reply[0], TOCLIENT_BLOCKDATA);
- writeS16(&reply[2], p.X);
- writeS16(&reply[4], p.Y);
- writeS16(&reply[6], p.Z);
- memcpy(&reply[8], *blockdata, blockdata.getSize());
- /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
- <<": \tpacket size: "<<replysize<<std::endl;*/
+ NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id);
- /*
- Send packet
- */
- m_clients.send(peer_id, 2, reply, true);
+ pkt << p;
+ pkt.putRawString(s.c_str(), s.size());
+ Send(&pkt);
}
void Server::SendBlocks(float dtime)
@@ -3872,13 +2170,11 @@ void Server::SendBlocks(float dtime)
{
ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
- std::list<u16> clients = m_clients.getClientIDs();
+ std::vector<u16> clients = m_clients.getClientIDs();
m_clients.Lock();
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
RemoteClient *client = m_clients.lockedGetClientNoEx(*i, CS_Active);
if (client == NULL)
@@ -3935,9 +2231,9 @@ void Server::fillMediaCache()
infostream<<"Server: Calculating media file checksums"<<std::endl;
// Collect all media file paths
- std::list<std::string> paths;
+ std::vector<std::string> paths;
for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++){
+ i != m_mods.end(); i++) {
const ModSpec &mod = *i;
paths.push_back(mod.path + DIR_DELIM + "textures");
paths.push_back(mod.path + DIR_DELIM + "sounds");
@@ -3947,19 +2243,18 @@ void Server::fillMediaCache()
paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server");
// Collect media file information from paths into cache
- for(std::list<std::string>::iterator i = paths.begin();
- i != paths.end(); i++)
- {
+ for(std::vector<std::string>::iterator i = paths.begin();
+ i != paths.end(); i++) {
std::string mediapath = *i;
std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
- for(u32 j=0; j<dirlist.size(); j++){
- if(dirlist[j].dir) // Ignode dirs
+ for (u32 j = 0; j < dirlist.size(); j++) {
+ if (dirlist[j].dir) // Ignode dirs
continue;
std::string filename = dirlist[j].name;
// If name contains illegal characters, ignore the file
- if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
+ if (!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)) {
infostream<<"Server: ignoring illegal file name: \""
- <<filename<<"\""<<std::endl;
+ << filename << "\"" << std::endl;
continue;
}
// If name is not in a supported format, ignore it
@@ -3970,42 +2265,42 @@ void Server::fillMediaCache()
".x", ".b3d", ".md2", ".obj",
NULL
};
- if(removeStringEnd(filename, supported_ext) == ""){
- infostream<<"Server: ignoring unsupported file extension: \""
- <<filename<<"\""<<std::endl;
+ if (removeStringEnd(filename, supported_ext) == ""){
+ infostream << "Server: ignoring unsupported file extension: \""
+ << filename << "\"" << std::endl;
continue;
}
// Ok, attempt to load the file and add to cache
std::string filepath = mediapath + DIR_DELIM + filename;
// Read data
std::ifstream fis(filepath.c_str(), std::ios_base::binary);
- if(fis.good() == false){
- errorstream<<"Server::fillMediaCache(): Could not open \""
- <<filename<<"\" for reading"<<std::endl;
+ if (!fis.good()) {
+ errorstream << "Server::fillMediaCache(): Could not open \""
+ << filename << "\" for reading" << std::endl;
continue;
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
- for(;;){
+ for(;;) {
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
tmp_os.write(buf, len);
- if(fis.eof())
+ if (fis.eof())
break;
- if(!fis.good()){
+ if (!fis.good()) {
bad = true;
break;
}
}
- if(bad){
+ if(bad) {
errorstream<<"Server::fillMediaCache(): Failed to read \""
- <<filename<<"\""<<std::endl;
+ << filename << "\"" << std::endl;
continue;
}
- if(tmp_os.str().length() == 0){
- errorstream<<"Server::fillMediaCache(): Empty file \""
- <<filepath<<"\""<<std::endl;
+ if(tmp_os.str().length() == 0) {
+ errorstream << "Server::fillMediaCache(): Empty file \""
+ << filepath << "\"" << std::endl;
continue;
}
@@ -4018,71 +2313,33 @@ void Server::fillMediaCache()
free(digest);
// Put in list
- this->m_media[filename] = MediaInfo(filepath, sha1_base64);
- verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
+ m_media[filename] = MediaInfo(filepath, sha1_base64);
+ verbosestream << "Server: " << sha1_hex << " is " << filename
+ << std::endl;
}
}
}
-struct SendableMediaAnnouncement
-{
- std::string name;
- std::string sha1_digest;
-
- SendableMediaAnnouncement(const std::string &name_="",
- const std::string &sha1_digest_=""):
- name(name_),
- sha1_digest(sha1_digest_)
- {}
-};
-
void Server::sendMediaAnnouncement(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);
- verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
- <<std::endl;
-
- std::list<SendableMediaAnnouncement> file_announcements;
-
- for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
- i != m_media.end(); i++){
- // Put in list
- file_announcements.push_back(
- SendableMediaAnnouncement(i->first, i->second.sha1_digest));
- }
+ verbosestream << "Server: Announcing files to id(" << peer_id << ")"
+ << std::endl;
// Make packet
std::ostringstream os(std::ios_base::binary);
- /*
- u16 command
- u32 number of files
- for each texture {
- u16 length of name
- string name
- u16 length of sha1_digest
- string sha1_digest
- }
- */
+ NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
+ pkt << (u16) m_media.size();
- writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
- writeU16(os, file_announcements.size());
-
- for(std::list<SendableMediaAnnouncement>::iterator
- j = file_announcements.begin();
- j != file_announcements.end(); ++j){
- os<<serializeString(j->name);
- os<<serializeString(j->sha1_digest);
+ for (std::map<std::string, MediaInfo>::iterator i = m_media.begin();
+ i != m_media.end(); ++i) {
+ pkt << i->first << i->second.sha1_digest;
}
- os<<serializeString(g_settings->get("remote_media"));
-
- // Make data buffer
- std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ pkt << g_settings->get("remote_media");
+ Send(&pkt);
}
struct SendableMedia
@@ -4100,7 +2357,7 @@ struct SendableMedia
};
void Server::sendRequestedMedia(u16 peer_id,
- const std::list<std::string> &tosend)
+ const std::vector<std::string> &tosend)
{
DSTACK(__FUNCTION_NAME);
@@ -4112,17 +2369,16 @@ void Server::sendRequestedMedia(u16 peer_id,
// Put 5kB in one bunch (this is not accurate)
u32 bytes_per_bunch = 5000;
- std::vector< std::list<SendableMedia> > file_bunches;
- file_bunches.push_back(std::list<SendableMedia>());
+ std::vector< std::vector<SendableMedia> > file_bunches;
+ file_bunches.push_back(std::vector<SendableMedia>());
u32 file_size_bunch_total = 0;
- for(std::list<std::string>::const_iterator i = tosend.begin();
- i != tosend.end(); ++i)
- {
+ for(std::vector<std::string>::const_iterator i = tosend.begin();
+ i != tosend.end(); ++i) {
const std::string &name = *i;
- if(m_media.find(name) == m_media.end()){
+ if(m_media.find(name) == m_media.end()) {
errorstream<<"Server::sendRequestedMedia(): Client asked for "
<<"unknown file \""<<(name)<<"\""<<std::endl;
continue;
@@ -4140,7 +2396,7 @@ void Server::sendRequestedMedia(u16 peer_id,
}
std::ostringstream tmp_os(std::ios_base::binary);
bool bad = false;
- for(;;){
+ for(;;) {
char buf[1024];
fis.read(buf, 1024);
std::streamsize len = fis.gcount();
@@ -4148,12 +2404,12 @@ void Server::sendRequestedMedia(u16 peer_id,
file_size_bunch_total += len;
if(fis.eof())
break;
- if(!fis.good()){
+ if(!fis.good()) {
bad = true;
break;
}
}
- if(bad){
+ if(bad) {
errorstream<<"Server::sendRequestedMedia(): Failed to read \""
<<name<<"\""<<std::endl;
continue;
@@ -4165,8 +2421,8 @@ void Server::sendRequestedMedia(u16 peer_id,
SendableMedia(name, tpath, tmp_os.str()));
// Start next bunch if got enough data
- if(file_size_bunch_total >= bytes_per_bunch){
- file_bunches.push_back(std::list<SendableMedia>());
+ if(file_size_bunch_total >= bytes_per_bunch) {
+ file_bunches.push_back(std::vector<SendableMedia>());
file_size_bunch_total = 0;
}
@@ -4174,11 +2430,8 @@ void Server::sendRequestedMedia(u16 peer_id,
/* Create and send packets */
- u32 num_bunches = file_bunches.size();
- for(u32 i=0; i<num_bunches; i++)
- {
- std::ostringstream os(std::ios_base::binary);
-
+ u16 num_bunches = file_bunches.size();
+ for(u16 i = 0; i < num_bunches; i++) {
/*
u16 command
u16 total number of texture bunches
@@ -4192,55 +2445,47 @@ void Server::sendRequestedMedia(u16 peer_id,
}
*/
- writeU16(os, TOCLIENT_MEDIA);
- writeU16(os, num_bunches);
- writeU16(os, i);
- writeU32(os, file_bunches[i].size());
+ NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
+ pkt << num_bunches << i << (u32) file_bunches[i].size();
- for(std::list<SendableMedia>::iterator
+ for(std::vector<SendableMedia>::iterator
j = file_bunches[i].begin();
- j != file_bunches[i].end(); ++j){
- os<<serializeString(j->name);
- os<<serializeLongString(j->data);
+ j != file_bunches[i].end(); ++j) {
+ pkt << j->name;
+ pkt.putLongString(j->data);
}
- // Make data buffer
- std::string s = os.str();
- verbosestream<<"Server::sendRequestedMedia(): bunch "
- <<i<<"/"<<num_bunches
- <<" files="<<file_bunches[i].size()
- <<" size=" <<s.size()<<std::endl;
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- // Send as reliable
- m_clients.send(peer_id, 2, data, true);
+ verbosestream << "Server::sendRequestedMedia(): bunch "
+ << i << "/" << num_bunches
+ << " files=" << file_bunches[i].size()
+ << " size=" << pkt.getSize() << std::endl;
+ Send(&pkt);
}
}
void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
{
- if(m_detached_inventories.count(name) == 0){
+ if(m_detached_inventories.count(name) == 0) {
errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
return;
}
Inventory *inv = m_detached_inventories[name];
-
std::ostringstream os(std::ios_base::binary);
- writeU16(os, TOCLIENT_DETACHED_INVENTORY);
- os<<serializeString(name);
+
+ os << serializeString(name);
inv->serialize(os);
// Make data buffer
std::string s = os.str();
- SharedBuffer<u8> data((u8*)s.c_str(), s.size());
- if (peer_id != PEER_ID_INEXISTENT)
- {
- // Send as reliable
- m_clients.send(peer_id, 0, data, true);
+ NetworkPacket pkt(TOCLIENT_DETACHED_INVENTORY, 0, peer_id);
+ pkt.putRawString(s.c_str(), s.size());
+
+ if (peer_id != PEER_ID_INEXISTENT) {
+ Send(&pkt);
}
- else
- {
- m_clients.sendToAll(0,data,true);
+ else {
+ m_clients.sendToAll(0, &pkt, true);
}
}
@@ -4250,7 +2495,7 @@ void Server::sendDetachedInventories(u16 peer_id)
for(std::map<std::string, Inventory*>::iterator
i = m_detached_inventories.begin();
- i != m_detached_inventories.end(); i++){
+ i != m_detached_inventories.end(); i++) {
const std::string &name = i->first;
//Inventory *inv = i->second;
sendDetachedInventory(name, peer_id);
@@ -4268,9 +2513,9 @@ void Server::DiePlayer(u16 peer_id)
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
- infostream<<"Server::DiePlayer(): Player "
- <<playersao->getPlayer()->getName()
- <<" dies"<<std::endl;
+ infostream << "Server::DiePlayer(): Player "
+ << playersao->getPlayer()->getName()
+ << " dies" << std::endl;
playersao->setHP(0);
@@ -4288,29 +2533,102 @@ void Server::RespawnPlayer(u16 peer_id)
PlayerSAO *playersao = getPlayerSAO(peer_id);
assert(playersao);
- infostream<<"Server::RespawnPlayer(): Player "
- <<playersao->getPlayer()->getName()
- <<" respawns"<<std::endl;
+ infostream << "Server::RespawnPlayer(): Player "
+ << playersao->getPlayer()->getName()
+ << " respawns" << std::endl;
playersao->setHP(PLAYER_MAX_HP);
playersao->setBreath(PLAYER_MAX_BREATH);
+ SendPlayerHP(peer_id);
+ SendPlayerBreath(peer_id);
+
bool repositioned = m_script->on_respawnplayer(playersao);
if(!repositioned){
- v3f pos = findSpawnPos(m_env->getServerMap());
+ v3f pos = findSpawnPos();
+ // setPos will send the new position to client
playersao->setPos(pos);
}
}
-void Server::DenyAccess(u16 peer_id, const std::wstring &reason)
+
+void Server::DenySudoAccess(u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
+ Send(&pkt);
+}
+
+
+void Server::DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
+ const std::string &str_reason, bool reconnect)
+{
+ if (proto_ver >= 25) {
+ SendAccessDenied(peer_id, reason, str_reason);
+ } else {
+ std::wstring wreason = utf8_to_wide(
+ reason == SERVER_ACCESSDENIED_CUSTOM_STRING ? str_reason :
+ accessDeniedStrings[(u8)reason]);
+ SendAccessDenied_Legacy(peer_id, wreason);
+ }
+
+ m_clients.event(peer_id, CSE_SetDenied);
+ m_con.DisconnectPeer(peer_id);
+}
+
+
+void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ SendAccessDenied(peer_id, reason, custom_reason);
+ m_clients.event(peer_id, CSE_SetDenied);
+ m_con.DisconnectPeer(peer_id);
+}
+
+// 13/03/15: remove this function when protocol version 25 will become
+// the minimum version for MT users, maybe in 1 year
+void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
{
DSTACK(__FUNCTION_NAME);
- SendAccessDenied(peer_id, reason);
+ SendAccessDenied_Legacy(peer_id, reason);
m_clients.event(peer_id, CSE_SetDenied);
m_con.DisconnectPeer(peer_id);
}
+void Server::acceptAuth(u16 peer_id, bool forSudoMode)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ if (!forSudoMode) {
+ RemoteClient* client = getClient(peer_id, CS_Invalid);
+
+ NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);
+
+ // Right now, the auth mechs don't change between login and sudo mode.
+ u32 sudo_auth_mechs = client->allowed_auth_mechs;
+ client->allowed_sudo_mechs = sudo_auth_mechs;
+
+ resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
+ << g_settings->getFloat("dedicated_server_step")
+ << sudo_auth_mechs;
+
+ Send(&resp_pkt);
+ m_clients.event(peer_id, CSE_AuthAccept);
+ } else {
+ NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);
+
+ // We only support SRP right now
+ u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;
+
+ resp_pkt << sudo_auth_mechs;
+ Send(&resp_pkt);
+ m_clients.event(peer_id, CSE_SudoSuccess);
+ }
+}
+
void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
{
DSTACK(__FUNCTION_NAME);
@@ -4363,26 +2681,24 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
Print out action
*/
{
- if(player != NULL && reason != CDR_DENY)
- {
+ if(player != NULL && reason != CDR_DENY) {
std::ostringstream os(std::ios_base::binary);
- std::list<u16> clients = m_clients.getClientIDs();
+ std::vector<u16> clients = m_clients.getClientIDs();
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i)
- {
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
// Get player
Player *player = m_env->getPlayer(*i);
if(!player)
continue;
+
// Get name of player
- os<<player->getName()<<" ";
+ os << player->getName() << " ";
}
- actionstream<<player->getName()<<" "
- <<(reason==CDR_TIMEOUT?"times out.":"leaves game.")
- <<" List of players: "<<os.str()<<std::endl;
+ actionstream << player->getName() << " "
+ << (reason == CDR_TIMEOUT ? "times out." : "leaves game.")
+ << " List of players: " << os.str() << std::endl;
}
}
{
@@ -4396,24 +2712,22 @@ void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
SendChatMessage(PEER_ID_INEXISTENT,message);
}
-void Server::UpdateCrafting(u16 peer_id)
+void Server::UpdateCrafting(Player* player)
{
DSTACK(__FUNCTION_NAME);
- Player* player = m_env->getPlayer(peer_id);
- assert(player);
-
// Get a preview for crafting
ItemStack preview;
InventoryLocation loc;
loc.setPlayer(player->getName());
- getCraftingResult(&player->inventory, preview, false, this);
+ std::vector<ItemStack> output_replacements;
+ getCraftingResult(&player->inventory, preview, output_replacements, false, this);
m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc);
// Put the new preview in
InventoryList *plist = player->inventory.getList("craftpreview");
- assert(plist);
- assert(plist->getSize() >= 1);
+ sanity_check(plist);
+ sanity_check(plist->getSize() >= 1);
plist->changeItem(0, preview);
}
@@ -4451,7 +2765,7 @@ std::wstring Server::getStatusString()
std::wostringstream os(std::ios_base::binary);
os<<L"# Server: ";
// Version
- os<<L"version="<<narrow_to_wide(minetest_version_simple);
+ os<<L"version="<<narrow_to_wide(g_version_string);
// Uptime
os<<L", uptime="<<m_uptime.get();
// Max lag estimate
@@ -4459,10 +2773,9 @@ std::wstring Server::getStatusString()
// Information about clients
bool first = true;
os<<L", clients={";
- std::list<u16> clients = m_clients.getClientIDs();
- for(std::list<u16>::iterator i = clients.begin();
- i != clients.end(); ++i)
- {
+ std::vector<u16> clients = m_clients.getClientIDs();
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
// Get player
Player *player = m_env->getPlayer(*i);
// Get name of player
@@ -4471,12 +2784,12 @@ std::wstring Server::getStatusString()
name = narrow_to_wide(player->getName());
// Add name to information string
if(!first)
- os<<L", ";
+ os << L", ";
else
first = false;
- os<<name;
+ os << name;
}
- os<<L"}";
+ os << L"}";
if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
if(g_settings->get("motd") != "")
@@ -4499,11 +2812,10 @@ bool Server::checkPriv(const std::string &name, const std::string &priv)
void Server::reportPrivsModified(const std::string &name)
{
- if(name == ""){
- std::list<u16> clients = m_clients.getClientIDs();
- for(std::list<u16>::iterator
- i = clients.begin();
- i != clients.end(); ++i){
+ if(name == "") {
+ std::vector<u16> clients = m_clients.getClientIDs();
+ for(std::vector<u16>::iterator i = clients.begin();
+ i != clients.end(); ++i) {
Player *player = m_env->getPlayer(*i);
reportPrivsModified(player->getName());
}
@@ -4546,8 +2858,12 @@ std::string Server::getBanDescription(const std::string &ip_or_name)
void Server::notifyPlayer(const char *name, const std::wstring &msg)
{
+ // m_env will be NULL if the server is initializing
+ if (!m_env)
+ return;
+
Player *player = m_env->getPlayer(name);
- if(!player)
+ if (!player)
return;
if (player->peer_id == PEER_ID_INEXISTENT)
@@ -4556,21 +2872,23 @@ void Server::notifyPlayer(const char *name, const std::wstring &msg)
SendChatMessage(player->peer_id, msg);
}
-bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname)
+bool Server::showFormspec(const char *playername, const std::string &formspec,
+ const std::string &formname)
{
- Player *player = m_env->getPlayer(playername);
+ // m_env will be NULL if the server is initializing
+ if (!m_env)
+ return false;
- if(!player)
- {
- infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
+ Player *player = m_env->getPlayer(playername);
+ if (!player)
return false;
- }
SendShowFormspecMessage(player->peer_id, formspec, formname);
return true;
}
-u32 Server::hudAdd(Player *player, HudElement *form) {
+u32 Server::hudAdd(Player *player, HudElement *form)
+{
if (!player)
return -1;
@@ -4596,7 +2914,8 @@ bool Server::hudRemove(Player *player, u32 id) {
return true;
}
-bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) {
+bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data)
+{
if (!player)
return false;
@@ -4604,7 +2923,8 @@ bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data)
return true;
}
-bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
+bool Server::hudSetFlags(Player *player, u32 flags, u32 mask)
+{
if (!player)
return false;
@@ -4620,37 +2940,67 @@ bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) {
return true;
}
-bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) {
+bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount)
+{
if (!player)
return false;
if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX)
return false;
+ player->setHotbarItemcount(hotbar_itemcount);
std::ostringstream os(std::ios::binary);
writeS32(os, hotbar_itemcount);
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str());
return true;
}
-void Server::hudSetHotbarImage(Player *player, std::string name) {
+s32 Server::hudGetHotbarItemcount(Player *player)
+{
+ if (!player)
+ return 0;
+ return player->getHotbarItemcount();
+}
+
+void Server::hudSetHotbarImage(Player *player, std::string name)
+{
if (!player)
return;
+ player->setHotbarImage(name);
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name);
}
-void Server::hudSetHotbarSelectedImage(Player *player, std::string name) {
+std::string Server::hudGetHotbarImage(Player *player)
+{
+ if (!player)
+ return "";
+ return player->getHotbarImage();
+}
+
+void Server::hudSetHotbarSelectedImage(Player *player, std::string name)
+{
if (!player)
return;
+ player->setHotbarSelectedImage(name);
SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
}
-bool Server::setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed)
+std::string Server::hudGetHotbarSelectedImage(Player *player)
+{
+ if (!player)
+ return "";
+
+ return player->getHotbarSelectedImage();
+}
+
+bool Server::setLocalPlayerAnimations(Player *player,
+ v2s32 animation_frames[4], f32 frame_speed)
{
if (!player)
return false;
+ player->setLocalAnimations(animation_frames, frame_speed);
SendLocalPlayerAnimations(player->peer_id, animation_frames, frame_speed);
return true;
}
@@ -4660,26 +3010,30 @@ bool Server::setPlayerEyeOffset(Player *player, v3f first, v3f third)
if (!player)
return false;
+ player->eye_offset_first = first;
+ player->eye_offset_third = third;
SendEyeOffset(player->peer_id, first, third);
return true;
}
bool Server::setSky(Player *player, const video::SColor &bgcolor,
- const std::string &type, const std::vector<std::string> &params)
+ const std::string &type, const std::vector<std::string> &params)
{
if (!player)
return false;
+ player->setSky(bgcolor, type, params);
SendSetSky(player->peer_id, bgcolor, type, params);
return true;
}
bool Server::overrideDayNightRatio(Player *player, bool do_override,
- float ratio)
+ float ratio)
{
if (!player)
return false;
+ player->overrideDayNightRatio(do_override, ratio);
SendOverrideDayNightRatio(player->peer_id, do_override, ratio);
return true;
}
@@ -4689,68 +3043,45 @@ void Server::notifyPlayers(const std::wstring &msg)
SendChatMessage(PEER_ID_INEXISTENT,msg);
}
-void Server::spawnParticle(const char *playername, v3f pos,
- v3f velocity, v3f acceleration,
- float expirationtime, float size, bool
- collisiondetection, bool vertical, std::string texture)
+void Server::spawnParticle(const std::string &playername, v3f pos,
+ v3f velocity, v3f acceleration,
+ float expirationtime, float size, bool
+ collisiondetection, bool vertical, const std::string &texture)
{
- Player *player = m_env->getPlayer(playername);
- if(!player)
+ // m_env will be NULL if the server is initializing
+ if (!m_env)
return;
- SendSpawnParticle(player->peer_id, pos, velocity, acceleration,
- expirationtime, size, collisiondetection, vertical, texture);
-}
-void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
- float expirationtime, float size,
- bool collisiondetection, bool vertical, std::string texture)
-{
- SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration,
+ u16 peer_id = PEER_ID_INEXISTENT;
+ if (playername != "") {
+ Player* player = m_env->getPlayer(playername.c_str());
+ if (!player)
+ return;
+ peer_id = player->peer_id;
+ }
+
+ SendSpawnParticle(peer_id, pos, velocity, acceleration,
expirationtime, size, collisiondetection, vertical, texture);
}
-u32 Server::addParticleSpawner(const char *playername,
- u16 amount, float spawntime,
- v3f minpos, v3f maxpos,
- v3f minvel, v3f maxvel,
- v3f minacc, v3f maxacc,
- float minexptime, float maxexptime,
- float minsize, float maxsize,
- bool collisiondetection, bool vertical, std::string texture)
+u32 Server::addParticleSpawner(u16 amount, float spawntime,
+ v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc,
+ float minexptime, float maxexptime, float minsize, float maxsize,
+ bool collisiondetection, bool vertical, const std::string &texture,
+ const std::string &playername)
{
- Player *player = m_env->getPlayer(playername);
- if(!player)
+ // m_env will be NULL if the server is initializing
+ if (!m_env)
return -1;
- u32 id = 0;
- for(;;) // look for unused particlespawner id
- {
- id++;
- if (std::find(m_particlespawner_ids.begin(),
- m_particlespawner_ids.end(), id)
- == m_particlespawner_ids.end())
- {
- m_particlespawner_ids.push_back(id);
- break;
- }
+ u16 peer_id = PEER_ID_INEXISTENT;
+ if (playername != "") {
+ Player* player = m_env->getPlayer(playername.c_str());
+ if (!player)
+ return -1;
+ peer_id = player->peer_id;
}
- SendAddParticleSpawner(player->peer_id, amount, spawntime,
- minpos, maxpos, minvel, maxvel, minacc, maxacc,
- minexptime, maxexptime, minsize, maxsize,
- collisiondetection, vertical, texture, id);
-
- return id;
-}
-
-u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
- v3f minpos, v3f maxpos,
- v3f minvel, v3f maxvel,
- v3f minacc, v3f maxacc,
- float minexptime, float maxexptime,
- float minsize, float maxsize,
- bool collisiondetection, bool vertical, std::string texture)
-{
u32 id = 0;
for(;;) // look for unused particlespawner id
{
@@ -4764,7 +3095,7 @@ u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
}
}
- SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime,
+ SendAddParticleSpawner(peer_id, amount, spawntime,
minpos, maxpos, minvel, maxvel, minacc, maxacc,
minexptime, maxexptime, minsize, maxsize,
collisiondetection, vertical, texture, id);
@@ -4772,26 +3103,25 @@ u32 Server::addParticleSpawnerAll(u16 amount, float spawntime,
return id;
}
-void Server::deleteParticleSpawner(const char *playername, u32 id)
+void Server::deleteParticleSpawner(const std::string &playername, u32 id)
{
- Player *player = m_env->getPlayer(playername);
- if(!player)
- return;
+ // m_env will be NULL if the server is initializing
+ if (!m_env)
+ throw ServerError("Can't delete particle spawners during initialisation!");
- m_particlespawner_ids.erase(
- std::remove(m_particlespawner_ids.begin(),
- m_particlespawner_ids.end(), id),
- m_particlespawner_ids.end());
- SendDeleteParticleSpawner(player->peer_id, id);
-}
+ u16 peer_id = PEER_ID_INEXISTENT;
+ if (playername != "") {
+ Player* player = m_env->getPlayer(playername.c_str());
+ if (!player)
+ return;
+ peer_id = player->peer_id;
+ }
-void Server::deleteParticleSpawnerAll(u32 id)
-{
m_particlespawner_ids.erase(
std::remove(m_particlespawner_ids.begin(),
m_particlespawner_ids.end(), id),
m_particlespawner_ids.end());
- SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id);
+ SendDeleteParticleSpawner(peer_id, id);
}
Inventory* Server::createDetachedInventory(const std::string &name)
@@ -4803,31 +3133,13 @@ Inventory* Server::createDetachedInventory(const std::string &name)
infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
}
Inventory *inv = new Inventory(m_itemdef);
- assert(inv);
+ sanity_check(inv);
m_detached_inventories[name] = inv;
//TODO find a better way to do this
sendDetachedInventory(name,PEER_ID_INEXISTENT);
return inv;
}
-class BoolScopeSet
-{
-public:
- BoolScopeSet(bool *dst, bool val):
- m_dst(dst)
- {
- m_orig_state = *m_dst;
- *m_dst = val;
- }
- ~BoolScopeSet()
- {
- *m_dst = m_orig_state;
- }
-private:
- bool *m_dst;
- bool m_orig_state;
-};
-
// actions: time-reversed list
// Return value: success/failure
bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
@@ -4877,27 +3189,29 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
// IGameDef interface
// Under envlock
-IItemDefManager* Server::getItemDefManager()
+IItemDefManager *Server::getItemDefManager()
{
return m_itemdef;
}
-INodeDefManager* Server::getNodeDefManager()
+
+INodeDefManager *Server::getNodeDefManager()
{
return m_nodedef;
}
-ICraftDefManager* Server::getCraftDefManager()
+
+ICraftDefManager *Server::getCraftDefManager()
{
return m_craftdef;
}
-ITextureSource* Server::getTextureSource()
+ITextureSource *Server::getTextureSource()
{
return NULL;
}
-IShaderSource* Server::getShaderSource()
+IShaderSource *Server::getShaderSource()
{
return NULL;
}
-scene::ISceneManager* Server::getSceneManager()
+scene::ISceneManager *Server::getSceneManager()
{
return NULL;
}
@@ -4906,67 +3220,73 @@ u16 Server::allocateUnknownNodeId(const std::string &name)
{
return m_nodedef->allocateDummy(name);
}
-ISoundManager* Server::getSoundManager()
+
+ISoundManager *Server::getSoundManager()
{
return &dummySoundManager;
}
-MtEventManager* Server::getEventManager()
+
+MtEventManager *Server::getEventManager()
{
return m_event;
}
-IWritableItemDefManager* Server::getWritableItemDefManager()
+IWritableItemDefManager *Server::getWritableItemDefManager()
{
return m_itemdef;
}
-IWritableNodeDefManager* Server::getWritableNodeDefManager()
+
+IWritableNodeDefManager *Server::getWritableNodeDefManager()
{
return m_nodedef;
}
-IWritableCraftDefManager* Server::getWritableCraftDefManager()
+
+IWritableCraftDefManager *Server::getWritableCraftDefManager()
{
return m_craftdef;
}
-const ModSpec* Server::getModSpec(const std::string &modname)
+const ModSpec *Server::getModSpec(const std::string &modname) const
{
- for(std::vector<ModSpec>::iterator i = m_mods.begin();
- i != m_mods.end(); i++){
- const ModSpec &mod = *i;
- if(mod.name == modname)
+ std::vector<ModSpec>::const_iterator it;
+ for (it = m_mods.begin(); it != m_mods.end(); ++it) {
+ const ModSpec &mod = *it;
+ if (mod.name == modname)
return &mod;
}
return NULL;
}
-void Server::getModNames(std::list<std::string> &modlist)
+
+void Server::getModNames(std::vector<std::string> &modlist)
{
- for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++)
- {
- modlist.push_back(i->name);
- }
+ std::vector<ModSpec>::iterator it;
+ for (it = m_mods.begin(); it != m_mods.end(); ++it)
+ modlist.push_back(it->name);
}
+
std::string Server::getBuiltinLuaPath()
{
return porting::path_share + DIR_DELIM + "builtin";
}
-v3f findSpawnPos(ServerMap &map)
+v3f Server::findSpawnPos()
{
- //return v3f(50,50,50)*BS;
-
- v3s16 nodepos;
+ ServerMap &map = m_env->getServerMap();
+ v3f nodeposf;
+ if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
+ return nodeposf * BS;
+ }
-#if 0
- nodepos = v2s16(0,0);
- groundheight = 20;
-#endif
+ // Default position is static_spawnpoint
+ // We will return it if we don't found a good place
+ v3s16 nodepos(nodeposf.X, nodeposf.Y, nodeposf.Z);
-#if 1
s16 water_level = map.getWaterLevel();
+ bool is_good = false;
+
// Try to find a good place a few times
- for(s32 i=0; i<1000; i++)
- {
+ for(s32 i = 0; i < 1000 && !is_good; i++) {
s32 range = 1 + i;
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
@@ -4981,7 +3301,7 @@ v3f findSpawnPos(ServerMap &map)
continue;
nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y);
- bool is_good = false;
+
s32 air_count = 0;
for (s32 i = 0; i < 10; i++) {
v3s16 blockpos = getNodeBlockPos(nodepos);
@@ -4996,26 +3316,19 @@ v3f findSpawnPos(ServerMap &map)
}
nodepos.Y++;
}
- if(is_good){
- // Found a good place
- //infostream<<"Searched through "<<i<<" places."<<std::endl;
- break;
- }
}
-#endif
return intToFloat(nodepos, BS);
}
-PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
+PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version)
{
- RemotePlayer *player = NULL;
bool newplayer = false;
/*
Try to get an existing player
*/
- player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
+ RemotePlayer *player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
// If player is already connected, cancel
if(player != NULL && player->peer_id != 0)
@@ -5046,7 +3359,7 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
// Set player position
infostream<<"Server: Finding spawn place for player \""
<<name<<"\""<<std::endl;
- v3f pos = findSpawnPos(m_env->getServerMap());
+ v3f pos = findSpawnPos();
player->setPosition(pos);
// Make sure the player is saved
@@ -5061,6 +3374,8 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
getPlayerEffectivePrivs(player->getName()),
isSingleplayer());
+ player->protocol_version = proto_version;
+
/* Clean up old HUD elements from previous sessions */
player->clearHud();
@@ -5120,5 +3435,3 @@ void dedicated_server_loop(Server &server, bool &kill)
}
}
}
-
-
diff --git a/src/server.h b/src/server.h
index 3d6b00d99..d16230967 100644
--- a/src/server.h
+++ b/src/server.h
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef SERVER_HEADER
#define SERVER_HEADER
-#include "connection.h"
+#include "network/connection.h"
#include "irr_v3d.h"
#include "map.h"
#include "hud.h"
@@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/thread.h"
#include "environment.h"
#include "clientiface.h"
+#include "network/networkpacket.h"
#include <string>
#include <list>
#include <map>
@@ -62,11 +63,6 @@ enum ClientDeletionReason {
CDR_DENY
};
-/*
- Some random functions
-*/
-v3f findSpawnPos(ServerMap &map);
-
class MapEditEventIgnorer
{
public:
@@ -187,7 +183,42 @@ public:
void AsyncRunStep(bool initial_step=false);
void Receive();
PlayerSAO* StageTwoClientInit(u16 peer_id);
- void ProcessData(u8 *data, u32 datasize, u16 peer_id);
+
+ /*
+ * Command Handlers
+ */
+
+ void handleCommand(NetworkPacket* pkt);
+
+ void handleCommand_Null(NetworkPacket* pkt) {};
+ void handleCommand_Deprecated(NetworkPacket* pkt);
+ void handleCommand_Init(NetworkPacket* pkt);
+ void handleCommand_Init_Legacy(NetworkPacket* pkt);
+ void handleCommand_Init2(NetworkPacket* pkt);
+ void handleCommand_RequestMedia(NetworkPacket* pkt);
+ void handleCommand_ReceivedMedia(NetworkPacket* pkt);
+ void handleCommand_ClientReady(NetworkPacket* pkt);
+ void handleCommand_GotBlocks(NetworkPacket* pkt);
+ void handleCommand_PlayerPos(NetworkPacket* pkt);
+ void handleCommand_DeletedBlocks(NetworkPacket* pkt);
+ void handleCommand_InventoryAction(NetworkPacket* pkt);
+ void handleCommand_ChatMessage(NetworkPacket* pkt);
+ void handleCommand_Damage(NetworkPacket* pkt);
+ void handleCommand_Breath(NetworkPacket* pkt);
+ void handleCommand_Password(NetworkPacket* pkt);
+ void handleCommand_PlayerItem(NetworkPacket* pkt);
+ void handleCommand_Respawn(NetworkPacket* pkt);
+ void handleCommand_Interact(NetworkPacket* pkt);
+ void handleCommand_RemovedSounds(NetworkPacket* pkt);
+ void handleCommand_NodeMetaFields(NetworkPacket* pkt);
+ void handleCommand_InventoryFields(NetworkPacket* pkt);
+ void handleCommand_FirstSrp(NetworkPacket* pkt);
+ void handleCommand_SrpBytesA(NetworkPacket* pkt);
+ void handleCommand_SrpBytesM(NetworkPacket* pkt);
+
+ void ProcessData(NetworkPacket *pkt);
+
+ void Send(NetworkPacket* pkt);
// Environment must be locked when called
void setTimeOfDay(u32 time);
@@ -203,7 +234,7 @@ public:
Shall be called with the environment and the connection locked.
*/
Inventory* getInventory(const InventoryLocation &loc);
- void setInventoryModified(const InventoryLocation &loc);
+ void setInventoryModified(const InventoryLocation &loc, bool playerSend = true);
// Connection must be locked when called
std::wstring getStatusString();
@@ -213,8 +244,13 @@ public:
{ return m_shutdown_requested; }
// request server to shutdown
- inline void requestShutdown(void)
- { m_shutdown_requested = true; }
+ inline void requestShutdown() { m_shutdown_requested = true; }
+ void requestShutdown(const std::string &msg, bool reconnect)
+ {
+ m_shutdown_requested = true;
+ m_shutdown_msg = msg;
+ m_shutdown_ask_reconnect = reconnect;
+ }
// Returns -1 if failed, sound handle on success
// Envlock
@@ -233,34 +269,21 @@ public:
void notifyPlayer(const char *name, const std::wstring &msg);
void notifyPlayers(const std::wstring &msg);
- void spawnParticle(const char *playername,
+ void spawnParticle(const std::string &playername,
v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size,
- bool collisiondetection, bool vertical, std::string texture);
+ bool collisiondetection, bool vertical, const std::string &texture);
- void spawnParticleAll(v3f pos, v3f velocity, v3f acceleration,
- float expirationtime, float size,
- bool collisiondetection, bool vertical, std::string texture);
-
- u32 addParticleSpawner(const char *playername,
- u16 amount, float spawntime,
+ u32 addParticleSpawner(u16 amount, float spawntime,
v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel,
v3f minacc, v3f maxacc,
float minexptime, float maxexptime,
float minsize, float maxsize,
- bool collisiondetection, bool vertical, std::string texture);
+ bool collisiondetection, bool vertical, const std::string &texture,
+ const std::string &playername);
- u32 addParticleSpawnerAll(u16 amount, float spawntime,
- v3f minpos, v3f maxpos,
- v3f minvel, v3f maxvel,
- v3f minacc, v3f maxacc,
- float minexptime, float maxexptime,
- float minsize, float maxsize,
- bool collisiondetection, bool vertical, std::string texture);
-
- void deleteParticleSpawner(const char *playername, u32 id);
- void deleteParticleSpawnerAll(u32 id);
+ void deleteParticleSpawner(const std::string &playername, u32 id);
// Creates or resets inventory
Inventory* createDetachedInventory(const std::string &name);
@@ -268,9 +291,6 @@ public:
// Envlock and conlock should be locked when using scriptapi
GameScripting *getScriptIface(){ return m_script; }
- //TODO: determine what (if anything) should be locked to access EmergeManager
- EmergeManager *getEmergeManager(){ return m_emerge; }
-
// actions: time-reversed list
// Return value: success/failure
bool rollbackRevertActions(const std::list<RollbackAction> &actions,
@@ -288,16 +308,16 @@ public:
virtual MtEventManager* getEventManager();
virtual scene::ISceneManager* getSceneManager();
virtual IRollbackManager *getRollbackManager() { return m_rollback; }
-
+ virtual EmergeManager *getEmergeManager() { return m_emerge; }
IWritableItemDefManager* getWritableItemDefManager();
IWritableNodeDefManager* getWritableNodeDefManager();
IWritableCraftDefManager* getWritableCraftDefManager();
- const ModSpec* getModSpec(const std::string &modname);
- void getModNames(std::list<std::string> &modlist);
+ const ModSpec* getModSpec(const std::string &modname) const;
+ void getModNames(std::vector<std::string> &modlist);
std::string getBuiltinLuaPath();
- inline std::string getWorldPath()
+ inline std::string getWorldPath() const
{ return m_path_world; }
inline bool isSingleplayer()
@@ -309,24 +329,27 @@ public:
bool showFormspec(const char *name, const std::string &formspec, const std::string &formname);
Map & getMap() { return m_env->getMap(); }
ServerEnvironment & getEnv() { return *m_env; }
-
+
u32 hudAdd(Player *player, HudElement *element);
bool hudRemove(Player *player, u32 id);
bool hudChange(Player *player, u32 id, HudElementStat stat, void *value);
bool hudSetFlags(Player *player, u32 flags, u32 mask);
bool hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount);
+ s32 hudGetHotbarItemcount(Player *player);
void hudSetHotbarImage(Player *player, std::string name);
+ std::string hudGetHotbarImage(Player *player);
void hudSetHotbarSelectedImage(Player *player, std::string name);
+ std::string hudGetHotbarSelectedImage(Player *player);
inline Address getPeerAddress(u16 peer_id)
{ return m_con.GetPeerAddress(peer_id); }
-
+
bool setLocalPlayerAnimations(Player *player, v2s32 animation_frames[4], f32 frame_speed);
bool setPlayerEyeOffset(Player *player, v3f first, v3f third);
bool setSky(Player *player, const video::SColor &bgcolor,
const std::string &type, const std::vector<std::string> &params);
-
+
bool overrideDayNightRatio(Player *player, bool do_override,
float brightness);
@@ -334,12 +357,22 @@ public:
void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout);
- void DenyAccess(u16 peer_id, const std::wstring &reason);
+ void DenySudoAccess(u16 peer_id);
+ void DenyAccessVerCompliant(u16 peer_id, u16 proto_ver, AccessDeniedCode reason,
+ const std::string &str_reason = "", bool reconnect = false);
+ void DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason="");
+ void acceptAuth(u16 peer_id, bool forSudoMode);
+ void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason);
bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval);
bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime,
u8* ser_vers, u16* prot_vers, u8* major, u8* minor, u8* patch,
std::string* vers_string);
+ void SendPlayerHPOrDie(PlayerSAO *player);
+ void SendPlayerBreath(u16 peer_id);
+ void SendInventory(PlayerSAO* playerSAO);
+ void SendMovePlayer(u16 peer_id);
+
// Bind address
Address m_bind_addr;
@@ -351,7 +384,9 @@ private:
void SendMovement(u16 peer_id);
void SendHP(u16 peer_id, u8 hp);
void SendBreath(u16 peer_id, u16 breath);
- void SendAccessDenied(u16 peer_id,const std::wstring &reason);
+ void SendAccessDenied(u16 peer_id, AccessDeniedCode reason,
+ const std::string &custom_reason, bool reconnect = false);
+ void SendAccessDenied_Legacy(u16 peer_id, const std::wstring &reason);
void SendDeathscreen(u16 peer_id,bool set_camera_point_target, v3f camera_point_target);
void SendItemDef(u16 peer_id,IItemDefManager *itemdef, u16 protocol_version);
void SendNodeDef(u16 peer_id,INodeDefManager *nodedef, u16 protocol_version);
@@ -359,13 +394,11 @@ private:
/* mark blocks not sent for all clients */
void SetBlocksNotSent(std::map<v3s16, MapBlock *>& block);
- // Envlock and conlock should be locked when calling these
- void SendInventory(u16 peer_id);
+
void SendChatMessage(u16 peer_id, const std::wstring &message);
void SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed);
void SendPlayerHP(u16 peer_id);
- void SendPlayerBreath(u16 peer_id);
- void SendMovePlayer(u16 peer_id);
+
void SendLocalPlayerAnimations(u16 peer_id, v2s32 animation_frames[4], f32 animation_speed);
void SendEyeOffset(u16 peer_id, v3f first, v3f third);
void SendPlayerPrivileges(u16 peer_id);
@@ -379,7 +412,7 @@ private:
void SendSetSky(u16 peer_id, const video::SColor &bgcolor,
const std::string &type, const std::vector<std::string> &params);
void SendOverrideDayNightRatio(u16 peer_id, bool do_override, float ratio);
-
+
/*
Send a node removal/addition event to all clients except ignore_id.
Additionally, if far_players!=NULL, players further away than
@@ -387,9 +420,9 @@ private:
*/
// Envlock and conlock should be locked when calling these
void sendRemoveNode(v3s16 p, u16 ignore_id=0,
- std::list<u16> *far_players=NULL, float far_d_nodes=100);
+ std::vector<u16> *far_players=NULL, float far_d_nodes=100);
void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0,
- std::list<u16> *far_players=NULL, float far_d_nodes=100,
+ std::vector<u16> *far_players=NULL, float far_d_nodes=100,
bool remove_metadata=true);
void setBlockNotSent(v3s16 p);
@@ -402,7 +435,7 @@ private:
void fillMediaCache();
void sendMediaAnnouncement(u16 peer_id);
void sendRequestedMedia(u16 peer_id,
- const std::list<std::string> &tosend);
+ const std::vector<std::string> &tosend);
void sendDetachedInventory(const std::string &name, u16 peer_id);
void sendDetachedInventories(u16 peer_id);
@@ -424,6 +457,8 @@ private:
float expirationtime, float size,
bool collisiondetection, bool vertical, std::string texture);
+ u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas);
+ void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true);
/*
Something random
*/
@@ -431,7 +466,9 @@ private:
void DiePlayer(u16 peer_id);
void RespawnPlayer(u16 peer_id);
void DeleteClient(u16 peer_id, ClientDeletionReason reason);
- void UpdateCrafting(u16 peer_id);
+ void UpdateCrafting(Player *player);
+
+ v3f findSpawnPos();
// When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id,ClientState state_min=CS_Active);
@@ -448,7 +485,7 @@ private:
Call with env and con locked.
*/
- PlayerSAO *emergePlayer(const char *name, u16 peer_id);
+ PlayerSAO *emergePlayer(const char *name, u16 peer_id, u16 proto_version);
void handlePeerChanges();
@@ -547,16 +584,15 @@ private:
Queues stuff from peerAdded() and deletingPeer() to
handlePeerChanges()
*/
- Queue<con::PeerChange> m_peer_change_queue;
+ std::queue<con::PeerChange> m_peer_change_queue;
/*
Random stuff
*/
- // Mod parent directory paths
- std::list<std::string> m_modspaths;
-
bool m_shutdown_requested;
+ std::string m_shutdown_msg;
+ bool m_shutdown_ask_reconnect;
/*
Map edit event queue. Automatically receives all map edits.
@@ -571,7 +607,7 @@ private:
Queue of map edits from the environment for sending to the clients
This is behind m_env_mutex
*/
- Queue<MapEditEvent*> m_unsent_map_edit_queue;
+ std::queue<MapEditEvent*> m_unsent_map_edit_queue;
/*
Set to true when the server itself is modifying the map and does
all sending of information by itself.
@@ -616,9 +652,9 @@ private:
/*
Runs a simple dedicated server loop.
- Shuts down when run is set to false.
+ Shuts down when kill is set to true.
*/
-void dedicated_server_loop(Server &server, bool &run);
+void dedicated_server_loop(Server &server, bool &kill);
#endif
diff --git a/src/serverlist.cpp b/src/serverlist.cpp
index 6732e5ac9..a33d1d6bf 100644
--- a/src/serverlist.cpp
+++ b/src/serverlist.cpp
@@ -17,18 +17,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#include "version.h"
-#include "main.h" // for g_settings
#include "settings.h"
#include "serverlist.h"
#include "filesys.h"
#include "porting.h"
#include "log.h"
-#include "clientserver.h"
+#include "network/networkprotocol.h"
#include "json/json.h"
#include "convert_json.h"
#include "httpfetch.h"
@@ -212,7 +212,7 @@ void sendAnnounce(const std::string &action,
bool strict_checking = g_settings->getBool("strict_protocol_version_checking");
server["name"] = g_settings->get("server_name");
server["description"] = g_settings->get("server_description");
- server["version"] = minetest_version_simple;
+ server["version"] = g_version_string;
server["proto_min"] = strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MIN;
server["proto_max"] = strict_checking ? LATEST_PROTOCOL_VERSION : SERVER_PROTOCOL_VERSION_MAX;
server["url"] = g_settings->get("server_url");
diff --git a/src/serverobject.cpp b/src/serverobject.cpp
index 81307bc34..699040bf2 100644
--- a/src/serverobject.cpp
+++ b/src/serverobject.cpp
@@ -38,15 +38,19 @@ ServerActiveObject::~ServerActiveObject()
{
}
-ServerActiveObject* ServerActiveObject::create(u8 type,
+ServerActiveObject* ServerActiveObject::create(ActiveObjectType type,
ServerEnvironment *env, u16 id, v3f pos,
const std::string &data)
{
// Find factory function
std::map<u16, Factory>::iterator n;
n = m_types.find(type);
- if(n == m_types.end())
- {
+ if(n == m_types.end()) {
+ // These are 0.3 entity types, return without error.
+ if (ACTIVEOBJECT_TYPE_ITEM <= type && type <= ACTIVEOBJECT_TYPE_MOBV2) {
+ return NULL;
+ }
+
// If factory is not found, just return.
dstream<<"WARNING: ServerActiveObject: No factory for type="
<<type<<std::endl;
@@ -86,14 +90,9 @@ ItemStack ServerActiveObject::getWieldedItem() const
bool ServerActiveObject::setWieldedItem(const ItemStack &item)
{
- Inventory *inv = getInventory();
- if(inv)
- {
- InventoryList *list = inv->getList(getWieldList());
- if (list)
- {
+ if(Inventory *inv = getInventory()) {
+ if (InventoryList *list = inv->getList(getWieldList())) {
list->changeItem(getWieldIndex(), item);
- setInventoryModified();
return true;
}
}
diff --git a/src/serverobject.h b/src/serverobject.h
index 8e80225e4..597eb63a8 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -58,7 +58,7 @@ public:
ServerActiveObject(ServerEnvironment *env, v3f pos);
virtual ~ServerActiveObject();
- virtual u8 getSendType() const
+ virtual ActiveObjectType getSendType() const
{ return getType(); }
// Called after id has been set and has been inserted in environment
@@ -71,7 +71,7 @@ public:
{ return true; }
// Create a certain type of ServerActiveObject
- static ServerActiveObject* create(u8 type,
+ static ServerActiveObject* create(ActiveObjectType type,
ServerEnvironment *env, u16 id, v3f pos,
const std::string &data);
@@ -147,14 +147,28 @@ public:
virtual void setArmorGroups(const ItemGroupList &armor_groups)
{}
+ virtual ItemGroupList getArmorGroups()
+ { return ItemGroupList(); }
virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity)
{}
- virtual void setAnimation(v2f frames, float frame_speed, float frame_blend)
+ virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop)
{}
- virtual void setBonePosition(std::string bone, v3f position, v3f rotation)
+ virtual void getAnimation(v2f *frames, float *frame_speed, float *frame_blend, bool *frame_loop)
{}
- virtual void setAttachment(int parent_id, std::string bone, v3f position, v3f rotation)
+ virtual void setBonePosition(const std::string &bone, v3f position, v3f rotation)
{}
+ virtual void getBonePosition(const std::string &bone, v3f *position, v3f *lotation)
+ {}
+ virtual void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation)
+ {}
+ virtual void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation)
+ {}
+ virtual void addAttachmentChild(int child_id)
+ {}
+ virtual void removeAttachmentChild(int child_id)
+ {}
+ virtual std::set<int> getAttachmentChildIds()
+ { return std::set<int>(); }
virtual ObjectProperties* accessObjectProperties()
{ return NULL; }
virtual void notifyObjectPropertiesModified()
@@ -218,7 +232,7 @@ public:
/*
Queue of messages to be sent to the client
*/
- Queue<ActiveObjectMessage> m_messages_out;
+ std::queue<ActiveObjectMessage> m_messages_out;
protected:
// Used for creating objects based on type
diff --git a/src/settings.cpp b/src/settings.cpp
index 7339af62b..e95bd436d 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -33,6 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cctype>
#include <algorithm>
+static Settings main_settings;
+Settings *g_settings = &main_settings;
+std::string g_settings_path;
Settings::~Settings()
{
@@ -65,10 +68,11 @@ Settings & Settings::operator = (const Settings &other)
bool Settings::checkNameValid(const std::string &name)
{
- size_t pos = name.find_first_of("\t\n\v\f\r\b =\"{}#");
- if (pos != std::string::npos) {
- errorstream << "Invalid character '" << name[pos]
- << "' found in setting name" << std::endl;
+ bool valid = name.find_first_of("=\"{}#") == std::string::npos;
+ if (valid) valid = trim(name) == name;
+ if (!valid) {
+ errorstream << "Invalid setting name \"" << name << "\""
+ << std::endl;
return false;
}
return true;
@@ -80,7 +84,7 @@ bool Settings::checkValueValid(const std::string &value)
if (value.substr(0, 3) == "\"\"\"" ||
value.find("\n\"\"\"") != std::string::npos) {
errorstream << "Invalid character sequence '\"\"\"' found in"
- " setting value" << std::endl;
+ " setting value!" << std::endl;
return false;
}
return true;
@@ -89,9 +93,9 @@ bool Settings::checkValueValid(const std::string &value)
std::string Settings::sanitizeName(const std::string &name)
{
- std::string n(name);
+ std::string n = trim(name);
- for (const char *s = "\t\n\v\f\r\b =\"{}#"; *s; s++)
+ for (const char *s = "=\"{}#"; *s; s++)
n.erase(std::remove(n.begin(), n.end(), *s), n.end());
return n;
@@ -264,7 +268,7 @@ bool Settings::updateConfigObject(std::istream &is, std::ostream &os,
it = m_settings.find(name);
if (it != m_settings.end() && it->second.is_group) {
os << line << "\n";
- assert(it->second.group != NULL);
+ sanity_check(it->second.group != NULL);
was_modified |= it->second.group->updateConfigObject(is, os,
"}", tab_depth + 1);
} else {
@@ -887,6 +891,11 @@ void Settings::clear()
clearNoLock();
}
+void Settings::clearDefaults()
+{
+ JMutexAutoLock lock(m_mutex);
+ clearDefaultsNoLock();
+}
void Settings::updateValue(const Settings &other, const std::string &name)
{
@@ -958,11 +967,18 @@ void Settings::clearNoLock()
delete it->second.group;
m_settings.clear();
+ clearDefaultsNoLock();
+}
+
+void Settings::clearDefaultsNoLock()
+{
+ std::map<std::string, SettingsEntry>::const_iterator it;
for (it = m_defaults.begin(); it != m_defaults.end(); ++it)
delete it->second.group;
m_defaults.clear();
}
+
void Settings::registerChangedCallback(std::string name,
setting_changed_callback cbf, void *userdata)
{
diff --git a/src/settings.h b/src/settings.h
index 1a7d9ab96..d41f134cd 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -31,8 +31,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class Settings;
struct NoiseParams;
+// Global objects
+extern Settings *g_settings;
+extern std::string g_settings_path;
+
/** function type to register a changed callback */
-typedef void (*setting_changed_callback)(const std::string, void*);
+typedef void (*setting_changed_callback)(const std::string &name, void *data);
enum ValueType {
VALUETYPE_STRING,
@@ -202,6 +206,7 @@ public:
// remove a setting
bool remove(const std::string &name);
void clear();
+ void clearDefaults();
void updateValue(const Settings &other, const std::string &name);
void update(const Settings &other);
void registerChangedCallback(std::string name, setting_changed_callback cbf, void *userdata = NULL);
@@ -211,6 +216,7 @@ private:
void updateNoLock(const Settings &other);
void clearNoLock();
+ void clearDefaultsNoLock();
void doCallbacks(std::string name);
diff --git a/src/shader.cpp b/src/shader.cpp
index 167045804..7e4f40810 100644
--- a/src/shader.cpp
+++ b/src/shader.cpp
@@ -18,15 +18,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <fstream>
+#include <iterator>
#include "shader.h"
#include "irrlichttypes_extrabloated.h"
#include "debug.h"
-#include "main.h" // for g_settings
#include "filesys.h"
#include "util/container.h"
#include "util/thread.h"
#include "settings.h"
-#include <iterator>
#include <ICameraSceneNode.h>
#include <IGPUProgrammingServices.h>
#include <IMaterialRenderer.h>
@@ -36,7 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "gamedef.h"
#include "strfnd.h" // trim()
-#include "tile.h"
+#include "client/tile.h"
/*
A cache from shader name to shader path
@@ -103,10 +103,8 @@ std::string getShaderPath(const std::string &name_of_shader,
class SourceShaderCache
{
public:
- void insert(const std::string &name_of_shader,
- const std::string &filename,
- const std::string &program,
- bool prefer_local)
+ void insert(const std::string &name_of_shader, const std::string &filename,
+ const std::string &program, bool prefer_local)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
// Try to use local shader instead if asked to
@@ -122,42 +120,43 @@ public:
}
m_programs[combined] = program;
}
+
std::string get(const std::string &name_of_shader,
- const std::string &filename)
+ const std::string &filename)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
- std::map<std::string, std::string>::iterator n;
- n = m_programs.find(combined);
- if(n != m_programs.end())
+ StringMap::iterator n = m_programs.find(combined);
+ if (n != m_programs.end())
return n->second;
return "";
}
+
// Primarily fetches from cache, secondarily tries to read from filesystem
std::string getOrLoad(const std::string &name_of_shader,
- const std::string &filename)
+ const std::string &filename)
{
std::string combined = name_of_shader + DIR_DELIM + filename;
- std::map<std::string, std::string>::iterator n;
- n = m_programs.find(combined);
- if(n != m_programs.end())
+ StringMap::iterator n = m_programs.find(combined);
+ if (n != m_programs.end())
return n->second;
std::string path = getShaderPath(name_of_shader, filename);
- if(path == ""){
- infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
- <<combined<<"\""<<std::endl;
+ if (path == "") {
+ infostream << "SourceShaderCache::getOrLoad(): No path found for \""
+ << combined << "\"" << std::endl;
return "";
}
- infostream<<"SourceShaderCache::getOrLoad(): Loading path \""<<path
- <<"\""<<std::endl;
+ infostream << "SourceShaderCache::getOrLoad(): Loading path \""
+ << path << "\"" << std::endl;
std::string p = readFile(path);
- if(p != ""){
+ if (p != "") {
m_programs[combined] = p;
return p;
}
return "";
}
private:
- std::map<std::string, std::string> m_programs;
+ StringMap m_programs;
+
std::string readFile(const std::string &path)
{
std::ifstream is(path.c_str(), std::ios::binary);
@@ -196,7 +195,7 @@ public:
virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
{
video::IVideoDriver *driver = services->getVideoDriver();
- assert(driver);
+ sanity_check(driver != NULL);
bool is_highlevel = userData;
@@ -219,7 +218,7 @@ public:
bool is_highlevel)
{
video::IVideoDriver *driver = services->getVideoDriver();
- assert(driver);
+ sanity_check(driver);
// set inverted world matrix
core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
@@ -274,23 +273,23 @@ public:
The id 0 points to a null shader. Its material is EMT_SOLID.
*/
- u32 getShaderIdDirect(const std::string &name,
+ u32 getShaderIdDirect(const std::string &name,
const u8 material_type, const u8 drawtype);
/*
If shader specified by the name pointed by the id doesn't
- exist, create it, then return id.
+ exist, create it, then return id.
Can be called from any thread. If called from some other thread
and not found in cache, the call is queued to the main thread
for processing.
*/
-
+
u32 getShader(const std::string &name,
const u8 material_type, const u8 drawtype);
-
+
ShaderInfo getShaderInfo(u32 id);
-
+
// Processes queued shader requests from other threads.
// Shall be called from the main thread.
void processQueue();
@@ -364,7 +363,7 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
ShaderSource::ShaderSource(IrrlichtDevice *device):
m_device(device)
{
- assert(m_device);
+ assert(m_device); // Pre-condition
m_shader_callback = new ShaderCallback(this, "default");
@@ -391,7 +390,7 @@ ShaderSource::~ShaderSource()
}
}
-u32 ShaderSource::getShader(const std::string &name,
+u32 ShaderSource::getShader(const std::string &name,
const u8 material_type, const u8 drawtype)
{
/*
@@ -435,7 +434,7 @@ u32 ShaderSource::getShader(const std::string &name,
/*
This method generates all the shaders
*/
-u32 ShaderSource::getShaderIdDirect(const std::string &name,
+u32 ShaderSource::getShaderIdDirect(const std::string &name,
const u8 material_type, const u8 drawtype)
{
//infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
@@ -494,7 +493,7 @@ ShaderInfo ShaderSource::getShaderInfo(u32 id)
void ShaderSource::processQueue()
{
-
+
}
@@ -505,7 +504,7 @@ void ShaderSource::insertSourceShader(const std::string &name_of_shader,
"name_of_shader=\""<<name_of_shader<<"\", "
"filename=\""<<filename<<"\""<<std::endl;*/
- assert(get_current_thread_id() == m_main_thread);
+ sanity_check(get_current_thread_id() == m_main_thread);
m_sourcecache.insert(name_of_shader, filename, program, true);
}
@@ -572,13 +571,13 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
-
+
bool enable_shaders = g_settings->getBool("enable_shaders");
if(!enable_shaders)
return shaderinfo;
video::IVideoDriver* driver = device->getVideoDriver();
- assert(driver);
+ sanity_check(driver);
video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
if(!gpu){
@@ -648,7 +647,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
"NDT_FIRELIKE",
"NDT_GLASSLIKE_FRAMED_OPTIONAL"
};
-
+
for (int i = 0; i < 14; i++){
shaders_header += "#define ";
shaders_header += drawTypes[i];
@@ -681,47 +680,62 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
shaders_header += itos(drawtype);
shaders_header += "\n";
- if (g_settings->getBool("generate_normalmaps")){
- shaders_header += "#define GENERATE_NORMALMAPS\n";
- shaders_header += "#define NORMALMAPS_STRENGTH ";
- shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
- shaders_header += "\n";
- float sample_step;
- int smooth = (int)g_settings->getFloat("normalmaps_smooth");
- switch (smooth){
- case 0:
- sample_step = 0.0078125; // 1.0 / 128.0
- break;
- case 1:
- sample_step = 0.00390625; // 1.0 / 256.0
- break;
- case 2:
- sample_step = 0.001953125; // 1.0 / 512.0
- break;
- default:
- sample_step = 0.0078125;
- break;
- }
- shaders_header += "#define SAMPLE_STEP ";
- shaders_header += ftos(sample_step);
- shaders_header += "\n";
+ if (g_settings->getBool("generate_normalmaps")) {
+ shaders_header += "#define GENERATE_NORMALMAPS 1\n";
+ } else {
+ shaders_header += "#define GENERATE_NORMALMAPS 0\n";
+ }
+ shaders_header += "#define NORMALMAPS_STRENGTH ";
+ shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
+ shaders_header += "\n";
+ float sample_step;
+ int smooth = (int)g_settings->getFloat("normalmaps_smooth");
+ switch (smooth){
+ case 0:
+ sample_step = 0.0078125; // 1.0 / 128.0
+ break;
+ case 1:
+ sample_step = 0.00390625; // 1.0 / 256.0
+ break;
+ case 2:
+ sample_step = 0.001953125; // 1.0 / 512.0
+ break;
+ default:
+ sample_step = 0.0078125;
+ break;
}
+ shaders_header += "#define SAMPLE_STEP ";
+ shaders_header += ftos(sample_step);
+ shaders_header += "\n";
if (g_settings->getBool("enable_bumpmapping"))
shaders_header += "#define ENABLE_BUMPMAPPING\n";
if (g_settings->getBool("enable_parallax_occlusion")){
+ int mode = g_settings->getFloat("parallax_occlusion_mode");
+ float scale = g_settings->getFloat("parallax_occlusion_scale");
+ float bias = g_settings->getFloat("parallax_occlusion_bias");
+ int iterations = g_settings->getFloat("parallax_occlusion_iterations");
shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
+ shaders_header += "#define PARALLAX_OCCLUSION_MODE ";
+ shaders_header += itos(mode);
+ shaders_header += "\n";
shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
- shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
+ shaders_header += ftos(scale);
shaders_header += "\n";
shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
- shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
+ shaders_header += ftos(bias);
+ shaders_header += "\n";
+ shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS ";
+ shaders_header += itos(iterations);
shaders_header += "\n";
}
+ shaders_header += "#define USE_NORMALMAPS ";
if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
- shaders_header += "#define USE_NORMALMAPS\n";
+ shaders_header += "1\n";
+ else
+ shaders_header += "0\n";
if (g_settings->getBool("enable_waving_water")){
shaders_header += "#define ENABLE_WAVING_WATER 1\n";
@@ -741,10 +755,10 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
shaders_header += "#define ENABLE_WAVING_LEAVES ";
if (g_settings->getBool("enable_waving_leaves"))
shaders_header += "1\n";
- else
+ else
shaders_header += "0\n";
- shaders_header += "#define ENABLE_WAVING_PLANTS ";
+ shaders_header += "#define ENABLE_WAVING_PLANTS ";
if (g_settings->getBool("enable_waving_plants"))
shaders_header += "1\n";
else
@@ -756,7 +770,6 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
vertex_program = shaders_header + vertex_program;
if(geometry_program != "")
geometry_program = shaders_header + geometry_program;
-
// Call addHighLevelShaderMaterial() or addShaderMaterial()
const c8* vertex_program_ptr = 0;
const c8* pixel_program_ptr = 0;
diff --git a/src/sky.cpp b/src/sky.cpp
index 664ed694b..01fb8ef86 100644
--- a/src/sky.cpp
+++ b/src/sky.cpp
@@ -3,14 +3,13 @@
#include "ISceneManager.h"
#include "ICameraSceneNode.h"
#include "S3DVertex.h"
-#include "tile.h"
-#include "noise.h" // easeCurve
-#include "main.h" // g_profiler
+#include "client/tile.h"
+#include "noise.h" // easeCurve
#include "profiler.h"
-#include "util/numeric.h" // MYMIN
+#include "util/numeric.h"
#include <cmath>
#include "settings.h"
-#include "camera.h" // CameraModes
+#include "camera.h" // CameraModes
//! constructor
Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
@@ -47,14 +46,14 @@ Sky::Sky(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id,
m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
m_materials[2] = mat;
- m_materials[2].setTexture(0, tsrc->getTexture("sunrisebg.png"));
+ m_materials[2].setTexture(0, tsrc->getTextureForMesh("sunrisebg.png"));
m_materials[2].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
//m_materials[2].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
m_sun_texture = tsrc->isKnownSourceImage("sun.png") ?
- tsrc->getTexture("sun.png") : NULL;
+ tsrc->getTextureForMesh("sun.png") : NULL;
m_moon_texture = tsrc->isKnownSourceImage("moon.png") ?
- tsrc->getTexture("moon.png") : NULL;
+ tsrc->getTextureForMesh("moon.png") : NULL;
m_sun_tonemap = tsrc->isKnownSourceImage("sun_tonemap.png") ?
tsrc->getTexture("sun_tonemap.png") : NULL;
m_moon_tonemap = tsrc->isKnownSourceImage("moon_tonemap.png") ?
diff --git a/src/socket.cpp b/src/socket.cpp
index df9c57c5f..e82052f77 100644
--- a/src/socket.cpp
+++ b/src/socket.cpp
@@ -32,7 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "debug.h"
#include "settings.h"
#include "log.h"
-#include "main.h" // for g_settings
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
@@ -45,9 +44,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
- #ifdef _MSC_VER
- #pragma comment(lib, "ws2_32.lib")
- #endif
typedef SOCKET socket_t;
typedef int socklen_t;
#else
@@ -155,7 +151,7 @@ void Address::Resolve(const char *name)
struct addrinfo *resolved, hints;
memset(&hints, 0, sizeof(hints));
-
+
// Setup hints
hints.ai_socktype = 0;
hints.ai_protocol = 0;
@@ -169,7 +165,7 @@ void Address::Resolve(const char *name)
{
hints.ai_family = AF_INET;
}
-
+
// Do getaddrinfo()
int e = getaddrinfo(name, NULL, &hints, &resolved);
if(e != 0)
@@ -515,7 +511,7 @@ int UDPSocket::Receive(Address & sender, void *data, int size)
dstream << (int) m_handle << " <- ";
sender.print(&dstream);
dstream << ", size=" << received;
-
+
// Print packet contents
dstream << ", data=";
for(int i = 0; i < received && i < 20; i++) {
@@ -526,7 +522,7 @@ int UDPSocket::Receive(Address & sender, void *data, int size)
}
if(received > 20)
dstream << "...";
-
+
dstream << std::endl;
}
@@ -587,7 +583,7 @@ bool UDPSocket::WaitData(int timeout_ms)
// No data
return false;
}
-
+
// There is data
return true;
}
diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp
index b2b424a19..cb4c7b581 100644
--- a/src/sound_openal.cpp
+++ b/src/sound_openal.cpp
@@ -37,10 +37,10 @@ with this program; ifnot, write to the Free Software Foundation, Inc.,
#include <AL/alext.h>
#endif
#include <vorbis/vorbisfile.h>
+#include <assert.h>
#include "log.h"
#include "filesys.h"
#include "util/numeric.h" // myrand()
-#include "debug.h" // assert()
#include "porting.h"
#include <map>
#include <vector>
diff --git a/src/staticobject.cpp b/src/staticobject.cpp
index 16d98605b..2e7d45a47 100644
--- a/src/staticobject.cpp
+++ b/src/staticobject.cpp
@@ -47,10 +47,9 @@ void StaticObjectList::serialize(std::ostream &os)
// count
u16 count = m_stored.size() + m_active.size();
writeU16(os, count);
- for(std::list<StaticObject>::iterator
+ for(std::vector<StaticObject>::iterator
i = m_stored.begin();
- i != m_stored.end(); ++i)
- {
+ i != m_stored.end(); ++i) {
StaticObject &s_obj = *i;
s_obj.serialize(os);
}
@@ -68,8 +67,7 @@ void StaticObjectList::deSerialize(std::istream &is)
u8 version = readU8(is);
// count
u16 count = readU16(is);
- for(u16 i=0; i<count; i++)
- {
+ for(u16 i = 0; i < count; i++) {
StaticObject s_obj;
s_obj.deSerialize(is, version);
m_stored.push_back(s_obj);
diff --git a/src/staticobject.h b/src/staticobject.h
index 575c15b18..95a1b945e 100644
--- a/src/staticobject.h
+++ b/src/staticobject.h
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_bloated.h"
#include <string>
#include <sstream>
-#include <list>
+#include <vector>
#include <map>
#include "debug.h"
@@ -68,8 +68,7 @@ public:
{
dstream<<"ERROR: StaticObjectList::insert(): "
<<"id already exists"<<std::endl;
- assert(0);
- return;
+ FATAL_ERROR("StaticObjectList::insert()");
}
m_active[id] = obj;
}
@@ -77,7 +76,7 @@ public:
void remove(u16 id)
{
- assert(id != 0);
+ assert(id != 0); // Pre-condition
if(m_active.find(id) == m_active.end())
{
dstream<<"WARNING: StaticObjectList::remove(): id="<<id
@@ -95,7 +94,7 @@ public:
from m_stored and inserted to m_active.
The caller directly manipulates these containers.
*/
- std::list<StaticObject> m_stored;
+ std::vector<StaticObject> m_stored;
std::map<u16, StaticObject> m_active;
private:
diff --git a/src/subgame.cpp b/src/subgame.cpp
index fd2679eae..f736a78c6 100644
--- a/src/subgame.cpp
+++ b/src/subgame.cpp
@@ -21,13 +21,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "filesys.h"
#include "settings.h"
-#include "main.h"
#include "log.h"
#include "strfnd.h"
+#include "defaultsettings.h" // for override_default_settings
+#include "mapgen.h" // for MapgenParams
+#include "util/string.h"
+
#ifndef SERVER
-#include "tile.h" // getImagePath
+ #include "client/tile.h" // getImagePath
#endif
-#include "util/string.h"
bool getGameMinetestConfig(const std::string &game_path, Settings &conf)
{
@@ -264,8 +266,18 @@ std::vector<WorldSpec> getAvailableWorlds()
return worlds;
}
-bool initializeWorld(const std::string &path, const std::string &gameid)
+bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec)
{
+ // Override defaults with those provided by the game.
+ // We clear and reload the defaults because the defaults
+ // might have been overridden by other subgame config
+ // files that were loaded before.
+ g_settings->clearDefaults();
+ set_default_settings(g_settings);
+ Settings game_defaults;
+ getGameMinetestConfig(gamespec.path, game_defaults);
+ override_default_settings(g_settings, &game_defaults);
+
infostream << "Initializing world at " << path << std::endl;
fs::CreateAllDirs(path);
@@ -274,7 +286,11 @@ bool initializeWorld(const std::string &path, const std::string &gameid)
std::string worldmt_path = path + DIR_DELIM "world.mt";
if (!fs::PathExists(worldmt_path)) {
std::ostringstream ss(std::ios_base::binary);
- ss << "gameid = " << gameid << "\nbackend = sqlite3\n";
+ ss << "gameid = " << gamespec.id
+ << "\nbackend = sqlite3"
+ << "\ncreative_mode = " << g_settings->get("creative_mode")
+ << "\nenable_damage = " << g_settings->get("enable_damage")
+ << "\n";
if (!fs::safeWriteToFile(worldmt_path, ss.str()))
return false;
@@ -282,21 +298,22 @@ bool initializeWorld(const std::string &path, const std::string &gameid)
}
// Create map_meta.txt if does not already exist
- std::string mapmeta_path = path + DIR_DELIM "map_meta.txt";
- if (!fs::PathExists(mapmeta_path)) {
- std::ostringstream ss(std::ios_base::binary);
- ss
- << "mg_name = " << g_settings->get("mg_name")
- << "\nseed = " << g_settings->get("fixed_map_seed")
- << "\nchunksize = " << g_settings->get("chunksize")
- << "\nwater_level = " << g_settings->get("water_level")
- << "\nmg_flags = " << g_settings->get("mg_flags")
- << "\n[end_of_params]\n";
- if (!fs::safeWriteToFile(mapmeta_path, ss.str()))
- return false;
+ std::string map_meta_path = path + DIR_DELIM + "map_meta.txt";
+ if (!fs::PathExists(map_meta_path)){
+ verbosestream << "Creating map_meta.txt (" << map_meta_path << ")" << std::endl;
+ fs::CreateAllDirs(path);
+ std::ostringstream oss(std::ios_base::binary);
- infostream << "Wrote map_meta.txt (" << mapmeta_path << ")" << std::endl;
- }
+ Settings conf;
+ MapgenParams params;
+
+ params.load(*g_settings);
+ params.save(conf);
+ conf.writeLines(oss);
+ oss << "[end_of_params]\n";
+ fs::safeWriteToFile(map_meta_path, oss.str());
+ }
return true;
}
+
diff --git a/src/subgame.h b/src/subgame.h
index 4b15faa8d..f3633ce2f 100644
--- a/src/subgame.h
+++ b/src/subgame.h
@@ -98,8 +98,9 @@ struct WorldSpec
std::vector<WorldSpec> getAvailableWorlds();
-// Create world directory and world.mt if they don't exist
-bool initializeWorld(const std::string &path, const std::string &gameid);
+// loads the subgame's config and creates world directory
+// and world.mt if they don't exist
+bool loadGameConfAndInitWorld(const std::string &path, const SubgameSpec &gamespec);
#endif
diff --git a/src/test.cpp b/src/test.cpp
deleted file mode 100644
index 80494e07a..000000000
--- a/src/test.cpp
+++ /dev/null
@@ -1,2217 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
-*/
-
-#include "test.h"
-#include "irrlichttypes_extrabloated.h"
-#include "debug.h"
-#include "map.h"
-#include "player.h"
-#include "main.h"
-#include "socket.h"
-#include "connection.h"
-#include "serialization.h"
-#include "voxel.h"
-#include "collision.h"
-#include <sstream>
-#include "porting.h"
-#include "content_mapnode.h"
-#include "nodedef.h"
-#include "mapsector.h"
-#include "settings.h"
-#include "log.h"
-#include "util/string.h"
-#include "filesys.h"
-#include "voxelalgorithms.h"
-#include "inventory.h"
-#include "util/numeric.h"
-#include "util/serialize.h"
-#include "noise.h" // PseudoRandom used for random data for compression
-#include "clientserver.h" // LATEST_PROTOCOL_VERSION
-#include <algorithm>
-
-/*
- Asserts that the exception occurs
-*/
-#define EXCEPTION_CHECK(EType, code)\
-{\
- bool exception_thrown = false;\
- try{ code; }\
- catch(EType &e) { exception_thrown = true; }\
- UASSERT(exception_thrown);\
-}
-
-#define UTEST(x, fmt, ...)\
-{\
- if(!(x)){\
- dstream << "Test (" #x ") failed: " fmt << std::endl; \
- test_failed = true;\
- }\
-}
-
-#define UASSERT(x) UTEST(x, "UASSERT")
-
-/*
- A few item and node definitions for those tests that need them
-*/
-
-static content_t CONTENT_STONE;
-static content_t CONTENT_GRASS;
-static content_t CONTENT_TORCH;
-
-void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef)
-{
- ItemDefinition itemdef;
- ContentFeatures f;
-
- /*
- Stone
- */
- itemdef = ItemDefinition();
- itemdef.type = ITEM_NODE;
- itemdef.name = "default:stone";
- itemdef.description = "Stone";
- itemdef.groups["cracky"] = 3;
- itemdef.inventory_image = "[inventorycube"
- "{default_stone.png"
- "{default_stone.png"
- "{default_stone.png";
- f = ContentFeatures();
- f.name = itemdef.name;
- for(int i = 0; i < 6; i++)
- f.tiledef[i].name = "default_stone.png";
- f.is_ground_content = true;
- idef->registerItem(itemdef);
- CONTENT_STONE = ndef->set(f.name, f);
-
- /*
- Grass
- */
- itemdef = ItemDefinition();
- itemdef.type = ITEM_NODE;
- itemdef.name = "default:dirt_with_grass";
- itemdef.description = "Dirt with grass";
- itemdef.groups["crumbly"] = 3;
- itemdef.inventory_image = "[inventorycube"
- "{default_grass.png"
- "{default_dirt.png&default_grass_side.png"
- "{default_dirt.png&default_grass_side.png";
- f = ContentFeatures();
- f.name = itemdef.name;
- f.tiledef[0].name = "default_grass.png";
- f.tiledef[1].name = "default_dirt.png";
- for(int i = 2; i < 6; i++)
- f.tiledef[i].name = "default_dirt.png^default_grass_side.png";
- f.is_ground_content = true;
- idef->registerItem(itemdef);
- CONTENT_GRASS = ndef->set(f.name, f);
-
- /*
- Torch (minimal definition for lighting tests)
- */
- itemdef = ItemDefinition();
- itemdef.type = ITEM_NODE;
- itemdef.name = "default:torch";
- f = ContentFeatures();
- f.name = itemdef.name;
- f.param_type = CPT_LIGHT;
- f.light_propagates = true;
- f.sunlight_propagates = true;
- f.light_source = LIGHT_MAX-1;
- idef->registerItem(itemdef);
- CONTENT_TORCH = ndef->set(f.name, f);
-}
-
-struct TestBase
-{
- bool test_failed;
- TestBase():
- test_failed(false)
- {}
-};
-
-struct TestUtilities: public TestBase
-{
- void Run()
- {
- /*infostream<<"wrapDegrees(100.0) = "<<wrapDegrees(100.0)<<std::endl;
- infostream<<"wrapDegrees(720.5) = "<<wrapDegrees(720.5)<<std::endl;
- infostream<<"wrapDegrees(-0.5) = "<<wrapDegrees(-0.5)<<std::endl;*/
- UASSERT(fabs(wrapDegrees(100.0) - 100.0) < 0.001);
- UASSERT(fabs(wrapDegrees(720.5) - 0.5) < 0.001);
- UASSERT(fabs(wrapDegrees(-0.5) - (-0.5)) < 0.001);
- UASSERT(fabs(wrapDegrees(-365.5) - (-5.5)) < 0.001);
- UASSERT(lowercase("Foo bAR") == "foo bar");
- UASSERT(trim("\n \t\r Foo bAR \r\n\t\t ") == "Foo bAR");
- UASSERT(trim("\n \t\r \r\n\t\t ") == "");
- UASSERT(is_yes("YeS") == true);
- UASSERT(is_yes("") == false);
- UASSERT(is_yes("FAlse") == false);
- UASSERT(is_yes("-1") == true);
- UASSERT(is_yes("0") == false);
- UASSERT(is_yes("1") == true);
- UASSERT(is_yes("2") == true);
- const char *ends[] = {"abc", "c", "bc", "", NULL};
- UASSERT(removeStringEnd("abc", ends) == "");
- UASSERT(removeStringEnd("bc", ends) == "b");
- UASSERT(removeStringEnd("12c", ends) == "12");
- UASSERT(removeStringEnd("foo", ends) == "");
- UASSERT(urlencode("\"Aardvarks lurk, OK?\"")
- == "%22Aardvarks%20lurk%2C%20OK%3F%22");
- UASSERT(urldecode("%22Aardvarks%20lurk%2C%20OK%3F%22")
- == "\"Aardvarks lurk, OK?\"");
- UASSERT(padStringRight("hello", 8) == "hello ");
- UASSERT(str_equal(narrow_to_wide("abc"), narrow_to_wide("abc")));
- UASSERT(str_equal(narrow_to_wide("ABC"), narrow_to_wide("abc"), true));
- UASSERT(trim(" a") == "a");
- UASSERT(trim(" a ") == "a");
- UASSERT(trim("a ") == "a");
- UASSERT(trim("") == "");
- UASSERT(mystoi("123", 0, 1000) == 123);
- UASSERT(mystoi("123", 0, 10) == 10);
- std::string test_str;
- test_str = "Hello there";
- str_replace(test_str, "there", "world");
- UASSERT(test_str == "Hello world");
- test_str = "ThisAisAaAtest";
- str_replace(test_str, 'A', ' ');
- UASSERT(test_str == "This is a test");
- UASSERT(string_allowed("hello", "abcdefghijklmno") == true);
- UASSERT(string_allowed("123", "abcdefghijklmno") == false);
- UASSERT(string_allowed_blacklist("hello", "123") == true);
- UASSERT(string_allowed_blacklist("hello123", "123") == false);
- UASSERT(wrap_rows("12345678",4) == "1234\n5678");
- UASSERT(is_number("123") == true);
- UASSERT(is_number("") == false);
- UASSERT(is_number("123a") == false);
- UASSERT(is_power_of_two(0) == false);
- UASSERT(is_power_of_two(1) == true);
- UASSERT(is_power_of_two(2) == true);
- UASSERT(is_power_of_two(3) == false);
- for (int exponent = 2; exponent <= 31; ++exponent) {
- UASSERT(is_power_of_two((1 << exponent) - 1) == false);
- UASSERT(is_power_of_two((1 << exponent)) == true);
- UASSERT(is_power_of_two((1 << exponent) + 1) == false);
- }
- UASSERT(is_power_of_two((u32)-1) == false);
- }
-};
-
-struct TestPath: public TestBase
-{
- // adjusts a POSIX path to system-specific conventions
- // -> changes '/' to DIR_DELIM
- // -> absolute paths start with "C:\\" on windows
- std::string p(std::string path)
- {
- for(size_t i = 0; i < path.size(); ++i){
- if(path[i] == '/'){
- path.replace(i, 1, DIR_DELIM);
- i += std::string(DIR_DELIM).size() - 1; // generally a no-op
- }
- }
-
- #ifdef _WIN32
- if(path[0] == '\\')
- path = "C:" + path;
- #endif
-
- return path;
- }
-
- void Run()
- {
- std::string path, result, removed;
-
- /*
- Test fs::IsDirDelimiter
- */
- UASSERT(fs::IsDirDelimiter('/') == true);
- UASSERT(fs::IsDirDelimiter('A') == false);
- UASSERT(fs::IsDirDelimiter(0) == false);
- #ifdef _WIN32
- UASSERT(fs::IsDirDelimiter('\\') == true);
- #else
- UASSERT(fs::IsDirDelimiter('\\') == false);
- #endif
-
- /*
- Test fs::PathStartsWith
- */
- {
- const int numpaths = 12;
- std::string paths[numpaths] = {
- "",
- p("/"),
- p("/home/user/minetest"),
- p("/home/user/minetest/bin"),
- p("/home/user/.minetest"),
- p("/tmp/dir/file"),
- p("/tmp/file/"),
- p("/tmP/file"),
- p("/tmp"),
- p("/tmp/dir"),
- p("/home/user2/minetest/worlds"),
- p("/home/user2/minetest/world"),
- };
- /*
- expected fs::PathStartsWith results
- 0 = returns false
- 1 = returns true
- 2 = returns false on windows, true elsewhere
- 3 = returns true on windows, false elsewhere
- 4 = returns true if and only if
- FILESYS_CASE_INSENSITIVE is true
- */
- int expected_results[numpaths][numpaths] = {
- {1,2,0,0,0,0,0,0,0,0,0,0},
- {1,1,0,0,0,0,0,0,0,0,0,0},
- {1,1,1,0,0,0,0,0,0,0,0,0},
- {1,1,1,1,0,0,0,0,0,0,0,0},
- {1,1,0,0,1,0,0,0,0,0,0,0},
- {1,1,0,0,0,1,0,0,1,1,0,0},
- {1,1,0,0,0,0,1,4,1,0,0,0},
- {1,1,0,0,0,0,4,1,4,0,0,0},
- {1,1,0,0,0,0,0,0,1,0,0,0},
- {1,1,0,0,0,0,0,0,1,1,0,0},
- {1,1,0,0,0,0,0,0,0,0,1,0},
- {1,1,0,0,0,0,0,0,0,0,0,1},
- };
-
- for (int i = 0; i < numpaths; i++)
- for (int j = 0; j < numpaths; j++){
- /*verbosestream<<"testing fs::PathStartsWith(\""
- <<paths[i]<<"\", \""
- <<paths[j]<<"\")"<<std::endl;*/
- bool starts = fs::PathStartsWith(paths[i], paths[j]);
- int expected = expected_results[i][j];
- if(expected == 0){
- UASSERT(starts == false);
- }
- else if(expected == 1){
- UASSERT(starts == true);
- }
- #ifdef _WIN32
- else if(expected == 2){
- UASSERT(starts == false);
- }
- else if(expected == 3){
- UASSERT(starts == true);
- }
- #else
- else if(expected == 2){
- UASSERT(starts == true);
- }
- else if(expected == 3){
- UASSERT(starts == false);
- }
- #endif
- else if(expected == 4){
- UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE);
- }
- }
- }
-
- /*
- Test fs::RemoveLastPathComponent
- */
- UASSERT(fs::RemoveLastPathComponent("") == "");
- path = p("/home/user/minetest/bin/..//worlds/world1");
- result = fs::RemoveLastPathComponent(path, &removed, 0);
- UASSERT(result == path);
- UASSERT(removed == "");
- result = fs::RemoveLastPathComponent(path, &removed, 1);
- UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
- UASSERT(removed == p("world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 2);
- UASSERT(result == p("/home/user/minetest/bin/.."));
- UASSERT(removed == p("worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 3);
- UASSERT(result == p("/home/user/minetest/bin"));
- UASSERT(removed == p("../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 4);
- UASSERT(result == p("/home/user/minetest"));
- UASSERT(removed == p("bin/../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 5);
- UASSERT(result == p("/home/user"));
- UASSERT(removed == p("minetest/bin/../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 6);
- UASSERT(result == p("/home"));
- UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 7);
- #ifdef _WIN32
- UASSERT(result == "C:");
- #else
- UASSERT(result == "");
- #endif
- UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
-
- /*
- Now repeat the test with a trailing delimiter
- */
- path = p("/home/user/minetest/bin/..//worlds/world1/");
- result = fs::RemoveLastPathComponent(path, &removed, 0);
- UASSERT(result == path);
- UASSERT(removed == "");
- result = fs::RemoveLastPathComponent(path, &removed, 1);
- UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
- UASSERT(removed == p("world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 2);
- UASSERT(result == p("/home/user/minetest/bin/.."));
- UASSERT(removed == p("worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 3);
- UASSERT(result == p("/home/user/minetest/bin"));
- UASSERT(removed == p("../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 4);
- UASSERT(result == p("/home/user/minetest"));
- UASSERT(removed == p("bin/../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 5);
- UASSERT(result == p("/home/user"));
- UASSERT(removed == p("minetest/bin/../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 6);
- UASSERT(result == p("/home"));
- UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
- result = fs::RemoveLastPathComponent(path, &removed, 7);
- #ifdef _WIN32
- UASSERT(result == "C:");
- #else
- UASSERT(result == "");
- #endif
- UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
-
- /*
- Test fs::RemoveRelativePathComponent
- */
- path = p("/home/user/minetest/bin");
- result = fs::RemoveRelativePathComponents(path);
- UASSERT(result == path);
- path = p("/home/user/minetest/bin/../worlds/world1");
- result = fs::RemoveRelativePathComponents(path);
- UASSERT(result == p("/home/user/minetest/worlds/world1"));
- path = p("/home/user/minetest/bin/../worlds/world1/");
- result = fs::RemoveRelativePathComponents(path);
- UASSERT(result == p("/home/user/minetest/worlds/world1"));
- path = p(".");
- result = fs::RemoveRelativePathComponents(path);
- UASSERT(result == "");
- path = p("./subdir/../..");
- result = fs::RemoveRelativePathComponents(path);
- UASSERT(result == "");
- path = p("/a/b/c/.././../d/../e/f/g/../h/i/j/../../../..");
- result = fs::RemoveRelativePathComponents(path);
- UASSERT(result == p("/a/e"));
- }
-};
-
-#define TEST_CONFIG_TEXT_BEFORE \
- "leet = 1337\n" \
- "leetleet = 13371337\n" \
- "leetleet_neg = -13371337\n" \
- "floaty_thing = 1.1\n" \
- "stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n" \
- "coord = (1, 2, 4.5)\n" \
- " # this is just a comment\n" \
- "this is an invalid line\n" \
- "asdf = {\n" \
- " a = 5\n" \
- " bb = 2.5\n" \
- " ccc = \"\"\"\n" \
- "testy\n" \
- " testa \n" \
- "\"\"\"\n" \
- "\n" \
- "}\n" \
- "blarg = \"\"\" \n" \
- "some multiline text\n" \
- " with leading whitespace!\n" \
- "\"\"\"\n" \
- "np_terrain = 5, 40, (250, 250, 250), 12341, 5, 0.7, 2.4\n" \
- "zoop = true"
-
-#define TEST_CONFIG_TEXT_AFTER \
- "leet = 1337\n" \
- "leetleet = 13371337\n" \
- "leetleet_neg = -13371337\n" \
- "floaty_thing = 1.1\n" \
- "stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n" \
- "coord = (1, 2, 4.5)\n" \
- " # this is just a comment\n" \
- "this is an invalid line\n" \
- "asdf = {\n" \
- " a = 5\n" \
- " bb = 2.5\n" \
- " ccc = \"\"\"\n" \
- "testy\n" \
- " testa \n" \
- "\"\"\"\n" \
- "\n" \
- "}\n" \
- "blarg = \"\"\" \n" \
- "some multiline text\n" \
- " with leading whitespace!\n" \
- "\"\"\"\n" \
- "np_terrain = {\n" \
- " flags = defaults\n" \
- " lacunarity = 2.4\n" \
- " octaves = 6\n" \
- " offset = 3.5\n" \
- " persistence = 0.7\n" \
- " scale = 40\n" \
- " seed = 12341\n" \
- " spread = (250,250,250)\n" \
- "}\n" \
- "zoop = true\n" \
- "coord2 = (1,2,3.3)\n" \
- "floaty_thing_2 = 1.2\n" \
- "groupy_thing = {\n" \
- " animals = cute\n" \
- " num_apples = 4\n" \
- " num_oranges = 53\n" \
- "}\n"
-
-struct TestSettings: public TestBase
-{
- void Run()
- {
- try {
- Settings s;
-
- // Test reading of settings
- std::istringstream is(TEST_CONFIG_TEXT_BEFORE);
- s.parseConfigLines(is);
-
- UASSERT(s.getS32("leet") == 1337);
- UASSERT(s.getS16("leetleet") == 32767);
- UASSERT(s.getS16("leetleet_neg") == -32768);
-
- // Not sure if 1.1 is an exact value as a float, but doesn't matter
- UASSERT(fabs(s.getFloat("floaty_thing") - 1.1) < 0.001);
- UASSERT(s.get("stringy_thing") == "asd /( ¤%&(/\" BLÖÄRP");
- UASSERT(fabs(s.getV3F("coord").X - 1.0) < 0.001);
- UASSERT(fabs(s.getV3F("coord").Y - 2.0) < 0.001);
- UASSERT(fabs(s.getV3F("coord").Z - 4.5) < 0.001);
-
- // Test the setting of settings too
- s.setFloat("floaty_thing_2", 1.2);
- s.setV3F("coord2", v3f(1, 2, 3.3));
- UASSERT(s.get("floaty_thing_2").substr(0,3) == "1.2");
- UASSERT(fabs(s.getFloat("floaty_thing_2") - 1.2) < 0.001);
- UASSERT(fabs(s.getV3F("coord2").X - 1.0) < 0.001);
- UASSERT(fabs(s.getV3F("coord2").Y - 2.0) < 0.001);
- UASSERT(fabs(s.getV3F("coord2").Z - 3.3) < 0.001);
-
- // Test settings groups
- Settings *group = s.getGroup("asdf");
- UASSERT(group != NULL);
- UASSERT(s.getGroupNoEx("zoop", group) == false);
- UASSERT(group->getS16("a") == 5);
- UASSERT(fabs(group->getFloat("bb") - 2.5) < 0.001);
-
- Settings *group3 = new Settings;
- group3->set("cat", "meow");
- group3->set("dog", "woof");
-
- Settings *group2 = new Settings;
- group2->setS16("num_apples", 4);
- group2->setS16("num_oranges", 53);
- group2->setGroup("animals", group3);
- group2->set("animals", "cute"); //destroys group 3
- s.setGroup("groupy_thing", group2);
-
- // Test set failure conditions
- UASSERT(s.set("Zoop = Poop\nsome_other_setting", "false") == false);
- UASSERT(s.set("sneaky", "\"\"\"\njabberwocky = false") == false);
- UASSERT(s.set("hehe", "asdfasdf\n\"\"\"\nsomething = false") == false);
-
- // Test multiline settings
- UASSERT(group->get("ccc") == "testy\n testa ");
-
- UASSERT(s.get("blarg") ==
- "some multiline text\n"
- " with leading whitespace!");
-
- // Test NoiseParams
- UASSERT(s.getEntry("np_terrain").is_group == false);
-
- NoiseParams np;
- UASSERT(s.getNoiseParams("np_terrain", np) == true);
- UASSERT(fabs(np.offset - 5) < 0.001);
- UASSERT(fabs(np.scale - 40) < 0.001);
- UASSERT(fabs(np.spread.X - 250) < 0.001);
- UASSERT(fabs(np.spread.Y - 250) < 0.001);
- UASSERT(fabs(np.spread.Z - 250) < 0.001);
- UASSERT(np.seed == 12341);
- UASSERT(np.octaves == 5);
- UASSERT(fabs(np.persist - 0.7) < 0.001);
-
- np.offset = 3.5;
- np.octaves = 6;
- s.setNoiseParams("np_terrain", np);
-
- UASSERT(s.getEntry("np_terrain").is_group == true);
-
- // Test writing
- std::ostringstream os(std::ios_base::binary);
- is.clear();
- is.seekg(0);
-
- UASSERT(s.updateConfigObject(is, os, "", 0) == true);
- //printf(">>>> expected config:\n%s\n", TEST_CONFIG_TEXT_AFTER);
- //printf(">>>> actual config:\n%s\n", os.str().c_str());
- UASSERT(os.str() == TEST_CONFIG_TEXT_AFTER);
- } catch (SettingNotFoundException &e) {
- UASSERT(!"Setting not found!");
- }
- }
-};
-
-struct TestSerialization: public TestBase
-{
- // To be used like this:
- // mkstr("Some\0string\0with\0embedded\0nuls")
- // since std::string("...") doesn't work as expected in that case.
- template<size_t N> std::string mkstr(const char (&s)[N])
- {
- return std::string(s, N - 1);
- }
-
- void Run()
- {
- // Tests some serialization primitives
-
- UASSERT(serializeString("") == mkstr("\0\0"));
- UASSERT(serializeWideString(L"") == mkstr("\0\0"));
- UASSERT(serializeLongString("") == mkstr("\0\0\0\0"));
- UASSERT(serializeJsonString("") == "\"\"");
-
- std::string teststring = "Hello world!";
- UASSERT(serializeString(teststring) ==
- mkstr("\0\14Hello world!"));
- UASSERT(serializeWideString(narrow_to_wide(teststring)) ==
- mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!"));
- UASSERT(serializeLongString(teststring) ==
- mkstr("\0\0\0\14Hello world!"));
- UASSERT(serializeJsonString(teststring) ==
- "\"Hello world!\"");
-
- std::string teststring2;
- std::wstring teststring2_w;
- std::string teststring2_w_encoded;
- {
- std::ostringstream tmp_os;
- std::wostringstream tmp_os_w;
- std::ostringstream tmp_os_w_encoded;
- for(int i = 0; i < 256; i++)
- {
- tmp_os<<(char)i;
- tmp_os_w<<(wchar_t)i;
- tmp_os_w_encoded<<(char)0<<(char)i;
- }
- teststring2 = tmp_os.str();
- teststring2_w = tmp_os_w.str();
- teststring2_w_encoded = tmp_os_w_encoded.str();
- }
- UASSERT(serializeString(teststring2) ==
- mkstr("\1\0") + teststring2);
- UASSERT(serializeWideString(teststring2_w) ==
- mkstr("\1\0") + teststring2_w_encoded);
- UASSERT(serializeLongString(teststring2) ==
- mkstr("\0\0\1\0") + teststring2);
- // MSVC fails when directly using "\\\\"
- std::string backslash = "\\";
- UASSERT(serializeJsonString(teststring2) ==
- mkstr("\"") +
- "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
- "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
- "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
- "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
- " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
- "\\/" + teststring2.substr(0x30, 0x5c-0x30) +
- backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
- "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
- "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
- "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
- "\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
- "\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
- "\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
- "\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
- "\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
- "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
- "\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
- "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
- "\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
- "\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
- "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
- "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
- "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
- "\"");
-
- {
- std::istringstream is(serializeString(teststring2), std::ios::binary);
- UASSERT(deSerializeString(is) == teststring2);
- UASSERT(!is.eof());
- is.get();
- UASSERT(is.eof());
- }
- {
- std::istringstream is(serializeWideString(teststring2_w), std::ios::binary);
- UASSERT(deSerializeWideString(is) == teststring2_w);
- UASSERT(!is.eof());
- is.get();
- UASSERT(is.eof());
- }
- {
- std::istringstream is(serializeLongString(teststring2), std::ios::binary);
- UASSERT(deSerializeLongString(is) == teststring2);
- UASSERT(!is.eof());
- is.get();
- UASSERT(is.eof());
- }
- {
- std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
- //dstream<<serializeJsonString(deSerializeJsonString(is));
- UASSERT(deSerializeJsonString(is) == teststring2);
- UASSERT(!is.eof());
- is.get();
- UASSERT(is.eof());
- }
- }
-};
-
-struct TestNodedefSerialization: public TestBase
-{
- void Run()
- {
- ContentFeatures f;
- f.name = "default:stone";
- for(int i = 0; i < 6; i++)
- f.tiledef[i].name = "default_stone.png";
- f.is_ground_content = true;
- std::ostringstream os(std::ios::binary);
- f.serialize(os, LATEST_PROTOCOL_VERSION);
- verbosestream<<"Test ContentFeatures size: "<<os.str().size()<<std::endl;
- std::istringstream is(os.str(), std::ios::binary);
- ContentFeatures f2;
- f2.deSerialize(is);
- UASSERT(f.walkable == f2.walkable);
- UASSERT(f.node_box.type == f2.node_box.type);
- }
-};
-
-struct TestCompress: public TestBase
-{
- void Run()
- {
- { // ver 0
-
- SharedBuffer<u8> fromdata(4);
- fromdata[0]=1;
- fromdata[1]=5;
- fromdata[2]=5;
- fromdata[3]=1;
-
- std::ostringstream os(std::ios_base::binary);
- compress(fromdata, os, 0);
-
- std::string str_out = os.str();
-
- infostream<<"str_out.size()="<<str_out.size()<<std::endl;
- infostream<<"TestCompress: 1,5,5,1 -> ";
- for(u32 i=0; i<str_out.size(); i++)
- {
- infostream<<(u32)str_out[i]<<",";
- }
- infostream<<std::endl;
-
- UASSERT(str_out.size() == 10);
-
- UASSERT(str_out[0] == 0);
- UASSERT(str_out[1] == 0);
- UASSERT(str_out[2] == 0);
- UASSERT(str_out[3] == 4);
- UASSERT(str_out[4] == 0);
- UASSERT(str_out[5] == 1);
- UASSERT(str_out[6] == 1);
- UASSERT(str_out[7] == 5);
- UASSERT(str_out[8] == 0);
- UASSERT(str_out[9] == 1);
-
- std::istringstream is(str_out, std::ios_base::binary);
- std::ostringstream os2(std::ios_base::binary);
-
- decompress(is, os2, 0);
- std::string str_out2 = os2.str();
-
- infostream<<"decompress: ";
- for(u32 i=0; i<str_out2.size(); i++)
- {
- infostream<<(u32)str_out2[i]<<",";
- }
- infostream<<std::endl;
-
- UASSERT(str_out2.size() == fromdata.getSize());
-
- for(u32 i=0; i<str_out2.size(); i++)
- {
- UASSERT(str_out2[i] == fromdata[i]);
- }
-
- }
-
- { // ver HIGHEST
-
- SharedBuffer<u8> fromdata(4);
- fromdata[0]=1;
- fromdata[1]=5;
- fromdata[2]=5;
- fromdata[3]=1;
-
- std::ostringstream os(std::ios_base::binary);
- compress(fromdata, os, SER_FMT_VER_HIGHEST_READ);
-
- std::string str_out = os.str();
-
- infostream<<"str_out.size()="<<str_out.size()<<std::endl;
- infostream<<"TestCompress: 1,5,5,1 -> ";
- for(u32 i=0; i<str_out.size(); i++)
- {
- infostream<<(u32)str_out[i]<<",";
- }
- infostream<<std::endl;
-
- std::istringstream is(str_out, std::ios_base::binary);
- std::ostringstream os2(std::ios_base::binary);
-
- decompress(is, os2, SER_FMT_VER_HIGHEST_READ);
- std::string str_out2 = os2.str();
-
- infostream<<"decompress: ";
- for(u32 i=0; i<str_out2.size(); i++)
- {
- infostream<<(u32)str_out2[i]<<",";
- }
- infostream<<std::endl;
-
- UASSERT(str_out2.size() == fromdata.getSize());
-
- for(u32 i=0; i<str_out2.size(); i++)
- {
- UASSERT(str_out2[i] == fromdata[i]);
- }
-
- }
-
- // Test zlib wrapper with large amounts of data (larger than its
- // internal buffers)
- {
- infostream<<"Test: Testing zlib wrappers with a large amount "
- <<"of pseudorandom data"<<std::endl;
- u32 size = 50000;
- infostream<<"Test: Input size of large compressZlib is "
- <<size<<std::endl;
- std::string data_in;
- data_in.resize(size);
- PseudoRandom pseudorandom(9420);
- for(u32 i=0; i<size; i++)
- data_in[i] = pseudorandom.range(0,255);
- std::ostringstream os_compressed(std::ios::binary);
- compressZlib(data_in, os_compressed);
- infostream<<"Test: Output size of large compressZlib is "
- <<os_compressed.str().size()<<std::endl;
- std::istringstream is_compressed(os_compressed.str(), std::ios::binary);
- std::ostringstream os_decompressed(std::ios::binary);
- decompressZlib(is_compressed, os_decompressed);
- infostream<<"Test: Output size of large decompressZlib is "
- <<os_decompressed.str().size()<<std::endl;
- std::string str_decompressed = os_decompressed.str();
- UTEST(str_decompressed.size() == data_in.size(), "Output size not"
- " equal (output: %u, input: %u)",
- (unsigned int)str_decompressed.size(), (unsigned int)data_in.size());
- for(u32 i=0; i<size && i<str_decompressed.size(); i++){
- UTEST(str_decompressed[i] == data_in[i],
- "index out[%i]=%i differs from in[%i]=%i",
- i, str_decompressed[i], i, data_in[i]);
- }
- }
- }
-};
-
-struct TestMapNode: public TestBase
-{
- void Run(INodeDefManager *nodedef)
- {
- MapNode n(CONTENT_AIR);
-
- UASSERT(n.getContent() == CONTENT_AIR);
- UASSERT(n.getLight(LIGHTBANK_DAY, nodedef) == 0);
- UASSERT(n.getLight(LIGHTBANK_NIGHT, nodedef) == 0);
-
- // Transparency
- n.setContent(CONTENT_AIR);
- UASSERT(nodedef->get(n).light_propagates == true);
- n.setContent(LEGN(nodedef, "CONTENT_STONE"));
- UASSERT(nodedef->get(n).light_propagates == false);
- }
-};
-
-struct TestVoxelManipulator: public TestBase
-{
- void Run(INodeDefManager *nodedef)
- {
- /*
- VoxelArea
- */
-
- VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1));
- UASSERT(a.index(0,0,0) == 1*3*3 + 1*3 + 1);
- UASSERT(a.index(-1,-1,-1) == 0);
-
- VoxelArea c(v3s16(-2,-2,-2), v3s16(2,2,2));
- // An area that is 1 bigger in x+ and z-
- VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2));
-
- std::list<VoxelArea> aa;
- d.diff(c, aa);
-
- // Correct results
- std::vector<VoxelArea> results;
- results.push_back(VoxelArea(v3s16(-2,-2,-3),v3s16(3,2,-3)));
- results.push_back(VoxelArea(v3s16(3,-2,-2),v3s16(3,2,2)));
-
- UASSERT(aa.size() == results.size());
-
- infostream<<"Result of diff:"<<std::endl;
- for(std::list<VoxelArea>::const_iterator
- i = aa.begin(); i != aa.end(); ++i)
- {
- i->print(infostream);
- infostream<<std::endl;
-
- std::vector<VoxelArea>::iterator j = std::find(results.begin(), results.end(), *i);
- UASSERT(j != results.end());
- results.erase(j);
- }
-
-
- /*
- VoxelManipulator
- */
-
- VoxelManipulator v;
-
- v.print(infostream, nodedef);
-
- infostream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl;
-
- v.setNodeNoRef(v3s16(-1,0,-1), MapNode(CONTENT_GRASS));
-
- v.print(infostream, nodedef);
-
- UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == CONTENT_GRASS);
-
- infostream<<"*** Reading from inexistent (0,0,-1) ***"<<std::endl;
-
- EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,0,-1)));
-
- v.print(infostream, nodedef);
-
- infostream<<"*** Adding area ***"<<std::endl;
-
- v.addArea(a);
-
- v.print(infostream, nodedef);
-
- UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == CONTENT_GRASS);
- EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1)));
- }
-};
-
-struct TestVoxelAlgorithms: public TestBase
-{
- void Run(INodeDefManager *ndef)
- {
- /*
- voxalgo::propagateSunlight
- */
- {
- VoxelManipulator v;
- for(u16 z=0; z<3; z++)
- for(u16 y=0; y<3; y++)
- for(u16 x=0; x<3; x++)
- {
- v3s16 p(x,y,z);
- v.setNodeNoRef(p, MapNode(CONTENT_AIR));
- }
- VoxelArea a(v3s16(0,0,0), v3s16(2,2,2));
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, true, light_sources, ndef);
- //v.print(dstream, ndef, VOXELPRINT_LIGHT_DAY);
- UASSERT(res.bottom_sunlight_valid == true);
- UASSERT(v.getNode(v3s16(1,1,1)).getLight(LIGHTBANK_DAY, ndef)
- == LIGHT_SUN);
- }
- v.setNodeNoRef(v3s16(0,0,0), MapNode(CONTENT_STONE));
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, true, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- UASSERT(v.getNode(v3s16(1,1,1)).getLight(LIGHTBANK_DAY, ndef)
- == LIGHT_SUN);
- }
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, false, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- UASSERT(v.getNode(v3s16(2,0,2)).getLight(LIGHTBANK_DAY, ndef)
- == 0);
- }
- v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_STONE));
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, true, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- UASSERT(v.getNode(v3s16(1,1,2)).getLight(LIGHTBANK_DAY, ndef)
- == 0);
- }
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, false, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- UASSERT(v.getNode(v3s16(1,0,2)).getLight(LIGHTBANK_DAY, ndef)
- == 0);
- }
- {
- MapNode n(CONTENT_AIR);
- n.setLight(LIGHTBANK_DAY, 10, ndef);
- v.setNodeNoRef(v3s16(1,-1,2), n);
- }
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, true, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- }
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, false, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- }
- {
- MapNode n(CONTENT_AIR);
- n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
- v.setNodeNoRef(v3s16(1,-1,2), n);
- }
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, true, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == false);
- }
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, false, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == false);
- }
- v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_IGNORE));
- {
- std::set<v3s16> light_sources;
- voxalgo::setLight(v, a, 0, ndef);
- voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
- v, a, true, light_sources, ndef);
- UASSERT(res.bottom_sunlight_valid == true);
- }
- }
- /*
- voxalgo::clearLightAndCollectSources
- */
- {
- VoxelManipulator v;
- for(u16 z=0; z<3; z++)
- for(u16 y=0; y<3; y++)
- for(u16 x=0; x<3; x++)
- {
- v3s16 p(x,y,z);
- v.setNode(p, MapNode(CONTENT_AIR));
- }
- VoxelArea a(v3s16(0,0,0), v3s16(2,2,2));
- v.setNodeNoRef(v3s16(0,0,0), MapNode(CONTENT_STONE));
- v.setNodeNoRef(v3s16(1,1,1), MapNode(CONTENT_TORCH));
- {
- MapNode n(CONTENT_AIR);
- n.setLight(LIGHTBANK_DAY, 1, ndef);
- v.setNode(v3s16(1,1,2), n);
- }
- {
- std::set<v3s16> light_sources;
- std::map<v3s16, u8> unlight_from;
- voxalgo::clearLightAndCollectSources(v, a, LIGHTBANK_DAY,
- ndef, light_sources, unlight_from);
- //v.print(dstream, ndef, VOXELPRINT_LIGHT_DAY);
- UASSERT(v.getNode(v3s16(0,1,1)).getLight(LIGHTBANK_DAY, ndef)
- == 0);
- UASSERT(light_sources.find(v3s16(1,1,1)) != light_sources.end());
- UASSERT(light_sources.size() == 1);
- UASSERT(unlight_from.find(v3s16(1,1,2)) != unlight_from.end());
- UASSERT(unlight_from.size() == 1);
- }
- }
- }
-};
-
-struct TestInventory: public TestBase
-{
- void Run(IItemDefManager *idef)
- {
- std::string serialized_inventory =
- "List 0 32\n"
- "Width 3\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Item default:cobble 61\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Item default:dirt 71\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Item default:dirt 99\n"
- "Item default:cobble 38\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "EndInventoryList\n"
- "EndInventory\n";
-
- std::string serialized_inventory_2 =
- "List main 32\n"
- "Width 5\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Item default:cobble 61\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Item default:dirt 71\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Item default:dirt 99\n"
- "Item default:cobble 38\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "Empty\n"
- "EndInventoryList\n"
- "EndInventory\n";
-
- Inventory inv(idef);
- std::istringstream is(serialized_inventory, std::ios::binary);
- inv.deSerialize(is);
- UASSERT(inv.getList("0"));
- UASSERT(!inv.getList("main"));
- inv.getList("0")->setName("main");
- UASSERT(!inv.getList("0"));
- UASSERT(inv.getList("main"));
- UASSERT(inv.getList("main")->getWidth() == 3);
- inv.getList("main")->setWidth(5);
- std::ostringstream inv_os(std::ios::binary);
- inv.serialize(inv_os);
- UASSERT(inv_os.str() == serialized_inventory_2);
- }
-};
-
-/*
- NOTE: These tests became non-working then NodeContainer was removed.
- These should be redone, utilizing some kind of a virtual
- interface for Map (IMap would be fine).
-*/
-#if 0
-struct TestMapBlock: public TestBase
-{
- class TC : public NodeContainer
- {
- public:
-
- MapNode node;
- bool position_valid;
- core::list<v3s16> validity_exceptions;
-
- TC()
- {
- position_valid = true;
- }
-
- virtual bool isValidPosition(v3s16 p)
- {
- //return position_valid ^ (p==position_valid_exception);
- bool exception = false;
- for(core::list<v3s16>::Iterator i=validity_exceptions.begin();
- i != validity_exceptions.end(); i++)
- {
- if(p == *i)
- {
- exception = true;
- break;
- }
- }
- return exception ? !position_valid : position_valid;
- }
-
- virtual MapNode getNode(v3s16 p)
- {
- if(isValidPosition(p) == false)
- throw InvalidPositionException();
- return node;
- }
-
- virtual void setNode(v3s16 p, MapNode & n)
- {
- if(isValidPosition(p) == false)
- throw InvalidPositionException();
- };
-
- virtual u16 nodeContainerId() const
- {
- return 666;
- }
- };
-
- void Run()
- {
- TC parent;
-
- MapBlock b(&parent, v3s16(1,1,1));
- v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
-
- UASSERT(b.getPosRelative() == relpos);
-
- UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE);
- UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1);
- UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE);
- UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1);
- UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE);
- UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1);
-
- UASSERT(b.isValidPosition(v3s16(0,0,0)) == true);
- UASSERT(b.isValidPosition(v3s16(-1,0,0)) == false);
- UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) == false);
- UASSERT(b.isValidPosition(v3s16(-124,142,2341)) == false);
- UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
- UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) == false);
-
- /*
- TODO: this method should probably be removed
- if the block size isn't going to be set variable
- */
- /*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE,
- MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/
-
- // Changed flag should be initially set
- UASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED);
- b.resetModified();
- UASSERT(b.getModified() == MOD_STATE_CLEAN);
-
- // All nodes should have been set to
- // .d=CONTENT_IGNORE and .getLight() = 0
- for(u16 z=0; z<MAP_BLOCKSIZE; z++)
- for(u16 y=0; y<MAP_BLOCKSIZE; y++)
- for(u16 x=0; x<MAP_BLOCKSIZE; x++)
- {
- //UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR);
- UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE);
- UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0);
- UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0);
- }
-
- {
- MapNode n(CONTENT_AIR);
- for(u16 z=0; z<MAP_BLOCKSIZE; z++)
- for(u16 y=0; y<MAP_BLOCKSIZE; y++)
- for(u16 x=0; x<MAP_BLOCKSIZE; x++)
- {
- b.setNode(v3s16(x,y,z), n);
- }
- }
-
- /*
- Parent fetch functions
- */
- parent.position_valid = false;
- parent.node.setContent(5);
-
- MapNode n;
-
- // Positions in the block should still be valid
- UASSERT(b.isValidPositionParent(v3s16(0,0,0)) == true);
- UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
- n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0));
- UASSERT(n.getContent() == CONTENT_AIR);
-
- // ...but outside the block they should be invalid
- UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == false);
- UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == false);
- UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == false);
-
- {
- bool exception_thrown = false;
- try{
- // This should throw an exception
- MapNode n = b.getNodeParent(v3s16(0,0,-1));
- }
- catch(InvalidPositionException &e)
- {
- exception_thrown = true;
- }
- UASSERT(exception_thrown);
- }
-
- parent.position_valid = true;
- // Now the positions outside should be valid
- UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == true);
- UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == true);
- UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == true);
- n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE));
- UASSERT(n.getContent() == 5);
-
- /*
- Set a node
- */
- v3s16 p(1,2,0);
- n.setContent(4);
- b.setNode(p, n);
- UASSERT(b.getNode(p).getContent() == 4);
- //TODO: Update to new system
- /*UASSERT(b.getNodeTile(p) == 4);
- UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*/
-
- /*
- propagateSunlight()
- */
- // Set lighting of all nodes to 0
- for(u16 z=0; z<MAP_BLOCKSIZE; z++){
- for(u16 y=0; y<MAP_BLOCKSIZE; y++){
- for(u16 x=0; x<MAP_BLOCKSIZE; x++){
- MapNode n = b.getNode(v3s16(x,y,z));
- n.setLight(LIGHTBANK_DAY, 0);
- n.setLight(LIGHTBANK_NIGHT, 0);
- b.setNode(v3s16(x,y,z), n);
- }
- }
- }
- {
- /*
- Check how the block handles being a lonely sky block
- */
- parent.position_valid = true;
- b.setIsUnderground(false);
- parent.node.setContent(CONTENT_AIR);
- parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN);
- parent.node.setLight(LIGHTBANK_NIGHT, 0);
- core::map<v3s16, bool> light_sources;
- // The bottom block is invalid, because we have a shadowing node
- UASSERT(b.propagateSunlight(light_sources) == false);
- UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
- UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
- UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) == 0);
- UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) == 0);
- UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) == 0);
- UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
- UASSERT(b.getFaceLight2(1000, p, v3s16(0,1,0)) == LIGHT_SUN);
- UASSERT(b.getFaceLight2(1000, p, v3s16(0,-1,0)) == 0);
- UASSERT(b.getFaceLight2(0, p, v3s16(0,-1,0)) == 0);
- // According to MapBlock::getFaceLight,
- // The face on the z+ side should have double-diminished light
- //UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX)));
- // The face on the z+ side should have diminished light
- UASSERT(b.getFaceLight2(1000, p, v3s16(0,0,1)) == diminish_light(LIGHT_MAX));
- }
- /*
- Check how the block handles being in between blocks with some non-sunlight
- while being underground
- */
- {
- // Make neighbours to exist and set some non-sunlight to them
- parent.position_valid = true;
- b.setIsUnderground(true);
- parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
- core::map<v3s16, bool> light_sources;
- // The block below should be valid because there shouldn't be
- // sunlight in there either
- UASSERT(b.propagateSunlight(light_sources, true) == true);
- // Should not touch nodes that are not affected (that is, all of them)
- //UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN);
- // Should set light of non-sunlighted blocks to 0.
- UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == 0);
- }
- /*
- Set up a situation where:
- - There is only air in this block
- - There is a valid non-sunlighted block at the bottom, and
- - Invalid blocks elsewhere.
- - the block is not underground.
-
- This should result in bottom block invalidity
- */
- {
- b.setIsUnderground(false);
- // Clear block
- for(u16 z=0; z<MAP_BLOCKSIZE; z++){
- for(u16 y=0; y<MAP_BLOCKSIZE; y++){
- for(u16 x=0; x<MAP_BLOCKSIZE; x++){
- MapNode n;
- n.setContent(CONTENT_AIR);
- n.setLight(LIGHTBANK_DAY, 0);
- b.setNode(v3s16(x,y,z), n);
- }
- }
- }
- // Make neighbours invalid
- parent.position_valid = false;
- // Add exceptions to the top of the bottom block
- for(u16 x=0; x<MAP_BLOCKSIZE; x++)
- for(u16 z=0; z<MAP_BLOCKSIZE; z++)
- {
- parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z));
- }
- // Lighting value for the valid nodes
- parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
- core::map<v3s16, bool> light_sources;
- // Bottom block is not valid
- UASSERT(b.propagateSunlight(light_sources) == false);
- }
- }
-};
-
-struct TestMapSector: public TestBase
-{
- class TC : public NodeContainer
- {
- public:
-
- MapNode node;
- bool position_valid;
-
- TC()
- {
- position_valid = true;
- }
-
- virtual bool isValidPosition(v3s16 p)
- {
- return position_valid;
- }
-
- virtual MapNode getNode(v3s16 p)
- {
- if(position_valid == false)
- throw InvalidPositionException();
- return node;
- }
-
- virtual void setNode(v3s16 p, MapNode & n)
- {
- if(position_valid == false)
- throw InvalidPositionException();
- };
-
- virtual u16 nodeContainerId() const
- {
- return 666;
- }
- };
-
- void Run()
- {
- TC parent;
- parent.position_valid = false;
-
- // Create one with no heightmaps
- ServerMapSector sector(&parent, v2s16(1,1));
-
- UASSERT(sector.getBlockNoCreateNoEx(0) == 0);
- UASSERT(sector.getBlockNoCreateNoEx(1) == 0);
-
- MapBlock * bref = sector.createBlankBlock(-2);
-
- UASSERT(sector.getBlockNoCreateNoEx(0) == 0);
- UASSERT(sector.getBlockNoCreateNoEx(-2) == bref);
-
- //TODO: Check for AlreadyExistsException
-
- /*bool exception_thrown = false;
- try{
- sector.getBlock(0);
- }
- catch(InvalidPositionException &e){
- exception_thrown = true;
- }
- UASSERT(exception_thrown);*/
-
- }
-};
-#endif
-
-struct TestCollision: public TestBase
-{
- void Run()
- {
- /*
- axisAlignedCollision
- */
-
- for(s16 bx = -3; bx <= 3; bx++)
- for(s16 by = -3; by <= 3; by++)
- for(s16 bz = -3; bz <= 3; bz++)
- {
- // X-
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
- v3f v(1, 0, 0);
- f32 dtime = 0;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 1.000) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
- v3f v(-1, 0, 0);
- f32 dtime = 0;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1);
- v3f v(1, 0, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
- v3f v(0.5, 0.1, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 3.000) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
- v3f v(0.5, 0.1, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 3.000) < 0.001);
- }
-
- // X+
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
- v3f v(-1, 0, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 1.000) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
- v3f v(1, 0, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5);
- v3f v(-1, 0, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
- v3f v(-0.5, 0.2, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1); // Y, not X!
- UASSERT(fabs(dtime - 2.500) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
- aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
- v3f v(-0.5, 0.3, 0);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 2.000) < 0.001);
- }
-
- // TODO: Y-, Y+, Z-, Z+
-
- // misc
- {
- aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
- aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2);
- v3f v(-1./3, -1./3, -1./3);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 0.9) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
- aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2);
- v3f v(-1./3, -1./3, -1./3);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1);
- UASSERT(fabs(dtime - 0.9) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
- aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2);
- v3f v(-1./3, -1./3, -1./3);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2);
- UASSERT(fabs(dtime - 0.9) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
- aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29);
- v3f v(1./7, 1./7, 1./7);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
- UASSERT(fabs(dtime - 16.1) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
- aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29);
- v3f v(1./7, 1./7, 1./7);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1);
- UASSERT(fabs(dtime - 16.1) < 0.001);
- }
- {
- aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
- aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3);
- v3f v(1./7, 1./7, 1./7);
- f32 dtime;
- UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2);
- UASSERT(fabs(dtime - 16.1) < 0.001);
- }
- }
- }
-};
-
-struct TestSocket: public TestBase
-{
- void Run()
- {
- const int port = 30003;
- Address address(0,0,0,0, port);
- Address address6((IPv6AddressBytes*) NULL, port);
-
- // IPv6 socket test
- {
- UDPSocket socket6;
-
- if (!socket6.init(true, true)) {
- /* Note: Failing to create an IPv6 socket is not technically an
- error because the OS may not support IPv6 or it may
- have been disabled. IPv6 is not /required/ by
- minetest and therefore this should not cause the unit
- test to fail
- */
- dstream << "WARNING: IPv6 socket creation failed (unit test)"
- << std::endl;
- } else {
- const char sendbuffer[] = "hello world!";
- IPv6AddressBytes bytes;
- bytes.bytes[15] = 1;
-
- socket6.Bind(address6);
-
- try {
- socket6.Send(Address(&bytes, port), sendbuffer, sizeof(sendbuffer));
-
- sleep_ms(50);
-
- char rcvbuffer[256] = { 0 };
- Address sender;
-
- for(;;) {
- if (socket6.Receive(sender, rcvbuffer, sizeof(rcvbuffer )) < 0)
- break;
- }
- //FIXME: This fails on some systems
- UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer)) == 0);
- UASSERT(memcmp(sender.getAddress6().sin6_addr.s6_addr,
- Address(&bytes, 0).getAddress6().sin6_addr.s6_addr, 16) == 0);
- }
- catch (SendFailedException e) {
- errorstream << "IPv6 support enabled but not available!"
- << std::endl;
- }
- }
- }
-
- // IPv4 socket test
- {
- UDPSocket socket(false);
- socket.Bind(address);
-
- const char sendbuffer[] = "hello world!";
- socket.Send(Address(127, 0, 0 ,1, port), sendbuffer, sizeof(sendbuffer));
-
- sleep_ms(50);
-
- char rcvbuffer[256] = { 0 };
- Address sender;
- for(;;) {
- if (socket.Receive(sender, rcvbuffer, sizeof(rcvbuffer)) < 0)
- break;
- }
- //FIXME: This fails on some systems
- UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer)) == 0);
- UASSERT(sender.getAddress().sin_addr.s_addr ==
- Address(127, 0, 0, 1, 0).getAddress().sin_addr.s_addr);
- }
- }
-};
-
-struct TestConnection: public TestBase
-{
- void TestHelpers()
- {
- /*
- Test helper functions
- */
-
- // Some constants for testing
- u32 proto_id = 0x12345678;
- u16 peer_id = 123;
- u8 channel = 2;
- SharedBuffer<u8> data1(1);
- data1[0] = 100;
- Address a(127,0,0,1, 10);
- const u16 seqnum = 34352;
-
- con::BufferedPacket p1 = con::makePacket(a, data1,
- proto_id, peer_id, channel);
- /*
- We should now have a packet with this data:
- Header:
- [0] u32 protocol_id
- [4] u16 sender_peer_id
- [6] u8 channel
- Data:
- [7] u8 data1[0]
- */
- UASSERT(readU32(&p1.data[0]) == proto_id);
- UASSERT(readU16(&p1.data[4]) == peer_id);
- UASSERT(readU8(&p1.data[6]) == channel);
- UASSERT(readU8(&p1.data[7]) == data1[0]);
-
- //infostream<<"initial data1[0]="<<((u32)data1[0]&0xff)<<std::endl;
-
- SharedBuffer<u8> p2 = con::makeReliablePacket(data1, seqnum);
-
- /*infostream<<"p2.getSize()="<<p2.getSize()<<", data1.getSize()="
- <<data1.getSize()<<std::endl;
- infostream<<"readU8(&p2[3])="<<readU8(&p2[3])
- <<" p2[3]="<<((u32)p2[3]&0xff)<<std::endl;
- infostream<<"data1[0]="<<((u32)data1[0]&0xff)<<std::endl;*/
-
- UASSERT(p2.getSize() == 3 + data1.getSize());
- UASSERT(readU8(&p2[0]) == TYPE_RELIABLE);
- UASSERT(readU16(&p2[1]) == seqnum);
- UASSERT(readU8(&p2[3]) == data1[0]);
- }
-
- struct Handler : public con::PeerHandler
- {
- Handler(const char *a_name)
- {
- count = 0;
- last_id = 0;
- name = a_name;
- }
- void peerAdded(con::Peer *peer)
- {
- infostream<<"Handler("<<name<<")::peerAdded(): "
- "id="<<peer->id<<std::endl;
- last_id = peer->id;
- count++;
- }
- void deletingPeer(con::Peer *peer, bool timeout)
- {
- infostream<<"Handler("<<name<<")::deletingPeer(): "
- "id="<<peer->id
- <<", timeout="<<timeout<<std::endl;
- last_id = peer->id;
- count--;
- }
-
- s32 count;
- u16 last_id;
- const char *name;
- };
-
- void Run()
- {
- DSTACK("TestConnection::Run");
-
- TestHelpers();
-
- /*
- Test some real connections
-
- NOTE: This mostly tests the legacy interface.
- */
-
- u32 proto_id = 0xad26846a;
-
- Handler hand_server("server");
- Handler hand_client("client");
-
- infostream<<"** Creating server Connection"<<std::endl;
- con::Connection server(proto_id, 512, 5.0, false, &hand_server);
- Address address(0,0,0,0, 30001);
- server.Serve(address);
-
- infostream<<"** Creating client Connection"<<std::endl;
- con::Connection client(proto_id, 512, 5.0, false, &hand_client);
-
- UASSERT(hand_server.count == 0);
- UASSERT(hand_client.count == 0);
-
- sleep_ms(50);
-
- Address server_address(127,0,0,1, 30001);
- infostream<<"** running client.Connect()"<<std::endl;
- client.Connect(server_address);
-
- sleep_ms(50);
-
- // Client should not have added client yet
- UASSERT(hand_client.count == 0);
-
- try
- {
- u16 peer_id;
- SharedBuffer<u8> data;
- infostream<<"** running client.Receive()"<<std::endl;
- u32 size = client.Receive(peer_id, data);
- infostream<<"** Client received: peer_id="<<peer_id
- <<", size="<<size
- <<std::endl;
- }
- catch(con::NoIncomingDataException &e)
- {
- }
-
- // Client should have added server now
- UASSERT(hand_client.count == 1);
- UASSERT(hand_client.last_id == 1);
- // Server should not have added client yet
- UASSERT(hand_server.count == 0);
-
- sleep_ms(100);
-
- try
- {
- u16 peer_id;
- SharedBuffer<u8> data;
- infostream<<"** running server.Receive()"<<std::endl;
- u32 size = server.Receive(peer_id, data);
- infostream<<"** Server received: peer_id="<<peer_id
- <<", size="<<size
- <<std::endl;
- }
- catch(con::NoIncomingDataException &e)
- {
- // No actual data received, but the client has
- // probably been connected
- }
-
- // Client should be the same
- UASSERT(hand_client.count == 1);
- UASSERT(hand_client.last_id == 1);
- // Server should have the client
- UASSERT(hand_server.count == 1);
- UASSERT(hand_server.last_id == 2);
-
- //sleep_ms(50);
-
- while(client.Connected() == false)
- {
- try
- {
- u16 peer_id;
- SharedBuffer<u8> data;
- infostream<<"** running client.Receive()"<<std::endl;
- u32 size = client.Receive(peer_id, data);
- infostream<<"** Client received: peer_id="<<peer_id
- <<", size="<<size
- <<std::endl;
- }
- catch(con::NoIncomingDataException &e)
- {
- }
- sleep_ms(50);
- }
-
- sleep_ms(50);
-
- try
- {
- u16 peer_id;
- SharedBuffer<u8> data;
- infostream<<"** running server.Receive()"<<std::endl;
- u32 size = server.Receive(peer_id, data);
- infostream<<"** Server received: peer_id="<<peer_id
- <<", size="<<size
- <<std::endl;
- }
- catch(con::NoIncomingDataException &e)
- {
- }
-#if 1
- /*
- Simple send-receive test
- */
- {
- /*u8 data[] = "Hello World!";
- u32 datasize = sizeof(data);*/
- SharedBuffer<u8> data = SharedBufferFromString("Hello World!");
-
- infostream<<"** running client.Send()"<<std::endl;
- client.Send(PEER_ID_SERVER, 0, data, true);
-
- sleep_ms(50);
-
- u16 peer_id;
- SharedBuffer<u8> recvdata;
- infostream<<"** running server.Receive()"<<std::endl;
- u32 size = server.Receive(peer_id, recvdata);
- infostream<<"** Server received: peer_id="<<peer_id
- <<", size="<<size
- <<", data="<<*data
- <<std::endl;
- UASSERT(memcmp(*data, *recvdata, data.getSize()) == 0);
- }
-#endif
- u16 peer_id_client = 2;
-#if 0
- /*
- Send consequent packets in different order
- Not compatible with new Connection, thus commented out.
- */
- {
- //u8 data1[] = "hello1";
- //u8 data2[] = "hello2";
- SharedBuffer<u8> data1 = SharedBufferFromString("hello1");
- SharedBuffer<u8> data2 = SharedBufferFromString("Hello2");
-
- Address client_address =
- server.GetPeerAddress(peer_id_client);
-
- infostream<<"*** Sending packets in wrong order (2,1,2)"
- <<std::endl;
-
- u8 chn = 0;
- con::Channel *ch = &server.getPeer(peer_id_client)->channels[chn];
- u16 sn = ch->next_outgoing_seqnum;
- ch->next_outgoing_seqnum = sn+1;
- server.Send(peer_id_client, chn, data2, true);
- ch->next_outgoing_seqnum = sn;
- server.Send(peer_id_client, chn, data1, true);
- ch->next_outgoing_seqnum = sn+1;
- server.Send(peer_id_client, chn, data2, true);
-
- sleep_ms(50);
-
- infostream<<"*** Receiving the packets"<<std::endl;
-
- u16 peer_id;
- SharedBuffer<u8> recvdata;
- u32 size;
-
- infostream<<"** running client.Receive()"<<std::endl;
- peer_id = 132;
- size = client.Receive(peer_id, recvdata);
- infostream<<"** Client received: peer_id="<<peer_id
- <<", size="<<size
- <<", data="<<*recvdata
- <<std::endl;
- UASSERT(size == data1.getSize());
- UASSERT(memcmp(*data1, *recvdata, data1.getSize()) == 0);
- UASSERT(peer_id == PEER_ID_SERVER);
-
- infostream<<"** running client.Receive()"<<std::endl;
- peer_id = 132;
- size = client.Receive(peer_id, recvdata);
- infostream<<"** Client received: peer_id="<<peer_id
- <<", size="<<size
- <<", data="<<*recvdata
- <<std::endl;
- UASSERT(size == data2.getSize());
- UASSERT(memcmp(*data2, *recvdata, data2.getSize()) == 0);
- UASSERT(peer_id == PEER_ID_SERVER);
-
- bool got_exception = false;
- try
- {
- infostream<<"** running client.Receive()"<<std::endl;
- peer_id = 132;
- size = client.Receive(peer_id, recvdata);
- infostream<<"** Client received: peer_id="<<peer_id
- <<", size="<<size
- <<", data="<<*recvdata
- <<std::endl;
- }
- catch(con::NoIncomingDataException &e)
- {
- infostream<<"** No incoming data for client"<<std::endl;
- got_exception = true;
- }
- UASSERT(got_exception);
- }
-#endif
-#if 0
- /*
- Send large amounts of packets (infinite test)
- Commented out because of infinity.
- */
- {
- infostream<<"Sending large amounts of packets (infinite test)"<<std::endl;
- int sendcount = 0;
- for(;;){
- int datasize = myrand_range(0,5)==0?myrand_range(100,10000):myrand_range(0,100);
- infostream<<"datasize="<<datasize<<std::endl;
- SharedBuffer<u8> data1(datasize);
- for(u16 i=0; i<datasize; i++)
- data1[i] = i/4;
-
- int sendtimes = myrand_range(1,10);
- for(int i=0; i<sendtimes; i++){
- server.Send(peer_id_client, 0, data1, true);
- sendcount++;
- }
- infostream<<"sendcount="<<sendcount<<std::endl;
-
- //int receivetimes = myrand_range(1,20);
- int receivetimes = 20;
- for(int i=0; i<receivetimes; i++){
- SharedBuffer<u8> recvdata;
- u16 peer_id = 132;
- u16 size = 0;
- bool received = false;
- try{
- size = client.Receive(peer_id, recvdata);
- received = true;
- }catch(con::NoIncomingDataException &e){
- }
- }
- }
- }
-#endif
- /*
- Send a large packet
- */
- {
- const int datasize = 30000;
- SharedBuffer<u8> data1(datasize);
- for(u16 i=0; i<datasize; i++){
- data1[i] = i/4;
- }
-
- infostream<<"Sending data (size="<<datasize<<"):";
- for(int i=0; i<datasize && i<20; i++){
- if(i%2==0) infostream<<" ";
- char buf[10];
- snprintf(buf, 10, "%.2X", ((int)((const char*)*data1)[i])&0xff);
- infostream<<buf;
- }
- if(datasize>20)
- infostream<<"...";
- infostream<<std::endl;
-
- server.Send(peer_id_client, 0, data1, true);
-
- //sleep_ms(3000);
-
- SharedBuffer<u8> recvdata;
- infostream<<"** running client.Receive()"<<std::endl;
- u16 peer_id = 132;
- u16 size = 0;
- bool received = false;
- u32 timems0 = porting::getTimeMs();
- for(;;){
- if(porting::getTimeMs() - timems0 > 5000 || received)
- break;
- try{
- size = client.Receive(peer_id, recvdata);
- received = true;
- }catch(con::NoIncomingDataException &e){
- }
- sleep_ms(10);
- }
- UASSERT(received);
- infostream<<"** Client received: peer_id="<<peer_id
- <<", size="<<size
- <<std::endl;
-
- infostream<<"Received data (size="<<size<<"): ";
- for(int i=0; i<size && i<20; i++){
- if(i%2==0) infostream<<" ";
- char buf[10];
- snprintf(buf, 10, "%.2X", ((int)(recvdata[i]))&0xff);
- infostream<<buf;
- }
- if(size>20)
- infostream<<"...";
- infostream<<std::endl;
-
- UASSERT(memcmp(*data1, *recvdata, data1.getSize()) == 0);
- UASSERT(peer_id == PEER_ID_SERVER);
- }
-
- // Check peer handlers
- UASSERT(hand_client.count == 1);
- UASSERT(hand_client.last_id == 1);
- UASSERT(hand_server.count == 1);
- UASSERT(hand_server.last_id == 2);
-
- //assert(0);
- }
-};
-
-#define TEST(X) do {\
- X x;\
- infostream<<"Running " #X <<std::endl;\
- x.Run();\
- tests_run++;\
- tests_failed += x.test_failed ? 1 : 0;\
-} while (0)
-
-#define TESTPARAMS(X, ...) do {\
- X x;\
- infostream<<"Running " #X <<std::endl;\
- x.Run(__VA_ARGS__);\
- tests_run++;\
- tests_failed += x.test_failed ? 1 : 0;\
-} while (0)
-
-void run_tests()
-{
- DSTACK(__FUNCTION_NAME);
-
- int tests_run = 0;
- int tests_failed = 0;
-
- // Create item and node definitions
- IWritableItemDefManager *idef = createItemDefManager();
- IWritableNodeDefManager *ndef = createNodeDefManager();
- define_some_nodes(idef, ndef);
-
- log_set_lev_silence(LMT_ERROR, true);
-
- infostream<<"run_tests() started"<<std::endl;
- TEST(TestUtilities);
- TEST(TestPath);
- TEST(TestSettings);
- TEST(TestCompress);
- TEST(TestSerialization);
- TEST(TestNodedefSerialization);
- TESTPARAMS(TestMapNode, ndef);
- TESTPARAMS(TestVoxelManipulator, ndef);
- TESTPARAMS(TestVoxelAlgorithms, ndef);
- TESTPARAMS(TestInventory, idef);
- //TEST(TestMapBlock);
- //TEST(TestMapSector);
- TEST(TestCollision);
- if(INTERNET_SIMULATOR == false){
- TEST(TestSocket);
- dout_con<<"=== BEGIN RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;
- TEST(TestConnection);
- dout_con<<"=== END RUNNING UNIT TESTS FOR CONNECTION ==="<<std::endl;
- }
-
- log_set_lev_silence(LMT_ERROR, false);
-
- delete idef;
- delete ndef;
-
- if(tests_failed == 0){
- infostream<<"run_tests(): "<<tests_failed<<" / "<<tests_run<<" tests failed."<<std::endl;
- infostream<<"run_tests() passed."<<std::endl;
- return;
- } else {
- errorstream<<"run_tests(): "<<tests_failed<<" / "<<tests_run<<" tests failed."<<std::endl;
- errorstream<<"run_tests() aborting."<<std::endl;
- abort();
- }
-}
-
diff --git a/src/touchscreengui.cpp b/src/touchscreengui.cpp
index a2c981cff..f5868133f 100644
--- a/src/touchscreengui.cpp
+++ b/src/touchscreengui.cpp
@@ -25,12 +25,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "gettime.h"
#include "util/numeric.h"
#include "porting.h"
+#include "guiscalingfilter.h"
#include <iostream>
#include <algorithm>
#include <ISceneCollisionManager.h>
+// Very slow button repeat frequency (in seconds)
+#define SLOW_BUTTON_REPEAT (1.0f)
+
using namespace irr::core;
extern Settings *g_settings;
@@ -121,38 +125,48 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver)
for (unsigned int i=0; i < after_last_element_id; i++) {
m_buttons[i].guibutton = 0;
m_buttons[i].repeatcounter = -1;
+ m_buttons[i].repeatdelay = BUTTON_REPEAT_DELAY;
}
m_screensize = m_device->getVideoDriver()->getScreenSize();
}
-void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path)
+void TouchScreenGUI::loadButtonTexture(button_info* btn, const char* path, rect<s32> button_rect)
{
unsigned int tid;
- video::ITexture *texture = m_texturesource->getTexture(path,&tid);
+ video::ITexture *texture = guiScalingImageButton(m_device->getVideoDriver(),
+ m_texturesource->getTexture(path, &tid), button_rect.getWidth(), button_rect.getHeight());
if (texture) {
btn->guibutton->setUseAlphaChannel(true);
- btn->guibutton->setImage(texture);
- btn->guibutton->setPressedImage(texture);
- btn->guibutton->setScaleImage(true);
+ if (g_settings->getBool("gui_scaling_filter")) {
+ rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
+ btn->guibutton->setImage(texture, txr_rect);
+ btn->guibutton->setPressedImage(texture, txr_rect);
+ btn->guibutton->setScaleImage(false);
+ } else {
+ btn->guibutton->setImage(texture);
+ btn->guibutton->setPressedImage(texture);
+ btn->guibutton->setScaleImage(true);
+ }
btn->guibutton->setDrawBorder(false);
btn->guibutton->setText(L"");
}
}
void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
- std::wstring caption, bool immediate_release )
+ std::wstring caption, bool immediate_release, float repeat_delay)
{
button_info* btn = &m_buttons[id];
btn->guibutton = m_guienv->addButton(button_rect, 0, id, caption.c_str());
btn->guibutton->grab();
btn->repeatcounter = -1;
+ btn->repeatdelay = repeat_delay;
btn->keycode = id2keycode(id);
btn->immediate_release = immediate_release;
btn->ids.clear();
- loadButtonTexture(btn,touchgui_button_imagenames[id]);
+ loadButtonTexture(btn,touchgui_button_imagenames[id], button_rect);
}
static int getMaxControlPadSize(float density) {
@@ -240,25 +254,25 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
rect<s32>(m_screensize.X - (0.75*button_size),
m_screensize.Y - (2.25*button_size),
m_screensize.X, m_screensize.Y - (button_size*1.5)),
- L"fly", true);
+ L"fly", false, SLOW_BUTTON_REPEAT);
/* init noclip button */
initButton(noclip_id,
rect<s32>(m_screensize.X - (0.75*button_size), 2.25*button_size,
m_screensize.X, 3*button_size),
- L"clip", true);
+ L"clip", false, SLOW_BUTTON_REPEAT);
/* init fast button */
initButton(fast_id,
rect<s32>(m_screensize.X - (0.75*button_size), 1.5*button_size,
m_screensize.X, 2.25*button_size),
- L"fast", true);
+ L"fast", false, SLOW_BUTTON_REPEAT);
/* init debug button */
initButton(debug_id,
rect<s32>(m_screensize.X - (0.75*button_size), 0.75*button_size,
m_screensize.X, 1.5*button_size),
- L"dbg", true);
+ L"dbg", false, SLOW_BUTTON_REPEAT);
/* init chat button */
initButton(chat_id,
@@ -270,13 +284,13 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc, float density)
initButton(camera_id,
rect<s32>(m_screensize.X - (1.5*button_size), 0,
m_screensize.X - (0.75*button_size), 0.75*button_size),
- L"cam", true);
+ L"cam", false, SLOW_BUTTON_REPEAT);
/* init rangeselect button */
initButton(range_id,
rect<s32>(m_screensize.X - (2.25*button_size), 0,
m_screensize.X - (1.5*button_size), 0.75*button_size),
- L"far", true);
+ L"far", false, SLOW_BUTTON_REPEAT);
}
touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
@@ -687,7 +701,7 @@ void TouchScreenGUI::step(float dtime)
if (m_move_id != -1)
m_move_has_really_moved = true;
- if (btn->repeatcounter < 0.2) continue;
+ if (btn->repeatcounter < btn->repeatdelay) continue;
btn->repeatcounter = 0;
SEvent translated;
diff --git a/src/touchscreengui.h b/src/touchscreengui.h
index 4fe731513..bb3231793 100644
--- a/src/touchscreengui.h
+++ b/src/touchscreengui.h
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <map>
#include "game.h"
-#include "tile.h"
+#include "client/tile.h"
using namespace irr;
using namespace irr::core;
@@ -54,6 +54,7 @@ typedef enum {
#define MIN_DIG_TIME_MS 500
#define MAX_TOUCH_COUNT 64
+#define BUTTON_REPEAT_DELAY 0.2f
extern const char** touchgui_button_imagenames;
@@ -105,6 +106,7 @@ private:
struct button_info {
float repeatcounter;
+ float repeatdelay;
irr::EKEY_CODE keycode;
std::vector<int> ids;
IGUIButton* guibutton;
@@ -124,10 +126,11 @@ private:
/* initialize a button */
void initButton(touch_gui_button_id id, rect<s32> button_rect,
- std::wstring caption, bool immediate_release );
+ std::wstring caption, bool immediate_release,
+ float repeat_delay = BUTTON_REPEAT_DELAY);
/* load texture */
- void loadButtonTexture(button_info* btn, const char* path);
+ void loadButtonTexture(button_info* btn, const char* path, rect<s32> button_rect);
struct id_status{
int id;
diff --git a/src/treegen.cpp b/src/treegen.cpp
index 5c95b250e..208f34552 100644
--- a/src/treegen.cpp
+++ b/src/treegen.cpp
@@ -45,80 +45,75 @@ void make_tree(MMVManip &vmanip, v3s16 p0,
PseudoRandom pr(seed);
s16 trunk_h = pr.range(4, 5);
v3s16 p1 = p0;
- for(s16 ii=0; ii<trunk_h; ii++)
- {
- if(vmanip.m_area.contains(p1))
- if(ii == 0 || vmanip.getNodeNoExNoEmerge(p1).getContent() == CONTENT_AIR)
- vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+ for (s16 ii = 0; ii < trunk_h; ii++) {
+ if (vmanip.m_area.contains(p1)) {
+ u32 vi = vmanip.m_area.index(p1);
+ vmanip.m_data[vi] = treenode;
+ }
p1.Y++;
}
// p1 is now the last piece of the trunk
p1.Y -= 1;
- VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2));
+ VoxelArea leaves_a(v3s16(-2, -1, -2), v3s16(2, 2, 2));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
- for(s32 i=0; i<leaves_a.getVolume(); i++)
+ for (s32 i = 0; i < leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
- {
- s16 d = 1;
- for(s16 z=-d; z<=d; z++)
- for(s16 y=-d; y<=d; y++)
- for(s16 x=-d; x<=d; x++)
- {
- leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
- }
+ s16 d = 1;
+ for (s16 z = -d; z <= d; z++)
+ for (s16 y = -d; y <= d; y++)
+ for (s16 x = -d; x <= d; x++) {
+ leaves_d[leaves_a.index(v3s16(x, y, z))] = 1;
}
// Add leaves randomly
- for(u32 iii=0; iii<7; iii++)
- {
- s16 d = 1;
-
+ for (u32 iii = 0; iii < 7; iii++) {
v3s16 p(
- pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
- pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
- pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
+ pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
+ pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
+ pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
);
- for(s16 z=0; z<=d; z++)
- for(s16 y=0; y<=d; y++)
- for(s16 x=0; x<=d; x++)
- {
- leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
+ for (s16 z = 0; z <= d; z++)
+ for (s16 y = 0; y <= d; y++)
+ for (s16 x = 0; x <= d; x++) {
+ leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
}
}
// Blit leaves to vmanip
- for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
- for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
- for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
- {
- v3s16 p(x,y,z);
- p += p1;
- if(vmanip.m_area.contains(p) == false)
- continue;
- u32 vi = vmanip.m_area.index(p);
- if(vmanip.m_data[vi].getContent() != CONTENT_AIR
- && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
- continue;
- u32 i = leaves_a.index(x,y,z);
- if(leaves_d[i] == 1) {
- bool is_apple = pr.range(0,99) < 10;
- if(is_apple_tree && is_apple) {
- vmanip.m_data[vi] = applenode;
- } else {
- vmanip.m_data[vi] = leavesnode;
+ for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
+ for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
+ v3s16 pmin(leaves_a.MinEdge.X, y, z);
+ u32 i = leaves_a.index(pmin);
+ u32 vi = vmanip.m_area.index(pmin + p1);
+ for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+ v3s16 p(x, y, z);
+ if (vmanip.m_area.contains(p + p1) == true &&
+ (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+ vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
+ if (leaves_d[i] == 1) {
+ bool is_apple = pr.range(0, 99) < 10;
+ if (is_apple_tree && is_apple)
+ vmanip.m_data[vi] = applenode;
+ else
+ vmanip.m_data[vi] = leavesnode;
+ }
}
+ vi++;
+ i++;
}
}
}
+
// L-System tree LUA spawner
-treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition)
+treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
+ INodeDefManager *ndef, TreeDef tree_definition)
{
ServerMap *map = &env->getServerMap();
std::map<v3s16, MapBlock*> modified_blocks;
@@ -126,8 +121,8 @@ treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *nd
v3s16 tree_blockp = getNodeBlockPos(p0);
treegen::error e;
- vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1));
- e = make_ltree (vmanip, p0, ndef, tree_definition);
+ vmanip.initialEmerge(tree_blockp - v3s16(1, 1, 1), tree_blockp + v3s16(1, 3, 1));
+ e = make_ltree(vmanip, p0, ndef, tree_definition);
if (e != SUCCESS)
return e;
@@ -140,30 +135,25 @@ treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *nd
// Send a MEET_OTHER event
MapEditEvent event;
event.type = MEET_OTHER;
- for(std::map<v3s16, MapBlock*>::iterator
- i = modified_blocks.begin();
- i != modified_blocks.end(); ++i)
- {
+ for (std::map<v3s16, MapBlock*>::iterator
+ i = modified_blocks.begin();
+ i != modified_blocks.end(); ++i)
event.modified_blocks.insert(i->first);
- }
map->dispatchEvent(&event);
return SUCCESS;
}
+
//L-System tree generator
-treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
- TreeDef tree_definition)
+treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
+ INodeDefManager *ndef, TreeDef tree_definition)
{
MapNode dirtnode(ndef->getId("mapgen_dirt"));
int seed;
if (tree_definition.explicit_seed)
- {
- seed = tree_definition.seed+14002;
- }
+ seed = tree_definition.seed + 14002;
else
- {
- seed = p0.X*2 + p0.Y*4 + p0.Z; // use the tree position to seed PRNG
- }
+ seed = p0.X * 2 + p0.Y * 4 + p0.Z; // use the tree position to seed PRNG
PseudoRandom ps(seed);
// chance of inserting abcd rules
@@ -174,18 +164,18 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
//randomize tree growth level, minimum=2
s16 iterations = tree_definition.iterations;
- if (tree_definition.iterations_random_level>0)
- iterations -= ps.range(0,tree_definition.iterations_random_level);
- if (iterations<2)
- iterations=2;
+ if (tree_definition.iterations_random_level > 0)
+ iterations -= ps.range(0, tree_definition.iterations_random_level);
+ if (iterations < 2)
+ iterations = 2;
s16 MAX_ANGLE_OFFSET = 5;
- double angle_in_radians = (double)tree_definition.angle*M_PI/180;
- double angleOffset_in_radians = (s16)(ps.range(0,1)%MAX_ANGLE_OFFSET)*M_PI/180;
+ double angle_in_radians = (double)tree_definition.angle * M_PI / 180;
+ double angleOffset_in_radians = (s16)(ps.range(0, 1) % MAX_ANGLE_OFFSET) * M_PI / 180;
//initialize rotation matrix, position and stacks for branches
core::matrix4 rotation;
- rotation = setRotationAxisRadians(rotation, M_PI/2,v3f(0,0,1));
+ rotation = setRotationAxisRadians(rotation, M_PI / 2, v3f(0, 0, 1));
v3f position;
position.X = p0.X;
position.Y = p0.Y;
@@ -195,63 +185,85 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
//generate axiom
std::string axiom = tree_definition.initial_axiom;
- for(s16 i=0; i<iterations; i++)
- {
+ for (s16 i = 0; i < iterations; i++) {
std::string temp = "";
- for(s16 j=0; j<(s16)axiom.size(); j++)
- {
+ for (s16 j = 0; j < (s16)axiom.size(); j++) {
char axiom_char = axiom.at(j);
- switch (axiom_char)
- {
+ switch (axiom_char) {
case 'A':
- temp+=tree_definition.rules_a;
+ temp += tree_definition.rules_a;
break;
case 'B':
- temp+=tree_definition.rules_b;
+ temp += tree_definition.rules_b;
break;
case 'C':
- temp+=tree_definition.rules_c;
+ temp += tree_definition.rules_c;
break;
case 'D':
- temp+=tree_definition.rules_d;
+ temp += tree_definition.rules_d;
break;
case 'a':
- if (prop_a >= ps.range(1,10))
- temp+=tree_definition.rules_a;
+ if (prop_a >= ps.range(1, 10))
+ temp += tree_definition.rules_a;
break;
case 'b':
- if (prop_b >= ps.range(1,10))
- temp+=tree_definition.rules_b;
+ if (prop_b >= ps.range(1, 10))
+ temp += tree_definition.rules_b;
break;
case 'c':
- if (prop_c >= ps.range(1,10))
- temp+=tree_definition.rules_c;
+ if (prop_c >= ps.range(1, 10))
+ temp += tree_definition.rules_c;
break;
case 'd':
- if (prop_d >= ps.range(1,10))
- temp+=tree_definition.rules_d;
+ if (prop_d >= ps.range(1, 10))
+ temp += tree_definition.rules_d;
break;
default:
- temp+=axiom_char;
+ temp += axiom_char;
break;
}
}
- axiom=temp;
+ axiom = temp;
}
//make sure tree is not floating in the air
- if (tree_definition.trunk_type == "double")
- {
- tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
- tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
- tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z+1),dirtnode);
- }
- else if (tree_definition.trunk_type == "crossed")
- {
- tree_node_placement(vmanip,v3f(position.X+1,position.Y-1,position.Z),dirtnode);
- tree_node_placement(vmanip,v3f(position.X-1,position.Y-1,position.Z),dirtnode);
- tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z+1),dirtnode);
- tree_node_placement(vmanip,v3f(position.X,position.Y-1,position.Z-1),dirtnode);
+ if (tree_definition.trunk_type == "double") {
+ tree_node_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y - 1, position.Z),
+ dirtnode
+ );
+ tree_node_placement(
+ vmanip,
+ v3f(position.X, position.Y - 1, position.Z + 1),
+ dirtnode
+ );
+ tree_node_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y - 1, position.Z + 1),
+ dirtnode
+ );
+ } else if (tree_definition.trunk_type == "crossed") {
+ tree_node_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y - 1, position.Z),
+ dirtnode
+ );
+ tree_node_placement(
+ vmanip,
+ v3f(position.X - 1, position.Y - 1, position.Z),
+ dirtnode
+ );
+ tree_node_placement(
+ vmanip,
+ v3f(position.X, position.Y - 1, position.Z + 1),
+ dirtnode
+ );
+ tree_node_placement(
+ vmanip,
+ v3f(position.X, position.Y - 1, position.Z - 1),
+ dirtnode
+ );
}
/* build tree out of generated axiom
@@ -283,84 +295,179 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
*/
s16 x,y,z;
- for(s16 i=0; i<(s16)axiom.size(); i++)
- {
+ for (s16 i = 0; i < (s16)axiom.size(); i++) {
char axiom_char = axiom.at(i);
core::matrix4 temp_rotation;
temp_rotation.makeIdentity();
v3f dir;
- switch (axiom_char)
- {
+ switch (axiom_char) {
case 'G':
- dir = v3f(1,0,0);
- dir = transposeMatrix(rotation,dir);
- position+=dir;
+ dir = v3f(1, 0, 0);
+ dir = transposeMatrix(rotation, dir);
+ position += dir;
break;
case 'T':
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
- if (tree_definition.trunk_type == "double" && !tree_definition.thin_branches)
- {
- tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
- }
- else if (tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches)
- {
- tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z),
+ tree_definition
+ );
+ if (tree_definition.trunk_type == "double" &&
+ !tree_definition.thin_branches) {
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y, position.Z),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z + 1),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y, position.Z + 1),
+ tree_definition
+ );
+ } else if (tree_definition.trunk_type == "crossed" &&
+ !tree_definition.thin_branches) {
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y, position.Z),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X - 1, position.Y, position.Z),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z + 1),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z - 1),
+ tree_definition
+ );
}
- dir = v3f(1,0,0);
- dir = transposeMatrix(rotation,dir);
- position+=dir;
+ dir = v3f(1, 0, 0);
+ dir = transposeMatrix(rotation, dir);
+ position += dir;
break;
case 'F':
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
- if ((stack_orientation.empty() && tree_definition.trunk_type == "double") ||
- (!stack_orientation.empty() && tree_definition.trunk_type == "double" && !tree_definition.thin_branches))
- {
- tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z+1),tree_definition);
- }
- else if ((stack_orientation.empty() && tree_definition.trunk_type == "crossed") ||
- (!stack_orientation.empty() && tree_definition.trunk_type == "crossed" && !tree_definition.thin_branches))
- {
- tree_trunk_placement(vmanip,v3f(position.X+1,position.Y,position.Z),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X-1,position.Y,position.Z),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z+1),tree_definition);
- tree_trunk_placement(vmanip,v3f(position.X,position.Y,position.Z-1),tree_definition);
- }
- if (stack_orientation.empty() == false)
- {
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z),
+ tree_definition
+ );
+ if ((stack_orientation.empty() &&
+ tree_definition.trunk_type == "double") ||
+ (!stack_orientation.empty() &&
+ tree_definition.trunk_type == "double" &&
+ !tree_definition.thin_branches)) {
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X +1 , position.Y, position.Z),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z + 1),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y, position.Z + 1),
+ tree_definition
+ );
+ } else if ((stack_orientation.empty() &&
+ tree_definition.trunk_type == "crossed") ||
+ (!stack_orientation.empty() &&
+ tree_definition.trunk_type == "crossed" &&
+ !tree_definition.thin_branches)) {
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X + 1, position.Y, position.Z),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X - 1, position.Y, position.Z),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z + 1),
+ tree_definition
+ );
+ tree_trunk_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z - 1),
+ tree_definition
+ );
+ } if (stack_orientation.empty() == false) {
s16 size = 1;
- for(x=-size; x<=size; x++)
- for(y=-size; y<=size; y++)
- for(z=-size; z<=size; z++)
- if (abs(x) == size && abs(y) == size && abs(z) == size)
- {
- tree_leaves_placement(vmanip,v3f(position.X+x+1,position.Y+y,position.Z+z),ps.next(), tree_definition);
- tree_leaves_placement(vmanip,v3f(position.X+x-1,position.Y+y,position.Z+z),ps.next(), tree_definition);
- tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z+1),ps.next(), tree_definition);
- tree_leaves_placement(vmanip,v3f(position.X+x,position.Y+y,position.Z+z-1),ps.next(), tree_definition);
- }
+ for (x = -size; x <= size; x++)
+ for (y = -size; y <= size; y++)
+ for (z = -size; z <= size; z++) {
+ if (abs(x) == size &&
+ abs(y) == size &&
+ abs(z) == size) {
+ tree_leaves_placement(
+ vmanip,
+ v3f(position.X + x + 1, position.Y + y,
+ position.Z + z),
+ ps.next(),
+ tree_definition
+ );
+ tree_leaves_placement(
+ vmanip,
+ v3f(position.X + x - 1, position.Y + y,
+ position.Z + z),
+ ps.next(),
+ tree_definition
+ );
+ tree_leaves_placement(
+ vmanip,v3f(position.X + x, position.Y + y,
+ position.Z + z + 1),
+ ps.next(),
+ tree_definition
+ );
+ tree_leaves_placement(
+ vmanip,v3f(position.X + x, position.Y + y,
+ position.Z + z - 1),
+ ps.next(),
+ tree_definition
+ );
+ }
+ }
}
- dir = v3f(1,0,0);
- dir = transposeMatrix(rotation,dir);
- position+=dir;
+ dir = v3f(1, 0, 0);
+ dir = transposeMatrix(rotation, dir);
+ position += dir;
break;
case 'f':
- tree_single_leaves_placement(vmanip,v3f(position.X,position.Y,position.Z),ps.next() ,tree_definition);
- dir = v3f(1,0,0);
- dir = transposeMatrix(rotation,dir);
- position+=dir;
+ tree_single_leaves_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z),
+ ps.next(),
+ tree_definition
+ );
+ dir = v3f(1, 0, 0);
+ dir = transposeMatrix(rotation, dir);
+ position += dir;
break;
case 'R':
- tree_fruit_placement(vmanip,v3f(position.X,position.Y,position.Z),tree_definition);
- dir = v3f(1,0,0);
- dir = transposeMatrix(rotation,dir);
- position+=dir;
+ tree_fruit_placement(
+ vmanip,
+ v3f(position.X, position.Y, position.Z),
+ tree_definition
+ );
+ dir = v3f(1, 0, 0);
+ dir = transposeMatrix(rotation, dir);
+ position += dir;
break;
// turtle orientation commands
@@ -371,40 +478,46 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
case ']':
if (stack_orientation.empty())
return UNBALANCED_BRACKETS;
- rotation=stack_orientation.top();
+ rotation = stack_orientation.top();
stack_orientation.pop();
- position=stack_position.top();
+ position = stack_position.top();
stack_position.pop();
break;
case '+':
temp_rotation.makeIdentity();
- temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,1));
- rotation*=temp_rotation;
+ temp_rotation = setRotationAxisRadians(temp_rotation,
+ angle_in_radians + angleOffset_in_radians, v3f(0, 0, 1));
+ rotation *= temp_rotation;
break;
case '-':
temp_rotation.makeIdentity();
- temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,0,-1));
- rotation*=temp_rotation;
+ temp_rotation = setRotationAxisRadians(temp_rotation,
+ angle_in_radians + angleOffset_in_radians, v3f(0, 0, -1));
+ rotation *= temp_rotation;
break;
case '&':
temp_rotation.makeIdentity();
- temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,1,0));
- rotation*=temp_rotation;
+ temp_rotation = setRotationAxisRadians(temp_rotation,
+ angle_in_radians + angleOffset_in_radians, v3f(0, 1, 0));
+ rotation *= temp_rotation;
break;
case '^':
temp_rotation.makeIdentity();
- temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians+angleOffset_in_radians,v3f(0,-1,0));
- rotation*=temp_rotation;
+ temp_rotation = setRotationAxisRadians(temp_rotation,
+ angle_in_radians + angleOffset_in_radians, v3f(0, -1, 0));
+ rotation *= temp_rotation;
break;
case '*':
temp_rotation.makeIdentity();
- temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(1,0,0));
- rotation*=temp_rotation;
+ temp_rotation = setRotationAxisRadians(temp_rotation,
+ angle_in_radians, v3f(1, 0, 0));
+ rotation *= temp_rotation;
break;
case '/':
temp_rotation.makeIdentity();
- temp_rotation=setRotationAxisRadians(temp_rotation, angle_in_radians,v3f(-1,0,0));
- rotation*=temp_rotation;
+ temp_rotation = setRotationAxisRadians(temp_rotation,
+ angle_in_radians, v3f(-1, 0, 0));
+ rotation *= temp_rotation;
break;
default:
break;
@@ -414,85 +527,87 @@ treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
return SUCCESS;
}
-void tree_node_placement(MMVManip &vmanip, v3f p0,
- MapNode node)
+
+void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node)
{
- v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
- if(vmanip.m_area.contains(p1) == false)
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
- if(vmanip.m_data[vi].getContent() != CONTENT_AIR
+ if (vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
vmanip.m_data[vmanip.m_area.index(p1)] = node;
}
-void tree_trunk_placement(MMVManip &vmanip, v3f p0,
- TreeDef &tree_definition)
+
+void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
{
- v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
- if(vmanip.m_area.contains(p1) == false)
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
- if(vmanip.m_data[vi].getContent() != CONTENT_AIR
+ if (vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.trunknode;
}
+
void tree_leaves_placement(MMVManip &vmanip, v3f p0,
- PseudoRandom ps ,TreeDef &tree_definition)
+ PseudoRandom ps, TreeDef &tree_definition)
{
- MapNode leavesnode=tree_definition.leavesnode;
- if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
- leavesnode=tree_definition.leaves2node;
- v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
- if(vmanip.m_area.contains(p1) == false)
+ MapNode leavesnode = tree_definition.leavesnode;
+ if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
+ leavesnode = tree_definition.leaves2node;
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
- if(vmanip.m_data[vi].getContent() != CONTENT_AIR
+ if (vmanip.m_data[vi].getContent() != CONTENT_AIR
&& vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
- if (tree_definition.fruit_chance>0)
- {
- if (ps.range(1,100) > 100-tree_definition.fruit_chance)
+ if (tree_definition.fruit_chance > 0) {
+ if (ps.range(1, 100) > 100 - tree_definition.fruit_chance)
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
else
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
- }
- else if (ps.range(1,100) > 20)
+ } else if (ps.range(1, 100) > 20) {
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
+ }
}
+
void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
PseudoRandom ps, TreeDef &tree_definition)
{
- MapNode leavesnode=tree_definition.leavesnode;
- if (ps.range(1,100) > 100-tree_definition.leaves2_chance)
- leavesnode=tree_definition.leaves2node;
- v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
- if(vmanip.m_area.contains(p1) == false)
+ MapNode leavesnode = tree_definition.leavesnode;
+ if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
+ leavesnode = tree_definition.leaves2node;
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
- if(vmanip.m_data[vi].getContent() != CONTENT_AIR
- && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+ if (vmanip.m_data[vi].getContent() != CONTENT_AIR
+ && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
}
-void tree_fruit_placement(MMVManip &vmanip, v3f p0,
- TreeDef &tree_definition)
+
+void tree_fruit_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
{
- v3s16 p1 = v3s16(myround(p0.X),myround(p0.Y),myround(p0.Z));
- if(vmanip.m_area.contains(p1) == false)
+ v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+ if (vmanip.m_area.contains(p1) == false)
return;
u32 vi = vmanip.m_area.index(p1);
- if(vmanip.m_data[vi].getContent() != CONTENT_AIR
- && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+ if (vmanip.m_data[vi].getContent() != CONTENT_AIR
+ && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
return;
vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
}
+
irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
{
double c = cos(angle);
@@ -520,20 +635,21 @@ irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3
return M;
}
+
v3f transposeMatrix(irr::core::matrix4 M, v3f v)
{
v3f translated;
double x = M[0] * v.X + M[4] * v.Y + M[8] * v.Z +M[12];
double y = M[1] * v.X + M[5] * v.Y + M[9] * v.Z +M[13];
double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
- translated.X=x;
- translated.Y=y;
- translated.Z=z;
+ translated.X = x;
+ translated.Y = y;
+ translated.Z = z;
return translated;
}
-void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
- INodeDefManager *ndef, int seed)
+
+void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, int seed)
{
/*
NOTE: Tree-placing code is currently duplicated in the engine
@@ -551,18 +667,17 @@ void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
MapNode leavesnode(c_leaves);
PseudoRandom pr(seed);
- for(s16 x=-1; x<=1; x++)
- for(s16 z=-1; z<=1; z++)
- {
- if(pr.range(0, 2) == 0)
+ for (s16 x= -1; x <= 1; x++)
+ for (s16 z= -1; z <= 1; z++) {
+ if (pr.range(0, 2) == 0)
continue;
- v3s16 p1 = p0 + v3s16(x,0,z);
- v3s16 p2 = p0 + v3s16(x,-1,z);
+ v3s16 p1 = p0 + v3s16(x, 0, z);
+ v3s16 p2 = p0 + v3s16(x, -1, z);
u32 vi1 = vmanip.m_area.index(p1);
u32 vi2 = vmanip.m_area.index(p2);
if (vmanip.m_area.contains(p2) &&
- vmanip.m_data[vi2].getContent() == CONTENT_AIR)
+ vmanip.m_data[vi2].getContent() == CONTENT_AIR)
vmanip.m_data[vi2] = treenode;
else if (vmanip.m_area.contains(p1) &&
vmanip.m_data[vi1].getContent() == CONTENT_AIR)
@@ -572,12 +687,10 @@ void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
s16 trunk_h = pr.range(8, 12);
v3s16 p1 = p0;
- for (s16 ii=0; ii<trunk_h; ii++)
- {
+ for (s16 ii = 0; ii < trunk_h; ii++) {
if (vmanip.m_area.contains(p1)) {
u32 vi = vmanip.m_area.index(p1);
- if (vmanip.m_data[vi].getContent() == CONTENT_AIR)
- vmanip.m_data[vi] = treenode;
+ vmanip.m_data[vi] = treenode;
}
p1.Y++;
}
@@ -585,58 +698,181 @@ void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
// p1 is now the last piece of the trunk
p1.Y -= 1;
- VoxelArea leaves_a(v3s16(-3,-2,-3), v3s16(3,2,3));
+ VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
//SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
Buffer<u8> leaves_d(leaves_a.getVolume());
- for(s32 i=0; i<leaves_a.getVolume(); i++)
+ for (s32 i = 0; i < leaves_a.getVolume(); i++)
leaves_d[i] = 0;
// Force leaves at near the end of the trunk
- {
- s16 d = 1;
- for(s16 z=-d; z<=d; z++)
- for(s16 y=-d; y<=d; y++)
- for(s16 x=-d; x<=d; x++)
- {
- leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
- }
+ s16 d = 1;
+ for (s16 z = -d; z <= d; z++)
+ for (s16 y = -d; y <= d; y++)
+ for (s16 x = -d; x <= d; x++) {
+ leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
}
// Add leaves randomly
- for(u32 iii=0; iii<30; iii++)
- {
- s16 d = 1;
-
+ for (u32 iii = 0; iii < 30; iii++) {
v3s16 p(
- pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d),
- pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d),
- pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d)
+ pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
+ pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
+ pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
);
- for(s16 z=0; z<=d; z++)
- for(s16 y=0; y<=d; y++)
- for(s16 x=0; x<=d; x++)
- {
- leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1;
+ for (s16 z = 0; z <= d; z++)
+ for (s16 y = 0; y <= d; y++)
+ for (s16 x = 0; x <= d; x++) {
+ leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
}
}
// Blit leaves to vmanip
- for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++)
- for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++)
- for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++)
- {
- v3s16 p(x,y,z);
- p += p1;
- if(vmanip.m_area.contains(p) == false)
- continue;
- u32 vi = vmanip.m_area.index(p);
- if (vmanip.m_data[vi].getContent() != CONTENT_AIR &&
- vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
- continue;
- u32 i = leaves_a.index(x,y,z);
- if(leaves_d[i] == 1)
- vmanip.m_data[vi] = leavesnode;
+ for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
+ for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
+ v3s16 pmin(leaves_a.MinEdge.X, y, z);
+ u32 i = leaves_a.index(pmin);
+ u32 vi = vmanip.m_area.index(pmin + p1);
+ for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+ v3s16 p(x, y, z);
+ if (vmanip.m_area.contains(p + p1) == true &&
+ (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+ vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
+ if (leaves_d[i] == 1)
+ vmanip.m_data[vi] = leavesnode;
+ }
+ vi++;
+ i++;
+ }
+ }
+}
+
+
+void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, int seed)
+{
+ /*
+ NOTE: Tree-placing code is currently duplicated in the engine
+ and in games that have saplings; both are deprecated but not
+ replaced yet
+ */
+ content_t c_tree = ndef->getId("mapgen_pine_tree");
+ content_t c_leaves = ndef->getId("mapgen_pine_needles");
+ content_t c_snow = ndef->getId("mapgen_snow");
+ if (c_tree == CONTENT_IGNORE)
+ c_tree = ndef->getId("mapgen_tree");
+ if (c_leaves == CONTENT_IGNORE)
+ c_leaves = ndef->getId("mapgen_leaves");
+ if (c_snow == CONTENT_IGNORE)
+ c_snow = CONTENT_AIR;
+
+ MapNode treenode(c_tree);
+ MapNode leavesnode(c_leaves);
+ MapNode snownode(c_snow);
+
+ PseudoRandom pr(seed);
+ s16 trunk_h = pr.range(9, 13);
+ v3s16 p1 = p0;
+ for (s16 ii = 0; ii < trunk_h; ii++) {
+ if (vmanip.m_area.contains(p1)) {
+ u32 vi = vmanip.m_area.index(p1);
+ vmanip.m_data[vi] = treenode;
+ }
+ p1.Y++;
+ }
+
+ // Make p1 the top node of the trunk
+ p1.Y -= 1;
+
+ VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
+ //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
+ Buffer<u8> leaves_d(leaves_a.getVolume());
+ for (s32 i = 0; i < leaves_a.getVolume(); i++)
+ leaves_d[i] = 0;
+
+ // Upper branches
+ s16 dev = 3;
+ for (s16 yy = -1; yy <= 1; yy++) {
+ for (s16 zz = -dev; zz <= dev; zz++) {
+ u32 i = leaves_a.index(v3s16(-dev, yy, zz));
+ u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
+ for (s16 xx = -dev; xx <= dev; xx++) {
+ if (pr.range(0, 20) <= 19 - dev) {
+ leaves_d[i] = 1;
+ leaves_d[ia] = 2;
+ }
+ i++;
+ ia++;
+ }
+ }
+ dev--;
+ }
+
+ // Centre top nodes
+ u32 i = leaves_a.index(v3s16(0, 1, 0));
+ leaves_d[i] = 1;
+ i = leaves_a.index(v3s16(0, 2, 0));
+ leaves_d[i] = 1;
+ i = leaves_a.index(v3s16(0, 3, 0));
+ leaves_d[i] = 2;
+
+ // Lower branches
+ s16 my = -6;
+ for (u32 iii = 0; iii < 20; iii++) {
+ s16 xi = pr.range(-3, 2);
+ s16 yy = pr.range(-6, -5);
+ s16 zi = pr.range(-3, 2);
+ if (yy > my)
+ my = yy;
+ for (s16 zz = zi; zz <= zi + 1; zz++) {
+ u32 i = leaves_a.index(v3s16(xi, yy, zz));
+ u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
+ for (s16 xx = xi; xx <= xi + 1; xx++) {
+ leaves_d[i] = 1;
+ if (leaves_d[ia] == 0)
+ leaves_d[ia] = 2;
+ i++;
+ ia++;
+ }
+ }
+ }
+
+ dev = 2;
+ for (s16 yy = my + 1; yy <= my + 2; yy++) {
+ for (s16 zz = -dev; zz <= dev; zz++) {
+ u32 i = leaves_a.index(v3s16(-dev, yy, zz));
+ u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
+ for (s16 xx = -dev; xx <= dev; xx++) {
+ if (pr.range(0, 20) <= 19 - dev) {
+ leaves_d[i] = 1;
+ leaves_d[ia] = 2;
+ }
+ i++;
+ ia++;
+ }
+ }
+ dev--;
+ }
+
+ // Blit leaves to vmanip
+ for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
+ for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
+ v3s16 pmin(leaves_a.MinEdge.X, y, z);
+ u32 i = leaves_a.index(pmin);
+ u32 vi = vmanip.m_area.index(pmin + p1);
+ for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+ v3s16 p(x, y, z);
+ if (vmanip.m_area.contains(p + p1) == true &&
+ (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+ vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
+ vmanip.m_data[vi] == snownode)) {
+ if (leaves_d[i] == 1)
+ vmanip.m_data[vi] = leavesnode;
+ else if (leaves_d[i] == 2)
+ vmanip.m_data[vi] = snownode;
+ }
+ vi++;
+ i++;
+ }
}
}
diff --git a/src/treegen.h b/src/treegen.h
index febbc29ee..4b0089d1e 100644
--- a/src/treegen.h
+++ b/src/treegen.h
@@ -62,7 +62,10 @@ namespace treegen {
void make_tree(MMVManip &vmanip, v3s16 p0,
bool is_apple_tree, INodeDefManager *ndef, int seed);
// Add jungle tree
- void make_jungletree(VoxelManipulator &vmanip, v3s16 p0,
+ void make_jungletree(MMVManip &vmanip, v3s16 p0,
+ INodeDefManager *ndef, int seed);
+ // Add pine tree
+ void make_pine_tree(MMVManip &vmanip, v3s16 p0,
INodeDefManager *ndef, int seed);
// Add L-Systems tree (used by engine)
diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt
new file mode 100644
index 000000000..bdff14f05
--- /dev/null
+++ b/src/unittest/CMakeLists.txt
@@ -0,0 +1,23 @@
+set (UNITTEST_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/test.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_areastore.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_collision.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_compression.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_connection.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_filepath.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_inventory.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_mapnode.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_nodedef.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_noderesolver.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_noise.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_objdef.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_profiler.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_random.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_schematic.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_serialization.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_settings.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_socket.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_utilities.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_voxelalgorithms.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/test_voxelmanipulator.cpp
+ PARENT_SCOPE)
diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp
new file mode 100644
index 000000000..d0ffb423f
--- /dev/null
+++ b/src/unittest/test.cpp
@@ -0,0 +1,638 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "debug.h"
+#include "log.h"
+#include "nodedef.h"
+#include "itemdef.h"
+#include "gamedef.h"
+
+content_t t_CONTENT_STONE;
+content_t t_CONTENT_GRASS;
+content_t t_CONTENT_TORCH;
+content_t t_CONTENT_WATER;
+content_t t_CONTENT_LAVA;
+content_t t_CONTENT_BRICK;
+
+////////////////////////////////////////////////////////////////////////////////
+
+////
+//// TestGameDef
+////
+
+class TestGameDef : public IGameDef {
+public:
+ TestGameDef();
+ ~TestGameDef();
+
+ IItemDefManager *getItemDefManager() { return m_itemdef; }
+ INodeDefManager *getNodeDefManager() { return m_nodedef; }
+ ICraftDefManager *getCraftDefManager() { return m_craftdef; }
+ ITextureSource *getTextureSource() { return m_texturesrc; }
+ IShaderSource *getShaderSource() { return m_shadersrc; }
+ ISoundManager *getSoundManager() { return m_soundmgr; }
+ MtEventManager *getEventManager() { return m_eventmgr; }
+ scene::ISceneManager *getSceneManager() { return m_scenemgr; }
+ IRollbackManager *getRollbackManager() { return m_rollbackmgr; }
+ EmergeManager *getEmergeManager() { return m_emergemgr; }
+
+ scene::IAnimatedMesh *getMesh(const std::string &filename) { return NULL; }
+ bool checkLocalPrivilege(const std::string &priv) { return false; }
+ u16 allocateUnknownNodeId(const std::string &name) { return 0; }
+
+ void defineSomeNodes();
+
+private:
+ IItemDefManager *m_itemdef;
+ INodeDefManager *m_nodedef;
+ ICraftDefManager *m_craftdef;
+ ITextureSource *m_texturesrc;
+ IShaderSource *m_shadersrc;
+ ISoundManager *m_soundmgr;
+ MtEventManager *m_eventmgr;
+ scene::ISceneManager *m_scenemgr;
+ IRollbackManager *m_rollbackmgr;
+ EmergeManager *m_emergemgr;
+};
+
+
+TestGameDef::TestGameDef()
+{
+ m_itemdef = createItemDefManager();
+ m_nodedef = createNodeDefManager();
+
+ defineSomeNodes();
+}
+
+
+TestGameDef::~TestGameDef()
+{
+ delete m_itemdef;
+ delete m_nodedef;
+}
+
+
+void TestGameDef::defineSomeNodes()
+{
+ IWritableItemDefManager *idef = (IWritableItemDefManager *)m_itemdef;
+ IWritableNodeDefManager *ndef = (IWritableNodeDefManager *)m_nodedef;
+
+ ItemDefinition itemdef;
+ ContentFeatures f;
+
+ //// Stone
+ itemdef = ItemDefinition();
+ itemdef.type = ITEM_NODE;
+ itemdef.name = "default:stone";
+ itemdef.description = "Stone";
+ itemdef.groups["cracky"] = 3;
+ itemdef.inventory_image = "[inventorycube"
+ "{default_stone.png"
+ "{default_stone.png"
+ "{default_stone.png";
+ f = ContentFeatures();
+ f.name = itemdef.name;
+ for(int i = 0; i < 6; i++)
+ f.tiledef[i].name = "default_stone.png";
+ f.is_ground_content = true;
+ idef->registerItem(itemdef);
+ t_CONTENT_STONE = ndef->set(f.name, f);
+
+ //// Grass
+ itemdef = ItemDefinition();
+ itemdef.type = ITEM_NODE;
+ itemdef.name = "default:dirt_with_grass";
+ itemdef.description = "Dirt with grass";
+ itemdef.groups["crumbly"] = 3;
+ itemdef.inventory_image = "[inventorycube"
+ "{default_grass.png"
+ "{default_dirt.png&default_grass_side.png"
+ "{default_dirt.png&default_grass_side.png";
+ f = ContentFeatures();
+ f.name = itemdef.name;
+ f.tiledef[0].name = "default_grass.png";
+ f.tiledef[1].name = "default_dirt.png";
+ for(int i = 2; i < 6; i++)
+ f.tiledef[i].name = "default_dirt.png^default_grass_side.png";
+ f.is_ground_content = true;
+ idef->registerItem(itemdef);
+ t_CONTENT_GRASS = ndef->set(f.name, f);
+
+ //// Torch (minimal definition for lighting tests)
+ itemdef = ItemDefinition();
+ itemdef.type = ITEM_NODE;
+ itemdef.name = "default:torch";
+ f = ContentFeatures();
+ f.name = itemdef.name;
+ f.param_type = CPT_LIGHT;
+ f.light_propagates = true;
+ f.sunlight_propagates = true;
+ f.light_source = LIGHT_MAX-1;
+ idef->registerItem(itemdef);
+ t_CONTENT_TORCH = ndef->set(f.name, f);
+
+ //// Water
+ itemdef = ItemDefinition();
+ itemdef.type = ITEM_NODE;
+ itemdef.name = "default:water";
+ itemdef.description = "Water";
+ itemdef.inventory_image = "[inventorycube"
+ "{default_water.png"
+ "{default_water.png"
+ "{default_water.png";
+ f = ContentFeatures();
+ f.name = itemdef.name;
+ f.alpha = 128;
+ f.liquid_type = LIQUID_SOURCE;
+ f.liquid_viscosity = 4;
+ f.is_ground_content = true;
+ f.groups["liquids"] = 3;
+ for(int i = 0; i < 6; i++)
+ f.tiledef[i].name = "default_water.png";
+ idef->registerItem(itemdef);
+ t_CONTENT_WATER = ndef->set(f.name, f);
+
+ //// Lava
+ itemdef = ItemDefinition();
+ itemdef.type = ITEM_NODE;
+ itemdef.name = "default:lava";
+ itemdef.description = "Lava";
+ itemdef.inventory_image = "[inventorycube"
+ "{default_lava.png"
+ "{default_lava.png"
+ "{default_lava.png";
+ f = ContentFeatures();
+ f.name = itemdef.name;
+ f.alpha = 128;
+ f.liquid_type = LIQUID_SOURCE;
+ f.liquid_viscosity = 7;
+ f.light_source = LIGHT_MAX-1;
+ f.is_ground_content = true;
+ f.groups["liquids"] = 3;
+ for(int i = 0; i < 6; i++)
+ f.tiledef[i].name = "default_lava.png";
+ idef->registerItem(itemdef);
+ t_CONTENT_LAVA = ndef->set(f.name, f);
+
+
+ //// Brick
+ itemdef = ItemDefinition();
+ itemdef.type = ITEM_NODE;
+ itemdef.name = "default:brick";
+ itemdef.description = "Brick";
+ itemdef.groups["cracky"] = 3;
+ itemdef.inventory_image = "[inventorycube"
+ "{default_brick.png"
+ "{default_brick.png"
+ "{default_brick.png";
+ f = ContentFeatures();
+ f.name = itemdef.name;
+ for(int i = 0; i < 6; i++)
+ f.tiledef[i].name = "default_brick.png";
+ f.is_ground_content = true;
+ idef->registerItem(itemdef);
+ t_CONTENT_BRICK = ndef->set(f.name, f);
+}
+
+////
+//// run_tests
+////
+
+void run_tests()
+{
+ DSTACK(__FUNCTION_NAME);
+
+ u32 t1 = porting::getTime(PRECISION_MILLI);
+ TestGameDef gamedef;
+
+ log_set_lev_silence(LMT_ERROR, true);
+
+ u32 num_modules_failed = 0;
+ u32 num_total_tests_failed = 0;
+ u32 num_total_tests_run = 0;
+ std::vector<TestBase *> &testmods = TestManager::getTestModules();
+ for (size_t i = 0; i != testmods.size(); i++) {
+ if (!testmods[i]->testModule(&gamedef))
+ num_modules_failed++;
+
+ num_total_tests_failed += testmods[i]->num_tests_failed;
+ num_total_tests_run += testmods[i]->num_tests_run;
+ }
+
+ u32 tdiff = porting::getTime(PRECISION_MILLI) - t1;
+
+ log_set_lev_silence(LMT_ERROR, false);
+
+ const char *overall_status = (num_modules_failed == 0) ? "PASSED" : "FAILED";
+
+ dstream
+ << "++++++++++++++++++++++++++++++++++++++++"
+ << "++++++++++++++++++++++++++++++++++++++++" << std::endl
+ << "Unit Test Results: " << overall_status << std::endl
+ << " " << num_modules_failed << " / " << testmods.size()
+ << " failed modules (" << num_total_tests_failed << " / "
+ << num_total_tests_run << " failed individual tests)." << std::endl
+ << " Testing took " << tdiff << "ms total." << std::endl
+ << "++++++++++++++++++++++++++++++++++++++++"
+ << "++++++++++++++++++++++++++++++++++++++++" << std::endl;
+
+ if (num_modules_failed)
+ abort();
+}
+
+////
+//// TestBase
+////
+
+bool TestBase::testModule(IGameDef *gamedef)
+{
+ dstream << "======== Testing module " << getName() << std::endl;
+ u32 t1 = porting::getTime(PRECISION_MILLI);
+
+
+ runTests(gamedef);
+
+ u32 tdiff = porting::getTime(PRECISION_MILLI) - t1;
+ dstream << "======== Module " << getName() << " "
+ << (num_tests_failed ? "failed" : "passed") << " (" << num_tests_failed
+ << " failures / " << num_tests_run << " tests) - " << tdiff
+ << "ms" << std::endl;
+
+ if (!m_test_dir.empty())
+ fs::RecursiveDelete(m_test_dir);
+
+ return num_tests_failed == 0;
+}
+
+std::string TestBase::getTestTempDirectory()
+{
+ if (!m_test_dir.empty())
+ return m_test_dir;
+
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%08X", myrand());
+
+ m_test_dir = fs::TempPath() + DIR_DELIM "mttest_" + buf;
+ if (!fs::CreateDir(m_test_dir))
+ throw TestFailedException();
+
+ return m_test_dir;
+}
+
+std::string TestBase::getTestTempFile()
+{
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%08X", myrand());
+
+ return getTestTempDirectory() + DIR_DELIM + buf + ".tmp";
+}
+
+
+/*
+ NOTE: These tests became non-working then NodeContainer was removed.
+ These should be redone, utilizing some kind of a virtual
+ interface for Map (IMap would be fine).
+*/
+#if 0
+struct TestMapBlock: public TestBase
+{
+ class TC : public NodeContainer
+ {
+ public:
+
+ MapNode node;
+ bool position_valid;
+ core::list<v3s16> validity_exceptions;
+
+ TC()
+ {
+ position_valid = true;
+ }
+
+ virtual bool isValidPosition(v3s16 p)
+ {
+ //return position_valid ^ (p==position_valid_exception);
+ bool exception = false;
+ for(core::list<v3s16>::Iterator i=validity_exceptions.begin();
+ i != validity_exceptions.end(); i++)
+ {
+ if(p == *i)
+ {
+ exception = true;
+ break;
+ }
+ }
+ return exception ? !position_valid : position_valid;
+ }
+
+ virtual MapNode getNode(v3s16 p)
+ {
+ if(isValidPosition(p) == false)
+ throw InvalidPositionException();
+ return node;
+ }
+
+ virtual void setNode(v3s16 p, MapNode & n)
+ {
+ if(isValidPosition(p) == false)
+ throw InvalidPositionException();
+ };
+
+ virtual u16 nodeContainerId() const
+ {
+ return 666;
+ }
+ };
+
+ void Run()
+ {
+ TC parent;
+
+ MapBlock b(&parent, v3s16(1,1,1));
+ v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
+
+ UASSERT(b.getPosRelative() == relpos);
+
+ UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE);
+ UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1);
+ UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE);
+ UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1);
+ UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE);
+ UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1);
+
+ UASSERT(b.isValidPosition(v3s16(0,0,0)) == true);
+ UASSERT(b.isValidPosition(v3s16(-1,0,0)) == false);
+ UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) == false);
+ UASSERT(b.isValidPosition(v3s16(-124,142,2341)) == false);
+ UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
+ UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) == false);
+
+ /*
+ TODO: this method should probably be removed
+ if the block size isn't going to be set variable
+ */
+ /*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE,
+ MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/
+
+ // Changed flag should be initially set
+ UASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED);
+ b.resetModified();
+ UASSERT(b.getModified() == MOD_STATE_CLEAN);
+
+ // All nodes should have been set to
+ // .d=CONTENT_IGNORE and .getLight() = 0
+ for(u16 z=0; z<MAP_BLOCKSIZE; z++)
+ for(u16 y=0; y<MAP_BLOCKSIZE; y++)
+ for(u16 x=0; x<MAP_BLOCKSIZE; x++)
+ {
+ //UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR);
+ UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE);
+ UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0);
+ UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0);
+ }
+
+ {
+ MapNode n(CONTENT_AIR);
+ for(u16 z=0; z<MAP_BLOCKSIZE; z++)
+ for(u16 y=0; y<MAP_BLOCKSIZE; y++)
+ for(u16 x=0; x<MAP_BLOCKSIZE; x++)
+ {
+ b.setNode(v3s16(x,y,z), n);
+ }
+ }
+
+ /*
+ Parent fetch functions
+ */
+ parent.position_valid = false;
+ parent.node.setContent(5);
+
+ MapNode n;
+
+ // Positions in the block should still be valid
+ UASSERT(b.isValidPositionParent(v3s16(0,0,0)) == true);
+ UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true);
+ n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0));
+ UASSERT(n.getContent() == CONTENT_AIR);
+
+ // ...but outside the block they should be invalid
+ UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == false);
+ UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == false);
+ UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == false);
+
+ {
+ bool exception_thrown = false;
+ try{
+ // This should throw an exception
+ MapNode n = b.getNodeParent(v3s16(0,0,-1));
+ }
+ catch(InvalidPositionException &e)
+ {
+ exception_thrown = true;
+ }
+ UASSERT(exception_thrown);
+ }
+
+ parent.position_valid = true;
+ // Now the positions outside should be valid
+ UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == true);
+ UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == true);
+ UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == true);
+ n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE));
+ UASSERT(n.getContent() == 5);
+
+ /*
+ Set a node
+ */
+ v3s16 p(1,2,0);
+ n.setContent(4);
+ b.setNode(p, n);
+ UASSERT(b.getNode(p).getContent() == 4);
+ //TODO: Update to new system
+ /*UASSERT(b.getNodeTile(p) == 4);
+ UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*/
+
+ /*
+ propagateSunlight()
+ */
+ // Set lighting of all nodes to 0
+ for(u16 z=0; z<MAP_BLOCKSIZE; z++){
+ for(u16 y=0; y<MAP_BLOCKSIZE; y++){
+ for(u16 x=0; x<MAP_BLOCKSIZE; x++){
+ MapNode n = b.getNode(v3s16(x,y,z));
+ n.setLight(LIGHTBANK_DAY, 0);
+ n.setLight(LIGHTBANK_NIGHT, 0);
+ b.setNode(v3s16(x,y,z), n);
+ }
+ }
+ }
+ {
+ /*
+ Check how the block handles being a lonely sky block
+ */
+ parent.position_valid = true;
+ b.setIsUnderground(false);
+ parent.node.setContent(CONTENT_AIR);
+ parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN);
+ parent.node.setLight(LIGHTBANK_NIGHT, 0);
+ core::map<v3s16, bool> light_sources;
+ // The bottom block is invalid, because we have a shadowing node
+ UASSERT(b.propagateSunlight(light_sources) == false);
+ UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
+ UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
+ UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) == 0);
+ UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) == 0);
+ UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) == 0);
+ UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN);
+ UASSERT(b.getFaceLight2(1000, p, v3s16(0,1,0)) == LIGHT_SUN);
+ UASSERT(b.getFaceLight2(1000, p, v3s16(0,-1,0)) == 0);
+ UASSERT(b.getFaceLight2(0, p, v3s16(0,-1,0)) == 0);
+ // According to MapBlock::getFaceLight,
+ // The face on the z+ side should have double-diminished light
+ //UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX)));
+ // The face on the z+ side should have diminished light
+ UASSERT(b.getFaceLight2(1000, p, v3s16(0,0,1)) == diminish_light(LIGHT_MAX));
+ }
+ /*
+ Check how the block handles being in between blocks with some non-sunlight
+ while being underground
+ */
+ {
+ // Make neighbours to exist and set some non-sunlight to them
+ parent.position_valid = true;
+ b.setIsUnderground(true);
+ parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
+ core::map<v3s16, bool> light_sources;
+ // The block below should be valid because there shouldn't be
+ // sunlight in there either
+ UASSERT(b.propagateSunlight(light_sources, true) == true);
+ // Should not touch nodes that are not affected (that is, all of them)
+ //UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN);
+ // Should set light of non-sunlighted blocks to 0.
+ UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == 0);
+ }
+ /*
+ Set up a situation where:
+ - There is only air in this block
+ - There is a valid non-sunlighted block at the bottom, and
+ - Invalid blocks elsewhere.
+ - the block is not underground.
+
+ This should result in bottom block invalidity
+ */
+ {
+ b.setIsUnderground(false);
+ // Clear block
+ for(u16 z=0; z<MAP_BLOCKSIZE; z++){
+ for(u16 y=0; y<MAP_BLOCKSIZE; y++){
+ for(u16 x=0; x<MAP_BLOCKSIZE; x++){
+ MapNode n;
+ n.setContent(CONTENT_AIR);
+ n.setLight(LIGHTBANK_DAY, 0);
+ b.setNode(v3s16(x,y,z), n);
+ }
+ }
+ }
+ // Make neighbours invalid
+ parent.position_valid = false;
+ // Add exceptions to the top of the bottom block
+ for(u16 x=0; x<MAP_BLOCKSIZE; x++)
+ for(u16 z=0; z<MAP_BLOCKSIZE; z++)
+ {
+ parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z));
+ }
+ // Lighting value for the valid nodes
+ parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2);
+ core::map<v3s16, bool> light_sources;
+ // Bottom block is not valid
+ UASSERT(b.propagateSunlight(light_sources) == false);
+ }
+ }
+};
+
+struct TestMapSector: public TestBase
+{
+ class TC : public NodeContainer
+ {
+ public:
+
+ MapNode node;
+ bool position_valid;
+
+ TC()
+ {
+ position_valid = true;
+ }
+
+ virtual bool isValidPosition(v3s16 p)
+ {
+ return position_valid;
+ }
+
+ virtual MapNode getNode(v3s16 p)
+ {
+ if(position_valid == false)
+ throw InvalidPositionException();
+ return node;
+ }
+
+ virtual void setNode(v3s16 p, MapNode & n)
+ {
+ if(position_valid == false)
+ throw InvalidPositionException();
+ };
+
+ virtual u16 nodeContainerId() const
+ {
+ return 666;
+ }
+ };
+
+ void Run()
+ {
+ TC parent;
+ parent.position_valid = false;
+
+ // Create one with no heightmaps
+ ServerMapSector sector(&parent, v2s16(1,1));
+
+ UASSERT(sector.getBlockNoCreateNoEx(0) == 0);
+ UASSERT(sector.getBlockNoCreateNoEx(1) == 0);
+
+ MapBlock * bref = sector.createBlankBlock(-2);
+
+ UASSERT(sector.getBlockNoCreateNoEx(0) == 0);
+ UASSERT(sector.getBlockNoCreateNoEx(-2) == bref);
+
+ //TODO: Check for AlreadyExistsException
+
+ /*bool exception_thrown = false;
+ try{
+ sector.getBlock(0);
+ }
+ catch(InvalidPositionException &e){
+ exception_thrown = true;
+ }
+ UASSERT(exception_thrown);*/
+
+ }
+};
+#endif
diff --git a/src/unittest/test.h b/src/unittest/test.h
new file mode 100644
index 000000000..47a441e02
--- /dev/null
+++ b/src/unittest/test.h
@@ -0,0 +1,146 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#ifndef TEST_HEADER
+#define TEST_HEADER
+
+#include <exception>
+#include <vector>
+
+#include "irrlichttypes_extrabloated.h"
+#include "porting.h"
+#include "filesys.h"
+#include "mapnode.h"
+
+class TestFailedException : public std::exception {
+};
+
+// Runs a unit test and reports results
+#define TEST(fxn, ...) do { \
+ u32 t1 = porting::getTime(PRECISION_MILLI); \
+ try { \
+ fxn(__VA_ARGS__); \
+ dstream << "[PASS] "; \
+ } catch (TestFailedException &e) { \
+ dstream << "[FAIL] "; \
+ num_tests_failed++; \
+ } catch (std::exception &e) { \
+ dstream << "Caught unhandled exception: " << e.what() << std::endl; \
+ dstream << "[FAIL] "; \
+ num_tests_failed++; \
+ } \
+ num_tests_run++; \
+ u32 tdiff = porting::getTime(PRECISION_MILLI) - t1; \
+ dstream << #fxn << " - " << tdiff << "ms" << std::endl; \
+} while (0)
+
+// Asserts the specified condition is true, or fails the current unit test
+#define UASSERT(x) do { \
+ if (!(x)) { \
+ dstream << "Test assertion failed: " #x << std::endl \
+ << " at " << fs::GetFilenameFromPath(__FILE__) \
+ << ":" << __LINE__ << std::endl; \
+ throw TestFailedException(); \
+ } \
+} while (0)
+
+// Asserts the specified condition is true, or fails the current unit test
+// and prints the format specifier fmt
+#define UTEST(x, fmt, ...) do { \
+ if (!(x)) { \
+ char utest_buf[1024]; \
+ snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \
+ dstream << "Test assertion failed: " << utest_buf << std::endl \
+ << " at " << fs::GetFilenameFromPath(__FILE__) \
+ << ":" << __LINE__ << std::endl; \
+ throw TestFailedException(); \
+ } \
+} while (0)
+
+// Asserts the comparison specified by CMP is true, or fails the current unit test
+#define UASSERTCMP(T, CMP, actual, expected) do { \
+ T a = (actual); \
+ T e = (expected); \
+ if (!(a CMP e)) { \
+ dstream << "Test assertion failed: " << #actual << " " << #CMP << " " \
+ << #expected << std::endl \
+ << " at " << fs::GetFilenameFromPath(__FILE__) << ":" \
+ << __LINE__ << std::endl \
+ << " actual: " << a << std::endl << " expected: " \
+ << e << std::endl; \
+ throw TestFailedException(); \
+ } \
+} while (0)
+
+#define UASSERTEQ(T, actual, expected) UASSERTCMP(T, ==, actual, expected)
+
+// UASSERTs that the specified exception occurs
+#define EXCEPTION_CHECK(EType, code) do { \
+ bool exception_thrown = false; \
+ try { \
+ code; \
+ } catch (EType &e) { \
+ exception_thrown = true; \
+ } \
+ UASSERT(exception_thrown); \
+} while (0)
+
+class IGameDef;
+
+class TestBase {
+public:
+ bool testModule(IGameDef *gamedef);
+ std::string getTestTempDirectory();
+ std::string getTestTempFile();
+
+ virtual void runTests(IGameDef *gamedef) = 0;
+ virtual const char *getName() = 0;
+
+ u32 num_tests_failed;
+ u32 num_tests_run;
+
+private:
+ std::string m_test_dir;
+};
+
+class TestManager {
+public:
+ static std::vector<TestBase *> &getTestModules()
+ {
+ static std::vector<TestBase *> m_modules_to_test;
+ return m_modules_to_test;
+ }
+
+ static void registerTestModule(TestBase *module)
+ {
+ getTestModules().push_back(module);
+ }
+};
+
+// A few item and node definitions for those tests that need them
+extern content_t t_CONTENT_STONE;
+extern content_t t_CONTENT_GRASS;
+extern content_t t_CONTENT_TORCH;
+extern content_t t_CONTENT_WATER;
+extern content_t t_CONTENT_LAVA;
+extern content_t t_CONTENT_BRICK;
+
+void run_tests();
+
+#endif
diff --git a/src/unittest/test_areastore.cpp b/src/unittest/test_areastore.cpp
new file mode 100644
index 000000000..a0dcada94
--- /dev/null
+++ b/src/unittest/test_areastore.cpp
@@ -0,0 +1,129 @@
+/*
+Minetest
+Copyright (C) 2015 est31, <MTest31@outlook.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.
+*/
+
+#include "test.h"
+
+#include "areastore.h"
+
+class TestAreaStore : public TestBase {
+public:
+ TestAreaStore() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestAreaStore"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void genericStoreTest(AreaStore *store);
+ void testVectorStore();
+ void testSpatialStore();
+};
+
+static TestAreaStore g_test_instance;
+
+void TestAreaStore::runTests(IGameDef *gamedef)
+{
+ TEST(testVectorStore);
+#if USE_SPATIAL
+ TEST(testSpatialStore);
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestAreaStore::testVectorStore()
+{
+ VectorAreaStore store;
+ genericStoreTest(&store);
+}
+
+void TestAreaStore::testSpatialStore()
+{
+#if USE_SPATIAL
+ SpatialAreaStore store;
+ genericStoreTest(&store);
+#endif
+}
+
+void TestAreaStore::genericStoreTest(AreaStore *store)
+{
+ Area a(v3s16(-10, -3, 5), v3s16(0, 29, 7));
+ a.id = 1;
+ Area b(v3s16(-5, -2, 5), v3s16(0, 28, 6));
+ b.id = 2;
+ Area c(v3s16(-7, -3, 6), v3s16(-1, 27, 7));
+ c.id = 3;
+ std::vector<Area *> res;
+
+ UASSERTEQ(size_t, store->size(), 0);
+ store->reserve(2); // sic
+ store->insertArea(a);
+ store->insertArea(b);
+ store->insertArea(c);
+ UASSERTEQ(size_t, store->size(), 3);
+
+ store->getAreasForPos(&res, v3s16(-1, 0, 6));
+ UASSERTEQ(size_t, res.size(), 3);
+ res.clear();
+ store->getAreasForPos(&res, v3s16(0, 0, 7));
+ UASSERTEQ(size_t, res.size(), 1);
+ UASSERTEQ(u32, res[0]->id, 1);
+ res.clear();
+
+ store->removeArea(1);
+
+ store->getAreasForPos(&res, v3s16(0, 0, 7));
+ UASSERTEQ(size_t, res.size(), 0);
+ res.clear();
+
+ store->insertArea(a);
+
+ store->getAreasForPos(&res, v3s16(0, 0, 7));
+ UASSERTEQ(size_t, res.size(), 1);
+ UASSERTEQ(u32, res[0]->id, 1);
+ res.clear();
+
+ store->getAreasInArea(&res, v3s16(-10, -3, 5), v3s16(0, 29, 7), false);
+ UASSERTEQ(size_t, res.size(), 3);
+ res.clear();
+
+ store->getAreasInArea(&res, v3s16(-100, 0, 6), v3s16(200, 0, 6), false);
+ UASSERTEQ(size_t, res.size(), 0);
+ res.clear();
+
+ store->getAreasInArea(&res, v3s16(-100, 0, 6), v3s16(200, 0, 6), true);
+ UASSERTEQ(size_t, res.size(), 3);
+ res.clear();
+
+ store->removeArea(1);
+ store->removeArea(2);
+ store->removeArea(3);
+
+ Area d(v3s16(-100, -300, -200), v3s16(-50, -200, -100));
+ d.id = 4;
+ d.data = "Hi!";
+ store->insertArea(d);
+
+ store->getAreasForPos(&res, v3s16(-75, -250, -150));
+ UASSERTEQ(size_t, res.size(), 1);
+ UASSERTEQ(u32, res[0]->id, 4);
+ UASSERTEQ(u16, res[0]->data.size(), 3);
+ UASSERT(strncmp(res[0]->data.c_str(), "Hi!", 3) == 0);
+ res.clear();
+
+ store->removeArea(4);
+}
diff --git a/src/unittest/test_collision.cpp b/src/unittest/test_collision.cpp
new file mode 100644
index 000000000..e505de450
--- /dev/null
+++ b/src/unittest/test_collision.cpp
@@ -0,0 +1,180 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "collision.h"
+
+class TestCollision : public TestBase {
+public:
+ TestCollision() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestCollision"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testAxisAlignedCollision();
+};
+
+static TestCollision g_test_instance;
+
+void TestCollision::runTests(IGameDef *gamedef)
+{
+ TEST(testAxisAlignedCollision);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestCollision::testAxisAlignedCollision()
+{
+ for (s16 bx = -3; bx <= 3; bx++)
+ for (s16 by = -3; by <= 3; by++)
+ for (s16 bz = -3; bz <= 3; bz++) {
+ // X-
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
+ v3f v(1, 0, 0);
+ f32 dtime = 0;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 1.000) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx-2, by, bz, bx-1, by+1, bz+1);
+ v3f v(-1, 0, 0);
+ f32 dtime = 0;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx-2, by+1.5, bz, bx-1, by+2.5, bz-1);
+ v3f v(1, 0, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
+ v3f v(0.5, 0.1, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 3.000) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx-2, by-1.5, bz, bx-1.5, by+0.5, bz+1);
+ v3f v(0.5, 0.1, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 3.000) < 0.001);
+ }
+
+ // X+
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
+ v3f v(-1, 0, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 1.000) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx+2, by, bz, bx+3, by+1, bz+1);
+ v3f v(1, 0, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx+2, by, bz+1.5, bx+3, by+1, bz+3.5);
+ v3f v(-1, 0, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == -1);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
+ v3f v(-0.5, 0.2, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1); // Y, not X!
+ UASSERT(fabs(dtime - 2.500) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+1, by+1, bz+1);
+ aabb3f m(bx+2, by-1.5, bz, bx+2.5, by-0.5, bz+1);
+ v3f v(-0.5, 0.3, 0);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 2.000) < 0.001);
+ }
+
+ // TODO: Y-, Y+, Z-, Z+
+
+ // misc
+ {
+ aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
+ aabb3f m(bx+2.3, by+2.29, bz+2.29, bx+4.2, by+4.2, bz+4.2);
+ v3f v(-1./3, -1./3, -1./3);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 0.9) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
+ aabb3f m(bx+2.29, by+2.3, bz+2.29, bx+4.2, by+4.2, bz+4.2);
+ v3f v(-1./3, -1./3, -1./3);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1);
+ UASSERT(fabs(dtime - 0.9) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
+ aabb3f m(bx+2.29, by+2.29, bz+2.3, bx+4.2, by+4.2, bz+4.2);
+ v3f v(-1./3, -1./3, -1./3);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2);
+ UASSERT(fabs(dtime - 0.9) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
+ aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.3, by-2.29, bz-2.29);
+ v3f v(1./7, 1./7, 1./7);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 0);
+ UASSERT(fabs(dtime - 16.1) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
+ aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.3, bz-2.29);
+ v3f v(1./7, 1./7, 1./7);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 1);
+ UASSERT(fabs(dtime - 16.1) < 0.001);
+ }
+ {
+ aabb3f s(bx, by, bz, bx+2, by+2, bz+2);
+ aabb3f m(bx-4.2, by-4.2, bz-4.2, bx-2.29, by-2.29, bz-2.3);
+ v3f v(1./7, 1./7, 1./7);
+ f32 dtime;
+ UASSERT(axisAlignedCollision(s, m, v, 0, dtime) == 2);
+ UASSERT(fabs(dtime - 16.1) < 0.001);
+ }
+ }
+}
diff --git a/src/unittest/test_compression.cpp b/src/unittest/test_compression.cpp
new file mode 100644
index 000000000..a3132aa17
--- /dev/null
+++ b/src/unittest/test_compression.cpp
@@ -0,0 +1,172 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include <sstream>
+
+#include "irrlichttypes_extrabloated.h"
+#include "log.h"
+#include "serialization.h"
+#include "nodedef.h"
+#include "noise.h"
+
+class TestCompression : public TestBase {
+public:
+ TestCompression() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestCompression"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testRLECompression();
+ void testZlibCompression();
+ void testZlibLargeData();
+};
+
+static TestCompression g_test_instance;
+
+void TestCompression::runTests(IGameDef *gamedef)
+{
+ TEST(testRLECompression);
+ TEST(testZlibCompression);
+ TEST(testZlibLargeData);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestCompression::testRLECompression()
+{
+ SharedBuffer<u8> fromdata(4);
+ fromdata[0]=1;
+ fromdata[1]=5;
+ fromdata[2]=5;
+ fromdata[3]=1;
+
+ std::ostringstream os(std::ios_base::binary);
+ compress(fromdata, os, 0);
+
+ std::string str_out = os.str();
+
+ infostream << "str_out.size()="<<str_out.size()<<std::endl;
+ infostream << "TestCompress: 1,5,5,1 -> ";
+ for (u32 i = 0; i < str_out.size(); i++)
+ infostream << (u32)str_out[i] << ",";
+ infostream << std::endl;
+
+ UASSERT(str_out.size() == 10);
+
+ UASSERT(str_out[0] == 0);
+ UASSERT(str_out[1] == 0);
+ UASSERT(str_out[2] == 0);
+ UASSERT(str_out[3] == 4);
+ UASSERT(str_out[4] == 0);
+ UASSERT(str_out[5] == 1);
+ UASSERT(str_out[6] == 1);
+ UASSERT(str_out[7] == 5);
+ UASSERT(str_out[8] == 0);
+ UASSERT(str_out[9] == 1);
+
+ std::istringstream is(str_out, std::ios_base::binary);
+ std::ostringstream os2(std::ios_base::binary);
+
+ decompress(is, os2, 0);
+ std::string str_out2 = os2.str();
+
+ infostream << "decompress: ";
+ for (u32 i = 0; i < str_out2.size(); i++)
+ infostream << (u32)str_out2[i] << ",";
+ infostream << std::endl;
+
+ UASSERTEQ(size_t, str_out2.size(), fromdata.getSize());
+
+ for (u32 i = 0; i < str_out2.size(); i++)
+ UASSERT(str_out2[i] == fromdata[i]);
+}
+
+void TestCompression::testZlibCompression()
+{
+ SharedBuffer<u8> fromdata(4);
+ fromdata[0]=1;
+ fromdata[1]=5;
+ fromdata[2]=5;
+ fromdata[3]=1;
+
+ std::ostringstream os(std::ios_base::binary);
+ compress(fromdata, os, SER_FMT_VER_HIGHEST_READ);
+
+ std::string str_out = os.str();
+
+ infostream << "str_out.size()=" << str_out.size() <<std::endl;
+ infostream << "TestCompress: 1,5,5,1 -> ";
+ for (u32 i = 0; i < str_out.size(); i++)
+ infostream << (u32)str_out[i] << ",";
+ infostream << std::endl;
+
+ std::istringstream is(str_out, std::ios_base::binary);
+ std::ostringstream os2(std::ios_base::binary);
+
+ decompress(is, os2, SER_FMT_VER_HIGHEST_READ);
+ std::string str_out2 = os2.str();
+
+ infostream << "decompress: ";
+ for (u32 i = 0; i < str_out2.size(); i++)
+ infostream << (u32)str_out2[i] << ",";
+ infostream << std::endl;
+
+ UASSERTEQ(size_t, str_out2.size(), fromdata.getSize());
+
+ for (u32 i = 0; i < str_out2.size(); i++)
+ UASSERT(str_out2[i] == fromdata[i]);
+}
+
+void TestCompression::testZlibLargeData()
+{
+ infostream << "Test: Testing zlib wrappers with a large amount "
+ "of pseudorandom data" << std::endl;
+
+ u32 size = 50000;
+ infostream << "Test: Input size of large compressZlib is "
+ << size << std::endl;
+
+ std::string data_in;
+ data_in.resize(size);
+ PseudoRandom pseudorandom(9420);
+ for (u32 i = 0; i < size; i++)
+ data_in[i] = pseudorandom.range(0, 255);
+
+ std::ostringstream os_compressed(std::ios::binary);
+ compressZlib(data_in, os_compressed);
+ infostream << "Test: Output size of large compressZlib is "
+ << os_compressed.str().size()<<std::endl;
+
+ std::istringstream is_compressed(os_compressed.str(), std::ios::binary);
+ std::ostringstream os_decompressed(std::ios::binary);
+ decompressZlib(is_compressed, os_decompressed);
+ infostream << "Test: Output size of large decompressZlib is "
+ << os_decompressed.str().size() << std::endl;
+
+ std::string str_decompressed = os_decompressed.str();
+ UASSERTEQ(size_t, str_decompressed.size(), data_in.size());
+
+ for (u32 i = 0; i < size && i < str_decompressed.size(); i++) {
+ UTEST(str_decompressed[i] == data_in[i],
+ "index out[%i]=%i differs from in[%i]=%i",
+ i, str_decompressed[i], i, data_in[i]);
+ }
+}
diff --git a/src/unittest/test_connection.cpp b/src/unittest/test_connection.cpp
new file mode 100644
index 000000000..49e412fc8
--- /dev/null
+++ b/src/unittest/test_connection.cpp
@@ -0,0 +1,348 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "log.h"
+#include "socket.h"
+#include "settings.h"
+#include "util/serialize.h"
+#include "network/connection.h"
+
+class TestConnection : public TestBase {
+public:
+ TestConnection()
+ {
+ if (INTERNET_SIMULATOR == false)
+ TestManager::registerTestModule(this);
+ }
+
+ const char *getName() { return "TestConnection"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testHelpers();
+ void testConnectSendReceive();
+};
+
+static TestConnection g_test_instance;
+
+void TestConnection::runTests(IGameDef *gamedef)
+{
+ TEST(testHelpers);
+ TEST(testConnectSendReceive);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct Handler : public con::PeerHandler
+{
+ Handler(const char *a_name)
+ {
+ count = 0;
+ last_id = 0;
+ name = a_name;
+ }
+
+ void peerAdded(con::Peer *peer)
+ {
+ infostream << "Handler(" << name << ")::peerAdded(): "
+ "id=" << peer->id << std::endl;
+ last_id = peer->id;
+ count++;
+ }
+
+ void deletingPeer(con::Peer *peer, bool timeout)
+ {
+ infostream << "Handler(" << name << ")::deletingPeer(): "
+ "id=" << peer->id << ", timeout=" << timeout << std::endl;
+ last_id = peer->id;
+ count--;
+ }
+
+ s32 count;
+ u16 last_id;
+ const char *name;
+};
+
+void TestConnection::testHelpers()
+{
+ // Some constants for testing
+ u32 proto_id = 0x12345678;
+ u16 peer_id = 123;
+ u8 channel = 2;
+ SharedBuffer<u8> data1(1);
+ data1[0] = 100;
+ Address a(127,0,0,1, 10);
+ const u16 seqnum = 34352;
+
+ con::BufferedPacket p1 = con::makePacket(a, data1,
+ proto_id, peer_id, channel);
+ /*
+ We should now have a packet with this data:
+ Header:
+ [0] u32 protocol_id
+ [4] u16 sender_peer_id
+ [6] u8 channel
+ Data:
+ [7] u8 data1[0]
+ */
+ UASSERT(readU32(&p1.data[0]) == proto_id);
+ UASSERT(readU16(&p1.data[4]) == peer_id);
+ UASSERT(readU8(&p1.data[6]) == channel);
+ UASSERT(readU8(&p1.data[7]) == data1[0]);
+
+ //infostream<<"initial data1[0]="<<((u32)data1[0]&0xff)<<std::endl;
+
+ SharedBuffer<u8> p2 = con::makeReliablePacket(data1, seqnum);
+
+ /*infostream<<"p2.getSize()="<<p2.getSize()<<", data1.getSize()="
+ <<data1.getSize()<<std::endl;
+ infostream<<"readU8(&p2[3])="<<readU8(&p2[3])
+ <<" p2[3]="<<((u32)p2[3]&0xff)<<std::endl;
+ infostream<<"data1[0]="<<((u32)data1[0]&0xff)<<std::endl;*/
+
+ UASSERT(p2.getSize() == 3 + data1.getSize());
+ UASSERT(readU8(&p2[0]) == TYPE_RELIABLE);
+ UASSERT(readU16(&p2[1]) == seqnum);
+ UASSERT(readU8(&p2[3]) == data1[0]);
+}
+
+
+void TestConnection::testConnectSendReceive()
+{
+ DSTACK("TestConnection::Run");
+
+ /*
+ Test some real connections
+
+ NOTE: This mostly tests the legacy interface.
+ */
+
+ u32 proto_id = 0xad26846a;
+
+ Handler hand_server("server");
+ Handler hand_client("client");
+
+ Address address(0, 0, 0, 0, 30001);
+ Address bind_addr(0, 0, 0, 0, 30001);
+ /*
+ * Try to use the bind_address for servers with no localhost address
+ * For example: FreeBSD jails
+ */
+ std::string bind_str = g_settings->get("bind_address");
+ try {
+ bind_addr.Resolve(bind_str.c_str());
+
+ if (!bind_addr.isIPv6()) {
+ address = bind_addr;
+ }
+ } catch (ResolveError &e) {
+ }
+
+ infostream << "** Creating server Connection" << std::endl;
+ con::Connection server(proto_id, 512, 5.0, false, &hand_server);
+ server.Serve(address);
+
+ infostream << "** Creating client Connection" << std::endl;
+ con::Connection client(proto_id, 512, 5.0, false, &hand_client);
+
+ UASSERT(hand_server.count == 0);
+ UASSERT(hand_client.count == 0);
+
+ sleep_ms(50);
+
+ Address server_address(127, 0, 0, 1, 30001);
+ if (address != Address(0, 0, 0, 0, 30001)) {
+ server_address = bind_addr;
+ }
+
+ infostream << "** running client.Connect()" << std::endl;
+ client.Connect(server_address);
+
+ sleep_ms(50);
+
+ // Client should not have added client yet
+ UASSERT(hand_client.count == 0);
+
+ try {
+ NetworkPacket pkt;
+ infostream << "** running client.Receive()" << std::endl;
+ client.Receive(&pkt);
+ infostream << "** Client received: peer_id=" << pkt.getPeerId()
+ << ", size=" << pkt.getSize() << std::endl;
+ } catch (con::NoIncomingDataException &e) {
+ }
+
+ // Client should have added server now
+ UASSERT(hand_client.count == 1);
+ UASSERT(hand_client.last_id == 1);
+ // Server should not have added client yet
+ UASSERT(hand_server.count == 0);
+
+ sleep_ms(100);
+
+ try {
+ NetworkPacket pkt;
+ infostream << "** running server.Receive()" << std::endl;
+ server.Receive(&pkt);
+ infostream << "** Server received: peer_id=" << pkt.getPeerId()
+ << ", size=" << pkt.getSize()
+ << std::endl;
+ } catch (con::NoIncomingDataException &e) {
+ // No actual data received, but the client has
+ // probably been connected
+ }
+
+ // Client should be the same
+ UASSERT(hand_client.count == 1);
+ UASSERT(hand_client.last_id == 1);
+ // Server should have the client
+ UASSERT(hand_server.count == 1);
+ UASSERT(hand_server.last_id == 2);
+
+ //sleep_ms(50);
+
+ while (client.Connected() == false) {
+ try {
+ NetworkPacket pkt;
+ infostream << "** running client.Receive()" << std::endl;
+ client.Receive(&pkt);
+ infostream << "** Client received: peer_id=" << pkt.getPeerId()
+ << ", size=" << pkt.getSize() << std::endl;
+ } catch (con::NoIncomingDataException &e) {
+ }
+ sleep_ms(50);
+ }
+
+ sleep_ms(50);
+
+ try {
+ NetworkPacket pkt;
+ infostream << "** running server.Receive()" << std::endl;
+ server.Receive(&pkt);
+ infostream << "** Server received: peer_id=" << pkt.getPeerId()
+ << ", size=" << pkt.getSize()
+ << std::endl;
+ } catch (con::NoIncomingDataException &e) {
+ }
+
+ /*
+ Simple send-receive test
+ */
+ {
+ NetworkPacket pkt;
+ pkt.putRawPacket((u8*) "Hello World !", 14, 0);
+
+ Buffer<u8> sentdata = pkt.oldForgePacket();
+
+ infostream<<"** running client.Send()"<<std::endl;
+ client.Send(PEER_ID_SERVER, 0, &pkt, true);
+
+ sleep_ms(50);
+
+ NetworkPacket recvpacket;
+ infostream << "** running server.Receive()" << std::endl;
+ server.Receive(&recvpacket);
+ infostream << "** Server received: peer_id=" << pkt.getPeerId()
+ << ", size=" << pkt.getSize()
+ << ", data=" << (const char*)pkt.getU8Ptr(0)
+ << std::endl;
+
+ Buffer<u8> recvdata = pkt.oldForgePacket();
+
+ UASSERT(memcmp(*sentdata, *recvdata, recvdata.getSize()) == 0);
+ }
+
+ u16 peer_id_client = 2;
+ /*
+ Send a large packet
+ */
+ {
+ const int datasize = 30000;
+ NetworkPacket pkt(0, datasize);
+ for (u16 i=0; i<datasize; i++) {
+ pkt << (u8) i/4;
+ }
+
+ infostream << "Sending data (size=" << datasize << "):";
+ for (int i = 0; i < datasize && i < 20; i++) {
+ if (i % 2 == 0)
+ infostream << " ";
+ char buf[10];
+ snprintf(buf, 10, "%.2X",
+ ((int)((const char *)pkt.getU8Ptr(0))[i]) & 0xff);
+ infostream<<buf;
+ }
+ if (datasize > 20)
+ infostream << "...";
+ infostream << std::endl;
+
+ Buffer<u8> sentdata = pkt.oldForgePacket();
+
+ server.Send(peer_id_client, 0, &pkt, true);
+
+ //sleep_ms(3000);
+
+ Buffer<u8> recvdata;
+ infostream << "** running client.Receive()" << std::endl;
+ u16 peer_id = 132;
+ u16 size = 0;
+ bool received = false;
+ u32 timems0 = porting::getTimeMs();
+ for (;;) {
+ if (porting::getTimeMs() - timems0 > 5000 || received)
+ break;
+ try {
+ NetworkPacket pkt;
+ client.Receive(&pkt);
+ size = pkt.getSize();
+ peer_id = pkt.getPeerId();
+ recvdata = pkt.oldForgePacket();
+ received = true;
+ } catch (con::NoIncomingDataException &e) {
+ }
+ sleep_ms(10);
+ }
+ UASSERT(received);
+ infostream << "** Client received: peer_id=" << peer_id
+ << ", size=" << size << std::endl;
+
+ infostream << "Received data (size=" << size << "): ";
+ for (int i = 0; i < size && i < 20; i++) {
+ if (i % 2 == 0)
+ infostream << " ";
+ char buf[10];
+ snprintf(buf, 10, "%.2X", ((int)(recvdata[i])) & 0xff);
+ infostream << buf;
+ }
+ if (size > 20)
+ infostream << "...";
+ infostream << std::endl;
+
+ UASSERT(memcmp(*sentdata, *recvdata, recvdata.getSize()) == 0);
+ UASSERT(peer_id == PEER_ID_SERVER);
+ }
+
+ // Check peer handlers
+ UASSERT(hand_client.count == 1);
+ UASSERT(hand_client.last_id == 1);
+ UASSERT(hand_server.count == 1);
+ UASSERT(hand_server.last_id == 2);
+}
diff --git a/src/unittest/test_filepath.cpp b/src/unittest/test_filepath.cpp
new file mode 100644
index 000000000..6ea7ac076
--- /dev/null
+++ b/src/unittest/test_filepath.cpp
@@ -0,0 +1,261 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include <sstream>
+
+#include "log.h"
+#include "serialization.h"
+#include "nodedef.h"
+#include "noise.h"
+
+class TestFilePath : public TestBase {
+public:
+ TestFilePath() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestFilePath"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testIsDirDelimiter();
+ void testPathStartsWith();
+ void testRemoveLastPathComponent();
+ void testRemoveLastPathComponentWithTrailingDelimiter();
+ void testRemoveRelativePathComponent();
+};
+
+static TestFilePath g_test_instance;
+
+void TestFilePath::runTests(IGameDef *gamedef)
+{
+ TEST(testIsDirDelimiter);
+ TEST(testPathStartsWith);
+ TEST(testRemoveLastPathComponent);
+ TEST(testRemoveLastPathComponentWithTrailingDelimiter);
+ TEST(testRemoveRelativePathComponent);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// adjusts a POSIX path to system-specific conventions
+// -> changes '/' to DIR_DELIM
+// -> absolute paths start with "C:\\" on windows
+std::string p(std::string path)
+{
+ for (size_t i = 0; i < path.size(); ++i) {
+ if (path[i] == '/') {
+ path.replace(i, 1, DIR_DELIM);
+ i += std::string(DIR_DELIM).size() - 1; // generally a no-op
+ }
+ }
+
+ #ifdef _WIN32
+ if (path[0] == '\\')
+ path = "C:" + path;
+ #endif
+
+ return path;
+}
+
+
+void TestFilePath::testIsDirDelimiter()
+{
+ UASSERT(fs::IsDirDelimiter('/') == true);
+ UASSERT(fs::IsDirDelimiter('A') == false);
+ UASSERT(fs::IsDirDelimiter(0) == false);
+#ifdef _WIN32
+ UASSERT(fs::IsDirDelimiter('\\') == true);
+#else
+ UASSERT(fs::IsDirDelimiter('\\') == false);
+#endif
+}
+
+
+void TestFilePath::testPathStartsWith()
+{
+ const int numpaths = 12;
+ std::string paths[numpaths] = {
+ "",
+ p("/"),
+ p("/home/user/minetest"),
+ p("/home/user/minetest/bin"),
+ p("/home/user/.minetest"),
+ p("/tmp/dir/file"),
+ p("/tmp/file/"),
+ p("/tmP/file"),
+ p("/tmp"),
+ p("/tmp/dir"),
+ p("/home/user2/minetest/worlds"),
+ p("/home/user2/minetest/world"),
+ };
+ /*
+ expected fs::PathStartsWith results
+ 0 = returns false
+ 1 = returns true
+ 2 = returns false on windows, true elsewhere
+ 3 = returns true on windows, false elsewhere
+ 4 = returns true if and only if
+ FILESYS_CASE_INSENSITIVE is true
+ */
+ int expected_results[numpaths][numpaths] = {
+ {1,2,0,0,0,0,0,0,0,0,0,0},
+ {1,1,0,0,0,0,0,0,0,0,0,0},
+ {1,1,1,0,0,0,0,0,0,0,0,0},
+ {1,1,1,1,0,0,0,0,0,0,0,0},
+ {1,1,0,0,1,0,0,0,0,0,0,0},
+ {1,1,0,0,0,1,0,0,1,1,0,0},
+ {1,1,0,0,0,0,1,4,1,0,0,0},
+ {1,1,0,0,0,0,4,1,4,0,0,0},
+ {1,1,0,0,0,0,0,0,1,0,0,0},
+ {1,1,0,0,0,0,0,0,1,1,0,0},
+ {1,1,0,0,0,0,0,0,0,0,1,0},
+ {1,1,0,0,0,0,0,0,0,0,0,1},
+ };
+
+ for (int i = 0; i < numpaths; i++)
+ for (int j = 0; j < numpaths; j++){
+ /*verbosestream<<"testing fs::PathStartsWith(\""
+ <<paths[i]<<"\", \""
+ <<paths[j]<<"\")"<<std::endl;*/
+ bool starts = fs::PathStartsWith(paths[i], paths[j]);
+ int expected = expected_results[i][j];
+ if(expected == 0){
+ UASSERT(starts == false);
+ }
+ else if(expected == 1){
+ UASSERT(starts == true);
+ }
+ #ifdef _WIN32
+ else if(expected == 2){
+ UASSERT(starts == false);
+ }
+ else if(expected == 3){
+ UASSERT(starts == true);
+ }
+ #else
+ else if(expected == 2){
+ UASSERT(starts == true);
+ }
+ else if(expected == 3){
+ UASSERT(starts == false);
+ }
+ #endif
+ else if(expected == 4){
+ UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE);
+ }
+ }
+}
+
+
+void TestFilePath::testRemoveLastPathComponent()
+{
+ std::string path, result, removed;
+
+ UASSERT(fs::RemoveLastPathComponent("") == "");
+ path = p("/home/user/minetest/bin/..//worlds/world1");
+ result = fs::RemoveLastPathComponent(path, &removed, 0);
+ UASSERT(result == path);
+ UASSERT(removed == "");
+ result = fs::RemoveLastPathComponent(path, &removed, 1);
+ UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
+ UASSERT(removed == p("world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 2);
+ UASSERT(result == p("/home/user/minetest/bin/.."));
+ UASSERT(removed == p("worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 3);
+ UASSERT(result == p("/home/user/minetest/bin"));
+ UASSERT(removed == p("../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 4);
+ UASSERT(result == p("/home/user/minetest"));
+ UASSERT(removed == p("bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 5);
+ UASSERT(result == p("/home/user"));
+ UASSERT(removed == p("minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 6);
+ UASSERT(result == p("/home"));
+ UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 7);
+#ifdef _WIN32
+ UASSERT(result == "C:");
+#else
+ UASSERT(result == "");
+#endif
+ UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
+}
+
+
+void TestFilePath::testRemoveLastPathComponentWithTrailingDelimiter()
+{
+ std::string path, result, removed;
+
+ path = p("/home/user/minetest/bin/..//worlds/world1/");
+ result = fs::RemoveLastPathComponent(path, &removed, 0);
+ UASSERT(result == path);
+ UASSERT(removed == "");
+ result = fs::RemoveLastPathComponent(path, &removed, 1);
+ UASSERT(result == p("/home/user/minetest/bin/..//worlds"));
+ UASSERT(removed == p("world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 2);
+ UASSERT(result == p("/home/user/minetest/bin/.."));
+ UASSERT(removed == p("worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 3);
+ UASSERT(result == p("/home/user/minetest/bin"));
+ UASSERT(removed == p("../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 4);
+ UASSERT(result == p("/home/user/minetest"));
+ UASSERT(removed == p("bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 5);
+ UASSERT(result == p("/home/user"));
+ UASSERT(removed == p("minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 6);
+ UASSERT(result == p("/home"));
+ UASSERT(removed == p("user/minetest/bin/../worlds/world1"));
+ result = fs::RemoveLastPathComponent(path, &removed, 7);
+#ifdef _WIN32
+ UASSERT(result == "C:");
+#else
+ UASSERT(result == "");
+#endif
+ UASSERT(removed == p("home/user/minetest/bin/../worlds/world1"));
+}
+
+
+void TestFilePath::testRemoveRelativePathComponent()
+{
+ std::string path, result, removed;
+
+ path = p("/home/user/minetest/bin");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == path);
+ path = p("/home/user/minetest/bin/../worlds/world1");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == p("/home/user/minetest/worlds/world1"));
+ path = p("/home/user/minetest/bin/../worlds/world1/");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == p("/home/user/minetest/worlds/world1"));
+ path = p(".");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == "");
+ path = p("./subdir/../..");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == "");
+ path = p("/a/b/c/.././../d/../e/f/g/../h/i/j/../../../..");
+ result = fs::RemoveRelativePathComponents(path);
+ UASSERT(result == p("/a/e"));
+}
diff --git a/src/unittest/test_inventory.cpp b/src/unittest/test_inventory.cpp
new file mode 100644
index 000000000..1a783afae
--- /dev/null
+++ b/src/unittest/test_inventory.cpp
@@ -0,0 +1,143 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include <sstream>
+
+#include "gamedef.h"
+#include "inventory.h"
+
+class TestInventory : public TestBase {
+public:
+ TestInventory() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestInventory"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testSerializeDeserialize(IItemDefManager *idef);
+
+ static const char *serialized_inventory;
+ static const char *serialized_inventory_2;
+};
+
+static TestInventory g_test_instance;
+
+void TestInventory::runTests(IGameDef *gamedef)
+{
+ TEST(testSerializeDeserialize, gamedef->getItemDefManager());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestInventory::testSerializeDeserialize(IItemDefManager *idef)
+{
+ Inventory inv(idef);
+ std::istringstream is(serialized_inventory, std::ios::binary);
+
+ inv.deSerialize(is);
+ UASSERT(inv.getList("0"));
+ UASSERT(!inv.getList("main"));
+
+ inv.getList("0")->setName("main");
+ UASSERT(!inv.getList("0"));
+ UASSERT(inv.getList("main"));
+ UASSERTEQ(u32, inv.getList("main")->getWidth(), 3);
+
+ inv.getList("main")->setWidth(5);
+ std::ostringstream inv_os(std::ios::binary);
+ inv.serialize(inv_os);
+ UASSERTEQ(std::string, inv_os.str(), serialized_inventory_2);
+}
+
+const char *TestInventory::serialized_inventory =
+ "List 0 32\n"
+ "Width 3\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Item default:cobble 61\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Item default:dirt 71\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Item default:dirt 99\n"
+ "Item default:cobble 38\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "EndInventoryList\n"
+ "EndInventory\n";
+
+const char *TestInventory::serialized_inventory_2 =
+ "List main 32\n"
+ "Width 5\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Item default:cobble 61\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Item default:dirt 71\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Item default:dirt 99\n"
+ "Item default:cobble 38\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "Empty\n"
+ "EndInventoryList\n"
+ "EndInventory\n";
diff --git a/src/unittest/test_mapnode.cpp b/src/unittest/test_mapnode.cpp
new file mode 100644
index 000000000..9ecc2f82d
--- /dev/null
+++ b/src/unittest/test_mapnode.cpp
@@ -0,0 +1,56 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "gamedef.h"
+#include "nodedef.h"
+#include "content_mapnode.h"
+
+class TestMapNode : public TestBase {
+public:
+ TestMapNode() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestMapNode"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testNodeProperties(INodeDefManager *nodedef);
+};
+
+static TestMapNode g_test_instance;
+
+void TestMapNode::runTests(IGameDef *gamedef)
+{
+ TEST(testNodeProperties, gamedef->getNodeDefManager());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestMapNode::testNodeProperties(INodeDefManager *nodedef)
+{
+ MapNode n(CONTENT_AIR);
+
+ UASSERT(n.getContent() == CONTENT_AIR);
+ UASSERT(n.getLight(LIGHTBANK_DAY, nodedef) == 0);
+ UASSERT(n.getLight(LIGHTBANK_NIGHT, nodedef) == 0);
+
+ // Transparency
+ n.setContent(CONTENT_AIR);
+ UASSERT(nodedef->get(n).light_propagates == true);
+}
diff --git a/src/unittest/test_nodedef.cpp b/src/unittest/test_nodedef.cpp
new file mode 100644
index 000000000..85093f52f
--- /dev/null
+++ b/src/unittest/test_nodedef.cpp
@@ -0,0 +1,66 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include <sstream>
+
+#include "gamedef.h"
+#include "nodedef.h"
+#include "network/networkprotocol.h"
+
+class TestNodeDef : public TestBase {
+public:
+ TestNodeDef() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestNodeDef"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testContentFeaturesSerialization();
+};
+
+static TestNodeDef g_test_instance;
+
+void TestNodeDef::runTests(IGameDef *gamedef)
+{
+ TEST(testContentFeaturesSerialization);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestNodeDef::testContentFeaturesSerialization()
+{
+ ContentFeatures f;
+
+ f.name = "default:stone";
+ for (int i = 0; i < 6; i++)
+ f.tiledef[i].name = "default_stone.png";
+ f.is_ground_content = true;
+
+ std::ostringstream os(std::ios::binary);
+ f.serialize(os, LATEST_PROTOCOL_VERSION);
+ //verbosestream<<"Test ContentFeatures size: "<<os.str().size()<<std::endl;
+
+ std::istringstream is(os.str(), std::ios::binary);
+ ContentFeatures f2;
+ f2.deSerialize(is);
+
+ UASSERT(f.walkable == f2.walkable);
+ UASSERT(f.node_box.type == f2.node_box.type);
+}
diff --git a/src/unittest/test_noderesolver.cpp b/src/unittest/test_noderesolver.cpp
new file mode 100644
index 000000000..55acece6a
--- /dev/null
+++ b/src/unittest/test_noderesolver.cpp
@@ -0,0 +1,208 @@
+/*
+Minetest
+Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#include "test.h"
+
+#include "util/numeric.h"
+#include "exceptions.h"
+#include "gamedef.h"
+#include "nodedef.h"
+
+
+class TestNodeResolver : public TestBase {
+public:
+ TestNodeResolver() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestNodeResolver"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testNodeResolving(IWritableNodeDefManager *ndef);
+ void testPendingResolveCancellation(IWritableNodeDefManager *ndef);
+ void testDirectResolveMethod(IWritableNodeDefManager *ndef);
+ void testNoneResolveMethod(IWritableNodeDefManager *ndef);
+};
+
+static TestNodeResolver g_test_instance;
+
+void TestNodeResolver::runTests(IGameDef *gamedef)
+{
+ IWritableNodeDefManager *ndef =
+ (IWritableNodeDefManager *)gamedef->getNodeDefManager();
+
+ ndef->resetNodeResolveState();
+ TEST(testNodeResolving, ndef);
+
+ ndef->resetNodeResolveState();
+ TEST(testPendingResolveCancellation, ndef);
+}
+
+class Foobar : public NodeResolver {
+public:
+ void resolveNodeNames();
+
+ content_t test_nr_node1;
+ content_t test_nr_node2;
+ content_t test_nr_node3;
+ content_t test_nr_node4;
+ content_t test_nr_node5;
+ std::vector<content_t> test_nr_list;
+ std::vector<content_t> test_nr_list_group;
+ std::vector<content_t> test_nr_list_required;
+ std::vector<content_t> test_nr_list_empty;
+};
+
+class Foobaz : public NodeResolver {
+public:
+ void resolveNodeNames();
+
+ content_t test_content1;
+ content_t test_content2;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+void Foobar::resolveNodeNames()
+{
+ UASSERT(getIdFromNrBacklog(&test_nr_node1, "", CONTENT_IGNORE) == true);
+ UASSERT(getIdsFromNrBacklog(&test_nr_list) == true);
+ UASSERT(getIdsFromNrBacklog(&test_nr_list_group) == true);
+ UASSERT(getIdsFromNrBacklog(&test_nr_list_required,
+ true, CONTENT_AIR) == false);
+ UASSERT(getIdsFromNrBacklog(&test_nr_list_empty) == true);
+
+ UASSERT(getIdFromNrBacklog(&test_nr_node2, "", CONTENT_IGNORE) == true);
+ UASSERT(getIdFromNrBacklog(&test_nr_node3,
+ "default:brick", CONTENT_IGNORE) == true);
+ UASSERT(getIdFromNrBacklog(&test_nr_node4,
+ "default:gobbledygook", CONTENT_AIR) == false);
+ UASSERT(getIdFromNrBacklog(&test_nr_node5, "", CONTENT_IGNORE) == false);
+}
+
+
+void Foobaz::resolveNodeNames()
+{
+ UASSERT(getIdFromNrBacklog(&test_content1, "", CONTENT_IGNORE) == true);
+ UASSERT(getIdFromNrBacklog(&test_content2, "", CONTENT_IGNORE) == false);
+}
+
+
+void TestNodeResolver::testNodeResolving(IWritableNodeDefManager *ndef)
+{
+ Foobar foobar;
+ size_t i;
+
+ foobar.m_nodenames.push_back("default:torch");
+
+ foobar.m_nodenames.push_back("default:dirt_with_grass");
+ foobar.m_nodenames.push_back("default:water");
+ foobar.m_nodenames.push_back("default:abloobloobloo");
+ foobar.m_nodenames.push_back("default:stone");
+ foobar.m_nodenames.push_back("default:shmegoldorf");
+ foobar.m_nnlistsizes.push_back(5);
+
+ foobar.m_nodenames.push_back("group:liquids");
+ foobar.m_nnlistsizes.push_back(1);
+
+ foobar.m_nodenames.push_back("default:warf");
+ foobar.m_nodenames.push_back("default:stone");
+ foobar.m_nodenames.push_back("default:bloop");
+ foobar.m_nnlistsizes.push_back(3);
+
+ foobar.m_nnlistsizes.push_back(0);
+
+ foobar.m_nodenames.push_back("default:brick");
+ foobar.m_nodenames.push_back("default:desert_stone");
+ foobar.m_nodenames.push_back("default:shnitzle");
+
+ ndef->pendNodeResolve(&foobar);
+ UASSERT(foobar.m_ndef == ndef);
+
+ ndef->setNodeRegistrationStatus(true);
+ ndef->runNodeResolveCallbacks();
+
+ // Check that we read single nodes successfully
+ UASSERTEQ(content_t, foobar.test_nr_node1, t_CONTENT_TORCH);
+ UASSERTEQ(content_t, foobar.test_nr_node2, t_CONTENT_BRICK);
+ UASSERTEQ(content_t, foobar.test_nr_node3, t_CONTENT_BRICK);
+ UASSERTEQ(content_t, foobar.test_nr_node4, CONTENT_AIR);
+ UASSERTEQ(content_t, foobar.test_nr_node5, CONTENT_IGNORE);
+
+ // Check that we read all the regular list items
+ static const content_t expected_test_nr_list[] = {
+ t_CONTENT_GRASS,
+ t_CONTENT_WATER,
+ t_CONTENT_STONE,
+ };
+ UASSERTEQ(size_t, foobar.test_nr_list.size(), 3);
+ for (i = 0; i != foobar.test_nr_list.size(); i++)
+ UASSERTEQ(content_t, foobar.test_nr_list[i], expected_test_nr_list[i]);
+
+ // Check that we read all the list items that were from a group entry
+ static const content_t expected_test_nr_list_group[] = {
+ t_CONTENT_WATER,
+ t_CONTENT_LAVA,
+ };
+ UASSERTEQ(size_t, foobar.test_nr_list_group.size(), 2);
+ for (i = 0; i != foobar.test_nr_list_group.size(); i++) {
+ UASSERT(CONTAINS(foobar.test_nr_list_group,
+ expected_test_nr_list_group[i]));
+ }
+
+ // Check that we read all the items we're able to in a required list
+ static const content_t expected_test_nr_list_required[] = {
+ CONTENT_AIR,
+ t_CONTENT_STONE,
+ CONTENT_AIR,
+ };
+ UASSERTEQ(size_t, foobar.test_nr_list_required.size(), 3);
+ for (i = 0; i != foobar.test_nr_list_required.size(); i++)
+ UASSERTEQ(content_t, foobar.test_nr_list_required[i],
+ expected_test_nr_list_required[i]);
+
+ // Check that the edge case of 0 is successful
+ UASSERTEQ(size_t, foobar.test_nr_list_empty.size(), 0);
+}
+
+
+void TestNodeResolver::testPendingResolveCancellation(IWritableNodeDefManager *ndef)
+{
+ Foobaz foobaz1;
+ foobaz1.test_content1 = 1234;
+ foobaz1.test_content2 = 5678;
+ foobaz1.m_nodenames.push_back("default:dirt_with_grass");
+ foobaz1.m_nodenames.push_back("default:abloobloobloo");
+ ndef->pendNodeResolve(&foobaz1);
+
+ Foobaz foobaz2;
+ foobaz2.test_content1 = 1234;
+ foobaz2.test_content2 = 5678;
+ foobaz2.m_nodenames.push_back("default:dirt_with_grass");
+ foobaz2.m_nodenames.push_back("default:abloobloobloo");
+ ndef->pendNodeResolve(&foobaz2);
+
+ ndef->cancelNodeResolveCallback(&foobaz1);
+
+ ndef->setNodeRegistrationStatus(true);
+ ndef->runNodeResolveCallbacks();
+
+ UASSERT(foobaz1.test_content1 == 1234);
+ UASSERT(foobaz1.test_content2 == 5678);
+ UASSERT(foobaz2.test_content1 == t_CONTENT_GRASS);
+ UASSERT(foobaz2.test_content2 == CONTENT_IGNORE);
+}
diff --git a/src/unittest/test_noise.cpp b/src/unittest/test_noise.cpp
new file mode 100644
index 000000000..d1821c950
--- /dev/null
+++ b/src/unittest/test_noise.cpp
@@ -0,0 +1,285 @@
+/*
+Minetest
+Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#include "test.h"
+
+#include "exceptions.h"
+#include "noise.h"
+
+class TestNoise : public TestBase {
+public:
+ TestNoise() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestNoise"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testNoise2dPoint();
+ void testNoise2dBulk();
+ void testNoise3dPoint();
+ void testNoise3dBulk();
+ void testNoiseInvalidParams();
+
+ static const float expected_2d_results[10 * 10];
+ static const float expected_3d_results[10 * 10 * 10];
+};
+
+static TestNoise g_test_instance;
+
+void TestNoise::runTests(IGameDef *gamedef)
+{
+ TEST(testNoise2dPoint);
+ TEST(testNoise2dBulk);
+ TEST(testNoise3dPoint);
+ TEST(testNoise3dBulk);
+ TEST(testNoiseInvalidParams);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestNoise::testNoise2dPoint()
+{
+ NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
+
+ u32 i = 0;
+ for (u32 y = 0; y != 10; y++)
+ for (u32 x = 0; x != 10; x++, i++) {
+ float actual = NoisePerlin2D(&np_normal, x, y, 1337);
+ float expected = expected_2d_results[i];
+ UASSERT(fabs(actual - expected) <= 0.00001);
+ }
+}
+
+void TestNoise::testNoise2dBulk()
+{
+ NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
+ Noise noise_normal_2d(&np_normal, 1337, 10, 10);
+ float *noisevals = noise_normal_2d.perlinMap2D(0, 0, NULL);
+
+ for (u32 i = 0; i != 10 * 10; i++) {
+ float actual = noisevals[i];
+ float expected = expected_2d_results[i];
+ UASSERT(fabs(actual - expected) <= 0.00001);
+ }
+}
+
+void TestNoise::testNoise3dPoint()
+{
+ NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
+
+ u32 i = 0;
+ for (u32 z = 0; z != 10; z++)
+ for (u32 y = 0; y != 10; y++)
+ for (u32 x = 0; x != 10; x++, i++) {
+ float actual = NoisePerlin3D(&np_normal, x, y, z, 1337);
+ float expected = expected_3d_results[i];
+ UASSERT(fabs(actual - expected) <= 0.00001);
+ }
+}
+
+void TestNoise::testNoise3dBulk()
+{
+ NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
+ Noise noise_normal_3d(&np_normal, 1337, 10, 10, 10);
+ float *noisevals = noise_normal_3d.perlinMap3D(0, 0, 0, NULL);
+
+ for (u32 i = 0; i != 10 * 10 * 10; i++) {
+ float actual = noisevals[i];
+ float expected = expected_3d_results[i];
+ UASSERT(fabs(actual - expected) <= 0.00001);
+ }
+}
+
+void TestNoise::testNoiseInvalidParams()
+{
+ bool exception_thrown = false;
+
+ try {
+ NoiseParams np_highmem(4, 70, v3f(1, 1, 1), 5, 60, 0.7, 10.0);
+ Noise noise_highmem_3d(&np_highmem, 1337, 200, 200, 200);
+ noise_highmem_3d.perlinMap3D(0, 0, 0, NULL);
+ } catch (InvalidNoiseParamsException) {
+ exception_thrown = true;
+ }
+
+ UASSERT(exception_thrown);
+}
+
+const float TestNoise::expected_2d_results[10 * 10] = {
+ 19.11726, 18.49626, 16.48476, 15.02135, 14.75713, 16.26008, 17.54822,
+ 18.06860, 18.57016, 18.48407, 18.49649, 17.89160, 15.94162, 14.54901,
+ 14.31298, 15.72643, 16.94669, 17.55494, 18.58796, 18.87925, 16.08101,
+ 15.53764, 13.83844, 12.77139, 12.73648, 13.95632, 14.97904, 15.81829,
+ 18.37694, 19.73759, 13.19182, 12.71924, 11.34560, 10.78025, 11.18980,
+ 12.52303, 13.45012, 14.30001, 17.43298, 19.15244, 10.93217, 10.48625,
+ 9.30923, 9.18632, 10.16251, 12.11264, 13.19697, 13.80801, 16.39567,
+ 17.66203, 10.40222, 9.86070, 8.47223, 8.45471, 10.04780, 13.54730,
+ 15.33709, 15.48503, 16.46177, 16.52508, 10.80333, 10.19045, 8.59420,
+ 8.47646, 10.22676, 14.43173, 16.48353, 16.24859, 16.20863, 15.52847,
+ 11.01179, 10.45209, 8.98678, 8.83986, 10.43004, 14.46054, 16.29387,
+ 15.73521, 15.01744, 13.85542, 10.55201, 10.33375, 9.85102, 10.07821,
+ 11.58235, 15.62046, 17.35505, 16.13181, 12.66011, 9.51853, 11.50994,
+ 11.54074, 11.77989, 12.29790, 13.76139, 17.81982, 19.49008, 17.79470,
+ 12.34344, 7.78363,
+};
+
+const float TestNoise::expected_3d_results[10 * 10 * 10] = {
+ 19.11726, 18.01059, 16.90392, 15.79725, 16.37154, 17.18597, 18.00040,
+ 18.33467, 18.50889, 18.68311, 17.85386, 16.90585, 15.95785, 15.00985,
+ 15.61132, 16.43415, 17.25697, 17.95415, 18.60942, 19.26471, 16.59046,
+ 15.80112, 15.01178, 14.22244, 14.85110, 15.68232, 16.51355, 17.57361,
+ 18.70996, 19.84631, 15.32705, 14.69638, 14.06571, 13.43504, 14.09087,
+ 14.93050, 15.77012, 17.19309, 18.81050, 20.42790, 15.06729, 14.45855,
+ 13.84981, 13.24107, 14.39364, 15.79782, 17.20201, 18.42640, 19.59085,
+ 20.75530, 14.95090, 14.34456, 13.73821, 13.13187, 14.84825, 16.89645,
+ 18.94465, 19.89025, 20.46832, 21.04639, 14.83452, 14.23057, 13.62662,
+ 13.02267, 15.30287, 17.99508, 20.68730, 21.35411, 21.34580, 21.33748,
+ 15.39817, 15.03590, 14.67364, 14.31137, 16.78242, 19.65824, 22.53405,
+ 22.54626, 21.60395, 20.66164, 16.18850, 16.14768, 16.10686, 16.06603,
+ 18.60362, 21.50956, 24.41549, 23.64784, 21.65566, 19.66349, 16.97884,
+ 17.25946, 17.54008, 17.82069, 20.42482, 23.36088, 26.29694, 24.74942,
+ 21.70738, 18.66534, 18.78506, 17.51834, 16.25162, 14.98489, 15.14217,
+ 15.50287, 15.86357, 16.40597, 17.00895, 17.61193, 18.20160, 16.98795,
+ 15.77430, 14.56065, 14.85059, 15.35533, 15.86007, 16.63399, 17.49763,
+ 18.36128, 17.61814, 16.45757, 15.29699, 14.13641, 14.55902, 15.20779,
+ 15.85657, 16.86200, 17.98632, 19.11064, 17.03468, 15.92718, 14.81968,
+ 13.71218, 14.26744, 15.06026, 15.85306, 17.09001, 18.47501, 19.86000,
+ 16.67870, 15.86256, 15.04641, 14.23026, 15.31397, 16.66909, 18.02420,
+ 18.89042, 19.59369, 20.29695, 16.35522, 15.86447, 15.37372, 14.88297,
+ 16.55165, 18.52883, 20.50600, 20.91547, 20.80237, 20.68927, 16.03174,
+ 15.86639, 15.70103, 15.53568, 17.78933, 20.38857, 22.98780, 22.94051,
+ 22.01105, 21.08159, 16.42434, 16.61407, 16.80381, 16.99355, 19.16133,
+ 21.61169, 24.06204, 23.65252, 22.28970, 20.92689, 17.05562, 17.61035,
+ 18.16508, 18.71981, 20.57809, 22.62260, 24.66711, 23.92686, 22.25835,
+ 20.58984, 17.68691, 18.60663, 19.52635, 20.44607, 21.99486, 23.63352,
+ 25.27217, 24.20119, 22.22699, 20.25279, 18.45285, 17.02608, 15.59931,
+ 14.17254, 13.91279, 13.81976, 13.72674, 14.47727, 15.50900, 16.54073,
+ 18.54934, 17.07005, 15.59076, 14.11146, 14.08987, 14.27651, 14.46316,
+ 15.31383, 16.38584, 17.45785, 18.64582, 17.11401, 15.58220, 14.05039,
+ 14.26694, 14.73326, 15.19958, 16.15038, 17.26268, 18.37498, 18.74231,
+ 17.15798, 15.57364, 13.98932, 14.44402, 15.19001, 15.93600, 16.98694,
+ 18.13952, 19.29210, 18.29012, 17.26656, 16.24301, 15.21946, 16.23430,
+ 17.54035, 18.84639, 19.35445, 19.59653, 19.83860, 17.75954, 17.38438,
+ 17.00923, 16.63407, 18.25505, 20.16120, 22.06734, 21.94068, 21.13642,
+ 20.33215, 17.22896, 17.50220, 17.77544, 18.04868, 20.27580, 22.78205,
+ 25.28829, 24.52691, 22.67631, 20.82571, 17.45050, 18.19224, 18.93398,
+ 19.67573, 21.54024, 23.56514, 25.59004, 24.75878, 22.97546, 21.19213,
+ 17.92274, 19.07302, 20.22330, 21.37358, 22.55256, 23.73565, 24.91873,
+ 24.20587, 22.86103, 21.51619, 18.39499, 19.95381, 21.51263, 23.07145,
+ 23.56490, 23.90615, 24.24741, 23.65296, 22.74660, 21.84024, 18.12065,
+ 16.53382, 14.94700, 13.36018, 12.68341, 12.13666, 11.58990, 12.54858,
+ 14.00906, 15.46955, 18.89708, 17.15214, 15.40721, 13.66227, 13.32914,
+ 13.19769, 13.06625, 13.99367, 15.27405, 16.55443, 19.67351, 17.77046,
+ 15.86741, 13.96436, 13.97486, 14.25873, 14.54260, 15.43877, 16.53904,
+ 17.63931, 20.44994, 18.38877, 16.32761, 14.26645, 14.62059, 15.31977,
+ 16.01895, 16.88387, 17.80403, 18.72419, 19.90153, 18.67057, 17.43962,
+ 16.20866, 17.15464, 18.41161, 19.66858, 19.81848, 19.59936, 19.38024,
+ 19.16386, 18.90429, 18.64473, 18.38517, 19.95845, 21.79357, 23.62868,
+ 22.96589, 21.47046, 19.97503, 18.42618, 19.13802, 19.84985, 20.56168,
+ 22.76226, 25.17553, 27.58879, 26.11330, 23.34156, 20.56982, 18.47667,
+ 19.77041, 21.06416, 22.35790, 23.91914, 25.51859, 27.11804, 25.86504,
+ 23.66121, 21.45738, 18.78986, 20.53570, 22.28153, 24.02736, 24.52704,
+ 24.84869, 25.17035, 24.48488, 23.46371, 22.44254, 19.10306, 21.30098,
+ 23.49890, 25.69682, 25.13494, 24.17879, 23.22265, 23.10473, 23.26621,
+ 23.42769, 17.93453, 16.72707, 15.51962, 14.31216, 12.96039, 11.58800,
+ 10.21561, 11.29675, 13.19573, 15.09471, 18.05853, 16.85308, 15.64762,
+ 14.44216, 13.72634, 13.08047, 12.43459, 13.48912, 15.11045, 16.73179,
+ 18.18253, 16.97908, 15.77562, 14.57217, 14.49229, 14.57293, 14.65357,
+ 15.68150, 17.02518, 18.36887, 18.30654, 17.10508, 15.90363, 14.70217,
+ 15.25825, 16.06540, 16.87255, 17.87387, 18.93991, 20.00595, 17.54117,
+ 17.32369, 17.10622, 16.88875, 18.07494, 19.46166, 20.84837, 21.12988,
+ 21.04298, 20.95609, 16.64874, 17.55554, 18.46234, 19.36913, 21.18461,
+ 23.12989, 25.07517, 24.53784, 23.17297, 21.80810, 15.75632, 17.78738,
+ 19.81845, 21.84951, 24.29427, 26.79812, 29.30198, 27.94580, 25.30295,
+ 22.66010, 15.98046, 18.43027, 20.88008, 23.32989, 25.21976, 27.02964,
+ 28.83951, 27.75863, 25.71416, 23.66970, 16.57679, 19.21017, 21.84355,
+ 24.47693, 25.41719, 26.11557, 26.81396, 26.37308, 25.55245, 24.73182,
+ 17.17313, 19.99008, 22.80702, 25.62397, 25.61462, 25.20151, 24.78840,
+ 24.98753, 25.39074, 25.79395, 17.76927, 17.01824, 16.26722, 15.51620,
+ 13.45256, 11.20141, 8.95025, 10.14162, 12.48049, 14.81936, 17.05051,
+ 16.49955, 15.94860, 15.39764, 14.28896, 13.10061, 11.91225, 13.10109,
+ 15.08232, 17.06355, 16.33175, 15.98086, 15.62998, 15.27909, 15.12537,
+ 14.99981, 14.87425, 16.06056, 17.68415, 19.30775, 15.61299, 15.46217,
+ 15.31136, 15.16054, 15.96177, 16.89901, 17.83625, 19.02003, 20.28599,
+ 21.55194, 14.61341, 15.58383, 16.55426, 17.52469, 18.99524, 20.53725,
+ 22.07925, 22.56233, 22.69243, 22.82254, 13.57371, 15.79697, 18.02024,
+ 20.24351, 22.34258, 24.42392, 26.50526, 26.18790, 25.07097, 23.95404,
+ 12.53401, 16.01011, 19.48622, 22.96232, 25.68993, 28.31060, 30.93126,
+ 29.81347, 27.44951, 25.08555, 12.98106, 16.67323, 20.36540, 24.05756,
+ 26.36633, 28.47748, 30.58862, 29.76471, 27.96244, 26.16016, 13.92370,
+ 17.48634, 21.04897, 24.61161, 26.15244, 27.40443, 28.65643, 28.49117,
+ 27.85349, 27.21581, 14.86633, 18.29944, 21.73255, 25.16566, 25.93854,
+ 26.33138, 26.72423, 27.21763, 27.74455, 28.27147, 17.60401, 17.30942,
+ 17.01482, 16.72023, 13.94473, 10.81481, 7.68490, 8.98648, 11.76524,
+ 14.54400, 16.04249, 16.14603, 16.24958, 16.35312, 14.85158, 13.12075,
+ 11.38991, 12.71305, 15.05418, 17.39531, 14.48097, 14.98265, 15.48433,
+ 15.98602, 15.75844, 15.42668, 15.09493, 16.43962, 18.34312, 20.24663,
+ 12.91945, 13.81927, 14.71909, 15.61891, 16.66530, 17.73262, 18.79995,
+ 20.16619, 21.63206, 23.09794, 11.68565, 13.84398, 16.00230, 18.16062,
+ 19.91554, 21.61284, 23.31013, 23.99478, 24.34188, 24.68898, 10.49868,
+ 14.03841, 17.57814, 21.11788, 23.50056, 25.71795, 27.93534, 27.83796,
+ 26.96897, 26.09999, 9.31170, 14.23284, 19.15399, 24.07513, 27.08558,
+ 29.82307, 32.56055, 31.68113, 29.59606, 27.51099, 9.98166, 14.91619,
+ 19.85071, 24.78524, 27.51291, 29.92532, 32.33773, 31.77077, 30.21070,
+ 28.65063, 11.27060, 15.76250, 20.25440, 24.74629, 26.88768, 28.69329,
+ 30.49889, 30.60925, 30.15453, 29.69981, 12.55955, 16.60881, 20.65808,
+ 24.70735, 26.26245, 27.46126, 28.66005, 29.44773, 30.09835, 30.74898,
+ 15.20134, 15.53016, 15.85898, 16.18780, 13.53087, 10.44740, 7.36393,
+ 8.95806, 12.11139, 15.26472, 13.87432, 14.52378, 15.17325, 15.82272,
+ 14.49093, 12.87611, 11.26130, 12.73342, 15.23453, 17.73563, 12.54730,
+ 13.51741, 14.48752, 15.45763, 15.45100, 15.30483, 15.15867, 16.50878,
+ 18.35766, 20.20654, 11.22027, 12.51103, 13.80179, 15.09254, 16.41106,
+ 17.73355, 19.05603, 20.28415, 21.48080, 22.67745, 10.27070, 12.53633,
+ 14.80195, 17.06758, 19.04654, 20.98454, 22.92254, 23.63840, 23.94687,
+ 24.25534, 9.37505, 12.70901, 16.04297, 19.37693, 21.92136, 24.35300,
+ 26.78465, 26.93249, 26.31907, 25.70565, 8.47939, 12.88168, 17.28398,
+ 21.68627, 24.79618, 27.72146, 30.64674, 30.22658, 28.69127, 27.15597,
+ 9.77979, 13.97583, 18.17186, 22.36790, 25.18828, 27.81215, 30.43601,
+ 30.34293, 29.34420, 28.34548, 11.81220, 15.37712, 18.94204, 22.50695,
+ 24.75282, 26.81024, 28.86766, 29.40003, 29.42404, 29.44806, 13.84461,
+ 16.77841, 19.71221, 22.64601, 24.31735, 25.80833, 27.29932, 28.45713,
+ 29.50388, 30.55064, 12.05287, 13.06077, 14.06866, 15.07656, 12.81500,
+ 10.08638, 7.35776, 9.30520, 12.81134, 16.31747, 11.31943, 12.47863,
+ 13.63782, 14.79702, 13.82253, 12.54323, 11.26392, 12.88993, 15.48436,
+ 18.07880, 10.58600, 11.89649, 13.20698, 14.51747, 14.83005, 15.00007,
+ 15.17009, 16.47465, 18.15739, 19.84013, 9.85256, 11.31435, 12.77614,
+ 14.23793, 15.83757, 17.45691, 19.07625, 20.05937, 20.83042, 21.60147,
+ 9.36002, 11.37275, 13.38548, 15.39822, 17.58109, 19.78828, 21.99546,
+ 22.68573, 22.87036, 23.05500, 8.90189, 11.52266, 14.14343, 16.76420,
+ 19.42976, 22.10172, 24.77368, 25.17519, 24.81987, 24.46455, 8.44375,
+ 11.67256, 14.90137, 18.13018, 21.27843, 24.41516, 27.55190, 27.66464,
+ 26.76937, 25.87411, 10.51042, 13.30769, 16.10496, 18.90222, 21.70659,
+ 24.51197, 27.31734, 27.77045, 27.43945, 27.10846, 13.41869, 15.43789,
+ 17.45709, 19.47628, 21.66124, 23.86989, 26.07853, 27.08170, 27.68305,
+ 28.28440, 16.32697, 17.56809, 18.80922, 20.05033, 21.61590, 23.22781,
+ 24.83972, 26.39296, 27.92665, 29.46033, 8.90439, 10.59137, 12.27835,
+ 13.96532, 12.09914, 9.72536, 7.35159, 9.65235, 13.51128, 17.37022,
+ 8.76455, 10.43347, 12.10239, 13.77132, 13.15412, 12.21033, 11.26655,
+ 13.04643, 15.73420, 18.42198, 8.62470, 10.27557, 11.92644, 13.57731,
+ 14.20910, 14.69531, 15.18151, 16.44051, 17.95712, 19.47373, 8.48485,
+ 10.11767, 11.75049, 13.38331, 15.26408, 17.18027, 19.09647, 19.83460,
+ 20.18004, 20.52548, 8.44933, 10.20917, 11.96901, 13.72885, 16.11565,
+ 18.59202, 21.06838, 21.73307, 21.79386, 21.85465, 8.42872, 10.33631,
+ 12.24389, 14.15147, 16.93816, 19.85044, 22.76272, 23.41788, 23.32067,
+ 23.22346, 8.40812, 10.46344, 12.51877, 14.57409, 17.76068, 21.10886,
+ 24.45705, 25.10269, 24.84748, 24.59226, 11.24106, 12.63955, 14.03805,
+ 15.43654, 18.22489, 21.21178, 24.19868, 25.19796, 25.53469, 25.87143,
+ 15.02519, 15.49866, 15.97213, 16.44560, 18.56967, 20.92953, 23.28940,
+ 24.76337, 25.94205, 27.12073, 18.80933, 18.35777, 17.90622, 17.45466,
+ 18.91445, 20.64729, 22.38013, 24.32880, 26.34941, 28.37003,
+};
diff --git a/src/unittest/test_objdef.cpp b/src/unittest/test_objdef.cpp
new file mode 100644
index 000000000..df2633b38
--- /dev/null
+++ b/src/unittest/test_objdef.cpp
@@ -0,0 +1,106 @@
+/*
+Minetest
+Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#include "test.h"
+
+#include "exceptions.h"
+#include "objdef.h"
+
+class TestObjDef : public TestBase {
+public:
+ TestObjDef() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestObjDef"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testHandles();
+ void testAddGetSetClear();
+};
+
+static TestObjDef g_test_instance;
+
+void TestObjDef::runTests(IGameDef *gamedef)
+{
+ TEST(testHandles);
+ TEST(testAddGetSetClear);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestObjDef::testHandles()
+{
+ u32 uid = 0;
+ u32 index = 0;
+ ObjDefType type = OBJDEF_GENERIC;
+
+ ObjDefHandle handle = ObjDefManager::createHandle(9530, OBJDEF_ORE, 47);
+
+ UASSERTEQ(ObjDefHandle, 0xAF507B55, handle);
+
+ UASSERT(ObjDefManager::decodeHandle(handle, &index, &type, &uid));
+
+ UASSERTEQ(u32, 9530, index);
+ UASSERTEQ(u32, 47, uid);
+ UASSERTEQ(ObjDefHandle, OBJDEF_ORE, type);
+}
+
+
+void TestObjDef::testAddGetSetClear()
+{
+ ObjDefManager testmgr(NULL, OBJDEF_GENERIC);
+ ObjDefHandle hObj0, hObj1, hObj2, hObj3;
+ ObjDef *obj0, *obj1, *obj2, *obj3;
+
+ UASSERTEQ(ObjDefType, testmgr.getType(), OBJDEF_GENERIC);
+
+ obj0 = new ObjDef;
+ obj0->name = "foobar";
+ hObj0 = testmgr.add(obj0);
+ UASSERT(hObj0 != OBJDEF_INVALID_HANDLE);
+ UASSERTEQ(u32, obj0->index, 0);
+
+ obj1 = new ObjDef;
+ obj1->name = "FooBaz";
+ hObj1 = testmgr.add(obj1);
+ UASSERT(hObj1 != OBJDEF_INVALID_HANDLE);
+ UASSERTEQ(u32, obj1->index, 1);
+
+ obj2 = new ObjDef;
+ obj2->name = "asdf";
+ hObj2 = testmgr.add(obj2);
+ UASSERT(hObj2 != OBJDEF_INVALID_HANDLE);
+ UASSERTEQ(u32, obj2->index, 2);
+
+ obj3 = new ObjDef;
+ obj3->name = "foobaz";
+ hObj3 = testmgr.add(obj3);
+ UASSERT(hObj3 == OBJDEF_INVALID_HANDLE);
+
+ UASSERTEQ(size_t, testmgr.getNumObjects(), 3);
+
+ UASSERT(testmgr.get(hObj0) == obj0);
+ UASSERT(testmgr.getByName("FOOBAZ") == obj1);
+
+ UASSERT(testmgr.set(hObj0, obj3) == obj0);
+ UASSERT(testmgr.get(hObj0) == obj3);
+ delete obj0;
+
+ testmgr.clear();
+ UASSERTEQ(size_t, testmgr.getNumObjects(), 0);
+}
diff --git a/src/unittest/test_profiler.cpp b/src/unittest/test_profiler.cpp
new file mode 100644
index 000000000..fbc03f232
--- /dev/null
+++ b/src/unittest/test_profiler.cpp
@@ -0,0 +1,72 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "profiler.h"
+
+class TestProfiler : public TestBase {
+public:
+ TestProfiler() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestProfiler"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testProfilerAverage();
+};
+
+static TestProfiler g_test_instance;
+
+void TestProfiler::runTests(IGameDef *gamedef)
+{
+ TEST(testProfilerAverage);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestProfiler::testProfilerAverage()
+{
+ Profiler p;
+
+ p.avg("Test1", 1.f);
+ UASSERT(p.getValue("Test1") == 1.f);
+
+ p.avg("Test1", 2.f);
+ UASSERT(p.getValue("Test1") == 1.5f);
+
+ p.avg("Test1", 3.f);
+ UASSERT(p.getValue("Test1") == 2.f);
+
+ p.avg("Test1", 486.f);
+ UASSERT(p.getValue("Test1") == 123.f);
+
+ p.avg("Test1", 8);
+ UASSERT(p.getValue("Test1") == 100.f);
+
+ p.avg("Test1", 700);
+ UASSERT(p.getValue("Test1") == 200.f);
+
+ p.avg("Test1", 10000);
+ UASSERT(p.getValue("Test1") == 1600.f);
+
+ p.avg("Test2", 123.56);
+ p.avg("Test2", 123.58);
+
+ UASSERT(p.getValue("Test2") == 123.57f);
+}
diff --git a/src/unittest/test_random.cpp b/src/unittest/test_random.cpp
new file mode 100644
index 000000000..bbee57719
--- /dev/null
+++ b/src/unittest/test_random.cpp
@@ -0,0 +1,274 @@
+ /*
+Minetest
+Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#include "test.h"
+
+#include "util/numeric.h"
+#include "exceptions.h"
+#include "noise.h"
+
+class TestRandom : public TestBase {
+public:
+ TestRandom() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestRandom"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testPseudoRandom();
+ void testPseudoRandomRange();
+ void testPcgRandom();
+ void testPcgRandomRange();
+ void testPcgRandomBytes();
+ void testPcgRandomNormalDist();
+
+ static const int expected_pseudorandom_results[256];
+ static const u32 expected_pcgrandom_results[256];
+ static const u8 expected_pcgrandom_bytes_result[24];
+ static const u8 expected_pcgrandom_bytes_result2[24];
+};
+
+static TestRandom g_test_instance;
+
+void TestRandom::runTests(IGameDef *gamedef)
+{
+ TEST(testPseudoRandom);
+ TEST(testPseudoRandomRange);
+ TEST(testPcgRandom);
+ TEST(testPcgRandomRange);
+ TEST(testPcgRandomBytes);
+ TEST(testPcgRandomNormalDist);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestRandom::testPseudoRandom()
+{
+ PseudoRandom pr(814538);
+
+ for (u32 i = 0; i != 256; i++)
+ UASSERTEQ(int, pr.next(), expected_pseudorandom_results[i]);
+}
+
+
+void TestRandom::testPseudoRandomRange()
+{
+ PseudoRandom pr((int)time(NULL));
+
+ EXCEPTION_CHECK(PrngException, pr.range(2000, 6000));
+ EXCEPTION_CHECK(PrngException, pr.range(5, 1));
+
+ for (u32 i = 0; i != 32768; i++) {
+ int min = (pr.next() % 3000) - 500;
+ int max = (pr.next() % 3000) - 500;
+ if (min > max)
+ SWAP(int, min, max);
+
+ int randval = pr.range(min, max);
+ UASSERT(randval >= min);
+ UASSERT(randval <= max);
+ }
+}
+
+
+void TestRandom::testPcgRandom()
+{
+ PcgRandom pr(814538, 998877);
+
+ for (u32 i = 0; i != 256; i++)
+ UASSERTEQ(u32, pr.next(), expected_pcgrandom_results[i]);
+}
+
+
+void TestRandom::testPcgRandomRange()
+{
+ PcgRandom pr((int)time(NULL));
+
+ EXCEPTION_CHECK(PrngException, pr.range(5, 1));
+
+ // Regression test for bug 3027
+ pr.range(pr.RANDOM_MIN, pr.RANDOM_MAX);
+
+ for (u32 i = 0; i != 32768; i++) {
+ int min = (pr.next() % 3000) - 500;
+ int max = (pr.next() % 3000) - 500;
+ if (min > max)
+ SWAP(int, min, max);
+
+ int randval = pr.range(min, max);
+ UASSERT(randval >= min);
+ UASSERT(randval <= max);
+ }
+}
+
+
+void TestRandom::testPcgRandomBytes()
+{
+ char buf[32];
+ PcgRandom r(1538, 877);
+
+ memset(buf, 0, sizeof(buf));
+ r.bytes(buf + 5, 23);
+ UASSERT(memcmp(buf + 5, expected_pcgrandom_bytes_result,
+ sizeof(expected_pcgrandom_bytes_result)) == 0);
+
+ memset(buf, 0, sizeof(buf));
+ r.bytes(buf, 17);
+ UASSERT(memcmp(buf, expected_pcgrandom_bytes_result2,
+ sizeof(expected_pcgrandom_bytes_result2)) == 0);
+}
+
+
+void TestRandom::testPcgRandomNormalDist()
+{
+ static const int max = 120;
+ static const int min = -120;
+ static const int num_trials = 20;
+ static const u32 num_samples = 61000;
+ s32 bins[max - min + 1];
+ memset(bins, 0, sizeof(bins));
+
+ PcgRandom r(486179 + (int)time(NULL));
+
+ for (u32 i = 0; i != num_samples; i++) {
+ s32 randval = r.randNormalDist(min, max, num_trials);
+ UASSERT(randval <= max);
+ UASSERT(randval >= min);
+ bins[randval - min]++;
+ }
+
+ // Note that here we divide variance by the number of trials;
+ // this is because variance is a biased estimator.
+ int range = (max - min + 1);
+ float mean = (max + min) / 2;
+ float variance = ((range * range - 1) / 12) / num_trials;
+ float stddev = sqrt(variance);
+
+ static const float prediction_intervals[] = {
+ 0.68269f, // 1.0
+ 0.86639f, // 1.5
+ 0.95450f, // 2.0
+ 0.98758f, // 2.5
+ 0.99730f, // 3.0
+ };
+
+ //// Simple normality test using the 68-95-99.7% rule
+ for (u32 i = 0; i != ARRLEN(prediction_intervals); i++) {
+ float deviations = i / 2.f + 1.f;
+ int lbound = myround(mean - deviations * stddev);
+ int ubound = myround(mean + deviations * stddev);
+ UASSERT(lbound >= min);
+ UASSERT(ubound <= max);
+
+ int accum = 0;
+ for (int j = lbound; j != ubound; j++)
+ accum += bins[j - min];
+
+ float actual = (float)accum / num_samples;
+ UASSERT(fabs(actual - prediction_intervals[i]) < 0.02);
+ }
+}
+
+
+const int TestRandom::expected_pseudorandom_results[256] = {
+ 0x02fa, 0x60d5, 0x6c10, 0x606b, 0x098b, 0x5f1e, 0x4f56, 0x3fbd, 0x77af,
+ 0x4fe9, 0x419a, 0x6fe1, 0x177b, 0x6858, 0x36f8, 0x6d83, 0x14fc, 0x2d62,
+ 0x1077, 0x23e2, 0x041b, 0x7a7e, 0x5b52, 0x215d, 0x682b, 0x4716, 0x47e3,
+ 0x08c0, 0x1952, 0x56ae, 0x146d, 0x4b4f, 0x239f, 0x3fd0, 0x6794, 0x7796,
+ 0x7be2, 0x75b7, 0x5691, 0x28ee, 0x2656, 0x40c0, 0x133c, 0x63cd, 0x2aeb,
+ 0x518f, 0x7dbc, 0x6ad8, 0x736e, 0x5b05, 0x160b, 0x589f, 0x6f64, 0x5edc,
+ 0x092c, 0x0a39, 0x199e, 0x1927, 0x562b, 0x2689, 0x3ba3, 0x366f, 0x46da,
+ 0x4e49, 0x0abb, 0x40a1, 0x3846, 0x40db, 0x7adb, 0x6ec1, 0x6efa, 0x01cc,
+ 0x6335, 0x4352, 0x72fb, 0x4b2d, 0x509a, 0x257e, 0x2f7d, 0x5891, 0x2195,
+ 0x6107, 0x5269, 0x56e3, 0x4849, 0x38f7, 0x2791, 0x04f2, 0x4e05, 0x78ff,
+ 0x6bae, 0x50b3, 0x74ad, 0x31af, 0x531e, 0x7d56, 0x11c9, 0x0b5e, 0x405e,
+ 0x1e15, 0x7f6a, 0x5bd3, 0x6649, 0x71b4, 0x3ec2, 0x6ab4, 0x520e, 0x6ad6,
+ 0x287e, 0x10b8, 0x18f2, 0x7107, 0x46ea, 0x1d85, 0x25cc, 0x2689, 0x35c1,
+ 0x3065, 0x6237, 0x3edd, 0x23d9, 0x6fb5, 0x37a1, 0x3211, 0x526a, 0x4b09,
+ 0x23f1, 0x58cc, 0x2e42, 0x341f, 0x5e16, 0x3d1a, 0x5e8c, 0x7a82, 0x4635,
+ 0x2bf8, 0x6577, 0x3603, 0x1daf, 0x539f, 0x2e91, 0x6bd8, 0x42d3, 0x7a93,
+ 0x26e3, 0x5a91, 0x6c67, 0x1b66, 0x3ac7, 0x18bf, 0x20d8, 0x7153, 0x558d,
+ 0x7262, 0x653d, 0x417d, 0x3ed3, 0x3117, 0x600d, 0x6d04, 0x719c, 0x3afd,
+ 0x6ba5, 0x17c5, 0x4935, 0x346c, 0x5479, 0x6ff6, 0x1fcc, 0x1054, 0x3f14,
+ 0x6266, 0x3acc, 0x3b77, 0x71d8, 0x478b, 0x20fa, 0x4e46, 0x7e77, 0x5554,
+ 0x3652, 0x719c, 0x072b, 0x61ad, 0x399f, 0x621d, 0x1bba, 0x41d0, 0x7fdc,
+ 0x3e6c, 0x6a2a, 0x5253, 0x094e, 0x0c10, 0x3f43, 0x73eb, 0x4c5f, 0x1f23,
+ 0x12c9, 0x0902, 0x5238, 0x50c0, 0x1b77, 0x3ffd, 0x0124, 0x302a, 0x26b9,
+ 0x3648, 0x30a6, 0x1abc, 0x3031, 0x4029, 0x6358, 0x6696, 0x74e8, 0x6142,
+ 0x4284, 0x0c00, 0x7e50, 0x41e3, 0x3782, 0x79a5, 0x60fe, 0x2d15, 0x3ed2,
+ 0x7f70, 0x2b27, 0x6366, 0x5100, 0x7c44, 0x3ee0, 0x4e76, 0x7d34, 0x3a60,
+ 0x140e, 0x613d, 0x1193, 0x268d, 0x1e2f, 0x3123, 0x6d61, 0x4e0b, 0x51ce,
+ 0x13bf, 0x58d4, 0x4f43, 0x05c6, 0x4d6a, 0x7eb5, 0x2921, 0x2c36, 0x1c89,
+ 0x63b9, 0x1555, 0x1f41, 0x2d9f,
+};
+
+const u32 TestRandom::expected_pcgrandom_results[256] = {
+ 0x48c593f8, 0x054f59f5, 0x0d062dc1, 0x23852a23, 0x7fbbc97b, 0x1f9f141e,
+ 0x364e6ed8, 0x995bba58, 0xc9307dc0, 0x73fb34c4, 0xcd8de88d, 0x52e8ce08,
+ 0x1c4a78e4, 0x25c0882e, 0x8a82e2e0, 0xe3bc3311, 0xb8068d42, 0x73186110,
+ 0x19988df4, 0x69bd970b, 0x7214728c, 0x0aee320c, 0x2a5a536c, 0xaf48d715,
+ 0x00bce504, 0xd2b8f548, 0x520df366, 0x96d8fff5, 0xa1bb510b, 0x63477049,
+ 0xb85990b7, 0x7e090689, 0x275fb468, 0x50206257, 0x8bab4f8a, 0x0d6823db,
+ 0x63faeaac, 0x2d92deeb, 0x2ba78024, 0x0d30f631, 0x338923a0, 0xd07248d8,
+ 0xa5db62d3, 0xddba8af6, 0x0ad454e9, 0x6f0fd13a, 0xbbfde2bf, 0x91188009,
+ 0x966b394d, 0xbb9d2012, 0x7e6926cb, 0x95183860, 0x5ff4c59b, 0x035f628a,
+ 0xb67085ef, 0x33867e23, 0x68d1b887, 0x2e3298d7, 0x84fd0650, 0x8bc91141,
+ 0x6fcb0452, 0x2836fee9, 0x2e83c0a3, 0xf1bafdc5, 0x9ff77777, 0xfdfbba87,
+ 0x527aebeb, 0x423e5248, 0xd1756490, 0xe41148fa, 0x3361f7b4, 0xa2824f23,
+ 0xf4e08072, 0xc50442be, 0x35adcc21, 0x36be153c, 0xc7709012, 0xf0eeb9f2,
+ 0x3d73114e, 0x1c1574ee, 0x92095b9c, 0x1503d01c, 0xd6ce0677, 0x026a8ec1,
+ 0x76d0084d, 0x86c23633, 0x36f75ce6, 0x08fa7bbe, 0x35f6ff2a, 0x31cc9525,
+ 0x2c1a35e6, 0x8effcd62, 0xc782fa07, 0x8a86e248, 0x8fdb7a9b, 0x77246626,
+ 0x5767723f, 0x3a78b699, 0xe548ce1c, 0x5820f37d, 0x148ed9b8, 0xf6796254,
+ 0x32232c20, 0x392bf3a2, 0xe9af6625, 0xd40b0d88, 0x636cfa23, 0x6a5de514,
+ 0xc4a69183, 0xc785c853, 0xab0de901, 0x16ae7e44, 0x376f13b5, 0x070f7f31,
+ 0x34cbc93b, 0xe6184345, 0x1b7f911f, 0x631fbe4b, 0x86d6e023, 0xc689b518,
+ 0x88ef4f7c, 0xddf06b45, 0xc97f18d4, 0x2aaee94b, 0x45694723, 0x6db111d2,
+ 0x91974fce, 0xe33e29e2, 0xc5e99494, 0x8017e02b, 0x3ebd8143, 0x471ffb80,
+ 0xc0d7ca1b, 0x4954c860, 0x48935d6a, 0xf2d27999, 0xb93d608d, 0x40696e90,
+ 0x60b18162, 0x1a156998, 0x09b8bbab, 0xc80a79b6, 0x8adbcfbc, 0xc375248c,
+ 0xa584e2ea, 0x5b46fe11, 0x58e84680, 0x8a8bc456, 0xd668b94f, 0x8b9035be,
+ 0x278509d4, 0x6663a140, 0x81a9817a, 0xd4f9d3cf, 0x6dc5f607, 0x6ae04450,
+ 0x694f22a4, 0x1d061788, 0x2e39ad8b, 0x748f4db2, 0xee569b52, 0xd157166d,
+ 0xdabc161e, 0xc8d50176, 0x7e3110e5, 0x9f7d033b, 0x128df67f, 0xb0078583,
+ 0xa3a75d26, 0xc1ad8011, 0x07dd89ec, 0xef04f456, 0x91bf866c, 0x6aac5306,
+ 0xdd5a1573, 0xf73ff97a, 0x4e1186ad, 0xb9680680, 0xc8894515, 0xdc95a08e,
+ 0xc894fd8e, 0xf84ade15, 0xd787f8c1, 0x40dcecca, 0x1b24743e, 0x1ce6ab23,
+ 0x72321653, 0xb80fbaf7, 0x1bcf099b, 0x1ff26805, 0x78f66c8e, 0xf93bf51a,
+ 0xfb0c06fe, 0xe50d48cf, 0x310947e0, 0x1b78804a, 0xe73e2c14, 0x8deb8381,
+ 0xe576122a, 0xe5a8df39, 0x42397c5e, 0xf5503f3c, 0xbe3dbf8d, 0x1b360e5c,
+ 0x9254caaf, 0x7a9f6744, 0x6d4144fa, 0xd77c65fe, 0x44ca7b12, 0xf58a4c00,
+ 0x159500d0, 0x92769857, 0x7134fdd4, 0xa3fea693, 0xbd044831, 0xeded39a1,
+ 0xe4570204, 0xaea37f2f, 0x9a302971, 0x620f8402, 0x1d2f3e5e, 0xf9c2f49c,
+ 0x738e813a, 0xb3c92251, 0x7ecba63b, 0xbe7eebc7, 0xf800267c, 0x3fdeb760,
+ 0xf12d5e7d, 0x5a18dce1, 0xb35a539c, 0xe565f057, 0x2babf38c, 0xae5800ad,
+ 0x421004dd, 0x6715acb6, 0xff529b64, 0xd520d207, 0x7cb193e7, 0xe9b18e4c,
+ 0xfd2a8a59, 0x47826ae3, 0x56ba43f8, 0x453b3d99, 0x8ae1675f, 0xf66f5c34,
+ 0x057a6ac1, 0x010769e4, 0xa8324158, 0x410379a5, 0x5dfc8c97, 0x72848afe,
+ 0x59f169e5, 0xe32acb78, 0x5dfaa9c4, 0x51bb956a,
+};
+
+const u8 TestRandom::expected_pcgrandom_bytes_result[24] = {
+ 0xf3, 0x79, 0x8f, 0x31, 0xac, 0xd9, 0x34, 0xf8, 0x3c, 0x6e, 0x82, 0x37,
+ 0x6b, 0x4b, 0x77, 0xe3, 0xbd, 0x0a, 0xee, 0x22, 0x79, 0x6e, 0x40, 0x00,
+};
+
+const u8 TestRandom::expected_pcgrandom_bytes_result2[24] = {
+ 0x47, 0x9e, 0x08, 0x3e, 0xd4, 0x21, 0x2d, 0xf6, 0xb4, 0xb1, 0x9d, 0x7a,
+ 0x60, 0x02, 0x5a, 0xb2, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
diff --git a/src/unittest/test_schematic.cpp b/src/unittest/test_schematic.cpp
new file mode 100644
index 000000000..480124428
--- /dev/null
+++ b/src/unittest/test_schematic.cpp
@@ -0,0 +1,280 @@
+ /*
+Minetest
+Copyright (C) 2010-2014 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+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.
+*/
+
+#include "test.h"
+
+#include "mg_schematic.h"
+#include "gamedef.h"
+#include "nodedef.h"
+
+class TestSchematic : public TestBase {
+public:
+ TestSchematic() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestSchematic"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testMtsSerializeDeserialize(INodeDefManager *ndef);
+ void testLuaTableSerialize(INodeDefManager *ndef);
+ void testFileSerializeDeserialize(INodeDefManager *ndef);
+
+ static const content_t test_schem1_data[7 * 6 * 4];
+ static const content_t test_schem2_data[3 * 3 * 3];
+ static const u8 test_schem2_prob[3 * 3 * 3];
+ static const char *expected_lua_output;
+};
+
+static TestSchematic g_test_instance;
+
+void TestSchematic::runTests(IGameDef *gamedef)
+{
+ IWritableNodeDefManager *ndef =
+ (IWritableNodeDefManager *)gamedef->getNodeDefManager();
+
+ ndef->setNodeRegistrationStatus(true);
+
+ TEST(testMtsSerializeDeserialize, ndef);
+ TEST(testLuaTableSerialize, ndef);
+ TEST(testFileSerializeDeserialize, ndef);
+
+ ndef->resetNodeResolveState();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestSchematic::testMtsSerializeDeserialize(INodeDefManager *ndef)
+{
+ static const v3s16 size(7, 6, 4);
+ static const u32 volume = size.X * size.Y * size.Z;
+
+ std::stringstream ss(std::ios_base::binary |
+ std::ios_base::in | std::ios_base::out);
+
+ std::vector<std::string> names;
+ names.push_back("foo");
+ names.push_back("bar");
+ names.push_back("baz");
+ names.push_back("qux");
+
+ Schematic schem, schem2;
+
+ schem.flags = 0;
+ schem.size = size;
+ schem.schemdata = new MapNode[volume];
+ schem.slice_probs = new u8[size.Y];
+ for (size_t i = 0; i != volume; i++)
+ schem.schemdata[i] = MapNode(test_schem1_data[i], MTSCHEM_PROB_ALWAYS, 0);
+ for (s16 y = 0; y != size.Y; y++)
+ schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
+
+ UASSERT(schem.serializeToMts(&ss, names));
+
+ ss.seekg(0);
+ names.clear();
+
+ UASSERT(schem2.deserializeFromMts(&ss, &names));
+
+ UASSERTEQ(size_t, names.size(), 4);
+ UASSERTEQ(std::string, names[0], "foo");
+ UASSERTEQ(std::string, names[1], "bar");
+ UASSERTEQ(std::string, names[2], "baz");
+ UASSERTEQ(std::string, names[3], "qux");
+
+ UASSERT(schem2.size == size);
+ for (size_t i = 0; i != volume; i++)
+ UASSERT(schem2.schemdata[i] == schem.schemdata[i]);
+ for (s16 y = 0; y != size.Y; y++)
+ UASSERTEQ(u8, schem2.slice_probs[y], schem.slice_probs[y]);
+}
+
+
+void TestSchematic::testLuaTableSerialize(INodeDefManager *ndef)
+{
+ static const v3s16 size(3, 3, 3);
+ static const u32 volume = size.X * size.Y * size.Z;
+
+ Schematic schem;
+
+ schem.flags = 0;
+ schem.size = size;
+ schem.schemdata = new MapNode[volume];
+ schem.slice_probs = new u8[size.Y];
+ for (size_t i = 0; i != volume; i++)
+ schem.schemdata[i] = MapNode(test_schem2_data[i], test_schem2_prob[i], 0);
+ for (s16 y = 0; y != size.Y; y++)
+ schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
+
+ std::vector<std::string> names;
+ names.push_back("air");
+ names.push_back("default:lava_source");
+ names.push_back("default:glass");
+
+ std::ostringstream ss(std::ios_base::binary);
+
+ UASSERT(schem.serializeToLua(&ss, names, false, 0));
+ UASSERTEQ(std::string, ss.str(), expected_lua_output);
+}
+
+
+void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
+{
+ static const v3s16 size(3, 3, 3);
+ static const u32 volume = size.X * size.Y * size.Z;
+ static const content_t content_map[] = {
+ CONTENT_AIR,
+ t_CONTENT_STONE,
+ t_CONTENT_LAVA,
+ };
+ static const content_t content_map2[] = {
+ CONTENT_AIR,
+ t_CONTENT_STONE,
+ t_CONTENT_WATER,
+ };
+ StringMap replace_names;
+ replace_names["default:lava"] = "default:water";
+
+ Schematic schem1, schem2;
+
+ //// Construct the schematic to save
+ schem1.flags = 0;
+ schem1.size = size;
+ schem1.schemdata = new MapNode[volume];
+ schem1.slice_probs = new u8[size.Y];
+ schem1.slice_probs[0] = 80;
+ schem1.slice_probs[1] = 160;
+ schem1.slice_probs[2] = 240;
+
+ for (size_t i = 0; i != volume; i++) {
+ content_t c = content_map[test_schem2_data[i]];
+ schem1.schemdata[i] = MapNode(c, test_schem2_prob[i], 0);
+ }
+
+ std::string temp_file = getTestTempFile();
+ UASSERT(schem1.saveSchematicToFile(temp_file, ndef));
+ UASSERT(schem2.loadSchematicFromFile(temp_file, ndef, &replace_names));
+
+ UASSERT(schem2.size == size);
+ UASSERT(schem2.slice_probs[0] == 80);
+ UASSERT(schem2.slice_probs[1] == 160);
+ UASSERT(schem2.slice_probs[2] == 240);
+
+ for (size_t i = 0; i != volume; i++) {
+ content_t c = content_map2[test_schem2_data[i]];
+ UASSERT(schem2.schemdata[i] == MapNode(c, test_schem2_prob[i], 0));
+ }
+}
+
+
+// Should form a cross-shaped-thing...?
+const content_t TestSchematic::test_schem1_data[7 * 6 * 4] = {
+ 3, 3, 1, 1, 1, 3, 3, // Y=0, Z=0
+ 3, 0, 1, 2, 1, 0, 3, // Y=1, Z=0
+ 3, 0, 1, 2, 1, 0, 3, // Y=2, Z=0
+ 3, 1, 1, 2, 1, 1, 3, // Y=3, Z=0
+ 3, 2, 2, 2, 2, 2, 3, // Y=4, Z=0
+ 3, 1, 1, 2, 1, 1, 3, // Y=5, Z=0
+
+ 0, 0, 1, 1, 1, 0, 0, // Y=0, Z=1
+ 0, 0, 1, 2, 1, 0, 0, // Y=1, Z=1
+ 0, 0, 1, 2, 1, 0, 0, // Y=2, Z=1
+ 1, 1, 1, 2, 1, 1, 1, // Y=3, Z=1
+ 1, 2, 2, 2, 2, 2, 1, // Y=4, Z=1
+ 1, 1, 1, 2, 1, 1, 1, // Y=5, Z=1
+
+ 0, 0, 1, 1, 1, 0, 0, // Y=0, Z=2
+ 0, 0, 1, 2, 1, 0, 0, // Y=1, Z=2
+ 0, 0, 1, 2, 1, 0, 0, // Y=2, Z=2
+ 1, 1, 1, 2, 1, 1, 1, // Y=3, Z=2
+ 1, 2, 2, 2, 2, 2, 1, // Y=4, Z=2
+ 1, 1, 1, 2, 1, 1, 1, // Y=5, Z=2
+
+ 3, 3, 1, 1, 1, 3, 3, // Y=0, Z=3
+ 3, 0, 1, 2, 1, 0, 3, // Y=1, Z=3
+ 3, 0, 1, 2, 1, 0, 3, // Y=2, Z=3
+ 3, 1, 1, 2, 1, 1, 3, // Y=3, Z=3
+ 3, 2, 2, 2, 2, 2, 3, // Y=4, Z=3
+ 3, 1, 1, 2, 1, 1, 3, // Y=5, Z=3
+};
+
+const content_t TestSchematic::test_schem2_data[3 * 3 * 3] = {
+ 0, 0, 0,
+ 0, 2, 0,
+ 0, 0, 0,
+
+ 0, 2, 0,
+ 2, 1, 2,
+ 0, 2, 0,
+
+ 0, 0, 0,
+ 0, 2, 0,
+ 0, 0, 0,
+};
+
+const u8 TestSchematic::test_schem2_prob[3 * 3 * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00,
+ 0x00, 0x00, 0x00,
+
+ 0x00, 0xFF, 0x00,
+ 0xFF, 0xFF, 0xFF,
+ 0x00, 0xFF, 0x00,
+
+ 0x00, 0x00, 0x00,
+ 0x00, 0xFF, 0x00,
+ 0x00, 0x00, 0x00,
+};
+
+const char *TestSchematic::expected_lua_output =
+ "schematic = {\n"
+ "\tsize = {x=3, y=3, z=3},\n"
+ "\tyslice_prob = {\n"
+ "\t\t{ypos=0, prob=254},\n"
+ "\t\t{ypos=1, prob=254},\n"
+ "\t\t{ypos=2, prob=254},\n"
+ "\t},\n"
+ "\tdata = {\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"default:lava_source\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t\t{name=\"air\", prob=0, param2=0},\n"
+ "\t},\n"
+ "}\n";
diff --git a/src/unittest/test_serialization.cpp b/src/unittest/test_serialization.cpp
new file mode 100644
index 000000000..49f348e9c
--- /dev/null
+++ b/src/unittest/test_serialization.cpp
@@ -0,0 +1,386 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "util/string.h"
+#include "util/serialize.h"
+
+class TestSerialization : public TestBase {
+public:
+ TestSerialization() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestSerialization"; }
+
+ void runTests(IGameDef *gamedef);
+ void buildTestStrings();
+
+ void testSerializeString();
+ void testSerializeWideString();
+ void testSerializeLongString();
+ void testSerializeJsonString();
+ void testSerializeHex();
+ void testDeSerializeString();
+ void testDeSerializeWideString();
+ void testDeSerializeLongString();
+ void testStreamRead();
+ void testStreamWrite();
+
+ std::string teststring2;
+ std::wstring teststring2_w;
+ std::string teststring2_w_encoded;
+
+ static const u8 test_serialized_data[12 * 13];
+};
+
+static TestSerialization g_test_instance;
+
+void TestSerialization::runTests(IGameDef *gamedef)
+{
+ buildTestStrings();
+
+ TEST(testSerializeString);
+ TEST(testDeSerializeString);
+ TEST(testSerializeWideString);
+ TEST(testDeSerializeWideString);
+ TEST(testSerializeLongString);
+ TEST(testDeSerializeLongString);
+ TEST(testSerializeJsonString);
+ TEST(testSerializeHex);
+ TEST(testStreamRead);
+ TEST(testStreamWrite);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// To be used like this:
+// mkstr("Some\0string\0with\0embedded\0nuls")
+// since std::string("...") doesn't work as expected in that case.
+template<size_t N> std::string mkstr(const char (&s)[N])
+{
+ return std::string(s, N - 1);
+}
+
+void TestSerialization::buildTestStrings()
+{
+ std::ostringstream tmp_os;
+ std::wostringstream tmp_os_w;
+ std::ostringstream tmp_os_w_encoded;
+ for (int i = 0; i < 256; i++) {
+ tmp_os << (char)i;
+ tmp_os_w << (wchar_t)i;
+ tmp_os_w_encoded << (char)0 << (char)i;
+ }
+ teststring2 = tmp_os.str();
+ teststring2_w = tmp_os_w.str();
+ teststring2_w_encoded = tmp_os_w_encoded.str();
+}
+
+void TestSerialization::testSerializeString()
+{
+ // Test blank string
+ UASSERT(serializeString("") == mkstr("\0\0"));
+
+ // Test basic string
+ UASSERT(serializeString("Hello world!") == mkstr("\0\14Hello world!"));
+
+ // Test character range
+ UASSERT(serializeString(teststring2) == mkstr("\1\0") + teststring2);
+}
+
+void TestSerialization::testDeSerializeString()
+{
+ // Test deserialize
+ {
+ std::istringstream is(serializeString(teststring2), std::ios::binary);
+ UASSERT(deSerializeString(is) == teststring2);
+ UASSERT(!is.eof());
+ is.get();
+ UASSERT(is.eof());
+ }
+
+ // Test deserialize an incomplete length specifier
+ {
+ std::istringstream is(mkstr("\x53"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeString(is));
+ }
+
+ // Test deserialize a string with incomplete data
+ {
+ std::istringstream is(mkstr("\x00\x55 abcdefg"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeString(is));
+ }
+}
+
+void TestSerialization::testSerializeWideString()
+{
+ // Test blank string
+ UASSERT(serializeWideString(L"") == mkstr("\0\0"));
+
+ // Test basic string
+ UASSERT(serializeWideString(utf8_to_wide("Hello world!")) ==
+ mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!"));
+
+ // Test character range
+ UASSERT(serializeWideString(teststring2_w) ==
+ mkstr("\1\0") + teststring2_w_encoded);
+}
+
+void TestSerialization::testDeSerializeWideString()
+{
+ // Test deserialize
+ {
+ std::istringstream is(serializeWideString(teststring2_w), std::ios::binary);
+ UASSERT(deSerializeWideString(is) == teststring2_w);
+ UASSERT(!is.eof());
+ is.get();
+ UASSERT(is.eof());
+ }
+
+ // Test deserialize an incomplete length specifier
+ {
+ std::istringstream is(mkstr("\x53"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
+ }
+
+ // Test deserialize a string with an incomplete character
+ {
+ std::istringstream is(mkstr("\x00\x07\0a\0b\0c\0d\0e\0f\0"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
+ }
+
+ // Test deserialize a string with incomplete data
+ {
+ std::istringstream is(mkstr("\x00\x08\0a\0b\0c\0d\0e\0f"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeWideString(is));
+ }
+}
+
+void TestSerialization::testSerializeLongString()
+{
+ // Test blank string
+ UASSERT(serializeLongString("") == mkstr("\0\0\0\0"));
+
+ // Test basic string
+ UASSERT(serializeLongString("Hello world!") == mkstr("\0\0\0\14Hello world!"));
+
+ // Test character range
+ UASSERT(serializeLongString(teststring2) == mkstr("\0\0\1\0") + teststring2);
+}
+
+void TestSerialization::testDeSerializeLongString()
+{
+ // Test deserialize
+ {
+ std::istringstream is(serializeLongString(teststring2), std::ios::binary);
+ UASSERT(deSerializeLongString(is) == teststring2);
+ UASSERT(!is.eof());
+ is.get();
+ UASSERT(is.eof());
+ }
+
+ // Test deserialize an incomplete length specifier
+ {
+ std::istringstream is(mkstr("\x53"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
+ }
+
+ // Test deserialize a string with incomplete data
+ {
+ std::istringstream is(mkstr("\x00\x00\x00\x05 abc"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
+ }
+
+ // Test deserialize a string with a length too large
+ {
+ std::istringstream is(mkstr("\xFF\xFF\xFF\xFF blah"), std::ios::binary);
+ EXCEPTION_CHECK(SerializationError, deSerializeLongString(is));
+ }
+}
+
+
+void TestSerialization::testSerializeJsonString()
+{
+ // Test blank string
+ UASSERT(serializeJsonString("") == "\"\"");
+
+ // Test basic string
+ UASSERT(serializeJsonString("Hello world!") == "\"Hello world!\"");
+
+ // MSVC fails when directly using "\\\\"
+ std::string backslash = "\\";
+ UASSERT(serializeJsonString(teststring2) ==
+ mkstr("\"") +
+ "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" +
+ "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" +
+ "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" +
+ "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" +
+ " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) +
+ "\\/" + teststring2.substr(0x30, 0x5c-0x30) +
+ backslash + backslash + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" +
+ "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" +
+ "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" +
+ "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" +
+ "\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" +
+ "\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" +
+ "\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" +
+ "\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" +
+ "\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" +
+ "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" +
+ "\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" +
+ "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" +
+ "\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" +
+ "\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" +
+ "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" +
+ "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" +
+ "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" +
+ "\"");
+
+ // Test deserialize
+ std::istringstream is(serializeJsonString(teststring2), std::ios::binary);
+ UASSERT(deSerializeJsonString(is) == teststring2);
+ UASSERT(!is.eof());
+ is.get();
+ UASSERT(is.eof());
+}
+
+void TestSerialization::testSerializeHex()
+{
+ // Test blank string
+ UASSERT(serializeHexString("") == "");
+ UASSERT(serializeHexString("", true) == "");
+
+ // Test basic string
+ UASSERT(serializeHexString("Hello world!") ==
+ "48656c6c6f20776f726c6421");
+ UASSERT(serializeHexString("Hello world!", true) ==
+ "48 65 6c 6c 6f 20 77 6f 72 6c 64 21");
+
+ // Test binary string
+ UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff")) ==
+ "000ab0631f00ff");
+ UASSERT(serializeHexString(mkstr("\x00\x0a\xb0\x63\x1f\x00\xff"), true) ==
+ "00 0a b0 63 1f 00 ff");
+}
+
+
+void TestSerialization::testStreamRead()
+{
+ std::string datastr(
+ (const char *)test_serialized_data,
+ sizeof(test_serialized_data));
+ std::istringstream is(datastr, std::ios_base::binary);
+
+ UASSERT(readU8(is) == 0x11);
+ UASSERT(readU16(is) == 0x2233);
+ UASSERT(readU32(is) == 0x44556677);
+ UASSERT(readU64(is) == 0x8899AABBCCDDEEFF);
+
+ UASSERT(readS8(is) == -128);
+ UASSERT(readS16(is) == 30000);
+ UASSERT(readS32(is) == -6);
+ UASSERT(readS64(is) == -43);
+
+ UASSERT(readF1000(is) == 53.534f);
+ UASSERT(readF1000(is) == -300000.32f);
+ UASSERT(readF1000(is) == F1000_MIN);
+ UASSERT(readF1000(is) == F1000_MAX);
+
+ UASSERT(deSerializeString(is) == "foobar!");
+
+ UASSERT(readV2S16(is) == v2s16(500, 500));
+ UASSERT(readV3S16(is) == v3s16(4207, 604, -30));
+ UASSERT(readV2S32(is) == v2s32(1920, 1080));
+ UASSERT(readV3S32(is) == v3s32(-400, 6400054, 290549855));
+ UASSERT(readV2F1000(is) == v2f(500.656f, 350.345f));
+
+ UASSERT(deSerializeWideString(is) == L"\x02~woof~\x5455");
+
+ UASSERT(readV3F1000(is) == v3f(500, 10024.2f, -192.54f));
+ UASSERT(readARGB8(is) == video::SColor(255, 128, 50, 128));
+
+ UASSERT(deSerializeLongString(is) == "some longer string here");
+
+ UASSERT(is.rdbuf()->in_avail() == 2);
+ UASSERT(readU16(is) == 0xF00D);
+ UASSERT(is.rdbuf()->in_avail() == 0);
+}
+
+
+void TestSerialization::testStreamWrite()
+{
+ std::ostringstream os(std::ios_base::binary);
+ std::string data;
+
+ writeU8(os, 0x11);
+ writeU16(os, 0x2233);
+ writeU32(os, 0x44556677);
+ writeU64(os, 0x8899AABBCCDDEEFF);
+
+ writeS8(os, -128);
+ writeS16(os, 30000);
+ writeS32(os, -6);
+ writeS64(os, -43);
+
+ writeF1000(os, 53.53467f);
+ writeF1000(os, -300000.32f);
+ writeF1000(os, F1000_MIN);
+ writeF1000(os, F1000_MAX);
+
+ os << serializeString("foobar!");
+
+ data = os.str();
+ UASSERT(data.size() < sizeof(test_serialized_data));
+ UASSERT(!memcmp(&data[0], test_serialized_data, data.size()));
+
+ writeV2S16(os, v2s16(500, 500));
+ writeV3S16(os, v3s16(4207, 604, -30));
+ writeV2S32(os, v2s32(1920, 1080));
+ writeV3S32(os, v3s32(-400, 6400054, 290549855));
+ writeV2F1000(os, v2f(500.65661f, 350.34567f));
+
+ os << serializeWideString(L"\x02~woof~\x5455");
+
+ writeV3F1000(os, v3f(500, 10024.2f, -192.54f));
+ writeARGB8(os, video::SColor(255, 128, 50, 128));
+
+ os << serializeLongString("some longer string here");
+
+ writeU16(os, 0xF00D);
+
+ data = os.str();
+ UASSERT(data.size() == sizeof(test_serialized_data));
+ UASSERT(!memcmp(&data[0], test_serialized_data, sizeof(test_serialized_data)));
+}
+
+
+const u8 TestSerialization::test_serialized_data[12 * 13] = {
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc,
+ 0xdd, 0xee, 0xff, 0x80, 0x75, 0x30, 0xff, 0xff, 0xff, 0xfa, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xd5, 0x00, 0x00, 0xd1, 0x1e, 0xee, 0x1e,
+ 0x5b, 0xc0, 0x80, 0x00, 0x02, 0x80, 0x7F, 0xFF, 0xFD, 0x80, 0x00, 0x07,
+ 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x21, 0x01, 0xf4, 0x01, 0xf4, 0x10,
+ 0x6f, 0x02, 0x5c, 0xff, 0xe2, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x04,
+ 0x38, 0xff, 0xff, 0xfe, 0x70, 0x00, 0x61, 0xa8, 0x36, 0x11, 0x51, 0x70,
+ 0x5f, 0x00, 0x07, 0xa3, 0xb0, 0x00, 0x05, 0x58, 0x89, 0x00, 0x08, 0x00,
+ 0x02, 0x00, 0x7e, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x66, 0x00,
+ 0x7e, 0x54, 0x55, 0x00, 0x07, 0xa1, 0x20, 0x00, 0x98, 0xf5, 0x08, 0xff,
+ 0xfd, 0x0f, 0xe4, 0xff, 0x80, 0x32, 0x80, 0x00, 0x00, 0x00, 0x17, 0x73,
+ 0x6f, 0x6d, 0x65, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x65, 0x72, 0x20, 0x73,
+ 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20, 0x68, 0x65, 0x72, 0x65, 0xF0, 0x0D,
+};
diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp
new file mode 100644
index 000000000..a82d734f0
--- /dev/null
+++ b/src/unittest/test_settings.cpp
@@ -0,0 +1,204 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "settings.h"
+#include "noise.h"
+
+class TestSettings : public TestBase {
+public:
+ TestSettings() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestSettings"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testAllSettings();
+
+ static const char *config_text_before;
+ static const char *config_text_after;
+};
+
+static TestSettings g_test_instance;
+
+void TestSettings::runTests(IGameDef *gamedef)
+{
+ TEST(testAllSettings);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+const char *TestSettings::config_text_before =
+ "leet = 1337\n"
+ "leetleet = 13371337\n"
+ "leetleet_neg = -13371337\n"
+ "floaty_thing = 1.1\n"
+ "stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n"
+ "coord = (1, 2, 4.5)\n"
+ " # this is just a comment\n"
+ "this is an invalid line\n"
+ "asdf = {\n"
+ " a = 5\n"
+ " bb = 2.5\n"
+ " ccc = \"\"\"\n"
+ "testy\n"
+ " testa \n"
+ "\"\"\"\n"
+ "\n"
+ "}\n"
+ "blarg = \"\"\" \n"
+ "some multiline text\n"
+ " with leading whitespace!\n"
+ "\"\"\"\n"
+ "np_terrain = 5, 40, (250, 250, 250), 12341, 5, 0.7, 2.4\n"
+ "zoop = true";
+
+const char *TestSettings::config_text_after =
+ "leet = 1337\n"
+ "leetleet = 13371337\n"
+ "leetleet_neg = -13371337\n"
+ "floaty_thing = 1.1\n"
+ "stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n"
+ "coord = (1, 2, 4.5)\n"
+ " # this is just a comment\n"
+ "this is an invalid line\n"
+ "asdf = {\n"
+ " a = 5\n"
+ " bb = 2.5\n"
+ " ccc = \"\"\"\n"
+ "testy\n"
+ " testa \n"
+ "\"\"\"\n"
+ "\n"
+ "}\n"
+ "blarg = \"\"\" \n"
+ "some multiline text\n"
+ " with leading whitespace!\n"
+ "\"\"\"\n"
+ "np_terrain = {\n"
+ " flags = defaults\n"
+ " lacunarity = 2.4\n"
+ " octaves = 6\n"
+ " offset = 3.5\n"
+ " persistence = 0.7\n"
+ " scale = 40\n"
+ " seed = 12341\n"
+ " spread = (250,250,250)\n"
+ "}\n"
+ "zoop = true\n"
+ "coord2 = (1,2,3.3)\n"
+ "floaty_thing_2 = 1.2\n"
+ "groupy_thing = {\n"
+ " animals = cute\n"
+ " num_apples = 4\n"
+ " num_oranges = 53\n"
+ "}\n";
+
+void TestSettings::testAllSettings()
+{
+ try {
+ Settings s;
+
+ // Test reading of settings
+ std::istringstream is(config_text_before);
+ s.parseConfigLines(is);
+
+ UASSERT(s.getS32("leet") == 1337);
+ UASSERT(s.getS16("leetleet") == 32767);
+ UASSERT(s.getS16("leetleet_neg") == -32768);
+
+ // Not sure if 1.1 is an exact value as a float, but doesn't matter
+ UASSERT(fabs(s.getFloat("floaty_thing") - 1.1) < 0.001);
+ UASSERT(s.get("stringy_thing") == "asd /( ¤%&(/\" BLÖÄRP");
+ UASSERT(fabs(s.getV3F("coord").X - 1.0) < 0.001);
+ UASSERT(fabs(s.getV3F("coord").Y - 2.0) < 0.001);
+ UASSERT(fabs(s.getV3F("coord").Z - 4.5) < 0.001);
+
+ // Test the setting of settings too
+ s.setFloat("floaty_thing_2", 1.2);
+ s.setV3F("coord2", v3f(1, 2, 3.3));
+ UASSERT(s.get("floaty_thing_2").substr(0,3) == "1.2");
+ UASSERT(fabs(s.getFloat("floaty_thing_2") - 1.2) < 0.001);
+ UASSERT(fabs(s.getV3F("coord2").X - 1.0) < 0.001);
+ UASSERT(fabs(s.getV3F("coord2").Y - 2.0) < 0.001);
+ UASSERT(fabs(s.getV3F("coord2").Z - 3.3) < 0.001);
+
+ // Test settings groups
+ Settings *group = s.getGroup("asdf");
+ UASSERT(group != NULL);
+ UASSERT(s.getGroupNoEx("zoop", group) == false);
+ UASSERT(group->getS16("a") == 5);
+ UASSERT(fabs(group->getFloat("bb") - 2.5) < 0.001);
+
+ Settings *group3 = new Settings;
+ group3->set("cat", "meow");
+ group3->set("dog", "woof");
+
+ Settings *group2 = new Settings;
+ group2->setS16("num_apples", 4);
+ group2->setS16("num_oranges", 53);
+ group2->setGroup("animals", group3);
+ group2->set("animals", "cute"); //destroys group 3
+ s.setGroup("groupy_thing", group2);
+
+ // Test set failure conditions
+ UASSERT(s.set("Zoop = Poop\nsome_other_setting", "false") == false);
+ UASSERT(s.set("sneaky", "\"\"\"\njabberwocky = false") == false);
+ UASSERT(s.set("hehe", "asdfasdf\n\"\"\"\nsomething = false") == false);
+
+ // Test multiline settings
+ UASSERT(group->get("ccc") == "testy\n testa ");
+
+ UASSERT(s.get("blarg") ==
+ "some multiline text\n"
+ " with leading whitespace!");
+
+ // Test NoiseParams
+ UASSERT(s.getEntry("np_terrain").is_group == false);
+
+ NoiseParams np;
+ UASSERT(s.getNoiseParams("np_terrain", np) == true);
+ UASSERT(fabs(np.offset - 5) < 0.001);
+ UASSERT(fabs(np.scale - 40) < 0.001);
+ UASSERT(fabs(np.spread.X - 250) < 0.001);
+ UASSERT(fabs(np.spread.Y - 250) < 0.001);
+ UASSERT(fabs(np.spread.Z - 250) < 0.001);
+ UASSERT(np.seed == 12341);
+ UASSERT(np.octaves == 5);
+ UASSERT(fabs(np.persist - 0.7) < 0.001);
+
+ np.offset = 3.5;
+ np.octaves = 6;
+ s.setNoiseParams("np_terrain", np);
+
+ UASSERT(s.getEntry("np_terrain").is_group == true);
+
+ // Test writing
+ std::ostringstream os(std::ios_base::binary);
+ is.clear();
+ is.seekg(0);
+
+ UASSERT(s.updateConfigObject(is, os, "", 0) == true);
+ //printf(">>>> expected config:\n%s\n", TEST_CONFIG_TEXT_AFTER);
+ //printf(">>>> actual config:\n%s\n", os.str().c_str());
+ UASSERT(os.str() == config_text_after);
+ } catch (SettingNotFoundException &e) {
+ UASSERT(!"Setting not found!");
+ }
+}
diff --git a/src/unittest/test_socket.cpp b/src/unittest/test_socket.cpp
new file mode 100644
index 000000000..33e568e79
--- /dev/null
+++ b/src/unittest/test_socket.cpp
@@ -0,0 +1,151 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "log.h"
+#include "socket.h"
+#include "settings.h"
+
+class TestSocket : public TestBase {
+public:
+ TestSocket()
+ {
+ if (INTERNET_SIMULATOR == false)
+ TestManager::registerTestModule(this);
+ }
+
+ const char *getName() { return "TestSocket"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testIPv4Socket();
+ void testIPv6Socket();
+
+ static const int port = 30003;
+};
+
+static TestSocket g_test_instance;
+
+void TestSocket::runTests(IGameDef *gamedef)
+{
+ TEST(testIPv4Socket);
+
+ if (g_settings->getBool("enable_ipv6"))
+ TEST(testIPv6Socket);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestSocket::testIPv4Socket()
+{
+ Address address(0, 0, 0, 0, port);
+ Address bind_addr(0, 0, 0, 0, port);
+
+ /*
+ * Try to use the bind_address for servers with no localhost address
+ * For example: FreeBSD jails
+ */
+ std::string bind_str = g_settings->get("bind_address");
+ try {
+ bind_addr.Resolve(bind_str.c_str());
+
+ if (!bind_addr.isIPv6()) {
+ address = bind_addr;
+ }
+ } catch (ResolveError &e) {
+ }
+
+ UDPSocket socket(false);
+ socket.Bind(address);
+
+ const char sendbuffer[] = "hello world!";
+ /*
+ * If there is a bind address, use it.
+ * It's useful in container environments
+ */
+ if (address != Address(0, 0, 0, 0, port))
+ socket.Send(address, sendbuffer, sizeof(sendbuffer));
+ else
+ socket.Send(Address(127, 0, 0, 1, port), sendbuffer, sizeof(sendbuffer));
+
+ sleep_ms(50);
+
+ char rcvbuffer[256] = { 0 };
+ Address sender;
+ for (;;) {
+ if (socket.Receive(sender, rcvbuffer, sizeof(rcvbuffer)) < 0)
+ break;
+ }
+ //FIXME: This fails on some systems
+ UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer)) == 0);
+
+ if (address != Address(0, 0, 0, 0, port)) {
+ UASSERT(sender.getAddress().sin_addr.s_addr ==
+ address.getAddress().sin_addr.s_addr);
+ } else {
+ UASSERT(sender.getAddress().sin_addr.s_addr ==
+ Address(127, 0, 0, 1, 0).getAddress().sin_addr.s_addr);
+ }
+}
+
+void TestSocket::testIPv6Socket()
+{
+ Address address6((IPv6AddressBytes *)NULL, port);
+ UDPSocket socket6;
+
+ if (!socket6.init(true, true)) {
+ /* Note: Failing to create an IPv6 socket is not technically an
+ error because the OS may not support IPv6 or it may
+ have been disabled. IPv6 is not /required/ by
+ minetest and therefore this should not cause the unit
+ test to fail
+ */
+ dstream << "WARNING: IPv6 socket creation failed (unit test)"
+ << std::endl;
+ return;
+ }
+
+ const char sendbuffer[] = "hello world!";
+ IPv6AddressBytes bytes;
+ bytes.bytes[15] = 1;
+
+ socket6.Bind(address6);
+
+ try {
+ socket6.Send(Address(&bytes, port), sendbuffer, sizeof(sendbuffer));
+
+ sleep_ms(50);
+
+ char rcvbuffer[256] = { 0 };
+ Address sender;
+
+ for(;;) {
+ if (socket6.Receive(sender, rcvbuffer, sizeof(rcvbuffer)) < 0)
+ break;
+ }
+ //FIXME: This fails on some systems
+ UASSERT(strncmp(sendbuffer, rcvbuffer, sizeof(sendbuffer)) == 0);
+ UASSERT(memcmp(sender.getAddress6().sin6_addr.s6_addr,
+ Address(&bytes, 0).getAddress6().sin6_addr.s6_addr, 16) == 0);
+ } catch (SendFailedException &e) {
+ errorstream << "IPv6 support enabled but not available!"
+ << std::endl;
+ }
+}
diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp
new file mode 100644
index 000000000..df90d37bd
--- /dev/null
+++ b/src/unittest/test_utilities.cpp
@@ -0,0 +1,295 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "util/numeric.h"
+#include "util/string.h"
+
+class TestUtilities : public TestBase {
+public:
+ TestUtilities() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestUtilities"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testAngleWrapAround();
+ void testLowercase();
+ void testTrim();
+ void testIsYes();
+ void testRemoveStringEnd();
+ void testUrlEncode();
+ void testUrlDecode();
+ void testPadString();
+ void testStartsWith();
+ void testStrEqual();
+ void testStringTrim();
+ void testStrToIntConversion();
+ void testStringReplace();
+ void testStringAllowed();
+ void testUTF8();
+ void testWrapRows();
+ void testIsNumber();
+ void testIsPowerOfTwo();
+ void testMyround();
+};
+
+static TestUtilities g_test_instance;
+
+void TestUtilities::runTests(IGameDef *gamedef)
+{
+ TEST(testAngleWrapAround);
+ TEST(testLowercase);
+ TEST(testTrim);
+ TEST(testIsYes);
+ TEST(testRemoveStringEnd);
+ TEST(testUrlEncode);
+ TEST(testUrlDecode);
+ TEST(testPadString);
+ TEST(testStartsWith);
+ TEST(testStrEqual);
+ TEST(testStringTrim);
+ TEST(testStrToIntConversion);
+ TEST(testStringReplace);
+ TEST(testStringAllowed);
+ TEST(testUTF8);
+ TEST(testWrapRows);
+ TEST(testIsNumber);
+ TEST(testIsPowerOfTwo);
+ TEST(testMyround);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+inline float ref_WrapDegrees180(float f)
+{
+ // This is a slower alternative to the wrapDegrees_180() function;
+ // used as a reference for testing
+ float value = fmodf(f + 180, 360);
+ if (value < 0)
+ value += 360;
+ return value - 180;
+}
+
+
+inline float ref_WrapDegrees_0_360(float f)
+{
+ // This is a slower alternative to the wrapDegrees_0_360() function;
+ // used as a reference for testing
+ float value = fmodf(f, 360);
+ if (value < 0)
+ value += 360;
+ return value < 0 ? value + 360 : value;
+}
+
+
+void TestUtilities::testAngleWrapAround()
+{
+ UASSERT(fabs(modulo360f(100.0) - 100.0) < 0.001);
+ UASSERT(fabs(modulo360f(720.5) - 0.5) < 0.001);
+ UASSERT(fabs(modulo360f(-0.5) - (-0.5)) < 0.001);
+ UASSERT(fabs(modulo360f(-365.5) - (-5.5)) < 0.001);
+
+ for (float f = -720; f <= -360; f += 0.25) {
+ UASSERT(fabs(modulo360f(f) - modulo360f(f + 360)) < 0.001);
+ }
+
+ for (float f = -1440; f <= 1440; f += 0.25) {
+ UASSERT(fabs(modulo360f(f) - fmodf(f, 360)) < 0.001);
+ UASSERT(fabs(wrapDegrees_180(f) - ref_WrapDegrees180(f)) < 0.001);
+ UASSERT(fabs(wrapDegrees_0_360(f) - ref_WrapDegrees_0_360(f)) < 0.001);
+ UASSERT(wrapDegrees_0_360(fabs(wrapDegrees_180(f) - wrapDegrees_0_360(f))) < 0.001);
+ }
+}
+
+
+void TestUtilities::testLowercase()
+{
+ UASSERT(lowercase("Foo bAR") == "foo bar");
+}
+
+
+void TestUtilities::testTrim()
+{
+ UASSERT(trim("") == "");
+ UASSERT(trim("dirt_with_grass") == "dirt_with_grass");
+ UASSERT(trim("\n \t\r Foo bAR \r\n\t\t ") == "Foo bAR");
+ UASSERT(trim("\n \t\r \r\n\t\t ") == "");
+}
+
+
+void TestUtilities::testIsYes()
+{
+ UASSERT(is_yes("YeS") == true);
+ UASSERT(is_yes("") == false);
+ UASSERT(is_yes("FAlse") == false);
+ UASSERT(is_yes("-1") == true);
+ UASSERT(is_yes("0") == false);
+ UASSERT(is_yes("1") == true);
+ UASSERT(is_yes("2") == true);
+}
+
+
+void TestUtilities::testRemoveStringEnd()
+{
+ const char *ends[] = {"abc", "c", "bc", "", NULL};
+ UASSERT(removeStringEnd("abc", ends) == "");
+ UASSERT(removeStringEnd("bc", ends) == "b");
+ UASSERT(removeStringEnd("12c", ends) == "12");
+ UASSERT(removeStringEnd("foo", ends) == "");
+}
+
+
+void TestUtilities::testUrlEncode()
+{
+ UASSERT(urlencode("\"Aardvarks lurk, OK?\"")
+ == "%22Aardvarks%20lurk%2C%20OK%3F%22");
+}
+
+
+void TestUtilities::testUrlDecode()
+{
+ UASSERT(urldecode("%22Aardvarks%20lurk%2C%20OK%3F%22")
+ == "\"Aardvarks lurk, OK?\"");
+}
+
+
+void TestUtilities::testPadString()
+{
+ UASSERT(padStringRight("hello", 8) == "hello ");
+}
+
+void TestUtilities::testStartsWith()
+{
+ UASSERT(str_starts_with(std::string(), std::string()) == true);
+ UASSERT(str_starts_with(std::string("the sharp pickaxe"),
+ std::string()) == true);
+ UASSERT(str_starts_with(std::string("the sharp pickaxe"),
+ std::string("the")) == true);
+ UASSERT(str_starts_with(std::string("the sharp pickaxe"),
+ std::string("The")) == false);
+ UASSERT(str_starts_with(std::string("the sharp pickaxe"),
+ std::string("The"), true) == true);
+ UASSERT(str_starts_with(std::string("T"), std::string("The")) == false);
+}
+
+void TestUtilities::testStrEqual()
+{
+ UASSERT(str_equal(narrow_to_wide("abc"), narrow_to_wide("abc")));
+ UASSERT(str_equal(narrow_to_wide("ABC"), narrow_to_wide("abc"), true));
+}
+
+
+void TestUtilities::testStringTrim()
+{
+ UASSERT(trim(" a") == "a");
+ UASSERT(trim(" a ") == "a");
+ UASSERT(trim("a ") == "a");
+ UASSERT(trim("") == "");
+}
+
+
+void TestUtilities::testStrToIntConversion()
+{
+ UASSERT(mystoi("123", 0, 1000) == 123);
+ UASSERT(mystoi("123", 0, 10) == 10);
+}
+
+
+void TestUtilities::testStringReplace()
+{
+ std::string test_str;
+ test_str = "Hello there";
+ str_replace(test_str, "there", "world");
+ UASSERT(test_str == "Hello world");
+ test_str = "ThisAisAaAtest";
+ str_replace(test_str, 'A', ' ');
+ UASSERT(test_str == "This is a test");
+}
+
+
+void TestUtilities::testStringAllowed()
+{
+ UASSERT(string_allowed("hello", "abcdefghijklmno") == true);
+ UASSERT(string_allowed("123", "abcdefghijklmno") == false);
+ UASSERT(string_allowed_blacklist("hello", "123") == true);
+ UASSERT(string_allowed_blacklist("hello123", "123") == false);
+}
+
+void TestUtilities::testUTF8()
+{
+ UASSERT(wide_to_utf8(utf8_to_wide("")) == "");
+ UASSERT(wide_to_utf8(utf8_to_wide("the shovel dug a crumbly node!"))
+ == "the shovel dug a crumbly node!");
+}
+
+void TestUtilities::testWrapRows()
+{
+ UASSERT(wrap_rows("12345678",4) == "1234\n5678");
+ // test that wrap_rows doesn't wrap inside multibyte sequences
+ {
+ const unsigned char s[] = {
+ 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x72, 0x61, 0x70, 0x74, 0x6f,
+ 0x72, 0x2f, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0x2f,
+ 0x6d, 0x69, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x62, 0x69,
+ 0x6e, 0x2f, 0x2e, 0x2e, 0};
+ std::string str((char *)s);
+ UASSERT(utf8_to_wide(wrap_rows(str, 20)) != L"<invalid UTF-8 string>");
+ };
+ {
+ const unsigned char s[] = {
+ 0x74, 0x65, 0x73, 0x74, 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81,
+ 0xd1, 0x82, 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82,
+ 0x20, 0xd1, 0x82, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0};
+ std::string str((char *)s);
+ UASSERT(utf8_to_wide(wrap_rows(str, 8)) != L"<invalid UTF-8 string>");
+ }
+}
+
+
+void TestUtilities::testIsNumber()
+{
+ UASSERT(is_number("123") == true);
+ UASSERT(is_number("") == false);
+ UASSERT(is_number("123a") == false);
+}
+
+
+void TestUtilities::testIsPowerOfTwo()
+{
+ UASSERT(is_power_of_two(0) == false);
+ UASSERT(is_power_of_two(1) == true);
+ UASSERT(is_power_of_two(2) == true);
+ UASSERT(is_power_of_two(3) == false);
+ for (int exponent = 2; exponent <= 31; ++exponent) {
+ UASSERT(is_power_of_two((1 << exponent) - 1) == false);
+ UASSERT(is_power_of_two((1 << exponent)) == true);
+ UASSERT(is_power_of_two((1 << exponent) + 1) == false);
+ }
+ UASSERT(is_power_of_two((u32)-1) == false);
+}
+
+void TestUtilities::testMyround()
+{
+ UASSERT(myround(4.6f) == 5);
+ UASSERT(myround(1.2f) == 1);
+ UASSERT(myround(-3.1f) == -3);
+ UASSERT(myround(-6.5f) == -7);
+}
+
diff --git a/src/unittest/test_voxelalgorithms.cpp b/src/unittest/test_voxelalgorithms.cpp
new file mode 100644
index 000000000..31b9cadd5
--- /dev/null
+++ b/src/unittest/test_voxelalgorithms.cpp
@@ -0,0 +1,204 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include "gamedef.h"
+#include "voxelalgorithms.h"
+
+class TestVoxelAlgorithms : public TestBase {
+public:
+ TestVoxelAlgorithms() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestVoxelAlgorithms"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testPropogateSunlight(INodeDefManager *ndef);
+ void testClearLightAndCollectSources(INodeDefManager *ndef);
+};
+
+static TestVoxelAlgorithms g_test_instance;
+
+void TestVoxelAlgorithms::runTests(IGameDef *gamedef)
+{
+ INodeDefManager *ndef = gamedef->getNodeDefManager();
+
+ TEST(testPropogateSunlight, ndef);
+ TEST(testClearLightAndCollectSources, ndef);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestVoxelAlgorithms::testPropogateSunlight(INodeDefManager *ndef)
+{
+ VoxelManipulator v;
+
+ for (u16 z = 0; z < 3; z++)
+ for (u16 y = 0; y < 3; y++)
+ for (u16 x = 0; x < 3; x++) {
+ v3s16 p(x,y,z);
+ v.setNodeNoRef(p, MapNode(CONTENT_AIR));
+ }
+
+ VoxelArea a(v3s16(0,0,0), v3s16(2,2,2));
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, true, light_sources, ndef);
+ //v.print(dstream, ndef, VOXELPRINT_LIGHT_DAY);
+ UASSERT(res.bottom_sunlight_valid == true);
+ UASSERT(v.getNode(v3s16(1,1,1)).getLight(LIGHTBANK_DAY, ndef)
+ == LIGHT_SUN);
+ }
+
+ v.setNodeNoRef(v3s16(0,0,0), MapNode(t_CONTENT_STONE));
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, true, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ UASSERT(v.getNode(v3s16(1,1,1)).getLight(LIGHTBANK_DAY, ndef)
+ == LIGHT_SUN);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, false, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ UASSERT(v.getNode(v3s16(2,0,2)).getLight(LIGHTBANK_DAY, ndef)
+ == 0);
+ }
+
+ v.setNodeNoRef(v3s16(1,3,2), MapNode(t_CONTENT_STONE));
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, true, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ UASSERT(v.getNode(v3s16(1,1,2)).getLight(LIGHTBANK_DAY, ndef)
+ == 0);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, false, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ UASSERT(v.getNode(v3s16(1,0,2)).getLight(LIGHTBANK_DAY, ndef)
+ == 0);
+ }
+
+ {
+ MapNode n(CONTENT_AIR);
+ n.setLight(LIGHTBANK_DAY, 10, ndef);
+ v.setNodeNoRef(v3s16(1,-1,2), n);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, true, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, false, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ }
+
+ {
+ MapNode n(CONTENT_AIR);
+ n.setLight(LIGHTBANK_DAY, LIGHT_SUN, ndef);
+ v.setNodeNoRef(v3s16(1,-1,2), n);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, true, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == false);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, false, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == false);
+ }
+
+ v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_IGNORE));
+
+ {
+ std::set<v3s16> light_sources;
+ voxalgo::setLight(v, a, 0, ndef);
+ voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight(
+ v, a, true, light_sources, ndef);
+ UASSERT(res.bottom_sunlight_valid == true);
+ }
+}
+
+void TestVoxelAlgorithms::testClearLightAndCollectSources(INodeDefManager *ndef)
+{
+ VoxelManipulator v;
+
+ for (u16 z = 0; z < 3; z++)
+ for (u16 y = 0; y < 3; y++)
+ for (u16 x = 0; x < 3; x++) {
+ v3s16 p(x,y,z);
+ v.setNode(p, MapNode(CONTENT_AIR));
+ }
+
+ VoxelArea a(v3s16(0,0,0), v3s16(2,2,2));
+ v.setNodeNoRef(v3s16(0,0,0), MapNode(t_CONTENT_STONE));
+ v.setNodeNoRef(v3s16(1,1,1), MapNode(t_CONTENT_TORCH));
+
+ {
+ MapNode n(CONTENT_AIR);
+ n.setLight(LIGHTBANK_DAY, 1, ndef);
+ v.setNode(v3s16(1,1,2), n);
+ }
+
+ {
+ std::set<v3s16> light_sources;
+ std::map<v3s16, u8> unlight_from;
+ voxalgo::clearLightAndCollectSources(v, a, LIGHTBANK_DAY,
+ ndef, light_sources, unlight_from);
+ //v.print(dstream, ndef, VOXELPRINT_LIGHT_DAY);
+ UASSERT(v.getNode(v3s16(0,1,1)).getLight(LIGHTBANK_DAY, ndef) == 0);
+ UASSERT(light_sources.find(v3s16(1,1,1)) != light_sources.end());
+ UASSERT(light_sources.size() == 1);
+ UASSERT(unlight_from.find(v3s16(1,1,2)) != unlight_from.end());
+ UASSERT(unlight_from.size() == 1);
+ }
+}
diff --git a/src/unittest/test_voxelmanipulator.cpp b/src/unittest/test_voxelmanipulator.cpp
new file mode 100644
index 000000000..7f8ba3849
--- /dev/null
+++ b/src/unittest/test_voxelmanipulator.cpp
@@ -0,0 +1,108 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@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.
+*/
+
+#include "test.h"
+
+#include <algorithm>
+
+#include "gamedef.h"
+#include "log.h"
+#include "voxel.h"
+
+class TestVoxelManipulator : public TestBase {
+public:
+ TestVoxelManipulator() { TestManager::registerTestModule(this); }
+ const char *getName() { return "TestVoxelManipulator"; }
+
+ void runTests(IGameDef *gamedef);
+
+ void testVoxelArea();
+ void testVoxelManipulator(INodeDefManager *nodedef);
+};
+
+static TestVoxelManipulator g_test_instance;
+
+void TestVoxelManipulator::runTests(IGameDef *gamedef)
+{
+ TEST(testVoxelArea);
+ TEST(testVoxelManipulator, gamedef->getNodeDefManager());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void TestVoxelManipulator::testVoxelArea()
+{
+ VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1));
+ UASSERT(a.index(0,0,0) == 1*3*3 + 1*3 + 1);
+ UASSERT(a.index(-1,-1,-1) == 0);
+
+ VoxelArea c(v3s16(-2,-2,-2), v3s16(2,2,2));
+ // An area that is 1 bigger in x+ and z-
+ VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2));
+
+ std::list<VoxelArea> aa;
+ d.diff(c, aa);
+
+ // Correct results
+ std::vector<VoxelArea> results;
+ results.push_back(VoxelArea(v3s16(-2,-2,-3), v3s16(3,2,-3)));
+ results.push_back(VoxelArea(v3s16(3,-2,-2), v3s16(3,2,2)));
+
+ UASSERT(aa.size() == results.size());
+
+ infostream<<"Result of diff:"<<std::endl;
+ for (std::list<VoxelArea>::const_iterator
+ it = aa.begin(); it != aa.end(); ++it) {
+ it->print(infostream);
+ infostream << std::endl;
+
+ std::vector<VoxelArea>::iterator j;
+ j = std::find(results.begin(), results.end(), *it);
+ UASSERT(j != results.end());
+ results.erase(j);
+ }
+}
+
+
+void TestVoxelManipulator::testVoxelManipulator(INodeDefManager *nodedef)
+{
+ VoxelManipulator v;
+
+ v.print(infostream, nodedef);
+
+ infostream << "*** Setting (-1,0,-1)=2 ***" << std::endl;
+ v.setNodeNoRef(v3s16(-1,0,-1), MapNode(t_CONTENT_GRASS));
+
+ v.print(infostream, nodedef);
+ UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == t_CONTENT_GRASS);
+
+ infostream << "*** Reading from inexistent (0,0,-1) ***" << std::endl;
+
+ EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,0,-1)));
+ v.print(infostream, nodedef);
+
+ infostream << "*** Adding area ***" << std::endl;
+
+ VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1));
+ v.addArea(a);
+ v.print(infostream, nodedef);
+
+ UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == t_CONTENT_GRASS);
+ EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1)));
+}
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
index 9cb8a19b6..33900a43a 100644
--- a/src/util/CMakeLists.txt
+++ b/src/util/CMakeLists.txt
@@ -1,8 +1,14 @@
set(UTIL_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/sha256.c
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
PARENT_SCOPE)
+
diff --git a/src/util/auth.cpp b/src/util/auth.cpp
new file mode 100644
index 000000000..df8940e87
--- /dev/null
+++ b/src/util/auth.cpp
@@ -0,0 +1,126 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <MTest31@outlook.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.
+*/
+
+#include <algorithm>
+#include <string>
+#include "auth.h"
+#include "base64.h"
+#include "sha1.h"
+#include "srp.h"
+#include "string.h"
+
+// Get an sha-1 hash of the player's name combined with
+// the password entered. That's what the server uses as
+// their password. (Exception : if the password field is
+// blank, we send a blank password - this is for backwards
+// compatibility with password-less players).
+std::string translatePassword(const std::string &name,
+ const std::string &password)
+{
+ if (password.length() == 0)
+ return "";
+
+ std::string slt = name + password;
+ SHA1 sha1;
+ sha1.addBytes(slt.c_str(), slt.length());
+ unsigned char *digest = sha1.getDigest();
+ std::string pwd = base64_encode(digest, 20);
+ free(digest);
+ return pwd;
+}
+
+void getSRPVerifier(const std::string &name,
+ const std::string &password, char **salt, size_t *salt_len,
+ char **bytes_v, size_t *len_v)
+{
+ std::string n_name = lowercase(name);
+ srp_create_salted_verification_key(SRP_SHA256, SRP_NG_2048,
+ n_name.c_str(), (const unsigned char *)password.c_str(),
+ password.size(), (unsigned char **)salt, salt_len,
+ (unsigned char **)bytes_v, len_v, NULL, NULL);
+}
+
+// Get a db-ready SRP verifier
+// If the salt param is NULL, one is automatically generated.
+// Please free() it afterwards. You shouldn't use it for other purposes,
+// as you will need the contents of salt_len too.
+inline static std::string getSRPVerifier(const std::string &name,
+ const std::string &password, char ** salt, size_t salt_len)
+{
+ char * bytes_v = NULL;
+ size_t len_v;
+ getSRPVerifier(name, password, salt, &salt_len,
+ &bytes_v, &len_v);
+ std::string ret_val = encodeSRPVerifier(std::string(bytes_v, len_v),
+ std::string(*salt, salt_len));
+ free(bytes_v);
+ return ret_val;
+}
+
+// Get a db-ready SRP verifier
+std::string getSRPVerifier(const std::string &name,
+ const std::string &password)
+{
+ char * salt = NULL;
+ std::string ret_val = getSRPVerifier(name,
+ password, &salt, 0);
+ free(salt);
+ return ret_val;
+}
+
+// Get a db-ready SRP verifier
+std::string getSRPVerifier(const std::string &name,
+ const std::string &password, const std::string &salt)
+{
+ // The implementation won't change the salt if its set,
+ // therefore we can cast.
+ char *salt_cstr = (char *)salt.c_str();
+ return getSRPVerifier(name, password,
+ &salt_cstr, salt.size());
+}
+
+// Make a SRP verifier db-ready
+std::string encodeSRPVerifier(const std::string &verifier,
+ const std::string &salt)
+{
+ std::ostringstream ret_str;
+ ret_str << "#1#"
+ << base64_encode((unsigned char*) salt.c_str(), salt.size()) << "#"
+ << base64_encode((unsigned char*) verifier.c_str(), verifier.size());
+ return ret_str.str();
+}
+
+bool decodeSRPVerifier(const std::string &enc_pwd,
+ std::string *salt, std::string *bytes_v)
+{
+ std::vector<std::string> pwd_components = str_split(enc_pwd, '#');
+
+ if ((pwd_components.size() != 4)
+ || (pwd_components[1] != "1") // 1 means srp
+ || !base64_is_valid(pwd_components[2])
+ || !base64_is_valid(pwd_components[3]))
+ return false;
+
+ std::string salt_str = base64_decode(pwd_components[2]);
+ std::string bytes_v_str = base64_decode(pwd_components[3]);
+ *salt = salt_str;
+ *bytes_v = bytes_v_str;
+ return true;
+
+}
diff --git a/src/util/auth.h b/src/util/auth.h
new file mode 100644
index 000000000..36d8c20a4
--- /dev/null
+++ b/src/util/auth.h
@@ -0,0 +1,37 @@
+/*
+Minetest
+Copyright (C) 2015 est31 <MTest31@outlook.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.
+*/
+
+#ifndef AUTH_H
+#define AUTH_H
+
+std::string translatePassword(const std::string &name,
+ const std::string &password);
+void getSRPVerifier(const std::string &name,
+ const std::string &password, char **salt, size_t *salt_len,
+ char **bytes_v, size_t *len_v);
+std::string getSRPVerifier(const std::string &name,
+ const std::string &password);
+std::string getSRPVerifier(const std::string &name,
+ const std::string &password, const std::string &salt);
+std::string encodeSRPVerifier(const std::string &verifier,
+ const std::string &salt);
+bool decodeSRPVerifier(const std::string &enc_pwd,
+ std::string *salt, std::string *bytes_v);
+
+#endif \ No newline at end of file
diff --git a/src/base64.cpp b/src/util/base64.cpp
index e14de7de2..e14de7de2 100644
--- a/src/base64.cpp
+++ b/src/util/base64.cpp
diff --git a/src/base64.h b/src/util/base64.h
index 1cb175518..1cb175518 100644
--- a/src/base64.h
+++ b/src/util/base64.h
diff --git a/src/util/container.h b/src/util/container.h
index 5e9f13d88..267d54c16 100644
--- a/src/util/container.h
+++ b/src/util/container.h
@@ -77,7 +77,6 @@ private:
std::queue<Value> m_queue;
};
-#if 1
template<typename Key, typename Value>
class MutexedMap
{
@@ -109,9 +108,9 @@ public:
return true;
}
- std::list<Value> getValues()
+ std::vector<Value> getValues()
{
- std::list<Value> result;
+ std::vector<Value> result;
for(typename std::map<Key, Value>::iterator
i = m_values.begin();
i != m_values.end(); ++i){
@@ -129,7 +128,6 @@ private:
std::map<Key, Value> m_values;
JMutex m_mutex;
};
-#endif
/*
Generates ids for comparable values.
@@ -186,67 +184,6 @@ private:
};
/*
-FIFO queue (well, actually a FILO also)
-*/
-template<typename T>
-class Queue
-{
-public:
- Queue():
- m_list_size(0)
- {}
-
- void push_back(T t)
- {
- m_list.push_back(t);
- ++m_list_size;
- }
-
- void push_front(T t)
- {
- m_list.push_front(t);
- ++m_list_size;
- }
-
- T pop_front()
- {
- if(m_list.empty())
- throw ItemNotFoundException("Queue: queue is empty");
-
- typename std::list<T>::iterator begin = m_list.begin();
- T t = *begin;
- m_list.erase(begin);
- --m_list_size;
- return t;
- }
- T pop_back()
- {
- if(m_list.empty())
- throw ItemNotFoundException("Queue: queue is empty");
-
- typename std::list<T>::iterator last = m_list.back();
- T t = *last;
- m_list.erase(last);
- --m_list_size;
- return t;
- }
-
- u32 size()
- {
- return m_list_size;
- }
-
- bool empty()
- {
- return m_list.empty();
- }
-
-protected:
- std::list<T> m_list;
- u32 m_list_size;
-};
-
-/*
Thread-safe FIFO queue (well, actually a FILO also)
*/
@@ -263,12 +200,12 @@ public:
bool empty()
{
JMutexAutoLock lock(m_mutex);
- return (m_size.GetValue() == 0);
+ return (m_queue.size() == 0);
}
void push_back(T t)
{
JMutexAutoLock lock(m_mutex);
- m_list.push_back(t);
+ m_queue.push_back(t);
m_size.Post();
}
@@ -277,34 +214,28 @@ public:
*/
T pop_frontNoEx(u32 wait_time_max_ms)
{
- if (m_size.Wait(wait_time_max_ms))
- {
+ if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
- typename std::list<T>::iterator begin = m_list.begin();
- T t = *begin;
- m_list.erase(begin);
+ T t = m_queue.front();
+ m_queue.pop_front();
return t;
}
- else
- {
+ else {
return T();
}
}
T pop_front(u32 wait_time_max_ms)
{
- if (m_size.Wait(wait_time_max_ms))
- {
+ if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
- typename std::list<T>::iterator begin = m_list.begin();
- T t = *begin;
- m_list.erase(begin);
+ T t = m_queue.front();
+ m_queue.pop_front();
return t;
}
- else
- {
+ else {
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
}
@@ -315,26 +246,21 @@ public:
JMutexAutoLock lock(m_mutex);
- typename std::list<T>::iterator begin = m_list.begin();
- T t = *begin;
- m_list.erase(begin);
+ T t = m_queue.front();
+ m_queue.pop_front();
return t;
}
T pop_back(u32 wait_time_max_ms=0)
{
- if (m_size.Wait(wait_time_max_ms))
- {
+ if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
- typename std::list<T>::iterator last = m_list.end();
- last--;
- T t = *last;
- m_list.erase(last);
+ T t = m_queue.back();
+ m_queue.pop_back();
return t;
}
- else
- {
+ else {
throw ItemNotFoundException("MutexedQueue: queue is empty");
}
}
@@ -344,18 +270,14 @@ public:
*/
T pop_backNoEx(u32 wait_time_max_ms=0)
{
- if (m_size.Wait(wait_time_max_ms))
- {
+ if (m_size.Wait(wait_time_max_ms)) {
JMutexAutoLock lock(m_mutex);
- typename std::list<T>::iterator last = m_list.end();
- last--;
- T t = *last;
- m_list.erase(last);
+ T t = m_queue.back();
+ m_queue.pop_back();
return t;
}
- else
- {
+ else {
return T();
}
}
@@ -366,10 +288,8 @@ public:
JMutexAutoLock lock(m_mutex);
- typename std::list<T>::iterator last = m_list.end();
- last--;
- T t = *last;
- m_list.erase(last);
+ T t = m_queue.back();
+ m_queue.pop_back();
return t;
}
@@ -379,17 +299,84 @@ protected:
return m_mutex;
}
- // NEVER EVER modify the >>list<< you got by using this function!
- // You may only modify it's content
- std::list<T> & getList()
+ std::deque<T> & getQueue()
{
- return m_list;
+ return m_queue;
}
+ std::deque<T> m_queue;
JMutex m_mutex;
- std::list<T> m_list;
JSemaphore m_size;
};
+template<typename K, typename V>
+class LRUCache
+{
+public:
+ LRUCache(size_t limit, void (*cache_miss)(void *data, const K &key, V *dest),
+ void *data)
+ {
+ m_limit = limit;
+ m_cache_miss = cache_miss;
+ m_cache_miss_data = data;
+ }
+
+ void setLimit(size_t limit)
+ {
+ m_limit = limit;
+ invalidate();
+ }
+
+ void invalidate()
+ {
+ m_map.clear();
+ m_queue.clear();
+ }
+
+ const V *lookupCache(K key)
+ {
+ typename cache_type::iterator it = m_map.find(key);
+ V *ret;
+ if (it != m_map.end()) {
+ // found!
+
+ cache_entry_t &entry = it->second;
+
+ ret = &entry.second;
+
+ // update the usage information
+ m_queue.erase(entry.first);
+ m_queue.push_front(key);
+ entry.first = m_queue.begin();
+ } else {
+ // cache miss -- enter into cache
+ cache_entry_t &entry =
+ m_map[key];
+ ret = &entry.second;
+ m_cache_miss(m_cache_miss_data, key, &entry.second);
+
+ // delete old entries
+ if (m_queue.size() == m_limit) {
+ const K &id = m_queue.back();
+ m_map.erase(id);
+ m_queue.pop_back();
+ }
+
+ m_queue.push_front(key);
+ entry.first = m_queue.begin();
+ }
+ return ret;
+ }
+private:
+ void (*m_cache_miss)(void *data, const K &key, V *dest);
+ void *m_cache_miss_data;
+ size_t m_limit;
+ typedef typename std::template pair<typename std::template list<K>::iterator, V> cache_entry_t;
+ typedef std::template map<K, cache_entry_t> cache_type;
+ cache_type m_map;
+ // we can't use std::deque here, because its iterators get invalidated
+ std::list<K> m_queue;
+};
+
#endif
diff --git a/src/hex.h b/src/util/hex.h
index 6f00a79bf..6f00a79bf 100644
--- a/src/hex.h
+++ b/src/util/hex.h
diff --git a/src/util/md32_common.h b/src/util/md32_common.h
new file mode 100644
index 000000000..085d1d7a5
--- /dev/null
+++ b/src/util/md32_common.h
@@ -0,0 +1,428 @@
+/* md32_common.h file used by sha256 implementation */
+/* ====================================================================
+ * Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ */
+
+/*-
+ * This is a generic 32 bit "collector" for message digest algorithms.
+ * Whenever needed it collects input character stream into chunks of
+ * 32 bit values and invokes a block function that performs actual hash
+ * calculations.
+ *
+ * Porting guide.
+ *
+ * Obligatory macros:
+ *
+ * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
+ * this macro defines byte order of input stream.
+ * HASH_CBLOCK
+ * size of a unit chunk HASH_BLOCK operates on.
+ * HASH_LONG
+ * has to be at lest 32 bit wide, if it's wider, then
+ * HASH_LONG_LOG2 *has to* be defined along
+ * HASH_CTX
+ * context structure that at least contains following
+ * members:
+ * typedef struct {
+ * ...
+ * HASH_LONG Nl,Nh;
+ * either {
+ * HASH_LONG data[HASH_LBLOCK];
+ * unsigned char data[HASH_CBLOCK];
+ * };
+ * unsigned int num;
+ * ...
+ * } HASH_CTX;
+ * data[] vector is expected to be zeroed upon first call to
+ * HASH_UPDATE.
+ * HASH_UPDATE
+ * name of "Update" function, implemented here.
+ * HASH_TRANSFORM
+ * name of "Transform" function, implemented here.
+ * HASH_FINAL
+ * name of "Final" function, implemented here.
+ * HASH_BLOCK_DATA_ORDER
+ * name of "block" function capable of treating *unaligned* input
+ * message in original (data) byte order, implemented externally.
+ * HASH_MAKE_STRING
+ * macro convering context variables to an ASCII hash string.
+ *
+ * MD5 example:
+ *
+ * #define DATA_ORDER_IS_LITTLE_ENDIAN
+ *
+ * #define HASH_LONG MD5_LONG
+ * #define HASH_LONG_LOG2 MD5_LONG_LOG2
+ * #define HASH_CTX MD5_CTX
+ * #define HASH_CBLOCK MD5_CBLOCK
+ * #define HASH_UPDATE MD5_Update
+ * #define HASH_TRANSFORM MD5_Transform
+ * #define HASH_FINAL MD5_Final
+ * #define HASH_BLOCK_DATA_ORDER md5_block_data_order
+ *
+ * <appro@fy.chalmers.se>
+ */
+
+#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+# error "DATA_ORDER must be defined!"
+#endif
+
+#ifndef HASH_CBLOCK
+# error "HASH_CBLOCK must be defined!"
+#endif
+#ifndef HASH_LONG
+# error "HASH_LONG must be defined!"
+#endif
+#ifndef HASH_CTX
+# error "HASH_CTX must be defined!"
+#endif
+
+#ifndef HASH_UPDATE
+# error "HASH_UPDATE must be defined!"
+#endif
+#ifndef HASH_TRANSFORM
+# error "HASH_TRANSFORM must be defined!"
+#endif
+#ifndef HASH_FINAL
+# error "HASH_FINAL must be defined!"
+#endif
+
+#ifndef HASH_BLOCK_DATA_ORDER
+# error "HASH_BLOCK_DATA_ORDER must be defined!"
+#endif
+
+/*
+ * Engage compiler specific rotate intrinsic function if available.
+ */
+#undef ROTATE
+#ifndef PEDANTIC
+# if defined(_MSC_VER)
+# define ROTATE(a,n) _lrotl(a,n)
+# elif defined(__ICC)
+# define ROTATE(a,n) _rotl(a,n)
+# elif defined(__MWERKS__)
+# if defined(__POWERPC__)
+# define ROTATE(a,n) __rlwinm(a,n,0,31)
+# elif defined(__MC68K__)
+ /* Motorola specific tweak. <appro@fy.chalmers.se> */
+# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) )
+# else
+# define ROTATE(a,n) __rol(a,n)
+# endif
+# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+ /*
+ * Some GNU C inline assembler templates. Note that these are
+ * rotates by *constant* number of bits! But that's exactly
+ * what we need here...
+ * <appro@fy.chalmers.se>
+ */
+# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+# define ROTATE(a,n) ({ register unsigned int ret; \
+ asm ( \
+ "roll %1,%0" \
+ : "=r"(ret) \
+ : "I"(n), "0"((unsigned int)(a)) \
+ : "cc"); \
+ ret; \
+ })
+# elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
+ defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__)
+# define ROTATE(a,n) ({ register unsigned int ret; \
+ asm ( \
+ "rlwinm %0,%1,%2,0,31" \
+ : "=r"(ret) \
+ : "r"(a), "I"(n)); \
+ ret; \
+ })
+# elif defined(__s390x__)
+# define ROTATE(a,n) ({ register unsigned int ret; \
+ asm ("rll %0,%1,%2" \
+ : "=r"(ret) \
+ : "r"(a), "I"(n)); \
+ ret; \
+ })
+# endif
+# endif
+#endif /* PEDANTIC */
+
+#ifndef ROTATE
+# define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
+
+#if defined(DATA_ORDER_IS_BIG_ENDIAN)
+
+# ifndef PEDANTIC
+# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+# if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \
+ (defined(__x86_64) || defined(__x86_64__))
+# if !defined(B_ENDIAN)
+ /*
+ * This gives ~30-40% performance improvement in SHA-256 compiled
+ * with gcc [on P4]. Well, first macro to be frank. We can pull
+ * this trick on x86* platforms only, because these CPUs can fetch
+ * unaligned data without raising an exception.
+ */
+# define HOST_c2l(c,l) ({ unsigned int r=*((const unsigned int *)(c)); \
+ asm ("bswapl %0":"=r"(r):"0"(r)); \
+ (c)+=4; (l)=r; })
+# define HOST_l2c(l,c) ({ unsigned int r=(l); \
+ asm ("bswapl %0":"=r"(r):"0"(r)); \
+ *((unsigned int *)(c))=r; (c)+=4; r; })
+# endif
+# elif defined(__aarch64__)
+# if defined(__BYTE_ORDER__)
+# if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define HOST_c2l(c,l) ({ unsigned int r; \
+ asm ("rev %w0,%w1" \
+ :"=r"(r) \
+ :"r"(*((const unsigned int *)(c))));\
+ (c)+=4; (l)=r; })
+# define HOST_l2c(l,c) ({ unsigned int r; \
+ asm ("rev %w0,%w1" \
+ :"=r"(r) \
+ :"r"((unsigned int)(l)));\
+ *((unsigned int *)(c))=r; (c)+=4; r; })
+# elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
+# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
+# endif
+# endif
+# endif
+# endif
+# if defined(__s390__) || defined(__s390x__)
+# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
+# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
+# endif
+# endif
+
+# ifndef HOST_c2l
+# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
+ l|=(((unsigned long)(*((c)++)))<<16), \
+ l|=(((unsigned long)(*((c)++)))<< 8), \
+ l|=(((unsigned long)(*((c)++))) ) )
+# endif
+# ifndef HOST_l2c
+# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+ *((c)++)=(unsigned char)(((l) )&0xff), \
+ l)
+# endif
+
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+
+# ifndef PEDANTIC
+# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+# if defined(__s390x__)
+# define HOST_c2l(c,l) ({ asm ("lrv %0,%1" \
+ :"=d"(l) :"m"(*(const unsigned int *)(c)));\
+ (c)+=4; (l); })
+# define HOST_l2c(l,c) ({ asm ("strv %1,%0" \
+ :"=m"(*(unsigned int *)(c)) :"d"(l));\
+ (c)+=4; (l); })
+# endif
+# endif
+# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+# ifndef B_ENDIAN
+ /* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */
+# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, l)
+# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, l)
+# endif
+# endif
+# endif
+
+# ifndef HOST_c2l
+# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
+ l|=(((unsigned long)(*((c)++)))<< 8), \
+ l|=(((unsigned long)(*((c)++)))<<16), \
+ l|=(((unsigned long)(*((c)++)))<<24) )
+# endif
+# ifndef HOST_l2c
+# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>16)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>24)&0xff), \
+ l)
+# endif
+
+#endif
+
+/*
+ * Time for some action:-)
+ */
+
+int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
+{
+ const unsigned char *data = data_;
+ unsigned char *p;
+ HASH_LONG l;
+ size_t n;
+
+ if (len == 0)
+ return 1;
+
+ l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;
+ /*
+ * 95-05-24 eay Fixed a bug with the overflow handling, thanks to Wei Dai
+ * <weidai@eskimo.com> for pointing it out.
+ */
+ if (l < c->Nl) /* overflow */
+ c->Nh++;
+ c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on
+ * 16-bit */
+ c->Nl = l;
+
+ n = c->num;
+ if (n != 0) {
+ p = (unsigned char *)c->data;
+
+ if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
+ memcpy(p + n, data, HASH_CBLOCK - n);
+ HASH_BLOCK_DATA_ORDER(c, p, 1);
+ n = HASH_CBLOCK - n;
+ data += n;
+ len -= n;
+ c->num = 0;
+ memset(p, 0, HASH_CBLOCK); /* keep it zeroed */
+ } else {
+ memcpy(p + n, data, len);
+ c->num += (unsigned int)len;
+ return 1;
+ }
+ }
+
+ n = len / HASH_CBLOCK;
+ if (n > 0) {
+ HASH_BLOCK_DATA_ORDER(c, data, n);
+ n *= HASH_CBLOCK;
+ data += n;
+ len -= n;
+ }
+
+ if (len != 0) {
+ p = (unsigned char *)c->data;
+ c->num = (unsigned int)len;
+ memcpy(p, data, len);
+ }
+ return 1;
+}
+
+void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)
+{
+ HASH_BLOCK_DATA_ORDER(c, data, 1);
+}
+
+int HASH_FINAL(unsigned char *md, HASH_CTX *c)
+{
+ unsigned char *p = (unsigned char *)c->data;
+ size_t n = c->num;
+
+ p[n] = 0x80; /* there is always room for one */
+ n++;
+
+ if (n > (HASH_CBLOCK - 8)) {
+ memset(p + n, 0, HASH_CBLOCK - n);
+ n = 0;
+ HASH_BLOCK_DATA_ORDER(c, p, 1);
+ }
+ memset(p + n, 0, HASH_CBLOCK - 8 - n);
+
+ p += HASH_CBLOCK - 8;
+#if defined(DATA_ORDER_IS_BIG_ENDIAN)
+ (void)HOST_l2c(c->Nh, p);
+ (void)HOST_l2c(c->Nl, p);
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+ (void)HOST_l2c(c->Nl, p);
+ (void)HOST_l2c(c->Nh, p);
+#endif
+ p -= HASH_CBLOCK;
+ HASH_BLOCK_DATA_ORDER(c, p, 1);
+ c->num = 0;
+ memset(p, 0, HASH_CBLOCK);
+
+#ifndef HASH_MAKE_STRING
+# error "HASH_MAKE_STRING must be defined!"
+#else
+ HASH_MAKE_STRING(c, md);
+#endif
+
+ return 1;
+}
+
+#ifndef MD32_REG_T
+# if defined(__alpha) || defined(__sparcv9) || defined(__mips)
+# define MD32_REG_T long
+/*
+ * This comment was originaly written for MD5, which is why it
+ * discusses A-D. But it basically applies to all 32-bit digests,
+ * which is why it was moved to common header file.
+ *
+ * In case you wonder why A-D are declared as long and not
+ * as MD5_LONG. Doing so results in slight performance
+ * boost on LP64 architectures. The catch is we don't
+ * really care if 32 MSBs of a 64-bit register get polluted
+ * with eventual overflows as we *save* only 32 LSBs in
+ * *either* case. Now declaring 'em long excuses the compiler
+ * from keeping 32 MSBs zeroed resulting in 13% performance
+ * improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
+ * Well, to be honest it should say that this *prevents*
+ * performance degradation.
+ * <appro@fy.chalmers.se>
+ */
+# else
+/*
+ * Above is not absolute and there are LP64 compilers that
+ * generate better code if MD32_REG_T is defined int. The above
+ * pre-processor condition reflects the circumstances under which
+ * the conclusion was made and is subject to further extension.
+ * <appro@fy.chalmers.se>
+ */
+# define MD32_REG_T int
+# endif
+#endif
diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp
index 6173515ee..3fd1c9cf9 100644
--- a/src/util/numeric.cpp
+++ b/src/util/numeric.cpp
@@ -20,79 +20,89 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "numeric.h"
#include "mathconstants.h"
-#include "../log.h"
+#include "log.h"
#include "../constants.h" // BS, MAP_BLOCKSIZE
+#include "../noise.h" // PseudoRandom, PcgRandom
+#include "../jthread/jmutexautolock.h"
#include <string.h>
#include <iostream>
+std::map<u16, std::vector<v3s16> > FacePositionCache::m_cache;
+JMutex FacePositionCache::m_cache_mutex;
// Calculate the borders of a "d-radius" cube
-void getFacePositions(std::list<v3s16> &list, u16 d)
+// TODO: Make it work without mutex and data races, probably thread-local
+std::vector<v3s16> FacePositionCache::getFacePositions(u16 d)
{
- if(d == 0)
- {
- list.push_back(v3s16(0,0,0));
+ JMutexAutoLock cachelock(m_cache_mutex);
+ if (m_cache.find(d) != m_cache.end())
+ return m_cache[d];
+
+ generateFacePosition(d);
+ return m_cache[d];
+
+}
+
+void FacePositionCache::generateFacePosition(u16 d)
+{
+ m_cache[d] = std::vector<v3s16>();
+ if(d == 0) {
+ m_cache[d].push_back(v3s16(0,0,0));
return;
}
- if(d == 1)
- {
+ if(d == 1) {
/*
This is an optimized sequence of coordinates.
*/
- list.push_back(v3s16( 0, 1, 0)); // top
- list.push_back(v3s16( 0, 0, 1)); // back
- list.push_back(v3s16(-1, 0, 0)); // left
- list.push_back(v3s16( 1, 0, 0)); // right
- list.push_back(v3s16( 0, 0,-1)); // front
- list.push_back(v3s16( 0,-1, 0)); // bottom
+ m_cache[d].push_back(v3s16( 0, 1, 0)); // top
+ m_cache[d].push_back(v3s16( 0, 0, 1)); // back
+ m_cache[d].push_back(v3s16(-1, 0, 0)); // left
+ m_cache[d].push_back(v3s16( 1, 0, 0)); // right
+ m_cache[d].push_back(v3s16( 0, 0,-1)); // front
+ m_cache[d].push_back(v3s16( 0,-1, 0)); // bottom
// 6
- list.push_back(v3s16(-1, 0, 1)); // back left
- list.push_back(v3s16( 1, 0, 1)); // back right
- list.push_back(v3s16(-1, 0,-1)); // front left
- list.push_back(v3s16( 1, 0,-1)); // front right
- list.push_back(v3s16(-1,-1, 0)); // bottom left
- list.push_back(v3s16( 1,-1, 0)); // bottom right
- list.push_back(v3s16( 0,-1, 1)); // bottom back
- list.push_back(v3s16( 0,-1,-1)); // bottom front
- list.push_back(v3s16(-1, 1, 0)); // top left
- list.push_back(v3s16( 1, 1, 0)); // top right
- list.push_back(v3s16( 0, 1, 1)); // top back
- list.push_back(v3s16( 0, 1,-1)); // top front
+ m_cache[d].push_back(v3s16(-1, 0, 1)); // back left
+ m_cache[d].push_back(v3s16( 1, 0, 1)); // back right
+ m_cache[d].push_back(v3s16(-1, 0,-1)); // front left
+ m_cache[d].push_back(v3s16( 1, 0,-1)); // front right
+ m_cache[d].push_back(v3s16(-1,-1, 0)); // bottom left
+ m_cache[d].push_back(v3s16( 1,-1, 0)); // bottom right
+ m_cache[d].push_back(v3s16( 0,-1, 1)); // bottom back
+ m_cache[d].push_back(v3s16( 0,-1,-1)); // bottom front
+ m_cache[d].push_back(v3s16(-1, 1, 0)); // top left
+ m_cache[d].push_back(v3s16( 1, 1, 0)); // top right
+ m_cache[d].push_back(v3s16( 0, 1, 1)); // top back
+ m_cache[d].push_back(v3s16( 0, 1,-1)); // top front
// 18
- list.push_back(v3s16(-1, 1, 1)); // top back-left
- list.push_back(v3s16( 1, 1, 1)); // top back-right
- list.push_back(v3s16(-1, 1,-1)); // top front-left
- list.push_back(v3s16( 1, 1,-1)); // top front-right
- list.push_back(v3s16(-1,-1, 1)); // bottom back-left
- list.push_back(v3s16( 1,-1, 1)); // bottom back-right
- list.push_back(v3s16(-1,-1,-1)); // bottom front-left
- list.push_back(v3s16( 1,-1,-1)); // bottom front-right
+ m_cache[d].push_back(v3s16(-1, 1, 1)); // top back-left
+ m_cache[d].push_back(v3s16( 1, 1, 1)); // top back-right
+ m_cache[d].push_back(v3s16(-1, 1,-1)); // top front-left
+ m_cache[d].push_back(v3s16( 1, 1,-1)); // top front-right
+ m_cache[d].push_back(v3s16(-1,-1, 1)); // bottom back-left
+ m_cache[d].push_back(v3s16( 1,-1, 1)); // bottom back-right
+ m_cache[d].push_back(v3s16(-1,-1,-1)); // bottom front-left
+ m_cache[d].push_back(v3s16( 1,-1,-1)); // bottom front-right
// 26
return;
}
// Take blocks in all sides, starting from y=0 and going +-y
- for(s16 y=0; y<=d-1; y++)
- {
+ for(s16 y=0; y<=d-1; y++) {
// Left and right side, including borders
- for(s16 z=-d; z<=d; z++)
- {
- list.push_back(v3s16(d,y,z));
- list.push_back(v3s16(-d,y,z));
- if(y != 0)
- {
- list.push_back(v3s16(d,-y,z));
- list.push_back(v3s16(-d,-y,z));
+ for(s16 z=-d; z<=d; z++) {
+ m_cache[d].push_back(v3s16(d,y,z));
+ m_cache[d].push_back(v3s16(-d,y,z));
+ if(y != 0) {
+ m_cache[d].push_back(v3s16(d,-y,z));
+ m_cache[d].push_back(v3s16(-d,-y,z));
}
}
// Back and front side, excluding borders
- for(s16 x=-d+1; x<=d-1; x++)
- {
- list.push_back(v3s16(x,y,d));
- list.push_back(v3s16(x,y,-d));
- if(y != 0)
- {
- list.push_back(v3s16(x,-y,d));
- list.push_back(v3s16(x,-y,-d));
+ for(s16 x=-d+1; x<=d-1; x++) {
+ m_cache[d].push_back(v3s16(x,y,d));
+ m_cache[d].push_back(v3s16(x,y,-d));
+ if(y != 0) {
+ m_cache[d].push_back(v3s16(x,-y,d));
+ m_cache[d].push_back(v3s16(x,-y,-d));
}
}
}
@@ -100,10 +110,9 @@ void getFacePositions(std::list<v3s16> &list, u16 d)
// Take the bottom and top face with borders
// -d<x<d, y=+-d, -d<z<d
for(s16 x=-d; x<=d; x++)
- for(s16 z=-d; z<=d; z++)
- {
- list.push_back(v3s16(x,-d,z));
- list.push_back(v3s16(x,d,z));
+ for(s16 z=-d; z<=d; z++) {
+ m_cache[d].push_back(v3s16(x,-d,z));
+ m_cache[d].push_back(v3s16(x,d,z));
}
}
@@ -111,36 +120,32 @@ void getFacePositions(std::list<v3s16> &list, u16 d)
myrand
*/
-static unsigned long next = 1;
+PcgRandom g_pcgrand;
+
+u32 myrand()
+{
+ return g_pcgrand.next();
+}
-/* RAND_MAX assumed to be 32767 */
-int myrand(void)
+void mysrand(unsigned int seed)
{
- next = next * 1103515245 + 12345;
- return((unsigned)(next/65536) % 32768);
+ g_pcgrand.seed(seed);
}
-void mysrand(unsigned seed)
+void myrand_bytes(void *out, size_t len)
{
- next = seed;
+ g_pcgrand.bytes(out, len);
}
int myrand_range(int min, int max)
{
- if(max-min > MYRAND_MAX)
- {
- errorstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"<<std::endl;
- max = min + MYRAND_MAX;
- }
- if(min > max)
- {
- errorstream<<"WARNING: myrand_range: min > max"<<std::endl;
- return max;
- }
- return (myrand()%(max-min+1))+min;
+ return g_pcgrand.range(min, max);
}
-// 64-bit unaligned version of MurmurHash
+
+/*
+ 64-bit unaligned version of MurmurHash
+*/
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
{
const u64 m = 0xc6a4a7935bd1e995ULL;
@@ -155,12 +160,12 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
memcpy(&k, data, sizeof(u64));
data++;
- k *= m;
- k ^= k >> r;
- k *= m;
-
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
h ^= k;
- h *= m;
+ h *= m;
}
const unsigned char *data2 = (const unsigned char *)data;
@@ -174,14 +179,13 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed)
case 1: h ^= (u64)data2[0];
h *= m;
}
-
+
h ^= h >> r;
h *= m;
h ^= h >> r;
-
- return h;
-}
+ return h;
+}
/*
blockpos: position of block in block coordinates
@@ -193,7 +197,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
f32 camera_fov, f32 range, f32 *distance_ptr)
{
v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
-
+
// Block center position
v3f blockpos(
((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
@@ -209,7 +213,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
if(distance_ptr)
*distance_ptr = d;
-
+
// If block is far away, it's not in sight
if(d > range)
return false;
@@ -217,7 +221,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
// Maximum radius of a block. The magic number is
// sqrt(3.0) / 2.0 in literal form.
f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS;
-
+
// If block is (nearly) touching the camera, don't
// bother validating further (that is, render it anyway)
if(d < block_max_radius)
@@ -238,11 +242,10 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
// Cosine of the angle between the camera direction
// and the block direction (camera_dir is an unit vector)
f32 cosangle = dforward / blockpos_adj.getLength();
-
+
// If block is not in the field of view, skip it
if(cosangle < cos(camera_fov / 2))
return false;
return true;
}
-
diff --git a/src/util/numeric.h b/src/util/numeric.h
index db1eb003e..9fe08434f 100644
--- a/src/util/numeric.h
+++ b/src/util/numeric.h
@@ -24,11 +24,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../irr_v2d.h"
#include "../irr_v3d.h"
#include "../irr_aabb3d.h"
+#include "../jthread/jmutex.h"
#include <list>
+#include <map>
+#include <vector>
#include <algorithm>
-// Calculate the borders of a "d-radius" cube
-void getFacePositions(std::list<v3s16> &list, u16 d);
+
+/*
+ * This class permits to cache getFacePosition call results
+ * This reduces CPU usage and vector calls
+ */
+class FacePositionCache
+{
+public:
+ static std::vector<v3s16> getFacePositions(u16 d);
+private:
+ static void generateFacePosition(u16 d);
+ static std::map<u16, std::vector<v3s16> > m_cache;
+ static JMutex m_cache_mutex;
+};
class IndentationRaiser
{
@@ -171,72 +186,93 @@ inline void sortBoxVerticies(v3s16 &p1, v3s16 &p2) {
}
-/*
- See test.cpp for example cases.
- wraps degrees to the range of -360...360
- NOTE: Wrapping to 0...360 is not used because pitch needs negative values.
-*/
-inline float wrapDegrees(float f)
+/** Returns \p f wrapped to the range [-360, 360]
+ *
+ * See test.cpp for example cases.
+ *
+ * \note This is also used in cases where degrees wrapped to the range [0, 360]
+ * is innapropriate (e.g. pitch needs negative values)
+ *
+ * \internal functionally equivalent -- although precision may vary slightly --
+ * to fmodf((f), 360.0f) however empirical tests indicate that this approach is
+ * faster.
+ */
+inline float modulo360f(float f)
{
- // Take examples of f=10, f=720.5, f=-0.5, f=-360.5
- // This results in
- // 10, 720, -1, -361
- int i = floor(f);
- // 0, 2, 0, -1
- int l = i / 360;
- // NOTE: This would be used for wrapping to 0...360
- // 0, 2, -1, -2
- /*if(i < 0)
- l -= 1;*/
- // 0, 720, 0, -360
- int k = l * 360;
- // 10, 0.5, -0.5, -0.5
- f -= float(k);
- return f;
+ int sign;
+ int whole;
+ float fraction;
+
+ if (f < 0) {
+ f = -f;
+ sign = -1;
+ } else {
+ sign = 1;
+ }
+
+ whole = f;
+
+ fraction = f - whole;
+ whole %= 360;
+
+ return sign * (whole + fraction);
}
-/* Wrap to 0...360 */
+
+/** Returns \p f wrapped to the range [0, 360]
+ */
inline float wrapDegrees_0_360(float f)
{
- // Take examples of f=10, f=720.5, f=-0.5, f=-360.5
- // This results in
- // 10, 720, -1, -361
- int i = floor(f);
- // 0, 2, 0, -1
- int l = i / 360;
- // Wrap to 0...360
- // 0, 2, -1, -2
- if(i < 0)
- l -= 1;
- // 0, 720, 0, -360
- int k = l * 360;
- // 10, 0.5, -0.5, -0.5
- f -= float(k);
- return f;
+ float value = modulo360f(f);
+ return value < 0 ? value + 360 : value;
}
-/* Wrap to -180...180 */
+
+/** Returns \p f wrapped to the range [-180, 180]
+ */
inline float wrapDegrees_180(float f)
{
- f += 180;
- f = wrapDegrees_0_360(f);
- f -= 180;
- return f;
+ float value = modulo360f(f + 180);
+ if (value < 0)
+ value += 360;
+ return value - 180;
}
/*
Pseudo-random (VC++ rand() sucks)
*/
-int myrand(void);
-void mysrand(unsigned seed);
-#define MYRAND_MAX 32767
-
+#define MYRAND_RANGE 0xffffffff
+u32 myrand();
+void mysrand(unsigned int seed);
+void myrand_bytes(void *out, size_t len);
int myrand_range(int min, int max);
/*
Miscellaneous functions
*/
+inline u32 get_bits(u32 x, u32 pos, u32 len)
+{
+ u32 mask = (1 << len) - 1;
+ return (x >> pos) & mask;
+}
+
+inline void set_bits(u32 *x, u32 pos, u32 len, u32 val)
+{
+ u32 mask = (1 << len) - 1;
+ *x &= ~(mask << pos);
+ *x |= (val & mask) << pos;
+}
+
+inline u32 calc_parity(u32 v)
+{
+ v ^= v >> 16;
+ v ^= v >> 8;
+ v ^= v >> 4;
+ v &= 0xf;
+ return (0x6996 >> v) & 1;
+}
+
u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed);
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
@@ -254,7 +290,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
*/
inline s32 myround(f32 f)
{
- return floor(f + 0.5);
+ return (s32)(f < 0.f ? (f - 0.5f) : (f + 0.5f));
}
/*
@@ -377,5 +413,16 @@ inline bool is_power_of_two(u32 n)
return n != 0 && (n & (n-1)) == 0;
}
-#endif
+// Compute next-higher power of 2 efficiently, e.g. for power-of-2 texture sizes.
+// Public Domain: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
+inline u32 npot2(u32 orig) {
+ orig--;
+ orig |= orig >> 1;
+ orig |= orig >> 2;
+ orig |= orig >> 4;
+ orig |= orig >> 8;
+ orig |= orig >> 16;
+ return orig + 1;
+}
+#endif
diff --git a/src/util/pointer.h b/src/util/pointer.h
index 7922a9b39..7f6654787 100644
--- a/src/util/pointer.h
+++ b/src/util/pointer.h
@@ -178,6 +178,14 @@ private:
unsigned int m_size;
};
+/************************************************
+ * !!! W A R N I N G !!! *
+ * !!! A C H T U N G !!! *
+ * *
+ * This smart pointer class is NOT thread safe. *
+ * ONLY use in a single-threaded context! *
+ * *
+ ************************************************/
template <typename T>
class SharedBuffer
{
diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp
index 8a108a0ff..c0168776e 100644
--- a/src/util/serialize.cpp
+++ b/src/util/serialize.cpp
@@ -28,81 +28,108 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iomanip>
#include <vector>
-// Creates a string with the length as the first two bytes
+////
+//// String
+////
+
std::string serializeString(const std::string &plain)
{
- //assert(plain.size() <= 65535);
- if(plain.size() > 65535)
- throw SerializationError("String too long for serializeString");
- char buf[2];
- writeU16((u8*)&buf[0], plain.size());
std::string s;
- s.append(buf, 2);
- s.append(plain);
- return s;
-}
+ char buf[2];
-// Creates a string with the length as the first two bytes from wide string
-std::string serializeWideString(const std::wstring &plain)
-{
- //assert(plain.size() <= 65535);
- if(plain.size() > 65535)
+ if (plain.size() > STRING_MAX_LEN)
throw SerializationError("String too long for serializeString");
- char buf[2];
- writeU16((u8*)buf, plain.size());
- std::string s;
+
+ writeU16((u8 *)&buf[0], plain.size());
s.append(buf, 2);
- for(u32 i=0; i<plain.size(); i++)
- {
- writeU16((u8*)buf, plain[i]);
- s.append(buf, 2);
- }
+
+ s.append(plain);
return s;
}
-// Reads a string with the length as the first two bytes
std::string deSerializeString(std::istream &is)
{
+ std::string s;
char buf[2];
+
is.read(buf, 2);
- if(is.gcount() != 2)
+ if (is.gcount() != 2)
throw SerializationError("deSerializeString: size not read");
- u16 s_size = readU16((u8*)buf);
- std::string s;
- if(s_size == 0)
+
+ u16 s_size = readU16((u8 *)buf);
+ if (s_size == 0)
return s;
+
Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size);
+ if (is.gcount() != s_size)
+ throw SerializationError("deSerializeString: couldn't read all chars");
+
s.reserve(s_size);
s.append(&buf2[0], s_size);
return s;
}
-// Reads a wide string with the length as the first two bytes
+////
+//// Wide String
+////
+
+std::string serializeWideString(const std::wstring &plain)
+{
+ std::string s;
+ char buf[2];
+
+ if (plain.size() > WIDE_STRING_MAX_LEN)
+ throw SerializationError("String too long for serializeWideString");
+
+ writeU16((u8 *)buf, plain.size());
+ s.append(buf, 2);
+
+ for (u32 i = 0; i < plain.size(); i++) {
+ writeU16((u8 *)buf, plain[i]);
+ s.append(buf, 2);
+ }
+ return s;
+}
+
std::wstring deSerializeWideString(std::istream &is)
{
+ std::wstring s;
char buf[2];
+
is.read(buf, 2);
- if(is.gcount() != 2)
- throw SerializationError("deSerializeString: size not read");
- u16 s_size = readU16((u8*)buf);
- std::wstring s;
- if(s_size == 0)
+ if (is.gcount() != 2)
+ throw SerializationError("deSerializeWideString: size not read");
+
+ u16 s_size = readU16((u8 *)buf);
+ if (s_size == 0)
return s;
+
s.reserve(s_size);
- for(u32 i=0; i<s_size; i++)
- {
+ for (u32 i = 0; i < s_size; i++) {
is.read(&buf[0], 2);
- wchar_t c16 = readU16((u8*)buf);
+ if (is.gcount() != 2) {
+ throw SerializationError(
+ "deSerializeWideString: couldn't read all chars");
+ }
+
+ wchar_t c16 = readU16((u8 *)buf);
s.append(&c16, 1);
}
return s;
}
-// Creates a string with the length as the first four bytes
+////
+//// Long String
+////
+
std::string serializeLongString(const std::string &plain)
{
char buf[4];
+
+ if (plain.size() > LONG_STRING_MAX_LEN)
+ throw SerializationError("String too long for serializeLongString");
+
writeU32((u8*)&buf[0], plain.size());
std::string s;
s.append(buf, 4);
@@ -110,62 +137,88 @@ std::string serializeLongString(const std::string &plain)
return s;
}
-// Reads a string with the length as the first four bytes
std::string deSerializeLongString(std::istream &is)
{
+ std::string s;
char buf[4];
+
is.read(buf, 4);
- if(is.gcount() != 4)
+ if (is.gcount() != 4)
throw SerializationError("deSerializeLongString: size not read");
- u32 s_size = readU32((u8*)buf);
- std::string s;
- if(s_size == 0)
+
+ u32 s_size = readU32((u8 *)buf);
+ if (s_size == 0)
return s;
+
+ // We don't really want a remote attacker to force us to allocate 4GB...
+ if (s_size > LONG_STRING_MAX_LEN) {
+ throw SerializationError("deSerializeLongString: "
+ "string too long: " + itos(s_size) + " bytes");
+ }
+
Buffer<char> buf2(s_size);
is.read(&buf2[0], s_size);
+ if (is.gcount() != s_size)
+ throw SerializationError("deSerializeLongString: couldn't read all chars");
+
s.reserve(s_size);
s.append(&buf2[0], s_size);
return s;
}
-// Creates a string encoded in JSON format (almost equivalent to a C string literal)
+////
+//// JSON
+////
+
std::string serializeJsonString(const std::string &plain)
{
std::ostringstream os(std::ios::binary);
- os<<"\"";
- for(size_t i = 0; i < plain.size(); i++)
- {
+ os << "\"";
+
+ for (size_t i = 0; i < plain.size(); i++) {
char c = plain[i];
- switch(c)
- {
- case '"': os<<"\\\""; break;
- case '\\': os<<"\\\\"; break;
- case '/': os<<"\\/"; break;
- case '\b': os<<"\\b"; break;
- case '\f': os<<"\\f"; break;
- case '\n': os<<"\\n"; break;
- case '\r': os<<"\\r"; break;
- case '\t': os<<"\\t"; break;
- default:
- {
- if(c >= 32 && c <= 126)
- {
- os<<c;
- }
- else
- {
- u32 cnum = (u32) (u8) c;
- os<<"\\u"<<std::hex<<std::setw(4)<<std::setfill('0')<<cnum;
+ switch (c) {
+ case '"':
+ os << "\\\"";
+ break;
+ case '\\':
+ os << "\\\\";
+ break;
+ case '/':
+ os << "\\/";
+ break;
+ case '\b':
+ os << "\\b";
+ break;
+ case '\f':
+ os << "\\f";
+ break;
+ case '\n':
+ os << "\\n";
+ break;
+ case '\r':
+ os << "\\r";
+ break;
+ case '\t':
+ os << "\\t";
+ break;
+ default: {
+ if (c >= 32 && c <= 126) {
+ os << c;
+ } else {
+ u32 cnum = (u8)c;
+ os << "\\u" << std::hex << std::setw(4)
+ << std::setfill('0') << cnum;
}
break;
}
}
}
- os<<"\"";
+
+ os << "\"";
return os.str();
}
-// Reads a string encoded in JSON format
std::string deSerializeJsonString(std::istream &is)
{
std::ostringstream os(std::ios::binary);
@@ -173,55 +226,66 @@ std::string deSerializeJsonString(std::istream &is)
// Parse initial doublequote
is >> c;
- if(c != '"')
+ if (c != '"')
throw SerializationError("JSON string must start with doublequote");
// Parse characters
- for(;;)
- {
+ for (;;) {
c = is.get();
- if(is.eof())
+ if (is.eof())
throw SerializationError("JSON string ended prematurely");
- if(c == '"')
- {
+
+ if (c == '"') {
return os.str();
- }
- else if(c == '\\')
- {
+ } else if (c == '\\') {
c2 = is.get();
- if(is.eof())
+ if (is.eof())
throw SerializationError("JSON string ended prematurely");
- switch(c2)
- {
- default: os<<c2; break;
- case 'b': os<<'\b'; break;
- case 'f': os<<'\f'; break;
- case 'n': os<<'\n'; break;
- case 'r': os<<'\r'; break;
- case 't': os<<'\t'; break;
- case 'u':
- {
- char hexdigits[4+1];
+ switch (c2) {
+ case 'b':
+ os << '\b';
+ break;
+ case 'f':
+ os << '\f';
+ break;
+ case 'n':
+ os << '\n';
+ break;
+ case 'r':
+ os << '\r';
+ break;
+ case 't':
+ os << '\t';
+ break;
+ case 'u': {
+ int hexnumber;
+ char hexdigits[4 + 1];
+
is.read(hexdigits, 4);
- if(is.eof())
+ if (is.eof())
throw SerializationError("JSON string ended prematurely");
hexdigits[4] = 0;
+
std::istringstream tmp_is(hexdigits, std::ios::binary);
- int hexnumber;
tmp_is >> std::hex >> hexnumber;
- os<<((char)hexnumber);
+ os << (char)hexnumber;
break;
}
+ default:
+ os << c2;
+ break;
}
- }
- else
- {
- os<<c;
+ } else {
+ os << c;
}
}
+
return os.str();
}
+////
+//// String/Struct conversions
+////
bool deSerializeStringToStruct(std::string valstr,
std::string format, void *out, size_t olen)
@@ -384,7 +448,6 @@ fail:
return true;
}
-
// Casts *buf to a signed or unsigned fixed-width integer of 'w' width
#define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf))
@@ -471,11 +534,33 @@ bool serializeStructToString(std::string *out,
*out = os.str();
// Trim off the trailing comma and space
- if (out->size() >= 2) {
+ if (out->size() >= 2)
out->resize(out->size() - 2);
- }
return true;
}
#undef SIGN_CAST
+
+////
+//// Other
+////
+
+std::string serializeHexString(const std::string &data, bool insert_spaces)
+{
+ std::string result;
+ result.reserve(data.size() * (2 + insert_spaces));
+
+ static const char hex_chars[] = "0123456789abcdef";
+
+ const size_t len = data.size();
+ for (size_t i = 0; i != len; i++) {
+ u8 byte = data[i];
+ result.push_back(hex_chars[(byte >> 4) & 0x0F]);
+ result.push_back(hex_chars[(byte >> 0) & 0x0F]);
+ if (insert_spaces && i != len - 1)
+ result.push_back(' ');
+ }
+
+ return result;
+}
diff --git a/src/util/serialize.h b/src/util/serialize.h
index 79907799f..bf0d9c863 100644
--- a/src/util/serialize.h
+++ b/src/util/serialize.h
@@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define UTIL_SERIALIZE_HEADER
#include "../irrlichttypes_bloated.h"
+#include "../debug.h" // for assert
#include "config.h"
#if HAVE_ENDIAN_H
#include <endian.h>
@@ -30,185 +31,155 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#define FIXEDPOINT_FACTOR 1000.0f
-#define FIXEDPOINT_INVFACTOR (1.0f/FIXEDPOINT_FACTOR)
+
+// 0x7FFFFFFF / 1000.0f is not serializable.
+// The limited float precision at this magnitude may cause the result to round
+// to a greater value than can be represented by a 32 bit integer when increased
+// by a factor of FIXEDPOINT_FACTOR. As a result, [F1000_MIN..F1000_MAX] does
+// not represent the full range, but rather the largest safe range, of values on
+// all supported architectures. Note: This definition makes assumptions on
+// platform float-to-int conversion behavior.
+#define F1000_MIN ((float)(s32)((-0x7FFFFFFF - 1) / FIXEDPOINT_FACTOR))
+#define F1000_MAX ((float)(s32)((0x7FFFFFFF) / FIXEDPOINT_FACTOR))
+
+#define STRING_MAX_LEN 0xFFFF
+#define WIDE_STRING_MAX_LEN 0xFFFF
+// 64 MB ought to be enough for anybody - Billy G.
+#define LONG_STRING_MAX_LEN (64 * 1024 * 1024)
+
#if HAVE_ENDIAN_H
// use machine native byte swapping routines
// Note: memcpy below is optimized out by modern compilers
-inline void writeU64(u8* data, u64 i)
-{
- u64 val = htobe64(i);
- memcpy(data, &val, 8);
-}
-
-inline void writeU32(u8* data, u32 i)
+inline u16 readU16(const u8 *data)
{
- u32 val = htobe32(i);
- memcpy(data, &val, 4);
+ u16 val;
+ memcpy(&val, data, 2);
+ return be16toh(val);
}
-inline void writeU16(u8* data, u16 i)
+inline u32 readU32(const u8 *data)
{
- u16 val = htobe16(i);
- memcpy(data, &val, 2);
+ u32 val;
+ memcpy(&val, data, 4);
+ return be32toh(val);
}
-inline u64 readU64(const u8* data)
+inline u64 readU64(const u8 *data)
{
u64 val;
memcpy(&val, data, 8);
return be64toh(val);
}
-inline u32 readU32(const u8* data)
+inline void writeU16(u8 *data, u16 i)
{
- u32 val;
- memcpy(&val, data, 4);
- return be32toh(val);
+ u16 val = htobe16(i);
+ memcpy(data, &val, 2);
}
-inline u16 readU16(const u8* data)
+inline void writeU32(u8 *data, u32 i)
{
- u16 val;
- memcpy(&val, data, 2);
- return be16toh(val);
+ u32 val = htobe32(i);
+ memcpy(data, &val, 4);
}
-#else
-// generic byte-swapping implementation
-
inline void writeU64(u8 *data, u64 i)
{
- data[0] = ((i>>56)&0xff);
- data[1] = ((i>>48)&0xff);
- data[2] = ((i>>40)&0xff);
- data[3] = ((i>>32)&0xff);
- data[4] = ((i>>24)&0xff);
- data[5] = ((i>>16)&0xff);
- data[6] = ((i>> 8)&0xff);
- data[7] = ((i>> 0)&0xff);
+ u64 val = htobe64(i);
+ memcpy(data, &val, 8);
}
-inline void writeU32(u8 *data, u32 i)
+#else
+// generic byte-swapping implementation
+
+inline u16 readU16(const u8 *data)
{
- data[0] = ((i>>24)&0xff);
- data[1] = ((i>>16)&0xff);
- data[2] = ((i>> 8)&0xff);
- data[3] = ((i>> 0)&0xff);
+ return
+ ((u16)data[0] << 8) | ((u16)data[1] << 0);
}
-inline void writeU16(u8 *data, u16 i)
+inline u32 readU32(const u8 *data)
{
- data[0] = ((i>> 8)&0xff);
- data[1] = ((i>> 0)&0xff);
+ return
+ ((u32)data[0] << 24) | ((u32)data[1] << 16) |
+ ((u32)data[2] << 8) | ((u32)data[3] << 0);
}
inline u64 readU64(const u8 *data)
{
- return ((u64)data[0]<<56) | ((u64)data[1]<<48)
- | ((u64)data[2]<<40) | ((u64)data[3]<<32)
- | ((u64)data[4]<<24) | ((u64)data[5]<<16)
- | ((u64)data[6]<<8) | ((u64)data[7]<<0);
+ return
+ ((u64)data[0] << 56) | ((u64)data[1] << 48) |
+ ((u64)data[2] << 40) | ((u64)data[3] << 32) |
+ ((u64)data[4] << 24) | ((u64)data[5] << 16) |
+ ((u64)data[6] << 8) | ((u64)data[7] << 0);
}
-inline u32 readU32(const u8 *data)
+inline void writeU16(u8 *data, u16 i)
{
- return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);
+ data[0] = (i >> 8) & 0xFF;
+ data[1] = (i >> 0) & 0xFF;
}
-inline u16 readU16(const u8 *data)
+inline void writeU32(u8 *data, u32 i)
{
- return (data[0]<<8) | (data[1]<<0);
+ data[0] = (i >> 24) & 0xFF;
+ data[1] = (i >> 16) & 0xFF;
+ data[2] = (i >> 8) & 0xFF;
+ data[3] = (i >> 0) & 0xFF;
}
-#endif
-
-inline void writeU8(u8 *data, u8 i)
+inline void writeU64(u8 *data, u64 i)
{
- data[0] = ((i>> 0)&0xff);
+ data[0] = (i >> 56) & 0xFF;
+ data[1] = (i >> 48) & 0xFF;
+ data[2] = (i >> 40) & 0xFF;
+ data[3] = (i >> 32) & 0xFF;
+ data[4] = (i >> 24) & 0xFF;
+ data[5] = (i >> 16) & 0xFF;
+ data[6] = (i >> 8) & 0xFF;
+ data[7] = (i >> 0) & 0xFF;
}
-inline u8 readU8(const u8 *data)
-{
- return (data[0]<<0);
-}
+#endif // HAVE_ENDIAN_H
-inline void writeS32(u8 *data, s32 i){
- writeU32(data, (u32)i);
-}
-inline s32 readS32(const u8 *data){
- return (s32)readU32(data);
-}
+//////////////// read routines ////////////////
-inline void writeS16(u8 *data, s16 i){
- writeU16(data, (u16)i);
-}
-inline s16 readS16(const u8 *data){
- return (s16)readU16(data);
+inline u8 readU8(const u8 *data)
+{
+ return ((u8)data[0] << 0);
}
-inline void writeS8(u8 *data, s8 i){
- writeU8(data, (u8)i);
-}
-inline s8 readS8(const u8 *data){
+inline s8 readS8(const u8 *data)
+{
return (s8)readU8(data);
}
-inline void writeF1000(u8 *data, f32 i){
- writeS32(data, i*FIXEDPOINT_FACTOR);
-}
-inline f32 readF1000(const u8 *data){
- return (f32)readS32(data)*FIXEDPOINT_INVFACTOR;
-}
-
-inline void writeV3S32(u8 *data, v3s32 p)
+inline s16 readS16(const u8 *data)
{
- writeS32(&data[0], p.X);
- writeS32(&data[4], p.Y);
- writeS32(&data[8], p.Z);
-}
-inline v3s32 readV3S32(const u8 *data)
-{
- v3s32 p;
- p.X = readS32(&data[0]);
- p.Y = readS32(&data[4]);
- p.Z = readS32(&data[8]);
- return p;
+ return (s16)readU16(data);
}
-inline void writeV3F1000(u8 *data, v3f p)
+inline s32 readS32(const u8 *data)
{
- writeF1000(&data[0], p.X);
- writeF1000(&data[4], p.Y);
- writeF1000(&data[8], p.Z);
-}
-inline v3f readV3F1000(const u8 *data)
-{
- v3f p;
- p.X = (float)readF1000(&data[0]);
- p.Y = (float)readF1000(&data[4]);
- p.Z = (float)readF1000(&data[8]);
- return p;
+ return (s32)readU32(data);
}
-inline void writeV2F1000(u8 *data, v2f p)
+inline s64 readS64(const u8 *data)
{
- writeF1000(&data[0], p.X);
- writeF1000(&data[4], p.Y);
+ return (s64)readU64(data);
}
-inline v2f readV2F1000(const u8 *data)
+
+inline f32 readF1000(const u8 *data)
{
- v2f p;
- p.X = (float)readF1000(&data[0]);
- p.Y = (float)readF1000(&data[4]);
- return p;
+ return (f32)readS32(data) / FIXEDPOINT_FACTOR;
}
-inline void writeV2S16(u8 *data, v2s16 p)
+inline video::SColor readARGB8(const u8 *data)
{
- writeS16(&data[0], p.X);
- writeS16(&data[2], p.Y);
+ video::SColor p(readU32(data));
+ return p;
}
inline v2s16 readV2S16(const u8 *data)
@@ -219,10 +190,13 @@ inline v2s16 readV2S16(const u8 *data)
return p;
}
-inline void writeV2S32(u8 *data, v2s32 p)
+inline v3s16 readV3S16(const u8 *data)
{
- writeS32(&data[0], p.X);
- writeS32(&data[4], p.Y);
+ v3s16 p;
+ p.X = readS16(&data[0]);
+ p.Y = readS16(&data[2]);
+ p.Z = readS16(&data[4]);
+ return p;
}
inline v2s32 readV2S32(const u8 *data)
@@ -233,198 +207,166 @@ inline v2s32 readV2S32(const u8 *data)
return p;
}
-inline void writeV3S16(u8 *data, v3s16 p)
-{
- writeS16(&data[0], p.X);
- writeS16(&data[2], p.Y);
- writeS16(&data[4], p.Z);
-}
-
-inline v3s16 readV3S16(const u8 *data)
+inline v3s32 readV3S32(const u8 *data)
{
- v3s16 p;
- p.X = readS16(&data[0]);
- p.Y = readS16(&data[2]);
- p.Z = readS16(&data[4]);
+ v3s32 p;
+ p.X = readS32(&data[0]);
+ p.Y = readS32(&data[4]);
+ p.Z = readS32(&data[8]);
return p;
}
-inline void writeARGB8(u8 *data, video::SColor p)
+inline v2f readV2F1000(const u8 *data)
{
- writeU32(data, p.color);
+ v2f p;
+ p.X = (float)readF1000(&data[0]);
+ p.Y = (float)readF1000(&data[4]);
+ return p;
}
-inline video::SColor readARGB8(const u8 *data)
+inline v3f readV3F1000(const u8 *data)
{
- video::SColor p(readU32(data));
+ v3f p;
+ p.X = (float)readF1000(&data[0]);
+ p.Y = (float)readF1000(&data[4]);
+ p.Z = (float)readF1000(&data[8]);
return p;
}
-/*
- The above stuff directly interfaced to iostream
-*/
-
-inline void writeU8(std::ostream &os, u8 p)
-{
- char buf[1];
- writeU8((u8*)buf, p);
- os.write(buf, 1);
-}
-inline u8 readU8(std::istream &is)
-{
- char buf[1] = {0};
- is.read(buf, 1);
- return readU8((u8*)buf);
-}
+/////////////// write routines ////////////////
-inline void writeU16(std::ostream &os, u16 p)
-{
- char buf[2];
- writeU16((u8*)buf, p);
- os.write(buf, 2);
-}
-inline u16 readU16(std::istream &is)
+inline void writeU8(u8 *data, u8 i)
{
- char buf[2] = {0};
- is.read(buf, 2);
- return readU16((u8*)buf);
+ data[0] = (i >> 0) & 0xFF;
}
-inline void writeU32(std::ostream &os, u32 p)
+inline void writeS8(u8 *data, s8 i)
{
- char buf[4];
- writeU32((u8*)buf, p);
- os.write(buf, 4);
-}
-inline u32 readU32(std::istream &is)
-{
- char buf[4] = {0};
- is.read(buf, 4);
- return readU32((u8*)buf);
+ writeU8(data, (u8)i);
}
-inline void writeS32(std::ostream &os, s32 p)
-{
- writeU32(os, (u32) p);
-}
-inline s32 readS32(std::istream &is)
+inline void writeS16(u8 *data, s16 i)
{
- return (s32)readU32(is);
+ writeU16(data, (u16)i);
}
-inline void writeS16(std::ostream &os, s16 p)
-{
- writeU16(os, (u16) p);
-}
-inline s16 readS16(std::istream &is)
+inline void writeS32(u8 *data, s32 i)
{
- return (s16)readU16(is);
+ writeU32(data, (u32)i);
}
-inline void writeS8(std::ostream &os, s8 p)
-{
- writeU8(os, (u8) p);
-}
-inline s8 readS8(std::istream &is)
+inline void writeS64(u8 *data, s64 i)
{
- return (s8)readU8(is);
+ writeU64(data, (u64)i);
}
-inline void writeF1000(std::ostream &os, f32 p)
+inline void writeF1000(u8 *data, f32 i)
{
- char buf[4];
- writeF1000((u8*)buf, p);
- os.write(buf, 4);
-}
-inline f32 readF1000(std::istream &is)
-{
- char buf[4] = {0};
- is.read(buf, 4);
- return readF1000((u8*)buf);
+ assert(i >= F1000_MIN && i <= F1000_MAX);
+ writeS32(data, i * FIXEDPOINT_FACTOR);
}
-inline void writeV3F1000(std::ostream &os, v3f p)
-{
- char buf[12];
- writeV3F1000((u8*)buf, p);
- os.write(buf, 12);
-}
-inline v3f readV3F1000(std::istream &is)
+inline void writeARGB8(u8 *data, video::SColor p)
{
- char buf[12];
- is.read(buf, 12);
- return readV3F1000((u8*)buf);
+ writeU32(data, p.color);
}
-inline void writeV2F1000(std::ostream &os, v2f p)
-{
- char buf[8];
- writeV2F1000((u8*)buf, p);
- os.write(buf, 8);
-}
-inline v2f readV2F1000(std::istream &is)
+inline void writeV2S16(u8 *data, v2s16 p)
{
- char buf[8] = {0};
- is.read(buf, 8);
- return readV2F1000((u8*)buf);
+ writeS16(&data[0], p.X);
+ writeS16(&data[2], p.Y);
}
-inline void writeV2S16(std::ostream &os, v2s16 p)
-{
- char buf[4];
- writeV2S16((u8*)buf, p);
- os.write(buf, 4);
-}
-inline v2s16 readV2S16(std::istream &is)
+inline void writeV3S16(u8 *data, v3s16 p)
{
- char buf[4] = {0};
- is.read(buf, 4);
- return readV2S16((u8*)buf);
+ writeS16(&data[0], p.X);
+ writeS16(&data[2], p.Y);
+ writeS16(&data[4], p.Z);
}
-inline void writeV2S32(std::ostream &os, v2s32 p)
-{
- char buf[8];
- writeV2S32((u8*)buf, p);
- os.write(buf, 8);
-}
-inline v2s32 readV2S32(std::istream &is)
+inline void writeV2S32(u8 *data, v2s32 p)
{
- char buf[8] = {0};
- is.read(buf, 8);
- return readV2S32((u8*)buf);
+ writeS32(&data[0], p.X);
+ writeS32(&data[4], p.Y);
}
-inline void writeV3S16(std::ostream &os, v3s16 p)
-{
- char buf[6];
- writeV3S16((u8*)buf, p);
- os.write(buf, 6);
-}
-inline v3s16 readV3S16(std::istream &is)
+inline void writeV3S32(u8 *data, v3s32 p)
{
- char buf[6] = {0};
- is.read(buf, 6);
- return readV3S16((u8*)buf);
+ writeS32(&data[0], p.X);
+ writeS32(&data[4], p.Y);
+ writeS32(&data[8], p.Z);
}
-inline void writeARGB8(std::ostream &os, video::SColor p)
+inline void writeV2F1000(u8 *data, v2f p)
{
- char buf[4];
- writeARGB8((u8*)buf, p);
- os.write(buf, 4);
+ writeF1000(&data[0], p.X);
+ writeF1000(&data[4], p.Y);
}
-inline video::SColor readARGB8(std::istream &is)
+inline void writeV3F1000(u8 *data, v3f p)
{
- char buf[4] = {0};
- is.read(buf, 4);
- return readARGB8((u8*)buf);
+ writeF1000(&data[0], p.X);
+ writeF1000(&data[4], p.Y);
+ writeF1000(&data[8], p.Z);
}
-/*
- More serialization stuff
-*/
+////
+//// Iostream wrapper for data read/write
+////
+
+#define MAKE_STREAM_READ_FXN(T, N, S) \
+ inline T read ## N(std::istream &is) \
+ { \
+ char buf[S] = {0}; \
+ is.read(buf, sizeof(buf)); \
+ return read ## N((u8 *)buf); \
+ }
+
+#define MAKE_STREAM_WRITE_FXN(T, N, S) \
+ inline void write ## N(std::ostream &os, T val) \
+ { \
+ char buf[S]; \
+ write ## N((u8 *)buf, val); \
+ os.write(buf, sizeof(buf)); \
+ }
+
+MAKE_STREAM_READ_FXN(u8, U8, 1);
+MAKE_STREAM_READ_FXN(u16, U16, 2);
+MAKE_STREAM_READ_FXN(u32, U32, 4);
+MAKE_STREAM_READ_FXN(u64, U64, 8);
+MAKE_STREAM_READ_FXN(s8, S8, 1);
+MAKE_STREAM_READ_FXN(s16, S16, 2);
+MAKE_STREAM_READ_FXN(s32, S32, 4);
+MAKE_STREAM_READ_FXN(s64, S64, 8);
+MAKE_STREAM_READ_FXN(f32, F1000, 4);
+MAKE_STREAM_READ_FXN(v2s16, V2S16, 4);
+MAKE_STREAM_READ_FXN(v3s16, V3S16, 6);
+MAKE_STREAM_READ_FXN(v2s32, V2S32, 8);
+MAKE_STREAM_READ_FXN(v3s32, V3S32, 12);
+MAKE_STREAM_READ_FXN(v2f, V2F1000, 8);
+MAKE_STREAM_READ_FXN(v3f, V3F1000, 12);
+MAKE_STREAM_READ_FXN(video::SColor, ARGB8, 4);
+
+MAKE_STREAM_WRITE_FXN(u8, U8, 1);
+MAKE_STREAM_WRITE_FXN(u16, U16, 2);
+MAKE_STREAM_WRITE_FXN(u32, U32, 4);
+MAKE_STREAM_WRITE_FXN(u64, U64, 8);
+MAKE_STREAM_WRITE_FXN(s8, S8, 1);
+MAKE_STREAM_WRITE_FXN(s16, S16, 2);
+MAKE_STREAM_WRITE_FXN(s32, S32, 4);
+MAKE_STREAM_WRITE_FXN(s64, S64, 8);
+MAKE_STREAM_WRITE_FXN(f32, F1000, 4);
+MAKE_STREAM_WRITE_FXN(v2s16, V2S16, 4);
+MAKE_STREAM_WRITE_FXN(v3s16, V3S16, 6);
+MAKE_STREAM_WRITE_FXN(v2s32, V2S32, 8);
+MAKE_STREAM_WRITE_FXN(v3s32, V3S32, 12);
+MAKE_STREAM_WRITE_FXN(v2f, V2F1000, 8);
+MAKE_STREAM_WRITE_FXN(v3f, V3F1000, 12);
+MAKE_STREAM_WRITE_FXN(video::SColor, ARGB8, 4);
+
+////
+//// More serialization stuff
+////
// Creates a string with the length as the first two bytes
std::string serializeString(const std::string &plain);
@@ -450,6 +392,9 @@ std::string serializeJsonString(const std::string &plain);
// Reads a string encoded in JSON format
std::string deSerializeJsonString(std::istream &is);
+// Creates a string consisting of the hexadecimal representation of `data`
+std::string serializeHexString(const std::string &data, bool insert_spaces=false);
+
// Creates a string containing comma delimited values of a struct whose layout is
// described by the parameter format
bool serializeStructToString(std::string *out,
@@ -461,4 +406,3 @@ bool deSerializeStringToStruct(std::string valstr,
std::string format, void *out, size_t olen);
#endif
-
diff --git a/src/sha1.cpp b/src/util/sha1.cpp
index 6ed7385d5..6ed7385d5 100644
--- a/src/sha1.cpp
+++ b/src/util/sha1.cpp
diff --git a/src/sha1.h b/src/util/sha1.h
index c04947373..c04947373 100644
--- a/src/sha1.h
+++ b/src/util/sha1.h
diff --git a/src/util/sha2.h b/src/util/sha2.h
new file mode 100644
index 000000000..6ac045feb
--- /dev/null
+++ b/src/util/sha2.h
@@ -0,0 +1,154 @@
+/* crypto/sha/sha.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_SHA_H
+# define HEADER_SHA_H
+
+# include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+# if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1))
+# error SHA is disabled.
+# endif
+
+# if defined(OPENSSL_FIPS)
+# define FIPS_SHA_SIZE_T size_t
+# endif
+
+/*
+ Compat stuff from OpenSSL land
+ */
+
+/* crypto.h */
+
+# define fips_md_init(alg) fips_md_init_ctx(alg, alg)
+
+# define fips_md_init_ctx(alg, cx) \
+ int alg##_Init(cx##_CTX *c)
+# define fips_cipher_abort(alg) while(0)
+
+/*-
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * ! SHA_LONG has to be at least 32 bits wide. If it's wider, then !
+ * ! SHA_LONG_LOG2 has to be defined along. !
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
+
+# if defined(__LP32__)
+# define SHA_LONG unsigned long
+# elif defined(__ILP64__)
+# define SHA_LONG unsigned long
+# define SHA_LONG_LOG2 3
+# else
+# define SHA_LONG unsigned int
+# endif
+
+# define SHA_LBLOCK 16
+# define SHA_CBLOCK (SHA_LBLOCK*4)/* SHA treats input data as a
+ * contiguous array of 32 bit wide
+ * big-endian values. */
+# define SHA_LAST_BLOCK (SHA_CBLOCK-8)
+# define SHA_DIGEST_LENGTH 20
+
+typedef struct SHAstate_st {
+ SHA_LONG h0, h1, h2, h3, h4;
+ SHA_LONG Nl, Nh;
+ SHA_LONG data[SHA_LBLOCK];
+ unsigned int num;
+} SHA_CTX;
+
+# define SHA256_CBLOCK (SHA_LBLOCK*4)/* SHA-256 treats input data as a
+ * contiguous array of 32 bit wide
+ * big-endian values. */
+# define SHA224_DIGEST_LENGTH 28
+# define SHA256_DIGEST_LENGTH 32
+
+typedef struct SHA256state_st {
+ SHA_LONG h[8];
+ SHA_LONG Nl, Nh;
+ SHA_LONG data[SHA_LBLOCK];
+ unsigned int num, md_len;
+} SHA256_CTX;
+
+# ifndef OPENSSL_NO_SHA256
+# ifdef OPENSSL_FIPS
+int private_SHA224_Init(SHA256_CTX *c);
+int private_SHA256_Init(SHA256_CTX *c);
+# endif
+int SHA224_Init(SHA256_CTX *c);
+int SHA224_Update(SHA256_CTX *c, const void *data, size_t len);
+int SHA224_Final(unsigned char *md, SHA256_CTX *c);
+unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md);
+int SHA256_Init(SHA256_CTX *c);
+int SHA256_Update(SHA256_CTX *c, const void *data, size_t len);
+int SHA256_Final(unsigned char *md, SHA256_CTX *c);
+unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);
+void SHA256_Transform(SHA256_CTX *c, const unsigned char *data);
+# endif
+
+# define SHA384_DIGEST_LENGTH 48
+# define SHA512_DIGEST_LENGTH 64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/util/sha256.c b/src/util/sha256.c
new file mode 100644
index 000000000..4c2bb71a8
--- /dev/null
+++ b/src/util/sha256.c
@@ -0,0 +1,404 @@
+/* crypto/sha/sha256.c */
+/* ====================================================================
+ * Copyright (c) 2004 The OpenSSL Project. All rights reserved
+ * according to the OpenSSL license [found in ../../LICENSE].
+ * ====================================================================
+ */
+# include <stdlib.h>
+# include <string.h>
+
+# include <util/sha2.h>
+
+# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2a 19 Mar 2015"
+# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
+
+const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT;
+
+/* mem_clr.c */
+unsigned static char cleanse_ctr = 0;
+static void OPENSSL_cleanse(void *ptr, size_t len)
+{
+ unsigned char *p = ptr;
+ size_t loop = len, ctr = cleanse_ctr;
+ while (loop--) {
+ *(p++) = (unsigned char)ctr;
+ ctr += (17 + ((size_t)p & 0xF));
+ }
+ p = memchr(ptr, (unsigned char)ctr, len);
+ if (p)
+ ctr += (63 + (size_t)p);
+ cleanse_ctr = (unsigned char)ctr;
+}
+
+# define fips_md_init(alg) fips_md_init_ctx(alg, alg)
+# define fips_md_init_ctx(alg, cx) \
+ int alg##_Init(cx##_CTX *c)
+# define fips_cipher_abort(alg) while(0)
+
+fips_md_init_ctx(SHA224, SHA256)
+{
+ memset(c, 0, sizeof(*c));
+ c->h[0] = 0xc1059ed8UL;
+ c->h[1] = 0x367cd507UL;
+ c->h[2] = 0x3070dd17UL;
+ c->h[3] = 0xf70e5939UL;
+ c->h[4] = 0xffc00b31UL;
+ c->h[5] = 0x68581511UL;
+ c->h[6] = 0x64f98fa7UL;
+ c->h[7] = 0xbefa4fa4UL;
+ c->md_len = SHA224_DIGEST_LENGTH;
+ return 1;
+}
+
+fips_md_init(SHA256)
+{
+ memset(c, 0, sizeof(*c));
+ c->h[0] = 0x6a09e667UL;
+ c->h[1] = 0xbb67ae85UL;
+ c->h[2] = 0x3c6ef372UL;
+ c->h[3] = 0xa54ff53aUL;
+ c->h[4] = 0x510e527fUL;
+ c->h[5] = 0x9b05688cUL;
+ c->h[6] = 0x1f83d9abUL;
+ c->h[7] = 0x5be0cd19UL;
+ c->md_len = SHA256_DIGEST_LENGTH;
+ return 1;
+}
+
+unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md)
+{
+ SHA256_CTX c;
+ static unsigned char m[SHA224_DIGEST_LENGTH];
+
+ if (md == NULL)
+ md = m;
+ SHA224_Init(&c);
+ SHA256_Update(&c, d, n);
+ SHA256_Final(md, &c);
+ OPENSSL_cleanse(&c, sizeof(c));
+ return (md);
+}
+
+unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md)
+{
+ SHA256_CTX c;
+ static unsigned char m[SHA256_DIGEST_LENGTH];
+
+ if (md == NULL)
+ md = m;
+ SHA256_Init(&c);
+ SHA256_Update(&c, d, n);
+ SHA256_Final(md, &c);
+ OPENSSL_cleanse(&c, sizeof(c));
+ return (md);
+}
+
+int SHA224_Update(SHA256_CTX *c, const void *data, size_t len)
+{
+ return SHA256_Update(c, data, len);
+}
+
+int SHA224_Final(unsigned char *md, SHA256_CTX *c)
+{
+ return SHA256_Final(md, c);
+}
+
+# define DATA_ORDER_IS_BIG_ENDIAN
+
+# define HASH_LONG SHA_LONG
+# define HASH_CTX SHA256_CTX
+# define HASH_CBLOCK SHA_CBLOCK
+/*
+ * Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
+ * default: case below covers for it. It's not clear however if it's
+ * permitted to truncate to amount of bytes not divisible by 4. I bet not,
+ * but if it is, then default: case shall be extended. For reference.
+ * Idea behind separate cases for pre-defined lenghts is to let the
+ * compiler decide if it's appropriate to unroll small loops.
+ */
+# define HASH_MAKE_STRING(c,s) do { \
+ unsigned long ll; \
+ unsigned int nn; \
+ switch ((c)->md_len) \
+ { case SHA224_DIGEST_LENGTH: \
+ for (nn=0;nn<SHA224_DIGEST_LENGTH/4;nn++) \
+ { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
+ break; \
+ case SHA256_DIGEST_LENGTH: \
+ for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++) \
+ { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
+ break; \
+ default: \
+ if ((c)->md_len > SHA256_DIGEST_LENGTH) \
+ return 0; \
+ for (nn=0;nn<(c)->md_len/4;nn++) \
+ { ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
+ break; \
+ } \
+ } while (0)
+
+# define HASH_UPDATE SHA256_Update
+# define HASH_TRANSFORM SHA256_Transform
+# define HASH_FINAL SHA256_Final
+# define HASH_BLOCK_DATA_ORDER sha256_block_data_order
+# ifndef SHA256_ASM
+static
+# endif
+void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num);
+
+# include "md32_common.h"
+
+# ifndef SHA256_ASM
+static const SHA_LONG K256[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/*
+ * FIPS specification refers to right rotations, while our ROTATE macro
+ * is left one. This is why you might notice that rotation coefficients
+ * differ from those observed in FIPS document by 32-N...
+ */
+# define Sigma0(x) (ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10))
+# define Sigma1(x) (ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7))
+# define sigma0(x) (ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3))
+# define sigma1(x) (ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10))
+
+# define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+# define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+# ifdef OPENSSL_SMALL_FOOTPRINT
+
+static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
+ size_t num)
+{
+ unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2;
+ SHA_LONG X[16], l;
+ int i;
+ const unsigned char *data = in;
+
+ while (num--) {
+
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+ f = ctx->h[5];
+ g = ctx->h[6];
+ h = ctx->h[7];
+
+ for (i = 0; i < 16; i++) {
+ HOST_c2l(data, l);
+ T1 = X[i] = l;
+ T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
+ T2 = Sigma0(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ for (; i < 64; i++) {
+ s0 = X[(i + 1) & 0x0f];
+ s0 = sigma0(s0);
+ s1 = X[(i + 14) & 0x0f];
+ s1 = sigma1(s1);
+
+ T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
+ T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
+ T2 = Sigma0(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+ }
+
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+ ctx->h[5] += f;
+ ctx->h[6] += g;
+ ctx->h[7] += h;
+
+ }
+}
+
+# else
+
+# define ROUND_00_15(i,a,b,c,d,e,f,g,h) do { \
+ T1 += h + Sigma1(e) + Ch(e,f,g) + K256[i]; \
+ h = Sigma0(a) + Maj(a,b,c); \
+ d += T1; h += T1; } while (0)
+
+# define ROUND_16_63(i,a,b,c,d,e,f,g,h,X) do { \
+ s0 = X[(i+1)&0x0f]; s0 = sigma0(s0); \
+ s1 = X[(i+14)&0x0f]; s1 = sigma1(s1); \
+ T1 = X[(i)&0x0f] += s0 + s1 + X[(i+9)&0x0f]; \
+ ROUND_00_15(i,a,b,c,d,e,f,g,h); } while (0)
+
+static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
+ size_t num)
+{
+ unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1;
+ SHA_LONG X[16];
+ int i;
+ const unsigned char *data = in;
+ const union {
+ long one;
+ char little;
+ } is_endian = {
+ 1
+ };
+
+ while (num--) {
+
+ a = ctx->h[0];
+ b = ctx->h[1];
+ c = ctx->h[2];
+ d = ctx->h[3];
+ e = ctx->h[4];
+ f = ctx->h[5];
+ g = ctx->h[6];
+ h = ctx->h[7];
+
+ if (!is_endian.little && sizeof(SHA_LONG) == 4
+ && ((size_t)in % 4) == 0) {
+ const SHA_LONG *W = (const SHA_LONG *)data;
+
+ T1 = X[0] = W[0];
+ ROUND_00_15(0, a, b, c, d, e, f, g, h);
+ T1 = X[1] = W[1];
+ ROUND_00_15(1, h, a, b, c, d, e, f, g);
+ T1 = X[2] = W[2];
+ ROUND_00_15(2, g, h, a, b, c, d, e, f);
+ T1 = X[3] = W[3];
+ ROUND_00_15(3, f, g, h, a, b, c, d, e);
+ T1 = X[4] = W[4];
+ ROUND_00_15(4, e, f, g, h, a, b, c, d);
+ T1 = X[5] = W[5];
+ ROUND_00_15(5, d, e, f, g, h, a, b, c);
+ T1 = X[6] = W[6];
+ ROUND_00_15(6, c, d, e, f, g, h, a, b);
+ T1 = X[7] = W[7];
+ ROUND_00_15(7, b, c, d, e, f, g, h, a);
+ T1 = X[8] = W[8];
+ ROUND_00_15(8, a, b, c, d, e, f, g, h);
+ T1 = X[9] = W[9];
+ ROUND_00_15(9, h, a, b, c, d, e, f, g);
+ T1 = X[10] = W[10];
+ ROUND_00_15(10, g, h, a, b, c, d, e, f);
+ T1 = X[11] = W[11];
+ ROUND_00_15(11, f, g, h, a, b, c, d, e);
+ T1 = X[12] = W[12];
+ ROUND_00_15(12, e, f, g, h, a, b, c, d);
+ T1 = X[13] = W[13];
+ ROUND_00_15(13, d, e, f, g, h, a, b, c);
+ T1 = X[14] = W[14];
+ ROUND_00_15(14, c, d, e, f, g, h, a, b);
+ T1 = X[15] = W[15];
+ ROUND_00_15(15, b, c, d, e, f, g, h, a);
+
+ data += SHA256_CBLOCK;
+ } else {
+ SHA_LONG l;
+
+ HOST_c2l(data, l);
+ T1 = X[0] = l;
+ ROUND_00_15(0, a, b, c, d, e, f, g, h);
+ HOST_c2l(data, l);
+ T1 = X[1] = l;
+ ROUND_00_15(1, h, a, b, c, d, e, f, g);
+ HOST_c2l(data, l);
+ T1 = X[2] = l;
+ ROUND_00_15(2, g, h, a, b, c, d, e, f);
+ HOST_c2l(data, l);
+ T1 = X[3] = l;
+ ROUND_00_15(3, f, g, h, a, b, c, d, e);
+ HOST_c2l(data, l);
+ T1 = X[4] = l;
+ ROUND_00_15(4, e, f, g, h, a, b, c, d);
+ HOST_c2l(data, l);
+ T1 = X[5] = l;
+ ROUND_00_15(5, d, e, f, g, h, a, b, c);
+ HOST_c2l(data, l);
+ T1 = X[6] = l;
+ ROUND_00_15(6, c, d, e, f, g, h, a, b);
+ HOST_c2l(data, l);
+ T1 = X[7] = l;
+ ROUND_00_15(7, b, c, d, e, f, g, h, a);
+ HOST_c2l(data, l);
+ T1 = X[8] = l;
+ ROUND_00_15(8, a, b, c, d, e, f, g, h);
+ HOST_c2l(data, l);
+ T1 = X[9] = l;
+ ROUND_00_15(9, h, a, b, c, d, e, f, g);
+ HOST_c2l(data, l);
+ T1 = X[10] = l;
+ ROUND_00_15(10, g, h, a, b, c, d, e, f);
+ HOST_c2l(data, l);
+ T1 = X[11] = l;
+ ROUND_00_15(11, f, g, h, a, b, c, d, e);
+ HOST_c2l(data, l);
+ T1 = X[12] = l;
+ ROUND_00_15(12, e, f, g, h, a, b, c, d);
+ HOST_c2l(data, l);
+ T1 = X[13] = l;
+ ROUND_00_15(13, d, e, f, g, h, a, b, c);
+ HOST_c2l(data, l);
+ T1 = X[14] = l;
+ ROUND_00_15(14, c, d, e, f, g, h, a, b);
+ HOST_c2l(data, l);
+ T1 = X[15] = l;
+ ROUND_00_15(15, b, c, d, e, f, g, h, a);
+ }
+
+ for (i = 16; i < 64; i += 8) {
+ ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X);
+ ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X);
+ ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X);
+ ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X);
+ ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X);
+ ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X);
+ ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X);
+ ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X);
+ }
+
+ ctx->h[0] += a;
+ ctx->h[1] += b;
+ ctx->h[2] += c;
+ ctx->h[3] += d;
+ ctx->h[4] += e;
+ ctx->h[5] += f;
+ ctx->h[6] += g;
+ ctx->h[7] += h;
+
+ }
+}
+
+# endif
+# endif /* SHA256_ASM */
diff --git a/src/util/srp.cpp b/src/util/srp.cpp
new file mode 100644
index 000000000..94426db92
--- /dev/null
+++ b/src/util/srp.cpp
@@ -0,0 +1,1038 @@
+/*
+ * Secure Remote Password 6a implementation
+ * https://github.com/est31/csrp-gmp
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifdef WIN32
+ #include <windows.h>
+ #include <wincrypt.h>
+#else
+ #include <time.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <config.h>
+
+#if USE_SYSTEM_GMP || defined (__ANDROID__) || defined (ANDROID)
+ #include <gmp.h>
+#else
+ #include <gmp/mini-gmp.h>
+#endif
+
+#include <util/sha2.h>
+
+#include "srp.h"
+//#define CSRP_USE_SHA1
+#define CSRP_USE_SHA256
+
+#define srp_dbg_data(data, datalen, prevtext) ;
+/*void srp_dbg_data(unsigned char * data, size_t datalen, char * prevtext)
+{
+ printf(prevtext);
+ size_t i;
+ for (i = 0; i < datalen; i++)
+ {
+ printf("%02X", data[i]);
+ }
+ printf("\n");
+}*/
+
+static int g_initialized = 0;
+
+#define RAND_BUFF_MAX 128
+static unsigned int g_rand_idx;
+static unsigned char g_rand_buff[RAND_BUFF_MAX];
+
+typedef struct
+{
+ mpz_t N;
+ mpz_t g;
+} NGConstant;
+
+struct NGHex
+{
+ const char* n_hex;
+ const char* g_hex;
+};
+
+/* All constants here were pulled from Appendix A of RFC 5054 */
+static struct NGHex global_Ng_constants[] = {
+ { /* 1024 */
+ "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C9C256576D674DF7496"
+ "EA81D3383B4813D692C6E0E0D5D8E250B98BE48E495C1D6089DAD15DC7D7B46154D6B6CE8E"
+ "F4AD69B15D4982559B297BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA"
+ "9AFD5138FE8376435B9FC61D2FC0EB06E3",
+ "2"
+ },
+ { /* 2048 */
+ "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC3192943DB56050A37329CBB4"
+ "A099ED8193E0757767A13DD52312AB4B03310DCD7F48A9DA04FD50E8083969EDB767B0CF60"
+ "95179A163AB3661A05FBD5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF"
+ "747359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A436C6481F1D2B907"
+ "8717461A5B9D32E688F87748544523B524B0D57D5EA77A2775D2ECFA032CFBDBF52FB37861"
+ "60279004E57AE6AF874E7303CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DB"
+ "FBB694B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F9E4AFF73",
+ "2"
+ },
+ { /* 4096 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199"
+ "FFFFFFFFFFFFFFFF",
+ "5"
+ },
+ { /* 8192 */
+ "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+ "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+ "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+ "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+ "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+ "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+ "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+ "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+ "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+ "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+ "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+ "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+ "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+ "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+ "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+ "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+ "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+ "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+ "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+ "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+ "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+ "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+ "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+ "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+ "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+ "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+ "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+ "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
+ "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
+ "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+ "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
+ "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
+ "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
+ "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
+ "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
+ "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71"
+ "60C980DD98EDD3DFFFFFFFFFFFFFFFFF",
+ "13"
+ },
+ {0,0} /* null sentinel */
+};
+
+
+static void delete_ng(NGConstant *ng)
+{
+ if (ng) {
+ mpz_clear(ng->N);
+ mpz_clear(ng->g);
+ free(ng);
+ }
+}
+
+static NGConstant *new_ng( SRP_NGType ng_type, const char *n_hex, const char *g_hex )
+{
+ NGConstant *ng = (NGConstant *) malloc(sizeof(NGConstant));
+ mpz_init(ng->N);
+ mpz_init(ng->g);
+
+ if (!ng)
+ return 0;
+
+ if (ng_type != SRP_NG_CUSTOM) {
+ n_hex = global_Ng_constants[ ng_type ].n_hex;
+ g_hex = global_Ng_constants[ ng_type ].g_hex;
+ }
+
+ int rv = 0;
+ rv = mpz_set_str(ng->N, n_hex, 16);
+ rv = rv | mpz_set_str(ng->g, g_hex, 16);
+
+ if (rv) {
+ delete_ng(ng);
+ return 0;
+ }
+
+ return ng;
+}
+
+
+typedef union
+{
+ SHA_CTX sha;
+ SHA256_CTX sha256;
+ //SHA512_CTX sha512;
+} HashCTX;
+
+
+struct SRPVerifier
+{
+ SRP_HashAlgorithm hash_alg;
+ NGConstant *ng;
+
+ char *username;
+ unsigned char *bytes_B;
+ int authenticated;
+
+ unsigned char M[SHA512_DIGEST_LENGTH];
+ unsigned char H_AMK[SHA512_DIGEST_LENGTH];
+ unsigned char session_key[SHA512_DIGEST_LENGTH];
+};
+
+
+struct SRPUser
+{
+ SRP_HashAlgorithm hash_alg;
+ NGConstant *ng;
+
+ mpz_t a;
+ mpz_t A;
+ mpz_t S;
+
+ unsigned char *bytes_A;
+ int authenticated;
+
+ char *username;
+ char *username_verifier;
+ unsigned char *password;
+ size_t password_len;
+
+ unsigned char M[SHA512_DIGEST_LENGTH];
+ unsigned char H_AMK[SHA512_DIGEST_LENGTH];
+ unsigned char session_key[SHA512_DIGEST_LENGTH];
+};
+
+
+static int hash_init(SRP_HashAlgorithm alg, HashCTX *c)
+{
+ switch (alg) {
+#ifdef CSRP_USE_SHA1
+ case SRP_SHA1: return SHA1_Init(&c->sha);
+#endif
+ /*case SRP_SHA224: return SHA224_Init(&c->sha256);*/
+#ifdef CSRP_USE_SHA256
+ case SRP_SHA256: return SHA256_Init(&c->sha256);
+#endif
+ /*case SRP_SHA384: return SHA384_Init(&c->sha512);
+ case SRP_SHA512: return SHA512_Init(&c->sha512);*/
+ default: return -1;
+ };
+}
+static int hash_update( SRP_HashAlgorithm alg, HashCTX *c, const void *data, size_t len )
+{
+ switch (alg) {
+#ifdef CSRP_USE_SHA1
+ case SRP_SHA1: return SHA1_Update(&c->sha, data, len);
+#endif
+ /*case SRP_SHA224: return SHA224_Update(&c->sha256, data, len);*/
+#ifdef CSRP_USE_SHA256
+ case SRP_SHA256: return SHA256_Update(&c->sha256, data, len);
+#endif
+ /*case SRP_SHA384: return SHA384_Update( &c->sha512, data, len );
+ case SRP_SHA512: return SHA512_Update( &c->sha512, data, len );*/
+ default: return -1;
+ };
+}
+static int hash_final( SRP_HashAlgorithm alg, HashCTX *c, unsigned char *md )
+{
+ switch (alg) {
+#ifdef CSRP_USE_SHA1
+ case SRP_SHA1: return SHA1_Final(md, &c->sha);
+#endif
+ /*case SRP_SHA224: return SHA224_Final(md, &c->sha256);*/
+#ifdef CSRP_USE_SHA256
+ case SRP_SHA256: return SHA256_Final(md, &c->sha256);
+#endif
+ /*case SRP_SHA384: return SHA384_Final(md, &c->sha512);
+ case SRP_SHA512: return SHA512_Final(md, &c->sha512);*/
+ default: return -1;
+ };
+}
+static unsigned char *hash(SRP_HashAlgorithm alg, const unsigned char *d, size_t n, unsigned char *md)
+{
+ switch (alg) {
+#ifdef CSRP_USE_SHA1
+ case SRP_SHA1: return SHA1(d, n, md);
+#endif
+ /*case SRP_SHA224: return SHA224( d, n, md );*/
+#ifdef CSRP_USE_SHA256
+ case SRP_SHA256: return SHA256(d, n, md);
+#endif
+ /*case SRP_SHA384: return SHA384( d, n, md );
+ case SRP_SHA512: return SHA512( d, n, md );*/
+ default: return 0;
+ };
+}
+static size_t hash_length(SRP_HashAlgorithm alg)
+{
+ switch (alg) {
+#ifdef CSRP_USE_SHA1
+ case SRP_SHA1: return SHA_DIGEST_LENGTH;
+#endif
+ /*case SRP_SHA224: return SHA224_DIGEST_LENGTH;*/
+#ifdef CSRP_USE_SHA256
+ case SRP_SHA256: return SHA256_DIGEST_LENGTH;
+#endif
+ /*case SRP_SHA384: return SHA384_DIGEST_LENGTH;
+ case SRP_SHA512: return SHA512_DIGEST_LENGTH;*/
+ default: return -1;
+ };
+}
+
+inline static int mpz_num_bytes(const mpz_t op)
+{
+ return (mpz_sizeinbase (op, 2) + 7) / 8;
+}
+
+inline static void mpz_to_bin(const mpz_t op, unsigned char *to)
+{
+ mpz_export(to, NULL, 1, 1, 1, 0, op);
+}
+
+inline static void mpz_from_bin(const unsigned char *s, size_t len, mpz_t ret)
+{
+ mpz_import(ret, len, 1, 1, 1, 0, s);
+}
+
+// set op to (op1 * op2) mod d, using tmp for the calculation
+inline static void mpz_mulm(mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
+{
+ mpz_mul(tmp, op1, op2);
+ mpz_mod(op, tmp, d);
+}
+
+// set op to (op1 + op2) mod d, using tmp for the calculation
+inline static void mpz_addm( mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp )
+{
+ mpz_add(tmp, op1, op2);
+ mpz_mod(op, tmp, d);
+}
+
+// set op to (op1 - op2) mod d, using tmp for the calculation
+inline static void mpz_subm(mpz_t op, const mpz_t op1, const mpz_t op2, const mpz_t d, mpz_t tmp)
+{
+ mpz_sub(tmp, op1, op2);
+ mpz_mod(op, tmp, d);
+}
+
+static int H_nn(mpz_t result, SRP_HashAlgorithm alg, const mpz_t N, const mpz_t n1, const mpz_t n2)
+{
+ unsigned char buff[SHA512_DIGEST_LENGTH];
+ size_t len_N = mpz_num_bytes(N);
+ size_t len_n1 = mpz_num_bytes(n1);
+ size_t len_n2 = mpz_num_bytes(n2);
+ size_t nbytes = len_N + len_N;
+ unsigned char *bin = (unsigned char *) malloc(nbytes);
+ if (!bin)
+ return 0;
+ if (len_n1 > len_N || len_n2 > len_N) {
+ free(bin);
+ return 0;
+ }
+ memset(bin, 0, nbytes);
+ mpz_to_bin(n1, bin + (len_N - len_n1));
+ mpz_to_bin(n2, bin + (len_N + len_N - len_n2));
+ hash( alg, bin, nbytes, buff );
+ free(bin);
+ mpz_from_bin(buff, hash_length(alg), result);
+ return 1;
+}
+
+static int H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *n, size_t len_n, const unsigned char *bytes, size_t len_bytes)
+{
+ unsigned char buff[SHA512_DIGEST_LENGTH];
+ size_t nbytes = len_n + len_bytes;
+ unsigned char *bin = (unsigned char *) malloc(nbytes);
+ if (!bin)
+ return 0;
+ memcpy(bin, n, len_n);
+ memcpy(bin + len_n, bytes, len_bytes);
+ hash(alg, bin, nbytes, buff);
+ free(bin);
+ mpz_from_bin(buff, hash_length(alg), result);
+ return 1;
+}
+
+static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char *salt, size_t salt_len, const char *username, const unsigned char *password, size_t password_len)
+{
+ unsigned char ucp_hash[SHA512_DIGEST_LENGTH];
+ HashCTX ctx;
+ hash_init(alg, &ctx);
+
+ srp_dbg_data((char*) username, strlen(username), "Username for x: ");
+ srp_dbg_data((char*) password, password_len, "Password for x: ");
+ hash_update(alg, &ctx, username, strlen(username));
+ hash_update(alg, &ctx, ":", 1);
+ hash_update(alg, &ctx, password, password_len);
+
+ hash_final(alg, &ctx, ucp_hash);
+
+ return H_ns(result, alg, salt, salt_len, ucp_hash, hash_length(alg));
+}
+
+static void update_hash_n(SRP_HashAlgorithm alg, HashCTX *ctx, const mpz_t n)
+{
+ size_t len = mpz_num_bytes(n);
+ unsigned char* n_bytes = (unsigned char *) malloc(len);
+ if (!n_bytes)
+ return;
+ mpz_to_bin(n, n_bytes);
+ hash_update(alg, ctx, n_bytes, len);
+ free(n_bytes);
+}
+
+static void hash_num( SRP_HashAlgorithm alg, const mpz_t n, unsigned char *dest )
+{
+ int nbytes = mpz_num_bytes(n);
+ unsigned char *bin = (unsigned char *) malloc(nbytes);
+ if(!bin)
+ return;
+ mpz_to_bin(n, bin);
+ hash(alg, bin, nbytes, dest);
+ free(bin);
+}
+
+static void calculate_M(SRP_HashAlgorithm alg, NGConstant *ng, unsigned char *dest,
+ const char *I, const unsigned char *s_bytes, size_t s_len,
+ const mpz_t A, const mpz_t B, const unsigned char *K)
+{
+ unsigned char H_N[SHA512_DIGEST_LENGTH];
+ unsigned char H_g[SHA512_DIGEST_LENGTH];
+ unsigned char H_I[SHA512_DIGEST_LENGTH];
+ unsigned char H_xor[SHA512_DIGEST_LENGTH];
+ HashCTX ctx;
+ size_t i = 0;
+ size_t hash_len = hash_length(alg);
+
+ hash_num(alg, ng->N, H_N);
+ hash_num(alg, ng->g, H_g);
+
+ hash(alg, (const unsigned char *)I, strlen(I), H_I);
+
+
+ for (i = 0; i < hash_len; i++ )
+ H_xor[i] = H_N[i] ^ H_g[i];
+
+ hash_init(alg, &ctx);
+
+ hash_update(alg, &ctx, H_xor, hash_len);
+ hash_update(alg, &ctx, H_I, hash_len);
+ hash_update(alg, &ctx, s_bytes, s_len);
+ update_hash_n(alg, &ctx, A);
+ update_hash_n(alg, &ctx, B);
+ hash_update(alg, &ctx, K, hash_len);
+
+ hash_final(alg, &ctx, dest);
+}
+
+static void calculate_H_AMK(SRP_HashAlgorithm alg, unsigned char *dest, const mpz_t A, const unsigned char *M, const unsigned char *K)
+{
+ HashCTX ctx;
+
+ hash_init(alg, &ctx);
+
+ update_hash_n(alg, &ctx, A);
+ hash_update(alg, &ctx, M, hash_length(alg));
+ hash_update(alg, &ctx, K, hash_length(alg));
+
+ hash_final(alg, &ctx, dest);
+}
+
+
+struct srp_pcgrandom {
+ unsigned long long int m_state;
+ unsigned long long int m_inc;
+}; typedef struct srp_pcgrandom srp_pcgrandom;
+
+static unsigned long int srp_pcgrandom_next(srp_pcgrandom *r)
+{
+ unsigned long long int oldstate = r->m_state;
+ r->m_state = oldstate * 6364136223846793005ULL + r->m_inc;
+
+ unsigned long int xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u;
+ unsigned long int rot = oldstate >> 59u;
+ return (xorshifted >> rot) | (xorshifted << ((-rot) & 31));
+}
+
+static void srp_pcgrandom_seed(srp_pcgrandom *r, unsigned long long int state,
+ unsigned long long int seq)
+{
+ r->m_state = 0U;
+ r->m_inc = (seq << 1u) | 1u;
+ srp_pcgrandom_next(r);
+ r->m_state += state;
+ srp_pcgrandom_next(r);
+}
+
+
+static int fill_buff()
+{
+ g_rand_idx = 0;
+
+#ifdef WIN32
+ HCRYPTPROV wctx;
+#else
+ FILE *fp = 0;
+#endif
+
+#ifdef WIN32
+
+ CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
+ CryptGenRandom(wctx, sizeof(g_rand_buff), (BYTE*) g_rand_buff);
+ CryptReleaseContext(wctx, 0);
+
+ return 1;
+
+#else
+ fp = fopen("/dev/urandom", "r");
+
+ if (fp) {
+ fread(g_rand_buff, sizeof(g_rand_buff), 1, fp);
+ fclose(fp);
+ } else {
+ srp_pcgrandom *r = (srp_pcgrandom *) malloc(sizeof(srp_pcgrandom));
+ srp_pcgrandom_seed(r, time(NULL) ^ clock(), 0xda3e39cb94b95bdbULL);
+ size_t i = 0;
+ for (i = 0; i < RAND_BUFF_MAX; i++) {
+ g_rand_buff[i] = srp_pcgrandom_next(r);
+ }
+ }
+#endif
+ return 1;
+}
+
+static void mpz_fill_random(mpz_t num)
+{
+ // was call: BN_rand(num, 256, -1, 0);
+ if (RAND_BUFF_MAX - g_rand_idx < 32)
+ fill_buff();
+ mpz_from_bin((const unsigned char *) (&g_rand_buff[g_rand_idx]), 32, num);
+ g_rand_idx += 32;
+}
+
+static void init_random()
+{
+ if (g_initialized)
+ return;
+ g_initialized = fill_buff();
+}
+
+#define srp_dbg_num(num, text) ;
+/*void srp_dbg_num(mpz_t num, char * prevtext)
+{
+ int len_num = mpz_num_bytes(num);
+ char *bytes_num = (char*) malloc(len_num);
+ mpz_to_bin(num, (unsigned char *) bytes_num);
+ srp_dbg_data(bytes_num, len_num, prevtext);
+ free(bytes_num);
+
+}*/
+
+/***********************************************************************************************************
+ *
+ * Exported Functions
+ *
+ ***********************************************************************************************************/
+
+void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
+ SRP_NGType ng_type, const char *username_for_verifier,
+ const unsigned char *password, size_t len_password,
+ unsigned char **bytes_s, size_t *len_s,
+ unsigned char **bytes_v, size_t *len_v,
+ const char *n_hex, const char *g_hex )
+{
+ mpz_t v; mpz_init(v);
+ mpz_t x; mpz_init(x);
+ NGConstant *ng = new_ng(ng_type, n_hex, g_hex);
+
+ if(!ng)
+ goto cleanup_and_exit;
+
+ init_random(); /* Only happens once */
+
+ if (*bytes_s == NULL) {
+ *len_s = 16;
+ if (RAND_BUFF_MAX - g_rand_idx < 16)
+ fill_buff();
+ *bytes_s = (unsigned char*)malloc(sizeof(char) * 16);
+ memcpy(*bytes_s, &g_rand_buff + g_rand_idx, sizeof(char) * 16);
+ g_rand_idx += 16;
+ }
+
+
+ if (!calculate_x(x, alg, *bytes_s, *len_s, username_for_verifier,
+ password, len_password))
+ goto cleanup_and_exit;
+
+ srp_dbg_num(x, "Server calculated x: ");
+
+ mpz_powm(v, ng->g, x, ng->N);
+
+ *len_v = mpz_num_bytes(v);
+
+ *bytes_v = (unsigned char*)malloc(*len_v);
+
+ if (!bytes_v)
+ goto cleanup_and_exit;
+
+ mpz_to_bin(v, *bytes_v);
+
+cleanup_and_exit:
+ delete_ng( ng );
+ mpz_clear(v);
+ mpz_clear(x);
+}
+
+
+
+/* Out: bytes_B, len_B.
+ *
+ * On failure, bytes_B will be set to NULL and len_B will be set to 0
+ */
+struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg,
+ SRP_NGType ng_type, const char *username,
+ const unsigned char *bytes_s, size_t len_s,
+ const unsigned char *bytes_v, size_t len_v,
+ const unsigned char *bytes_A, size_t len_A,
+ const unsigned char *bytes_b, size_t len_b,
+ unsigned char **bytes_B, size_t *len_B,
+ const char *n_hex, const char *g_hex )
+{
+ mpz_t v; mpz_init(v); mpz_from_bin(bytes_v, len_v, v);
+ mpz_t A; mpz_init(A); mpz_from_bin(bytes_A, len_A, A);
+ mpz_t u; mpz_init(u);
+ mpz_t B; mpz_init(B);
+ mpz_t S; mpz_init(S);
+ mpz_t b; mpz_init(b);
+ mpz_t k; mpz_init(k);
+ mpz_t tmp1; mpz_init(tmp1);
+ mpz_t tmp2; mpz_init(tmp2);
+ mpz_t tmp3; mpz_init(tmp3);
+ size_t ulen = strlen(username) + 1;
+ NGConstant *ng = new_ng(ng_type, n_hex, g_hex);
+ struct SRPVerifier *ver = 0;
+
+ *len_B = 0;
+ *bytes_B = 0;
+
+ if (!ng)
+ goto cleanup_and_exit;
+
+ ver = (struct SRPVerifier *) malloc( sizeof(struct SRPVerifier) );
+
+ if (!ver)
+ goto cleanup_and_exit;
+
+ init_random(); /* Only happens once */
+
+ ver->username = (char *) malloc(ulen);
+ ver->hash_alg = alg;
+ ver->ng = ng;
+
+ if (!ver->username) {
+ free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
+
+ memcpy((char*)ver->username, username, ulen);
+
+ ver->authenticated = 0;
+
+ /* SRP-6a safety check */
+ mpz_mod(tmp1, A, ng->N);
+ if (mpz_sgn(tmp1) != 0) {
+ if (bytes_b) {
+ mpz_from_bin(bytes_b, len_b, b);
+ } else {
+ mpz_fill_random(b);
+ }
+
+ if (!H_nn(k, alg, ng->N, ng->N, ng->g)) {
+ free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
+
+ /* B = kv + g^b */
+ mpz_mulm(tmp1, k, v, ng->N, tmp3);
+ mpz_powm(tmp2, ng->g, b, ng->N);
+ mpz_addm(B, tmp1, tmp2, ng->N, tmp3);
+
+ if (!H_nn(u, alg, ng->N, A, B)) {
+ free(ver);
+ ver = 0;
+ goto cleanup_and_exit;
+ }
+
+ srp_dbg_num(u, "Server calculated u: ");
+
+ /* S = (A *(v^u)) ^ b */
+ mpz_powm(tmp1, v, u, ng->N);
+ mpz_mulm(tmp2, A, tmp1, ng->N, tmp3);
+ mpz_powm(S, tmp2, b, ng->N);
+
+ hash_num(alg, S, ver->session_key);
+
+ calculate_M(alg, ng, ver->M, username, bytes_s, len_s, A, B, ver->session_key);
+ calculate_H_AMK(alg, ver->H_AMK, A, ver->M, ver->session_key);
+
+ *len_B = mpz_num_bytes(B);
+ *bytes_B = (unsigned char*)malloc(*len_B);
+
+ if (!*bytes_B) {
+ free(ver->username);
+ free(ver);
+ ver = 0;
+ *len_B = 0;
+ goto cleanup_and_exit;
+ }
+
+ mpz_to_bin(B, *bytes_B);
+
+ ver->bytes_B = *bytes_B;
+ } else {
+ free(ver);
+ ver = 0;
+ }
+
+cleanup_and_exit:
+ mpz_clear(v);
+ mpz_clear(A);
+ mpz_clear(u);
+ mpz_clear(k);
+ mpz_clear(B);
+ mpz_clear(S);
+ mpz_clear(b);
+ mpz_clear(tmp1);
+ mpz_clear(tmp2);
+ mpz_clear(tmp3);
+ return ver;
+}
+
+
+
+
+void srp_verifier_delete(struct SRPVerifier *ver)
+{
+ if (ver) {
+ delete_ng(ver->ng);
+ free(ver->username);
+ free(ver->bytes_B);
+ memset(ver, 0, sizeof(*ver));
+ free(ver);
+ }
+}
+
+
+
+int srp_verifier_is_authenticated(struct SRPVerifier *ver)
+{
+ return ver->authenticated;
+}
+
+
+const char *srp_verifier_get_username(struct SRPVerifier *ver)
+{
+ return ver->username;
+}
+
+
+const unsigned char *srp_verifier_get_session_key(struct SRPVerifier *ver, size_t *key_length)
+{
+ if (key_length)
+ *key_length = hash_length(ver->hash_alg);
+ return ver->session_key;
+}
+
+
+size_t srp_verifier_get_session_key_length(struct SRPVerifier *ver)
+{
+ return hash_length(ver->hash_alg);
+}
+
+
+/* user_M must be exactly SHA512_DIGEST_LENGTH bytes in size */
+void srp_verifier_verify_session(struct SRPVerifier *ver, const unsigned char *user_M, unsigned char **bytes_HAMK)
+{
+ if (memcmp(ver->M, user_M, hash_length(ver->hash_alg)) == 0) {
+ ver->authenticated = 1;
+ *bytes_HAMK = ver->H_AMK;
+ } else
+ *bytes_HAMK = NULL;
+}
+
+/*******************************************************************************/
+
+struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
+ const char *username, const char *username_for_verifier,
+ const unsigned char *bytes_password, size_t len_password,
+ const char *n_hex, const char *g_hex)
+{
+ struct SRPUser *usr = (struct SRPUser *) malloc(sizeof(struct SRPUser));
+ size_t ulen = strlen(username) + 1;
+ size_t uvlen = strlen(username_for_verifier) + 1;
+
+ if (!usr)
+ goto err_exit;
+
+ init_random(); /* Only happens once */
+
+ usr->hash_alg = alg;
+ usr->ng = new_ng(ng_type, n_hex, g_hex);
+
+ mpz_init(usr->a);
+ mpz_init(usr->A);
+ mpz_init(usr->S);
+
+ if (!usr->ng)
+ goto err_exit;
+
+ usr->username = (char*)malloc(ulen);
+ usr->username_verifier = (char*)malloc(uvlen);
+ usr->password = (unsigned char*)malloc(len_password);
+ usr->password_len = len_password;
+
+ if (!usr->username || !usr->password)
+ goto err_exit;
+
+ memcpy(usr->username, username, ulen);
+ memcpy(usr->username_verifier, username_for_verifier, uvlen);
+ memcpy(usr->password, bytes_password, len_password);
+
+ usr->authenticated = 0;
+
+ usr->bytes_A = 0;
+
+ return usr;
+
+err_exit:
+ if (usr) {
+ mpz_clear(usr->a);
+ mpz_clear(usr->A);
+ mpz_clear(usr->S);
+ if (usr->ng)
+ delete_ng(usr->ng);
+ if (usr->username)
+ free(usr->username);
+ if (usr->username_verifier)
+ free(usr->username_verifier);
+ if (usr->password) {
+ memset(usr->password, 0, usr->password_len);
+ free(usr->password);
+ }
+ free(usr);
+ }
+
+ return 0;
+}
+
+
+
+void srp_user_delete(struct SRPUser *usr)
+{
+ if(usr) {
+ mpz_clear(usr->a);
+ mpz_clear(usr->A);
+ mpz_clear(usr->S);
+
+ delete_ng(usr->ng);
+
+ memset(usr->password, 0, usr->password_len);
+
+ free(usr->username);
+ free(usr->username_verifier);
+ free(usr->password);
+
+ if (usr->bytes_A)
+ free(usr->bytes_A);
+
+ memset(usr, 0, sizeof(*usr));
+ free(usr);
+ }
+}
+
+
+
+int srp_user_is_authenticated(struct SRPUser *usr)
+{
+ return usr->authenticated;
+}
+
+
+const char *srp_user_get_username(struct SRPUser *usr)
+{
+ return usr->username;
+}
+
+
+const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length)
+{
+ if (key_length)
+ *key_length = hash_length(usr->hash_alg);
+ return usr->session_key;
+}
+
+
+size_t srp_user_get_session_key_length(struct SRPUser *usr)
+{
+ return hash_length(usr->hash_alg);
+}
+
+
+/* Output: username, bytes_A, len_A */
+void srp_user_start_authentication(struct SRPUser *usr, char **username,
+ const unsigned char *bytes_a, size_t len_a,
+ unsigned char **bytes_A, size_t *len_A)
+{
+ if (bytes_a) {
+ mpz_from_bin(bytes_a, len_a, usr->a);
+ } else {
+ mpz_fill_random(usr->a);
+ }
+
+ mpz_powm(usr->A, usr->ng->g, usr->a, usr->ng->N);
+
+ *len_A = mpz_num_bytes(usr->A);
+ *bytes_A = (unsigned char*)malloc(*len_A);
+
+ if (!*bytes_A) {
+ *len_A = 0;
+ *bytes_A = 0;
+ *username = 0;
+ return;
+ }
+
+ mpz_to_bin(usr->A, *bytes_A);
+
+ usr->bytes_A = *bytes_A;
+ if (username)
+ *username = usr->username;
+}
+
+
+/* Output: bytes_M. Buffer length is SHA512_DIGEST_LENGTH */
+void srp_user_process_challenge(struct SRPUser *usr,
+ const unsigned char *bytes_s, size_t len_s,
+ const unsigned char *bytes_B, size_t len_B,
+ unsigned char **bytes_M, size_t *len_M)
+{
+ mpz_t B; mpz_init(B); mpz_from_bin(bytes_B, len_B, B);
+ mpz_t u; mpz_init(u);
+ mpz_t x; mpz_init(x);
+ mpz_t k; mpz_init(k);
+ mpz_t v; mpz_init(v);
+ mpz_t tmp1; mpz_init(tmp1);
+ mpz_t tmp2; mpz_init(tmp2);
+ mpz_t tmp3; mpz_init(tmp3);
+ mpz_t tmp4; mpz_init(tmp4);
+
+ *len_M = 0;
+ *bytes_M = 0;
+
+ if (!H_nn(u, usr->hash_alg, usr->ng->N, usr->A, B))
+ goto cleanup_and_exit;
+
+ srp_dbg_num(u, "Client calculated u: ");
+
+ if (!calculate_x(x, usr->hash_alg, bytes_s, len_s,
+ usr->username_verifier, usr->password, usr->password_len))
+ goto cleanup_and_exit;
+
+ srp_dbg_num(x, "Client calculated x: ");
+
+ if (!H_nn(k, usr->hash_alg, usr->ng->N, usr->ng->N, usr->ng->g))
+ goto cleanup_and_exit;
+
+ /* SRP-6a safety check */
+ if ( mpz_sgn(B) != 0 && mpz_sgn(u) != 0 ) {
+ mpz_powm(v, usr->ng->g, x, usr->ng->N);
+
+ srp_dbg_num(v, "Client calculated v: ");
+
+ /* S = (B - k*(g^x)) ^ (a + ux) */
+ mpz_mul(tmp1, u, x);
+ mpz_add(tmp2, usr->a, tmp1); /* tmp2 = (a + ux) */
+ mpz_powm(tmp1, usr->ng->g, x, usr->ng->N); /* tmp1 = g^x */
+ mpz_mulm(tmp3, k, tmp1, usr->ng->N, tmp4); /* tmp3 = k*(g^x) */
+ mpz_subm(tmp1, B, tmp3, usr->ng->N, tmp4); /* tmp1 = (B - K*(g^x)) */
+ mpz_powm(usr->S, tmp1, tmp2, usr->ng->N);
+
+ hash_num(usr->hash_alg, usr->S, usr->session_key);
+
+ calculate_M( usr->hash_alg, usr->ng, usr->M, usr->username, bytes_s, len_s, usr->A,B, usr->session_key );
+ calculate_H_AMK( usr->hash_alg, usr->H_AMK, usr->A, usr->M, usr->session_key );
+
+ *bytes_M = usr->M;
+ if (len_M)
+ *len_M = hash_length( usr->hash_alg );
+ } else {
+ *bytes_M = NULL;
+ if (len_M)
+ *len_M = 0;
+ }
+
+cleanup_and_exit:
+
+ mpz_clear(B);
+ mpz_clear(u);
+ mpz_clear(x);
+ mpz_clear(k);
+ mpz_clear(v);
+ mpz_clear(tmp1);
+ mpz_clear(tmp2);
+ mpz_clear(tmp3);
+ mpz_clear(tmp4);
+}
+
+
+void srp_user_verify_session(struct SRPUser *usr, const unsigned char *bytes_HAMK)
+{
+ if (memcmp(usr->H_AMK, bytes_HAMK, hash_length(usr->hash_alg)) == 0)
+ usr->authenticated = 1;
+}
diff --git a/src/util/srp.h b/src/util/srp.h
new file mode 100644
index 000000000..15a2b8a68
--- /dev/null
+++ b/src/util/srp.h
@@ -0,0 +1,171 @@
+/*
+ * Secure Remote Password 6a implementation
+ * https://github.com/est31/csrp-gmp
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*
+ *
+ * Purpose: This is a direct implementation of the Secure Remote Password
+ * Protocol version 6a as described by
+ * http://srp.stanford.edu/design.html
+ *
+ * Author: tom.cocagne@gmail.com (Tom Cocagne)
+ *
+ * Dependencies: LibGMP
+ *
+ * Usage: Refer to test_srp.c for a demonstration
+ *
+ * Notes:
+ * This library allows multiple combinations of hashing algorithms and
+ * prime number constants. For authentication to succeed, the hash and
+ * prime number constants must match between
+ * srp_create_salted_verification_key(), srp_user_new(),
+ * and srp_verifier_new(). A recommended approach is to determine the
+ * desired level of security for an application and globally define the
+ * hash and prime number constants to the predetermined values.
+ *
+ * As one might suspect, more bits means more security. As one might also
+ * suspect, more bits also means more processing time. The test_srp.c
+ * program can be easily modified to profile various combinations of
+ * hash & prime number pairings.
+ */
+
+#ifndef SRP_H
+#define SRP_H
+
+
+struct SRPVerifier;
+struct SRPUser;
+
+typedef enum
+{
+ SRP_NG_1024,
+ SRP_NG_2048,
+ SRP_NG_4096,
+ SRP_NG_8192,
+ SRP_NG_CUSTOM
+} SRP_NGType;
+
+typedef enum
+{
+ /*SRP_SHA1,*/
+ /*SRP_SHA224,*/
+ SRP_SHA256,
+ /*SRP_SHA384,
+ SRP_SHA512*/
+} SRP_HashAlgorithm;
+
+/* Out: bytes_v, len_v
+ *
+ * The caller is responsible for freeing the memory allocated for bytes_v
+ *
+ * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type.
+ * If provided, they must contain ASCII text of the hexidecimal notation.
+ *
+ * If bytes_s == NULL, it is filled with random data. The caller is responsible for freeing.
+ */
+void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
+ SRP_NGType ng_type, const char *username_for_verifier,
+ const unsigned char *password, size_t len_password,
+ unsigned char **bytes_s, size_t *len_s,
+ unsigned char **bytes_v, size_t *len_v,
+ const char * n_hex, const char *g_hex );
+
+/* Out: bytes_B, len_B.
+ *
+ * On failure, bytes_B will be set to NULL and len_B will be set to 0
+ *
+ * The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
+ *
+ * If bytes_b == NULL, random data is used for b.
+ */
+struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
+ const char *username,
+ const unsigned char *bytes_s, size_t len_s,
+ const unsigned char *bytes_v, size_t len_v,
+ const unsigned char *bytes_A, size_t len_A,
+ const unsigned char *bytes_b, size_t len_b,
+ unsigned char** bytes_B, size_t *len_B,
+ const char* n_hex, const char* g_hex);
+
+
+void srp_verifier_delete( struct SRPVerifier* ver );
+
+
+int srp_verifier_is_authenticated( struct SRPVerifier* ver );
+
+
+const char * srp_verifier_get_username( struct SRPVerifier* ver );
+
+/* key_length may be null */
+const unsigned char* srp_verifier_get_session_key( struct SRPVerifier* ver,
+ size_t *key_length );
+
+
+size_t srp_verifier_get_session_key_length(struct SRPVerifier* ver);
+
+
+/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */
+void srp_verifier_verify_session( struct SRPVerifier* ver,
+ const unsigned char* user_M, unsigned char** bytes_HAMK );
+
+/*******************************************************************************/
+
+/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */
+struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
+ const char *username, const char *username_for_verifier,
+ const unsigned char *bytes_password, size_t len_password,
+ const char *n_hex, const char *g_hex);
+
+void srp_user_delete(struct SRPUser * usr);
+
+int srp_user_is_authenticated(struct SRPUser * usr);
+
+
+const char* srp_user_get_username(struct SRPUser * usr);
+
+/* key_length may be null */
+const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length);
+
+size_t srp_user_get_session_key_length(struct SRPUser* usr);
+
+/* Output: username, bytes_A, len_A. If you don't want it get written, set username to NULL.
+ * If bytes_a == NULL, random data is used for a. */
+void srp_user_start_authentication(struct SRPUser* usr, char** username,
+ const unsigned char* bytes_a, size_t len_a,
+ unsigned char** bytes_A, size_t* len_A);
+
+/* Output: bytes_M, len_M (len_M may be null and will always be
+ * srp_user_get_session_key_length() bytes in size) */
+void srp_user_process_challenge(struct SRPUser *usr,
+ const unsigned char *bytes_s, size_t len_s,
+ const unsigned char *bytes_B, size_t len_B,
+ unsigned char **bytes_M, size_t *len_M);
+
+/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */
+void srp_user_verify_session(struct SRPUser* usr, const unsigned char* bytes_HAMK);
+
+#endif /* Include Guard */
diff --git a/src/util/string.cpp b/src/util/string.cpp
index 956a1ac44..2c4143c76 100644
--- a/src/util/string.cpp
+++ b/src/util/string.cpp
@@ -22,25 +22,163 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "numeric.h"
#include "log.h"
-#include "sha1.h"
-#include "base64.h"
#include "hex.h"
#include "../porting.h"
-#include <algorithm>
#include <sstream>
#include <iomanip>
#include <map>
+#ifndef _WIN32
+ #include <iconv.h>
+#else
+ #define _WIN32_WINNT 0x0501
+ #include <windows.h>
+#endif
+
+#if defined(_ICONV_H_) && (defined(__FreeBSD__) || defined(__NetBSD__) || \
+ defined(__OpenBSD__) || defined(__DragonFly__))
+ #define BSD_ICONV_USED
+#endif
+
static bool parseHexColorString(const std::string &value, video::SColor &color);
static bool parseNamedColorString(const std::string &value, video::SColor &color);
+#ifndef _WIN32
+
+bool convert(const char *to, const char *from, char *outbuf,
+ size_t outbuf_size, char *inbuf, size_t inbuf_size)
+{
+ iconv_t cd = iconv_open(to, from);
+
+#ifdef BSD_ICONV_USED
+ const char *inbuf_ptr = inbuf;
+#else
+ char *inbuf_ptr = inbuf;
+#endif
+
+ char *outbuf_ptr = outbuf;
+
+ size_t *inbuf_left_ptr = &inbuf_size;
+ size_t *outbuf_left_ptr = &outbuf_size;
+
+ size_t old_size = inbuf_size;
+ while (inbuf_size > 0) {
+ iconv(cd, &inbuf_ptr, inbuf_left_ptr, &outbuf_ptr, outbuf_left_ptr);
+ if (inbuf_size == old_size) {
+ iconv_close(cd);
+ return false;
+ }
+ old_size = inbuf_size;
+ }
+
+ iconv_close(cd);
+ return true;
+}
+
+std::wstring utf8_to_wide(const std::string &input)
+{
+ size_t inbuf_size = input.length() + 1;
+ // maximum possible size, every character is sizeof(wchar_t) bytes
+ size_t outbuf_size = (input.length() + 1) * sizeof(wchar_t);
+
+ char *inbuf = new char[inbuf_size];
+ memcpy(inbuf, input.c_str(), inbuf_size);
+ char *outbuf = new char[outbuf_size];
+ memset(outbuf, 0, outbuf_size);
+
+ if (!convert("WCHAR_T", "UTF-8", outbuf, outbuf_size, inbuf, inbuf_size)) {
+ infostream << "Couldn't convert UTF-8 string 0x" << hex_encode(input)
+ << " into wstring" << std::endl;
+ delete[] inbuf;
+ delete[] outbuf;
+ return L"<invalid UTF-8 string>";
+ }
+ std::wstring out((wchar_t *)outbuf);
+
+ delete[] inbuf;
+ delete[] outbuf;
+
+ return out;
+}
+
+#ifdef __ANDROID__
+// TODO: this is an ugly fix for wide_to_utf8 somehow not working on android
+std::string wide_to_utf8(const std::wstring &input)
+{
+ return wide_to_narrow(input);
+}
+#else
+std::string wide_to_utf8(const std::wstring &input)
+{
+ size_t inbuf_size = (input.length() + 1) * sizeof(wchar_t);
+ // maximum possible size: utf-8 encodes codepoints using 1 up to 6 bytes
+ size_t outbuf_size = (input.length() + 1) * 6;
+
+ char *inbuf = new char[inbuf_size];
+ memcpy(inbuf, input.c_str(), inbuf_size);
+ char *outbuf = new char[outbuf_size];
+ memset(outbuf, 0, outbuf_size);
+
+ if (!convert("UTF-8", "WCHAR_T", outbuf, outbuf_size, inbuf, inbuf_size)) {
+ infostream << "Couldn't convert wstring 0x" << hex_encode(inbuf, inbuf_size)
+ << " into UTF-8 string" << std::endl;
+ delete[] inbuf;
+ delete[] outbuf;
+ return "<invalid wstring>";
+ }
+ std::string out(outbuf);
+
+ delete[] inbuf;
+ delete[] outbuf;
+
+ return out;
+}
+
+#endif
+#else // _WIN32
+
+std::wstring utf8_to_wide(const std::string &input)
+{
+ size_t outbuf_size = input.size() + 1;
+ wchar_t *outbuf = new wchar_t[outbuf_size];
+ memset(outbuf, 0, outbuf_size * sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, input.c_str(), input.size(),
+ outbuf, outbuf_size);
+ std::wstring out(outbuf);
+ delete[] outbuf;
+ return out;
+}
+
+std::string wide_to_utf8(const std::wstring &input)
+{
+ size_t outbuf_size = (input.size() + 1) * 6;
+ char *outbuf = new char[outbuf_size];
+ memset(outbuf, 0, outbuf_size);
+ WideCharToMultiByte(CP_UTF8, 0, input.c_str(), input.size(),
+ outbuf, outbuf_size, NULL, NULL);
+ std::string out(outbuf);
+ delete[] outbuf;
+ return out;
+}
+
+#endif // _WIN32
+
+wchar_t *utf8_to_wide_c(const char *str)
+{
+ std::wstring ret = utf8_to_wide(std::string(str)).c_str();
+ size_t len = ret.length();
+ wchar_t *ret_c = new wchar_t[len + 1];
+ memset(ret_c, 0, (len + 1) * sizeof(wchar_t));
+ memcpy(ret_c, ret.c_str(), len * sizeof(wchar_t));
+ return ret_c;
+}
// You must free the returned string!
// The returned string is allocated using new
wchar_t *narrow_to_wide_c(const char *str)
{
- wchar_t* nstr = 0;
+ wchar_t *nstr = NULL;
#if defined(_WIN32)
int nResult = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) str, -1, 0, 0);
if (nResult == 0) {
@@ -51,7 +189,7 @@ wchar_t *narrow_to_wide_c(const char *str)
}
#else
size_t len = strlen(str);
- nstr = new wchar_t[len+1];
+ nstr = new wchar_t[len + 1];
std::wstring intermediate = narrow_to_wide(str);
memset(nstr, 0, (len + 1) * sizeof(wchar_t));
@@ -63,6 +201,7 @@ wchar_t *narrow_to_wide_c(const char *str)
#ifdef __ANDROID__
+
const wchar_t* wide_chars =
L" !\"#$%&'()*+,-./0123456789:;<=>?@"
L"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
@@ -175,25 +314,6 @@ std::string wide_to_narrow(const std::wstring &wcs)
#endif
-// Get an sha-1 hash of the player's name combined with
-// the password entered. That's what the server uses as
-// their password. (Exception : if the password field is
-// blank, we send a blank password - this is for backwards
-// compatibility with password-less players).
-std::string translatePassword(std::string playername, std::wstring password)
-{
- if (password.length() == 0)
- return "";
-
- std::string slt = playername + wide_to_narrow(password);
- SHA1 sha1;
- sha1.addBytes(slt.c_str(), slt.length());
- unsigned char *digest = sha1.getDigest();
- std::string pwd = base64_encode(digest, 20);
- free(digest);
- return pwd;
-}
-
std::string urlencode(std::string str)
{
// Encodes non-unreserved URI characters by a percent sign
@@ -319,7 +439,7 @@ char *mystrtok_r(char *s, const char *sep, char **lasts)
}
t++;
}
-
+
*lasts = t;
return s;
}
@@ -328,15 +448,15 @@ u64 read_seed(const char *str)
{
char *endptr;
u64 num;
-
+
if (str[0] == '0' && str[1] == 'x')
num = strtoull(str, &endptr, 16);
else
num = strtoull(str, &endptr, 10);
-
+
if (*endptr)
num = murmur_hash_64_ua(str, (int)strlen(str), 0x1337);
-
+
return num;
}
@@ -467,8 +587,8 @@ ColorContainer::ColorContainer()
colors["greenyellow"] = 0xadff2f;
colors["honeydew"] = 0xf0fff0;
colors["hotpink"] = 0xff69b4;
- colors["indianred "] = 0xcd5c5c;
- colors["indigo "] = 0x4b0082;
+ colors["indianred"] = 0xcd5c5c;
+ colors["indigo"] = 0x4b0082;
colors["ivory"] = 0xfffff0;
colors["khaki"] = 0xf0e68c;
colors["lavender"] = 0xe6e6fa;
@@ -613,4 +733,3 @@ void str_replace(std::string &str, char from, char to)
{
std::replace(str.begin(), str.end(), from, to);
}
-
diff --git a/src/util/string.h b/src/util/string.h
index dc520e3a8..793baad0e 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -25,25 +25,40 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <string>
#include <cstring>
#include <vector>
+#include <map>
#include <sstream>
#include <cctype>
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
+// Checks whether a byte is an inner byte for an utf-8 multibyte sequence
+#define IS_UTF8_MULTB_INNER(x) (((unsigned char)x >= 0x80) && ((unsigned char)x < 0xc0))
+
+typedef std::map<std::string, std::string> StringMap;
+
struct FlagDesc {
const char *name;
u32 flag;
};
+// try not to convert between wide/utf8 encodings; this can result in data loss
+// try to only convert between them when you need to input/output stuff via Irrlicht
+std::wstring utf8_to_wide(const std::string &input);
+std::string wide_to_utf8(const std::wstring &input);
+
+wchar_t *utf8_to_wide_c(const char *str);
+
+// NEVER use those two functions unless you have a VERY GOOD reason to
+// they just convert between wide and multibyte encoding
+// multibyte encoding depends on current locale, this is no good, especially on Windows
// You must free the returned string!
// The returned string is allocated using new
wchar_t *narrow_to_wide_c(const char *str);
-
std::wstring narrow_to_wide(const std::string &mbs);
std::string wide_to_narrow(const std::wstring &wcs);
-std::string translatePassword(std::string playername, std::wstring password);
+
std::string urlencode(std::string str);
std::string urldecode(std::string str);
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
@@ -150,6 +165,24 @@ inline bool str_starts_with(const std::basic_string<T> &str,
return true;
}
+/**
+ * Check whether \p str begins with the string prefix. If \p case_insensitive
+ * is true then the check is case insensitve (default is false; i.e. case is
+ * significant).
+ *
+ * @param str
+ * @param prefix
+ * @param case_insensitive
+ * @return true if the str begins with prefix
+ */
+template <typename T>
+inline bool str_starts_with(const std::basic_string<T> &str,
+ const T *prefix,
+ bool case_insensitive = false)
+{
+ return str_starts_with(str, std::basic_string<T>(prefix),
+ case_insensitive);
+}
/**
* Splits a string into its component parts separated by the character
@@ -383,7 +416,10 @@ inline bool string_allowed_blacklist(const std::string &str,
* every \p row_len characters whether it breaks a word or not. It is
* intended to be used for, for example, showing paths in the GUI.
*
- * @param from The string to be wrapped into rows.
+ * @note This function doesn't wrap inside utf-8 multibyte sequences and also
+ * counts multibyte sequences correcly as single characters.
+ *
+ * @param from The (utf-8) string to be wrapped into rows.
* @param row_len The row length (in characters).
* @return A new string with the wrapping applied.
*/
@@ -392,9 +428,14 @@ inline std::string wrap_rows(const std::string &from,
{
std::string to;
+ size_t character_idx = 0;
for (size_t i = 0; i < from.size(); i++) {
- if (i != 0 && i % row_len == 0)
- to += '\n';
+ if (!IS_UTF8_MULTB_INNER(from[i])) {
+ // Wrap string after last inner byte of char
+ if (character_idx > 0 && character_idx % row_len == 0)
+ to += '\n';
+ character_idx++;
+ }
to += from[i];
}
diff --git a/src/util/thread.h b/src/util/thread.h
index 8b3c33621..b3a5e68a2 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -25,15 +25,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "../jthread/jmutex.h"
#include "../jthread/jmutexautolock.h"
#include "porting.h"
+#include "log.h"
template<typename T>
-class MutexedVariable
-{
+class MutexedVariable {
public:
MutexedVariable(T value):
m_value(value)
- {
- }
+ {}
T get()
{
@@ -46,13 +45,13 @@ public:
JMutexAutoLock lock(m_mutex);
m_value = value;
}
-
+
// You'll want to grab this in a SharedPtr
- JMutexAutoLock * getLock()
+ JMutexAutoLock *getLock()
{
return new JMutexAutoLock(m_mutex);
}
-
+
// You pretty surely want to grab the lock when accessing this
T m_value;
@@ -64,8 +63,7 @@ private:
A single worker thread - multiple client threads queue framework.
*/
template<typename Key, typename T, typename Caller, typename CallerData>
-class GetResult
-{
+class GetResult {
public:
Key key;
T item;
@@ -73,34 +71,27 @@ public:
};
template<typename Key, typename T, typename Caller, typename CallerData>
-class ResultQueue: public MutexedQueue< GetResult<Key, T, Caller, CallerData> >
-{
+class ResultQueue : public MutexedQueue<GetResult<Key, T, Caller, CallerData> > {
};
template<typename Caller, typename Data, typename Key, typename T>
-class CallerInfo
-{
+class CallerInfo {
public:
Caller caller;
Data data;
- ResultQueue< Key, T, Caller, Data>* dest;
+ ResultQueue<Key, T, Caller, Data> *dest;
};
template<typename Key, typename T, typename Caller, typename CallerData>
-class GetRequest
-{
+class GetRequest {
public:
- GetRequest()
- {
- }
- GetRequest(Key a_key)
- {
+ GetRequest() {}
+ ~GetRequest() {}
+
+ GetRequest(Key a_key) {
key = a_key;
}
- ~GetRequest()
- {
- }
-
+
Key key;
std::list<CallerInfo<Caller, CallerData, Key, T> > callers;
};
@@ -113,8 +104,7 @@ public:
* @param CallerData data passed back to caller
*/
template<typename Key, typename T, typename Caller, typename CallerData>
-class RequestQueue
-{
+class RequestQueue {
public:
bool empty()
{
@@ -122,40 +112,36 @@ public:
}
void add(Key key, Caller caller, CallerData callerdata,
- ResultQueue<Key, T, Caller, CallerData> *dest)
+ ResultQueue<Key, T, Caller, CallerData> *dest)
{
+ typename std::deque<GetRequest<Key, T, Caller, CallerData> >::iterator i;
+ typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator j;
+
{
JMutexAutoLock lock(m_queue.getMutex());
/*
If the caller is already on the list, only update CallerData
*/
- for(typename std::list< GetRequest<Key, T, Caller, CallerData> >::iterator
- i = m_queue.getList().begin();
- i != m_queue.getList().end(); ++i)
- {
+ for (i = m_queue.getQueue().begin(); i != m_queue.getQueue().end(); ++i) {
GetRequest<Key, T, Caller, CallerData> &request = *i;
-
- if(request.key == key)
- {
- for(typename std::list< CallerInfo<Caller, CallerData, Key, T> >::iterator
- i = request.callers.begin();
- i != request.callers.end(); ++i)
- {
- CallerInfo<Caller, CallerData, Key, T> &ca = *i;
- if(ca.caller == caller)
- {
- ca.data = callerdata;
- return;
- }
+ if (request.key != key)
+ continue;
+
+ for (j = request.callers.begin(); j != request.callers.end(); ++j) {
+ CallerInfo<Caller, CallerData, Key, T> &ca = *j;
+ if (ca.caller == caller) {
+ ca.data = callerdata;
+ return;
}
- CallerInfo<Caller, CallerData, Key, T> ca;
- ca.caller = caller;
- ca.data = callerdata;
- ca.dest = dest;
- request.callers.push_back(ca);
- return;
}
+
+ CallerInfo<Caller, CallerData, Key, T> ca;
+ ca.caller = caller;
+ ca.data = callerdata;
+ ca.dest = dest;
+ request.callers.push_back(ca);
+ return;
}
}
@@ -170,7 +156,7 @@ public:
ca.data = callerdata;
ca.dest = dest;
request.callers.push_back(ca);
-
+
m_queue.push_back(request);
}
@@ -184,13 +170,11 @@ public:
return m_queue.pop_frontNoEx();
}
- void pushResult(GetRequest<Key, T, Caller, CallerData> req,
- T res) {
-
- for(typename std::list< CallerInfo<Caller, CallerData, Key, T> >::iterator
+ void pushResult(GetRequest<Key, T, Caller, CallerData> req, T res)
+ {
+ for (typename std::list<CallerInfo<Caller, CallerData, Key, T> >::iterator
i = req.callers.begin();
- i != req.callers.end(); ++i)
- {
+ i != req.callers.end(); ++i) {
CallerInfo<Caller, CallerData, Key, T> &ca = *i;
GetResult<Key,T,Caller,CallerData> result;
@@ -205,7 +189,62 @@ public:
}
private:
- MutexedQueue< GetRequest<Key, T, Caller, CallerData> > m_queue;
+ MutexedQueue<GetRequest<Key, T, Caller, CallerData> > m_queue;
+};
+
+class UpdateThread : public JThread {
+public:
+ UpdateThread() {}
+ virtual ~UpdateThread() {}
+
+ void deferUpdate()
+ {
+ m_update_sem.Post();
+ }
+
+ void Stop()
+ {
+ JThread::Stop();
+
+ // give us a nudge
+ m_update_sem.Post();
+ }
+
+ void *Thread()
+ {
+ ThreadStarted();
+
+ const char *thread_name = getName();
+ log_register_thread(thread_name);
+ porting::setThreadName(thread_name);
+
+ DSTACK(__FUNCTION_NAME);
+ BEGIN_DEBUG_EXCEPTION_HANDLER
+
+ while (!StopRequested()) {
+ m_update_sem.Wait();
+
+ // Empty the queue, just in case doUpdate() is expensive
+ while (m_update_sem.GetValue())
+ m_update_sem.Wait();
+
+ if (StopRequested())
+ break;
+
+ doUpdate();
+ }
+
+ END_DEBUG_EXCEPTION_HANDLER(errorstream)
+
+ return NULL;
+ }
+
+protected:
+ virtual void doUpdate() = 0;
+ virtual const char *getName() = 0;
+
+private:
+ JSemaphore m_update_sem;
};
#endif
diff --git a/src/version.cpp b/src/version.cpp
index ecfeb95f8..ca206bded 100644
--- a/src/version.cpp
+++ b/src/version.cpp
@@ -20,27 +20,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "version.h"
#include "config.h"
-#ifdef __ANDROID__
+#if defined(__ANDROID__)
#include "android_version.h"
+ #include "android_version_githash.h"
#elif defined(USE_CMAKE_CONFIG_H)
#include "cmake_config_githash.h"
#endif
-#ifdef CMAKE_VERSION_GITHASH
- #define VERSION_GITHASH CMAKE_VERSION_GITHASH
-#else
+#ifndef VERSION_GITHASH
#define VERSION_GITHASH VERSION_STRING
#endif
-const char *minetest_version_simple = VERSION_STRING;
-const char *minetest_version_hash = VERSION_GITHASH;
-#ifdef USE_CMAKE_CONFIG_H
-const char *minetest_build_info =
- "VER=" VERSION_GITHASH " " CMAKE_BUILD_INFO;
-#elif defined(ANDROID)
-const char *minetest_build_info = "android jni";
-#else
-const char *minetest_build_info = "non-cmake";
-#endif
+const char *g_version_string = VERSION_STRING;
+const char *g_version_hash = VERSION_GITHASH;
+const char *g_build_info = "VER=" VERSION_GITHASH " " BUILD_INFO;
diff --git a/src/version.h b/src/version.h
index b23e770aa..0b803b82c 100644
--- a/src/version.h
+++ b/src/version.h
@@ -20,9 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef VERSION_HEADER
#define VERSION_HEADER
-extern const char *minetest_version_simple;
-extern const char *minetest_version_hash;
-extern const char *minetest_build_info;
+extern const char *g_version_string;
+extern const char *g_version_hash;
+extern const char *g_build_info;
#endif
diff --git a/src/voxel.cpp b/src/voxel.cpp
index 8ac786aab..87773b240 100644
--- a/src/voxel.cpp
+++ b/src/voxel.cpp
@@ -390,7 +390,6 @@ void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight,
}
}
-#if 1
/*
Goes recursively through the neighbours of the node.
@@ -421,115 +420,6 @@ void VoxelManipulator::unspreadLight(enum LightBank bank,
unspreadLight(bank, j->first, j->second, light_sources, nodemgr);
}
}
-#endif
-
-#if 0
-/*
- Goes recursively through the neighbours of the node.
-
- Alters only transparent nodes.
-
- If the lighting of the neighbour is lower than the lighting of
- the node was (before changing it to 0 at the step before), the
- lighting of the neighbour is set to 0 and then the same stuff
- repeats for the neighbour.
-
- The ending nodes of the routine are stored in light_sources.
- This is useful when a light is removed. In such case, this
- routine can be called for the light node and then again for
- light_sources to re-light the area without the removed light.
-
- values of from_nodes are lighting values.
-*/
-void VoxelManipulator::unspreadLight(enum LightBank bank,
- core::map<v3s16, u8> & from_nodes,
- core::map<v3s16, bool> & light_sources)
-{
- v3s16 dirs[6] = {
- v3s16(0,0,1), // back
- v3s16(0,1,0), // top
- v3s16(1,0,0), // right
- v3s16(0,0,-1), // front
- v3s16(0,-1,0), // bottom
- v3s16(-1,0,0), // left
- };
-
- if(from_nodes.size() == 0)
- return;
-
- core::map<v3s16, u8> unlighted_nodes;
- core::map<v3s16, u8>::Iterator j;
- j = from_nodes.getIterator();
-
- for(; j.atEnd() == false; j++)
- {
- v3s16 pos = j.getNode()->getKey();
-
- addArea(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1)));
-
- //MapNode &n = m_data[m_area.index(pos)];
-
- u8 oldlight = j.getNode()->getValue();
-
- // Loop through 6 neighbors
- for(u16 i=0; i<6; i++)
- {
- // Get the position of the neighbor node
- v3s16 n2pos = pos + dirs[i];
-
- u32 n2i = m_area.index(n2pos);
-
- if(m_flags[n2i] & VOXELFLAG_NO_DATA)
- continue;
-
- MapNode &n2 = m_data[n2i];
-
- /*
- If the neighbor is dimmer than what was specified
- as oldlight (the light of the previous node)
- */
- if(n2.getLight(bank, nodemgr) < oldlight)
- {
- /*
- And the neighbor is transparent and it has some light
- */
- if(nodemgr->get(n2).light_propagates && n2.getLight(bank, nodemgr) != 0)
- {
- /*
- Set light to 0 and add to queue
- */
-
- u8 current_light = n2.getLight(bank, nodemgr);
- n2.setLight(bank, 0);
-
- unlighted_nodes.insert(n2pos, current_light);
-
- /*
- Remove from light_sources if it is there
- NOTE: This doesn't happen nearly at all
- */
- /*if(light_sources.find(n2pos))
- {
- std::cout<<"Removed from light_sources"<<std::endl;
- light_sources.remove(n2pos);
- }*/
- }
- }
- else{
- light_sources.insert(n2pos, true);
- }
- }
- }
-
- /*dstream<<"unspreadLight(): Changed block "
- <<blockchangecount<<" times"
- <<" for "<<from_nodes.size()<<" nodes"
- <<std::endl;*/
-
- if(unlighted_nodes.size() > 0)
- unspreadLight(bank, unlighted_nodes, light_sources);
-}
-#endif
void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
INodeDefManager *nodemgr)
@@ -594,34 +484,9 @@ void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p,
}
}
-#if 0
-/*
- Lights neighbors of from_nodes, collects all them and then
- goes on recursively.
-
- NOTE: This is faster on small areas but will overflow the
- stack on large areas. Thus it is not used.
-*/
-void VoxelManipulator::spreadLight(enum LightBank bank,
- core::map<v3s16, bool> & from_nodes)
-{
- if(from_nodes.size() == 0)
- return;
-
- core::map<v3s16, bool> lighted_nodes;
- core::map<v3s16, bool>::Iterator j;
- j = from_nodes.getIterator();
-
- for(; j.atEnd() == false; j++)
- {
- v3s16 pos = j.getNode()->getKey();
- spreadLight(bank, pos);
- }
-}
-#endif
+const MapNode VoxelManipulator::ContentIgnoreNode = MapNode(CONTENT_IGNORE);
-#if 1
/*
Lights neighbors of from_nodes, collects all them and then
goes on recursively.
@@ -714,6 +579,5 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
if(!lighted_nodes.empty())
spreadLight(bank, lighted_nodes, nodemgr);
}
-#endif
//END
diff --git a/src/voxel.h b/src/voxel.h
index 52274ac19..58ad39be4 100644
--- a/src/voxel.h
+++ b/src/voxel.h
@@ -213,7 +213,7 @@ public:
return;
}
- assert(contains(a));
+ assert(contains(a)); // pre-condition
// Take back area, XY inclusive
{
@@ -413,10 +413,21 @@ public:
}
// Stuff explodes if non-emerged area is touched with this.
// Emerge first, and check VOXELFLAG_NO_DATA if appropriate.
- MapNode & getNodeRefUnsafe(v3s16 p)
+ MapNode & getNodeRefUnsafe(const v3s16 &p)
{
return m_data[m_area.index(p)];
}
+
+ const MapNode & getNodeRefUnsafeCheckFlags(const v3s16 &p)
+ {
+ s32 index = m_area.index(p);
+
+ if (m_flags[index] & VOXELFLAG_NO_DATA)
+ return ContentIgnoreNode;
+
+ return m_data[index];
+ }
+
u8 & getFlagsRefUnsafe(v3s16 p)
{
return m_flags[m_area.index(p)];
@@ -569,6 +580,8 @@ public:
*/
u8 *m_flags;
+ static const MapNode ContentIgnoreNode;
+
//TODO: Use these or remove them
//TODO: Would these make any speed improvement?
//bool m_pressure_route_valid;
diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp
index 4ddae36d4..bc2977a0e 100644
--- a/src/wieldmesh.cpp
+++ b/src/wieldmesh.cpp
@@ -17,7 +17,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#include "main.h"
#include "settings.h"
#include "wieldmesh.h"
#include "inventory.h"
@@ -26,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "nodedef.h"
#include "mesh.h"
#include "mapblock_mesh.h"
-#include "tile.h"
+#include "client/tile.h"
#include "log.h"
#include "util/numeric.h"
#include <map>
@@ -35,10 +34,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define WIELD_SCALE_FACTOR 30.0
#define WIELD_SCALE_FACTOR_EXTRUDED 40.0
-#define MIN_EXTRUSION_MESH_RESOLUTION 32 // not 16: causes too many "holes"
+#define MIN_EXTRUSION_MESH_RESOLUTION 16
#define MAX_EXTRUSION_MESH_RESOLUTION 512
-static scene::IMesh* createExtrusionMesh(int resolution_x, int resolution_y)
+static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
{
const f32 r = 0.5;
@@ -115,7 +114,9 @@ static scene::IMesh* createExtrusionMesh(int resolution_x, int resolution_y)
mesh->addMeshBuffer(buf);
buf->drop();
scaleMesh(mesh, scale); // also recalculates bounding box
- return mesh;
+ scene::IMesh *newmesh = createForsythOptimizedMesh(mesh);
+ mesh->drop();
+ return newmesh;
}
/*
@@ -170,7 +171,7 @@ public:
if (it == m_extrusion_meshes.end()) {
// no viable resolution found; use largest one
it = m_extrusion_meshes.find(MAX_EXTRUSION_MESH_RESOLUTION);
- assert(it != m_extrusion_meshes.end());
+ sanity_check(it != m_extrusion_meshes.end());
}
scene::IMesh *mesh = it->second;
@@ -231,7 +232,7 @@ WieldMeshSceneNode::WieldMeshSceneNode(
WieldMeshSceneNode::~WieldMeshSceneNode()
{
- assert(g_extrusion_mesh_cache);
+ sanity_check(g_extrusion_mesh_cache);
if (g_extrusion_mesh_cache->drop())
g_extrusion_mesh_cache = NULL;
}
@@ -260,14 +261,20 @@ void WieldMeshSceneNode::setCube(const TileSpec tiles[6],
}
void WieldMeshSceneNode::setExtruded(const std::string &imagename,
- v3f wield_scale, ITextureSource *tsrc)
+ v3f wield_scale, ITextureSource *tsrc, u8 num_frames)
{
video::ITexture *texture = tsrc->getTexture(imagename);
if (!texture) {
changeToMesh(NULL);
return;
}
+
core::dimension2d<u32> dim = texture->getSize();
+ // Detect animation texture and pull off top frame instead of using entire thing
+ if (num_frames > 1) {
+ u32 frame_height = dim.Height / num_frames;
+ dim = core::dimension2d<u32>(dim.Width, frame_height);
+ }
scene::IMesh *mesh = g_extrusion_mesh_cache->create(dim);
changeToMesh(mesh);
mesh->drop();
@@ -276,7 +283,9 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
// Customize material
video::SMaterial &material = m_meshnode->getMaterial(0);
- material.setTexture(0, texture);
+ material.setTexture(0, tsrc->getTexture(imagename));
+ material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
+ material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
material.MaterialType = m_material_type;
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
// Enable bi/trilinear filtering only for high resolution textures
@@ -292,35 +301,29 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
#if (IRRLICHT_VERSION_MAJOR >= 1 && IRRLICHT_VERSION_MINOR >= 8) || IRRLICHT_VERSION_MAJOR >= 2
material.setFlag(video::EMF_USE_MIP_MAPS, false);
#endif
-
-#if 0
-//// TODO(RealBadAngel): Reactivate when shader is added for wield items
- if (m_enable_shaders)
- material.setTexture(2, tsrc->getTexture("disable_img.png"));
-#endif
+ if (m_enable_shaders) {
+ material.setTexture(2, tsrc->getShaderFlagsTexture(false));
+ }
}
void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
{
ITextureSource *tsrc = gamedef->getTextureSource();
IItemDefManager *idef = gamedef->getItemDefManager();
- //IShaderSource *shdrsrc = gamedef->getShaderSource();
+ IShaderSource *shdrsrc = gamedef->getShaderSource();
INodeDefManager *ndef = gamedef->getNodeDefManager();
const ItemDefinition &def = item.getDefinition(idef);
const ContentFeatures &f = ndef->get(def.name);
content_t id = ndef->getId(def.name);
-#if 0
-//// TODO(RealBadAngel): Reactivate when shader is added for wield items
if (m_enable_shaders) {
- u32 shader_id = shdrsrc->getShader("nodes_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
+ u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
m_material_type = shdrsrc->getShaderInfo(shader_id).material;
}
-#endif
// If wield_image is defined, it overrides everything else
if (def.wield_image != "") {
- setExtruded(def.wield_image, def.wield_scale, tsrc);
+ setExtruded(def.wield_image, def.wield_scale, tsrc, 1);
return;
}
// Handle nodes
@@ -336,11 +339,11 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
} else if (f.drawtype == NDT_AIRLIKE) {
changeToMesh(NULL);
} else if (f.drawtype == NDT_PLANTLIKE) {
- setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc);
+ setExtruded(tsrc->getTextureName(f.tiles[0].texture_id), def.wield_scale, tsrc, f.tiles[0].animation_frame_count);
} else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) {
setCube(f.tiles, def.wield_scale, tsrc);
} else {
- MeshMakeData mesh_make_data(gamedef);
+ MeshMakeData mesh_make_data(gamedef, false);
MapNode mesh_make_node(id, 255, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0));
@@ -350,8 +353,13 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
def.wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale));
}
- for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) {
- assert(i < 6);
+ u32 material_count = m_meshnode->getMaterialCount();
+ if (material_count > 6) {
+ errorstream << "WieldMeshSceneNode::setItem: Invalid material "
+ "count " << material_count << ", truncating to 6" << std::endl;
+ material_count = 6;
+ }
+ for (u32 i = 0; i < material_count; ++i) {
video::SMaterial &material = m_meshnode->getMaterial(i);
material.setFlag(video::EMF_BACK_FACE_CULLING, true);
material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter);
@@ -364,8 +372,6 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
material.setTexture(0, f.tiles[i].texture);
}
material.MaterialType = m_material_type;
-#if 0
-//// TODO(RealBadAngel): Reactivate when shader is added for wield items
if (m_enable_shaders) {
if (f.tiles[i].normal_texture) {
if (animated) {
@@ -374,17 +380,14 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef)
} else {
material.setTexture(1, f.tiles[i].normal_texture);
}
- material.setTexture(2, tsrc->getTexture("enable_img.png"));
- } else {
- material.setTexture(2, tsrc->getTexture("disable_img.png"));
}
+ material.setTexture(2, f.tiles[i].flags_texture);
}
-#endif
}
return;
}
else if (def.inventory_image != "") {
- setExtruded(def.inventory_image, def.wield_scale, tsrc);
+ setExtruded(def.inventory_image, def.wield_scale, tsrc, 1);
return;
}
@@ -396,6 +399,7 @@ void WieldMeshSceneNode::setColor(video::SColor color)
{
assert(!m_lighting);
setMeshColor(m_meshnode->getMesh(), color);
+ shadeMeshFaces(m_meshnode->getMesh());
}
void WieldMeshSceneNode::render()
diff --git a/src/wieldmesh.h b/src/wieldmesh.h
index b7739f18c..3f4f4fc04 100644
--- a/src/wieldmesh.h
+++ b/src/wieldmesh.h
@@ -41,13 +41,16 @@ public:
void setCube(const TileSpec tiles[6],
v3f wield_scale, ITextureSource *tsrc);
void setExtruded(const std::string &imagename,
- v3f wield_scale, ITextureSource *tsrc);
+ v3f wield_scale, ITextureSource *tsrc, u8 num_frames);
void setItem(const ItemStack &item, IGameDef *gamedef);
// Sets the vertex color of the wield mesh.
// Must only be used if the constructor was called with lighting = false
void setColor(video::SColor color);
+ scene::IMesh *getMesh()
+ { return m_meshnode->getMesh(); }
+
virtual void render();
virtual const core::aabbox3d<f32>& getBoundingBox() const
diff --git a/textures/base/pack/disable_img.png b/textures/base/pack/disable_img.png
deleted file mode 100644
index 4d3a1b45d..000000000
--- a/textures/base/pack/disable_img.png
+++ /dev/null
Binary files differ
diff --git a/textures/base/pack/enable_img.png b/textures/base/pack/enable_img.png
deleted file mode 100644
index e07f4e9dd..000000000
--- a/textures/base/pack/enable_img.png
+++ /dev/null
Binary files differ
diff --git a/textures/base/pack/minimap_mask_round.png b/textures/base/pack/minimap_mask_round.png
new file mode 100644
index 000000000..797c780c7
--- /dev/null
+++ b/textures/base/pack/minimap_mask_round.png
Binary files differ
diff --git a/textures/base/pack/minimap_mask_square.png b/textures/base/pack/minimap_mask_square.png
new file mode 100644
index 000000000..5dd23f7d8
--- /dev/null
+++ b/textures/base/pack/minimap_mask_square.png
Binary files differ
diff --git a/textures/base/pack/minimap_overlay_round.png b/textures/base/pack/minimap_overlay_round.png
new file mode 100644
index 000000000..1a6b3cc41
--- /dev/null
+++ b/textures/base/pack/minimap_overlay_round.png
Binary files differ
diff --git a/textures/base/pack/minimap_overlay_square.png b/textures/base/pack/minimap_overlay_square.png
new file mode 100644
index 000000000..6d14dc78c
--- /dev/null
+++ b/textures/base/pack/minimap_overlay_square.png
Binary files differ
diff --git a/textures/base/pack/no_screenshot.png b/textures/base/pack/no_screenshot.png
index e309a3eed..60e00f730 100644
--- a/textures/base/pack/no_screenshot.png
+++ b/textures/base/pack/no_screenshot.png
Binary files differ
diff --git a/textures/base/pack/player_marker.png b/textures/base/pack/player_marker.png
new file mode 100644
index 000000000..a5aedfece
--- /dev/null
+++ b/textures/base/pack/player_marker.png
Binary files differ
diff --git a/util/buildbot/toolchain_mingw64.cmake b/util/buildbot/toolchain_mingw64.cmake
index 52d06cdc3..9a7ed82d8 100644
--- a/util/buildbot/toolchain_mingw64.cmake
+++ b/util/buildbot/toolchain_mingw64.cmake
@@ -2,12 +2,12 @@
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
-SET(CMAKE_C_COMPILER x86_64-mingw32-gcc)
-SET(CMAKE_CXX_COMPILER x86_64-mingw32-g++)
-SET(CMAKE_RC_COMPILER x86_64-mingw32-windres)
+SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
+SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
+SET(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
# here is the target environment located
-SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-mingw32)
+SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
diff --git a/util/bump_version.sh b/util/bump_version.sh
index b45c63664..5ff69c8af 100755
--- a/util/bump_version.sh
+++ b/util/bump_version.sh
@@ -87,7 +87,7 @@ sed -i -re "s/^set\(VERSION_MINOR [0-9]+\)$/set(VERSION_MINOR $NEW_VERSION_MINOR
sed -i -re "s/^set\(VERSION_PATCH [0-9]+\)$/set(VERSION_PATCH $NEW_VERSION_PATCH)/" CMakeLists.txt || die "Failed to update VERSION_PATCH"
-sed -i -re "s/^\tset\(VERSION_PATCH \\\$.VERSION_PATCH}-dev\)$/\t#set(VERSION_PATCH \${VERSION_PATCH}-dev)/" CMakeLists.txt || die "Failed to disable -dev suffix"
+sed -i -re "s/^set\(DEVELOPMENT_BUILD TRUE\)$/set(DEVELOPMENT_BUILD FALSE)/" CMakeLists.txt || die "Failed to unset DEVELOPMENT_BUILD"
sed -i -re "s/^ANDROID_VERSION_CODE = [0-9]+$/ANDROID_VERSION_CODE = $NEW_ANDROID_VERSION_CODE/" build/android/Makefile || die "Failed to update ANDROID_VERSION_CODE"
@@ -98,3 +98,24 @@ sed -i -re "1s/[0-9]+\.[0-9]+\.[0-9]+/$NEW_VERSION/g" doc/menu_lua_api.txt || di
git add -f CMakeLists.txt build/android/Makefile doc/lua_api.txt doc/menu_lua_api.txt || die "git add failed"
git commit -m "Bump version to $NEW_VERSION" || die "git commit failed"
+
+############
+# Create tag
+############
+
+echo "Tagging $NEW_VERSION"
+
+git tag -a "$NEW_VERSION" -m "$NEW_VERSION" || die 'Adding tag failed'
+
+######################
+# Create revert commit
+######################
+
+echo 'Creating "revert to development" commit'
+
+sed -i -re 's/^set\(DEVELOPMENT_BUILD FALSE\)$/set(DEVELOPMENT_BUILD TRUE)/' CMakeLists.txt || die 'Failed to set DEVELOPMENT_BUILD'
+
+git add -f CMakeLists.txt || die 'git add failed'
+
+git commit -m "Continue with $NEW_VERSION-dev" || die 'git commit failed'
+
diff --git a/util/travis/before_install.sh b/util/travis/before_install.sh
index 0b1332308..b61118044 100755
--- a/util/travis/before_install.sh
+++ b/util/travis/before_install.sh
@@ -1,6 +1,6 @@
#!/bin/bash -e
-if [ $CC = "clang" ]; then
+if [[ $CC == "clang" ]]; then
export PATH="/usr/bin/:$PATH"
sudo sh -c 'echo "deb http://ppa.launchpad.net/eudoxos/llvm-3.1/ubuntu precise main" >> /etc/apt/sources.list'
sudo apt-key adv --keyserver pool.sks-keyservers.net --recv-keys 92DE8183
@@ -8,22 +8,25 @@ if [ $CC = "clang" ]; then
sudo apt-get install llvm-3.1
sudo apt-get install clang
fi
-if [ $WINDOWS = "no" ]; then
+sudo apt-get install p7zip-full
+if [[ $PLATFORM == "Linux" ]]; then
sudo apt-get install libirrlicht-dev cmake libbz2-dev libpng12-dev \
- libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev \
- libvorbis-dev libopenal-dev gettext
-else
- sudo apt-get install p7zip-full
- if [ $WINDOWS = "32" ]; then
- wget http://sfan5.pf-control.de/mingw_w64_i686_ubuntu12.04_4.9.1.7z -O mingw.7z
- sed -e "s|%PREFIX%|i686-w64-mingw32|" \
- -e "s|%ROOTPATH%|/usr/i686-w64-mingw32|" \
- < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw.cmake
- elif [ $WINDOWS = "64" ]; then
- wget http://sfan5.pf-control.de/mingw_w64_x86_64_ubuntu12.04_4.9.1.7z -O mingw.7z
- sed -e "s|%PREFIX%|x86_64-w64-mingw32|" \
- -e "s|%ROOTPATH%|/usr/x86_64-w64-mingw32|" \
- < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw64.cmake
- fi
+ libjpeg-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev \
+ libhiredis-dev libogg-dev libgmp-dev libvorbis-dev libopenal-dev gettext
+ # Linking to LevelDB is broken, use a custom build
+ wget http://sfan5.pf-control.de/libleveldb-1.18-ubuntu12.04.7z
+ sudo 7z x -o/usr libleveldb-1.18-ubuntu12.04.7z
+elif [[ $PLATFORM == "Win32" ]]; then
+ wget http://sfan5.pf-control.de/mingw_w64_i686_ubuntu12.04_4.9.1.7z -O mingw.7z
+ sed -e "s|%PREFIX%|i686-w64-mingw32|" \
+ -e "s|%ROOTPATH%|/usr/i686-w64-mingw32|" \
+ < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw.cmake
+ sudo 7z x -y -o/usr mingw.7z
+elif [[ $PLATFORM == "Win64" ]]; then
+ wget http://sfan5.pf-control.de/mingw_w64_x86_64_ubuntu12.04_4.9.1.7z -O mingw.7z
+ sed -e "s|%PREFIX%|x86_64-w64-mingw32|" \
+ -e "s|%ROOTPATH%|/usr/x86_64-w64-mingw32|" \
+ < util/travis/toolchain_mingw.cmake.in > util/buildbot/toolchain_mingw64.cmake
sudo 7z x -y -o/usr mingw.7z
fi
+
diff --git a/util/travis/script.sh b/util/travis/script.sh
index a8e2634e9..756cc1de8 100755
--- a/util/travis/script.sh
+++ b/util/travis/script.sh
@@ -1,12 +1,21 @@
#!/bin/bash -e
-if [ $WINDOWS = "no" ]; then
+if [[ $PLATFORM == "Linux" ]]; then
mkdir -p travisbuild
cd travisbuild
- cmake -DENABLE_GETTEXT=1 ..
+ CMAKE_FLAGS='-DCMAKE_BUILD_TYPE=Debug \
+ -DRUN_IN_PLACE=TRUE \
+ -DENABLE_GETTEXT=TRUE'
+ # Clang builds with FreeType fail on Travis
+ if [[ $CC == "clang" ]]; then
+ CMAKE_FLAGS+=' -DENABLE_FREETYPE=FALSE'
+ fi
+ cmake $CMAKE_FLAGS ..
make -j2
-else
- [ $CC = "clang" ] && exit 1 # Not supposed to happen
+ echo "Running unit tests."
+ ../bin/minetest --run-unittests && exit 0
+elif [[ $PLATFORM == Win* ]]; then
+ [[ $CC == "clang" ]] && exit 1 # Not supposed to happen
# We need to have our build directory outside of the minetest directory because
# CMake will otherwise get very very confused with symlinks and complain that
# something is not a subdirectory of something even if it actually is.
@@ -17,8 +26,17 @@ else
# \/ \/ \/
# /home/travis/minetest/minetest/travisbuild/minetest/travisbuild/minetest/travisbuild/minetest
# You get the idea.
- OLDDIR=`pwd`
+ OLDDIR=$(pwd)
cd ..
- [ $WINDOWS = "32" ] && EXISTING_MINETEST_DIR=$OLDDIR NO_MINETEST_GAME=1 $OLDDIR/util/buildbot/buildwin32.sh travisbuild && exit 0
- [ $WINDOWS = "64" ] && EXISTING_MINETEST_DIR=$OLDDIR NO_MINETEST_GAME=1 $OLDDIR/util/buildbot/buildwin64.sh travisbuild && exit 0
+ export EXISTING_MINETEST_DIR=$OLDDIR
+ export NO_MINETEST_GAME=1
+ if [[ $PLATFORM == "Win32" ]]; then
+ $OLDDIR/util/buildbot/buildwin32.sh travisbuild && exit 0
+ elif [[ $PLATFORM == "Win64" ]]; then
+ $OLDDIR/util/buildbot/buildwin64.sh travisbuild && exit 0
+ fi
+else
+ echo "Unknown platform \"${PLATFORM}\"."
+ exit 1
fi
+
diff --git a/util/travis/toolchain_mingw.cmake.in b/util/travis/toolchain_mingw.cmake.in
index 44830eb60..65f67517e 100644
--- a/util/travis/toolchain_mingw.cmake.in
+++ b/util/travis/toolchain_mingw.cmake.in
@@ -1,17 +1,18 @@
-# name of the target operating system
-SET(CMAKE_SYSTEM_NAME Windows)
+# Target operating system name
+set(CMAKE_SYSTEM_NAME Windows)
-# which compilers to use for C and C++
-SET(CMAKE_C_COMPILER %PREFIX%-gcc)
-SET(CMAKE_CXX_COMPILER %PREFIX%-g++)
-SET(CMAKE_RC_COMPILER %PREFIX%-windres)
+# Compilers to use
+set(CMAKE_C_COMPILER %PREFIX%-gcc)
+set(CMAKE_CXX_COMPILER %PREFIX%-g++)
+set(CMAKE_RC_COMPILER %PREFIX%-windres)
-# here is the target environment located
-SET(CMAKE_FIND_ROOT_PATH %ROOTPATH%)
+# Location of the target environment
+set(CMAKE_FIND_ROOT_PATH %ROOTPATH%)
-# adjust the default behaviour of the FIND_XXX() commands:
-# search headers and libraries in the target environment, search
-# programs in the host environment
+# Adjust the default behaviour of the FIND_XXX() commands:
+# search for headers and libraries in the target environment,
+# search for programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+