CMakeLists.txt0000644000175000017500000001757012261107224014131 0ustar mquinsonmquinsoncmake_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") # This can be read from ${PROJECT_NAME} after project() is called project(minetest) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) set(VERSION_PATCH 9) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) else() # Comment the following line during release #set(VERSION_PATCH ${VERSION_PATCH}-dev) endif() set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") MESSAGE(STATUS "*** Will build version ${VERSION_STRING} ***") # Configuration options 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") endif() # 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(WARN_ALL 1 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 find_package(Irrlicht) # # Installation # if(WIN32) set(SHAREDIR ".") set(BINDIR "bin") set(DOCDIR "doc") set(EXAMPLE_CONF_DIR ".") set(LOCALEDIR "locale") elseif(APPLE) # Random placeholders; this isn't usually used and may not work # See https://github.com/toabi/minetest-mac/ set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}") set(BINDIR "bin") set(DOCDIR "share/doc/${PROJECT_NAME}") set(EXAMPLE_CONF_DIR ${DOCDIR}) set(LOCALEDIR "locale") elseif(UNIX) # Linux, BSD etc if(RUN_IN_PLACE) set(SHAREDIR ".") set(BINDIR "bin") set(DOCDIR "doc") set(EXAMPLE_CONF_DIR ".") set(MANDIR "unix/man") set(XDG_APPS_DIR "unix/applications") set(ICONDIR "unix/icons") set(LOCALEDIR "locale") else() set(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}") set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin") set(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}") set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man") set(EXAMPLE_CONF_DIR ${DOCDIR}) set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications") set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons") set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/locale") endif() endif() set(CUSTOM_SHAREDIR "" CACHE STRING "Directory to install data files into") 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") set(MINETEST_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/minetest_game") if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE}) install(FILES ${MINETEST_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/minetest_game/") install(FILES ${MINETEST_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/minetest_game/") install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game") install(DIRECTORY ${MINETEST_GAME_SOURCE}/menu DESTINATION "${SHAREDIR}/games/minetest_game") endif() if(BUILD_CLIENT) #install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/sounds/base/pack" DESTINATION "${SHAREDIR}/sounds/base") install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/textures/base/pack" DESTINATION "${SHAREDIR}/textures/base") endif() if(RUN_IN_PLACE) install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/mods/mods_here.txt" DESTINATION "${SHAREDIR}/mods") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/texture_packs_here.txt" DESTINATION "${SHAREDIR}/textures") endif() install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/fonts" DESTINATION "${SHAREDIR}") install(FILES "README.txt" DESTINATION "${DOCDIR}") install(FILES "doc/lua_api.txt" DESTINATION "${DOCDIR}") install(FILES "doc/mapformat.txt" DESTINATION "${DOCDIR}") install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}") if(UNIX) install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6") install(FILES "misc/minetest.desktop" DESTINATION "${XDG_APPS_DIR}") install(FILES "misc/minetest-icon.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps") 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") set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) set(CPACK_PACKAGE_VENDOR "celeron55") set(CPACK_PACKAGE_CONTACT "Perttu Ahola ") 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() set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-win32") 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) # TODO # see http://cmake.org/Wiki/CMake:CPackPackageGenerators#Bundle_.28OSX_only.29 # set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-osx") set(CPACK_PACKAGE_ICON "") set(CPACK_BUNDLE_NAME ${PROJECT_NAME}) set(CPACK_BUNDLE_ICON "") set(CPACK_BUNDLE_PLIST "") set(CPACK_BUNDLE_STARTUP_COMMAND "Contents/MacOS/${PROJECT_NAME}") set(CPACK_GENERATOR "Bundle") else() set(CPACK_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_STRING}-linux") set(CPACK_GENERATOR TGZ) set(CPACK_SOURCE_GENERATOR TGZ) endif() include(CPack) README.txt0000644000175000017500000003432512261107224013064 0ustar mquinsonmquinsonMinetest ============ An InfiniMiner/Minecraft inspired game. Copyright (c) 2010-2013 Perttu Ahola and contributors (see source file comments and the version control log) In case you downloaded the source code: --------------------------------------- If you downloaded the Minetest Engine source code in which this file is contained, you probably want to download the minetest_game project too: https://github.com/minetest/minetest_game/ See the README.txt in it. Further documentation ---------------------- - Website: http://minetest.net/ - Wiki: http://wiki.minetest.net/ - Developer wiki: http://dev.minetest.net/ - Forum: http://forum.minetest.net/ - Github: https://github.com/minetest/minetest/ - doc/ directory of source distribution This game is not finished -------------------------- - Don't expect it to work as well as a finished game will. - Please report any bugs. When doing that, debug.txt is useful. Default Controls ----------------- - WASD: move - Space: jump/climb - Shift: sneak/go down - Q: drop item - I: inventory - Mouse: turn/look - Mouse left: dig/punch - Mouse right: place/use - Mouse wheel: select item - Esc: pause menu - T: chat - Settable in the configuration file, see the section below. Paths ------ $bin - Compiled binaries $share - Distributed read-only data $user - User-created modifiable data Windows .zip / RUN_IN_PLACE source: $bin = bin $share = . $user = . Linux installed: $bin = /usr/bin $share = /usr/share/minetest $user = ~/.minetest OS X: $bin = ? $share = ? $user = ~/Library/Application Support/minetest World directory ---------------- - Worlds can be found as separate folders in: $user/worlds/ Configuration file: ------------------- - Default location: $user/minetest.conf - It is created by Minetest when it is ran the first time. - A specific file can be specified on the command line: --config Command-line options: --------------------- - Use --help 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 Download source, extract (this is the URL to the latest of source repository, which might not work at all times): $ wget https://github.com/minetest/minetest/tarball/master -O master.tar.gz $ tar xf master.tar.gz $ cd minetest-minetest-286edd4 (or similar) Download minetest_game (otherwise only the "Minimal development test" game is available) $ cd games/ $ wget https://github.com/minetest/minetest_game/tarball/master -O minetest_game.tar.gz $ tar xf minetest_game.tar.gz $ mv minetest-minetest_game-* minetest_game $ cd .. Build a version that runs directly from the source directory: $ cmake . -DRUN_IN_PLACE=1 $ make -j2 Run it: $ cd 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 - You can select between Release and Debug build by -DCMAKE_BUILD_TYPE= - 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 Compiling on Windows: --------------------- - This section is outdated. In addition to what is described here: - In addition to minetest, you need to download minetest_game. - If you wish to have sound support, you need libogg, libvorbis and libopenal - You need: * CMake: http://www.cmake.org/cmake/resources/software.html * MinGW or Visual Studio http://www.mingw.org/ http://msdn.microsoft.com/en-us/vstudio/default * Irrlicht SDK 1.7: http://irrlicht.sourceforge.net/downloads.html * Zlib headers (zlib125.zip) http://www.winimage.com/zLibDll/index.html * Zlib library (zlibwapi.lib and zlibwapi.dll from zlib125dll.zip): http://www.winimage.com/zLibDll/index.html * Optional: gettext library and tools: http://gnuwin32.sourceforge.net/downlinks/gettext.php - This is used for other UI languages. Feel free to leave it out. * And, of course, Minetest: http://minetest.net/download.php - Steps: - Select a directory called DIR hereafter in which you will operate. - Make sure you have CMake and a compiler installed. - Download all the other stuff to DIR and extract them into there. ("extract here", not "extract to packagename/") NOTE: zlib125dll.zip needs to be extracted into zlib125dll - All those packages contain a nice base directory in them, which should end up being the direct subdirectories of DIR. - You will end up with a directory structure like this (+=dir, -=file): ----------------- + DIR - zlib-1.2.5.tar.gz - zlib125dll.zip - irrlicht-1.7.1.zip - 110214175330.zip (or whatever, this is the minetest source) + zlib-1.2.5 - zlib.h + win32 ... + zlib125dll - readme.txt + dll32 ... + irrlicht-1.7.1 + lib + include ... + gettext (optional) +bin +include +lib + minetest + src + doc - CMakeLists.txt ... ----------------- - Start up the CMake GUI - Select "Browse Source..." and select DIR/minetest - Now, if using MSVC: - Select "Browse Build..." and select DIR/minetest-build - Else if using MinGW: - Select "Browse Build..." and select DIR/minetest - Select "Configure" - Select your compiler - It will warn about missing stuff, ignore that at this point. (later don't) - Make sure the configuration is as follows (note that the versions may differ for you): ----------------- BUILD_CLIENT [X] BUILD_SERVER [ ] CMAKE_BUILD_TYPE Release CMAKE_INSTALL_PREFIX DIR/minetest-install IRRLICHT_SOURCE_DIR DIR/irrlicht-1.7.1 RUN_IN_PLACE [X] WARN_ALL [ ] ZLIB_DLL DIR/zlib125dll/dll32/zlibwapi.dll ZLIB_INCLUDE_DIR DIR/zlib-1.2.5 ZLIB_LIBRARIES DIR/zlib125dll/dll32/zlibwapi.lib GETTEXT_BIN_DIR DIR/gettext/bin GETTEXT_INCLUDE_DIR DIR/gettext/include GETTEXT_LIBRARIES DIR/gettext/lib/intl.lib GETTEXT_MSGFMT DIR/gettext/bin/msgfmt ----------------- - Hit "Configure" - Hit "Configure" once again 8) - If something is still coloured red, you have a problem. - Hit "Generate" If using MSVC: - Open the generated minetest.sln - The project defaults to the "Debug" configuration. Make very sure to select "Release", unless you want to debug some stuff (it's slower and might not even work at all) - Build the ALL_BUILD project - Build the INSTALL project - You should now have a working game with the executable in DIR/minetest-install/bin/minetest.exe - Additionally you may create a zip package by building the PACKAGE project. If using MinGW: - Using the command line, browse to the build directory and run 'make' (or mingw32-make or whatever it happens to be) - You may need to copy some of the downloaded DLLs into bin/, see what running the produced executable tells you it doesn't have. - You should now have a working game with the executable in DIR/minetest/bin/minetest.exe Windows releases of minetest are built using a bat script like this: -------------------------------------------------------------------- set sourcedir=%CD% set installpath="C:\tmp\minetest_install" 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% 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 "C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" INSTALL.vcxproj /p:Configuration=Release if %errorlevel% neq 0 goto fail "C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe" PACKAGE.vcxproj /p:Configuration=Release if %errorlevel% neq 0 goto fail popd echo Finished. exit /b 0 :fail popd echo Failed. exit /b 1 License of Minetest textures and sounds --------------------------------------- This applies to textures and sounds contained in the main Minetest distribution. Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) http://creativecommons.org/licenses/by-sa/3.0/ Authors of media files ----------------------- Everything not listed in here: Copyright (C) 2010-2012 celeron55, Perttu Ahola BlockMen: textures/base/pack/menuheader.png erlehmann: misc/minetest-icon-24x24.png misc/minetest-icon.ico misc/minetest-icon.svg textures/base/pack/logo.png License of Minetest source code ------------------------------- Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola 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. Irrlicht --------------- This program uses the Irrlicht Engine. http://irrlicht.sourceforge.net/ The Irrlicht Engine License Copyright © 2002-2005 Nikolaus Gebhardt This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. JThread --------------- This program uses the JThread library. License for JThread follows: Copyright (c) 2000-2006 Jori Liesenborgs (jori.liesenborgs@gmail.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. Lua --------------- Lua is licensed under the terms of the MIT license reproduced below. This means that Lua is free software and can be used for both academic and commercial purposes at absolutely no cost. For details and rationale, see http://www.lua.org/license.html . Copyright (C) 1994-2008 Lua.org, PUC-Rio. 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. Fonts --------------- DejaVu Sans Mono: Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) Bitstream Vera Fonts Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. Arev Fonts Copyright: Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. Liberation Fonts Copyright: Copyright (c) 2007 Red Hat, Inc. All rights reserved. LIBERATION is a trademark of Red Hat, Inc. DroidSansFallback: Copyright (C) 2008 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. builtin/0000755000175000017500000000000012261107224013025 5ustar mquinsonmquinsonbuiltin/static_spawn.lua0000644000175000017500000000162312261107224016231 0ustar mquinsonmquinson-- Minetest: builtin/static_spawn.lua local function warn_invalid_static_spawnpoint() if minetest.setting_get("static_spawnpoint") and not minetest.setting_get_pos("static_spawnpoint") then minetest.log('error', "The static_spawnpoint setting is invalid: \"".. minetest.setting_get("static_spawnpoint").."\"") end end warn_invalid_static_spawnpoint() local function put_player_in_spawn(obj) warn_invalid_static_spawnpoint() local static_spawnpoint = minetest.setting_get_pos("static_spawnpoint") if not static_spawnpoint then return false end minetest.log('action', "Moving "..obj:get_player_name().. " to static spawnpoint at ".. minetest.pos_to_string(static_spawnpoint)) obj:setpos(static_spawnpoint) return true end minetest.register_on_newplayer(function(obj) put_player_in_spawn(obj) end) minetest.register_on_respawnplayer(function(obj) return put_player_in_spawn(obj) end) builtin/vector.lua0000644000175000017500000000531512261107224015036 0ustar mquinsonmquinson vector = {} local function assert_vector(v) assert(type(v) == "table" and v.x and v.y and v.z, "Invalid vector") end function vector.new(a, b, c) if type(a) == "table" then assert(a.x and a.y and a.z, "Invalid vector passed to vector.new()") return {x=a.x, y=a.y, z=a.z} elseif a then assert(b and c, "Invalid arguments for vector.new()") return {x=a, y=b, z=c} end return {x=0, y=0, z=0} end function vector.equals(a, b) assert_vector(a) assert_vector(b) return a.x == b.x and a.y == b.y and a.z == b.z end function vector.length(v) assert_vector(v) return math.hypot(v.x, math.hypot(v.y, v.z)) end function vector.normalize(v) assert_vector(v) local len = vector.length(v) if len == 0 then return {x=0, y=0, z=0} else return vector.divide(v, len) end end function vector.round(v) assert_vector(v) return { x = math.floor(v.x + 0.5), y = math.floor(v.y + 0.5), z = math.floor(v.z + 0.5) } end function vector.distance(a, b) assert_vector(a) assert_vector(b) local x = a.x - b.x local y = a.y - b.y local z = a.z - b.z return math.hypot(x, math.hypot(y, z)) end function vector.direction(pos1, pos2) assert_vector(pos1) assert_vector(pos2) local x_raw = pos2.x - pos1.x local y_raw = pos2.y - pos1.y local z_raw = pos2.z - pos1.z local x_abs = math.abs(x_raw) local y_abs = math.abs(y_raw) local z_abs = math.abs(z_raw) if x_abs >= y_abs and x_abs >= z_abs then y_raw = y_raw * (1 / x_abs) z_raw = z_raw * (1 / x_abs) x_raw = x_raw / x_abs end if y_abs >= x_abs and y_abs >= z_abs then x_raw = x_raw * (1 / y_abs) z_raw = z_raw * (1 / y_abs) y_raw = y_raw / y_abs end if z_abs >= y_abs and z_abs >= x_abs then x_raw = x_raw * (1 / z_abs) y_raw = y_raw * (1 / z_abs) z_raw = z_raw / z_abs end return {x=x_raw, y=y_raw, z=z_raw} end function vector.add(a, b) assert_vector(a) if type(b) == "table" then assert_vector(b) return {x = a.x + b.x, y = a.y + b.y, z = a.z + b.z} else return {x = a.x + b, y = a.y + b, z = a.z + b} end end function vector.subtract(a, b) assert_vector(a) if type(b) == "table" then assert_vector(b) return {x = a.x - b.x, y = a.y - b.y, z = a.z - b.z} else return {x = a.x - b, y = a.y - b, z = a.z - b} end end function vector.multiply(a, b) assert_vector(a) if type(b) == "table" then assert_vector(b) return {x = a.x * b.x, y = a.y * b.y, z = a.z * b.z} else return {x = a.x * b, y = a.y * b, z = a.z * b} end end function vector.divide(a, b) assert_vector(a) if type(b) == "table" then assert_vector(b) return {x = a.x / b.x, y = a.y / b.y, z = a.z / b.z} else return {x = a.x / b, y = a.y / b, z = a.z / b} end end builtin/async_env.lua0000644000175000017500000000103512261107224015514 0ustar mquinsonmquinsonengine.log("info","Initializing Asynchronous environment") dofile(SCRIPTDIR .. DIR_DELIM .. "misc_helpers.lua") function engine.job_processor(serialized_function, serialized_data) local fct = marshal.decode(serialized_function) local params = marshal.decode(serialized_data) local retval = marshal.encode(nil) if fct ~= nil and type(fct) == "function" then local result = fct(params) retval = marshal.encode(result) else engine.log("error","ASYNC WORKER: unable to deserialize function") end return retval,retval:len() end builtin/features.lua0000644000175000017500000000120612261107224015345 0ustar mquinsonmquinson-- Minetest: builtin/features.lua minetest.features = { glasslike_framed = true, nodebox_as_selectionbox = true, chat_send_player_param3 = true, get_all_craft_recipes_works = true, use_texture_alpha = true, no_legacy_abms = true, } function minetest.has_feature(arg) if type(arg) == "table" then missing_features = {} result = true for ft, _ in pairs(arg) do if not minetest.features[ftr] then missing_features[ftr] = true result = false end end return result, missing_features elseif type(arg) == "string" then if not minetest.features[arg] then return false, {[arg]=true} end return true, {} end end builtin/chatcommands.lua0000644000175000017500000005465612261107224016211 0ustar mquinsonmquinson-- Minetest: builtin/chatcommands.lua -- -- Chat command handler -- minetest.chatcommands = {} function minetest.register_chatcommand(cmd, def) def = def or {} def.params = def.params or "" def.description = def.description or "" def.privs = def.privs or {} minetest.chatcommands[cmd] = def end minetest.register_on_chat_message(function(name, message) local cmd, param = string.match(message, "^/([^ ]+) *(.*)") if not param then param = "" end local cmd_def = minetest.chatcommands[cmd] if cmd_def then local has_privs, missing_privs = minetest.check_player_privs(name, cmd_def.privs) if has_privs then cmd_def.func(name, param) else minetest.chat_send_player(name, "You don't have permission to run this command (missing privileges: "..table.concat(missing_privs, ", ")..")") end return true -- handled chat message end return false end) -- -- Chat commands -- minetest.register_chatcommand("me", { params = "", description = "chat action (eg. /me orders a pizza)", privs = {shout=true}, func = function(name, param) minetest.chat_send_all("* " .. name .. " " .. param) end, }) minetest.register_chatcommand("help", { privs = {}, params = "(nothing)/all/privs/", description = "Get help for commands or list privileges", func = function(name, param) local format_help_line = function(cmd, def) local msg = "/"..cmd if def.params and def.params ~= "" then msg = msg .. " " .. def.params end if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end return msg end if param == "" then local msg = "" cmds = {} for cmd, def in pairs(minetest.chatcommands) do if minetest.check_player_privs(name, def.privs) then table.insert(cmds, cmd) end end minetest.chat_send_player(name, "Available commands: "..table.concat(cmds, " ")) minetest.chat_send_player(name, "Use '/help ' to get more information, or '/help all' to list everything.") elseif param == "all" then minetest.chat_send_player(name, "Available commands:") for cmd, def in pairs(minetest.chatcommands) do if minetest.check_player_privs(name, def.privs) then minetest.chat_send_player(name, format_help_line(cmd, def)) end end elseif param == "privs" then minetest.chat_send_player(name, "Available privileges:") for priv, def in pairs(minetest.registered_privileges) do minetest.chat_send_player(name, priv..": "..def.description) end else local cmd = param def = minetest.chatcommands[cmd] if not def then minetest.chat_send_player(name, "Command not available: "..cmd) else minetest.chat_send_player(name, format_help_line(cmd, def)) end end end, }) minetest.register_chatcommand("privs", { params = "", description = "print out privileges of player", func = function(name, param) if param == "" then param = name else --[[if not minetest.check_player_privs(name, {privs=true}) then minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.") return end]] end minetest.chat_send_player(name, "Privileges of "..param..": "..minetest.privs_to_string(minetest.get_player_privs(param), ' ')) end, }) minetest.register_chatcommand("grant", { params = " |all", description = "Give privilege to player", privs = {}, func = function(name, param) if not minetest.check_player_privs(name, {privs=true}) and not minetest.check_player_privs(name, {basic_privs=true}) then minetest.chat_send_player(name, "Your privileges are insufficient.") return end local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") if not grantname or not grantprivstr then minetest.chat_send_player(name, "Invalid parameters (see /help grant)") return elseif not minetest.auth_table[grantname] then minetest.chat_send_player(name, "Player "..grantname.." does not exist.") return end local grantprivs = minetest.string_to_privs(grantprivstr) if grantprivstr == "all" then grantprivs = minetest.registered_privileges end local privs = minetest.get_player_privs(grantname) local privs_known = true for priv, _ in pairs(grantprivs) do if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then minetest.chat_send_player(name, "Your privileges are insufficient.") return end if not minetest.registered_privileges[priv] then minetest.chat_send_player(name, "Unknown privilege: "..priv) privs_known = false end privs[priv] = true end if not privs_known then return end minetest.set_player_privs(grantname, privs) minetest.log(name..' granted ('..minetest.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) minetest.chat_send_player(name, "Privileges of "..grantname..": "..minetest.privs_to_string(minetest.get_player_privs(grantname), ' ')) if grantname ~= name then minetest.chat_send_player(grantname, name.." granted you privileges: "..minetest.privs_to_string(grantprivs, ' ')) end end, }) minetest.register_chatcommand("revoke", { params = " |all", description = "Remove privilege from player", privs = {}, func = function(name, param) if not minetest.check_player_privs(name, {privs=true}) and not minetest.check_player_privs(name, {basic_privs=true}) then minetest.chat_send_player(name, "Your privileges are insufficient.") return end local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)") if not revokename or not revokeprivstr then minetest.chat_send_player(name, "Invalid parameters (see /help revoke)") return elseif not minetest.auth_table[revokename] then minetest.chat_send_player(name, "Player "..revokename.." does not exist.") return end local revokeprivs = minetest.string_to_privs(revokeprivstr) local privs = minetest.get_player_privs(revokename) for priv, _ in pairs(revokeprivs) do if priv ~= "interact" and priv ~= "shout" and priv ~= "interact_extra" and not minetest.check_player_privs(name, {privs=true}) then minetest.chat_send_player(name, "Your privileges are insufficient.") return end end if revokeprivstr == "all" then privs = {} else for priv, _ in pairs(revokeprivs) do privs[priv] = nil end end minetest.set_player_privs(revokename, privs) minetest.log(name..' revoked ('..minetest.privs_to_string(revokeprivs, ', ')..') privileges from '..revokename) minetest.chat_send_player(name, "Privileges of "..revokename..": "..minetest.privs_to_string(minetest.get_player_privs(revokename), ' ')) if revokename ~= name then minetest.chat_send_player(revokename, name.." revoked privileges from you: "..minetest.privs_to_string(revokeprivs, ' ')) end end, }) minetest.register_chatcommand("setpassword", { params = " ", description = "set given password", privs = {password=true}, func = function(name, param) local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$") if not toname then toname = string.match(param, "^([^ ]+) *$") raw_password = nil end if not toname then minetest.chat_send_player(name, "Name field required") return end local actstr = "?" if not raw_password then minetest.set_player_password(toname, "") actstr = "cleared" else minetest.set_player_password(toname, minetest.get_password_hash(toname, raw_password)) actstr = "set" end minetest.chat_send_player(name, "Password of player \""..toname.."\" "..actstr) if toname ~= name then minetest.chat_send_player(toname, "Your password was "..actstr.." by "..name) end end, }) minetest.register_chatcommand("clearpassword", { params = "", description = "set empty password", privs = {password=true}, func = function(name, param) toname = param if toname == "" then minetest.chat_send_player(name, "Name field required") return end minetest.set_player_password(toname, '') minetest.chat_send_player(name, "Password of player \""..toname.."\" cleared") end, }) minetest.register_chatcommand("auth_reload", { params = "", description = "reload authentication data", privs = {server=true}, func = function(name, param) local done = minetest.auth_reload() if done then minetest.chat_send_player(name, "Done.") else minetest.chat_send_player(name, "Failed.") end end, }) minetest.register_chatcommand("teleport", { params = ",, | | ,, | ", description = "teleport to given position", privs = {teleport=true}, func = function(name, param) -- Returns (pos, true) if found, otherwise (pos, false) local function find_free_position_near(pos) local tries = { {x=1,y=0,z=0}, {x=-1,y=0,z=0}, {x=0,y=0,z=1}, {x=0,y=0,z=-1}, } for _, d in ipairs(tries) do local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z} local n = minetest.get_node(p) if not minetest.registered_nodes[n.name].walkable then return p, true end end return pos, false end local teleportee = nil local p = {} p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") p.x = tonumber(p.x) p.y = tonumber(p.y) p.z = tonumber(p.z) teleportee = minetest.get_player_by_name(name) if teleportee and p.x and p.y and p.z then minetest.chat_send_player(name, "Teleporting to ("..p.x..", "..p.y..", "..p.z..")") teleportee:setpos(p) return end local teleportee = nil local p = nil local target_name = nil target_name = string.match(param, "^([^ ]+)$") teleportee = minetest.get_player_by_name(name) if target_name then local target = minetest.get_player_by_name(target_name) if target then p = target:getpos() end end if teleportee and p then p = find_free_position_near(p) minetest.chat_send_player(name, "Teleporting to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")") teleportee:setpos(p) return end if minetest.check_player_privs(name, {bring=true}) then local teleportee = nil local p = {} local teleportee_name = nil teleportee_name, p.x, p.y, p.z = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") p.x = tonumber(p.x) p.y = tonumber(p.y) p.z = tonumber(p.z) if teleportee_name then teleportee = minetest.get_player_by_name(teleportee_name) end if teleportee and p.x and p.y and p.z then minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to ("..p.x..", "..p.y..", "..p.z..")") teleportee:setpos(p) return end local teleportee = nil local p = nil local teleportee_name = nil local target_name = nil teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$") if teleportee_name then teleportee = minetest.get_player_by_name(teleportee_name) end if target_name then local target = minetest.get_player_by_name(target_name) if target then p = target:getpos() end end if teleportee and p then p = find_free_position_near(p) minetest.chat_send_player(name, "Teleporting "..teleportee_name.." to "..target_name.." at ("..p.x..", "..p.y..", "..p.z..")") teleportee:setpos(p) return end end minetest.chat_send_player(name, "Invalid parameters (\""..param.."\") or player not found (see /help teleport)") return end, }) minetest.register_chatcommand("set", { params = "[-n] | ", description = "set or read server configuration setting", privs = {server=true}, func = function(name, param) local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)") if arg and arg == "-n" and setname and setvalue then minetest.setting_set(setname, setvalue) minetest.chat_send_player(name, setname.." = "..setvalue) return end local setname, setvalue = string.match(param, "([^ ]+) (.+)") if setname and setvalue then if not minetest.setting_get(setname) then minetest.chat_send_player(name, "Failed. Use '/set -n ' to create a new setting.") return end minetest.setting_set(setname, setvalue) minetest.chat_send_player(name, setname.." = "..setvalue) return end local setname = string.match(param, "([^ ]+)") if setname then local setvalue = minetest.setting_get(setname) if not setvalue then setvalue = "" end minetest.chat_send_player(name, setname.." = "..setvalue) return end minetest.chat_send_player(name, "Invalid parameters (see /help set)") end, }) minetest.register_chatcommand("mods", { params = "", description = "lists mods installed on the server", privs = {}, func = function(name, param) local response = "" local modnames = minetest.get_modnames() for i, mod in ipairs(modnames) do response = response .. mod -- Add space if not at the end if i ~= #modnames then response = response .. " " end end minetest.chat_send_player(name, response) end, }) local function handle_give_command(cmd, giver, receiver, stackstring) minetest.log("action", giver.." invoked "..cmd..', stackstring="' ..stackstring..'"') minetest.log(cmd..' invoked, stackstring="'..stackstring..'"') local itemstack = ItemStack(stackstring) if itemstack:is_empty() then minetest.chat_send_player(giver, 'error: cannot give an empty item') return elseif not itemstack:is_known() then minetest.chat_send_player(giver, 'error: cannot give an unknown item') return end local receiverref = minetest.get_player_by_name(receiver) if receiverref == nil then minetest.chat_send_player(giver, receiver..' is not a known player') return end local leftover = receiverref:get_inventory():add_item("main", itemstack) if leftover:is_empty() then partiality = "" elseif leftover:get_count() == itemstack:get_count() then partiality = "could not be " else partiality = "partially " end -- The actual item stack string may be different from what the "giver" -- entered (e.g. big numbers are always interpreted as 2^16-1). stackstring = itemstack:to_string() if giver == receiver then minetest.chat_send_player(giver, '"'..stackstring ..'" '..partiality..'added to inventory.'); else minetest.chat_send_player(giver, '"'..stackstring ..'" '..partiality..'added to '..receiver..'\'s inventory.'); minetest.chat_send_player(receiver, '"'..stackstring ..'" '..partiality..'added to inventory.'); end end minetest.register_chatcommand("give", { params = " ", description = "give item to player", privs = {give=true}, func = function(name, param) local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$") if not toname or not itemstring then minetest.chat_send_player(name, "name and itemstring required") return end handle_give_command("/give", name, toname, itemstring) end, }) minetest.register_chatcommand("giveme", { params = "", description = "give item to yourself", privs = {give=true}, func = function(name, param) local itemstring = string.match(param, "(.+)$") if not itemstring then minetest.chat_send_player(name, "itemstring required") return end handle_give_command("/giveme", name, name, itemstring) end, }) minetest.register_chatcommand("spawnentity", { params = "", description = "spawn entity at your position", privs = {give=true, interact=true}, func = function(name, param) local entityname = string.match(param, "(.+)$") if not entityname then minetest.chat_send_player(name, "entityname required") return end minetest.log("action", '/spawnentity invoked, entityname="'..entityname..'"') local player = minetest.get_player_by_name(name) if player == nil then minetest.log("error", "Unable to spawn entity, player is nil") return true -- Handled chat message end local p = player:getpos() p.y = p.y + 1 minetest.add_entity(p, entityname) minetest.chat_send_player(name, '"'..entityname ..'" spawned.'); end, }) minetest.register_chatcommand("pulverize", { params = "", description = "delete item in hand", privs = {}, func = function(name, param) local player = minetest.get_player_by_name(name) if player == nil then minetest.log("error", "Unable to pulverize, player is nil") return true -- Handled chat message end if player:get_wielded_item():is_empty() then minetest.chat_send_player(name, 'Unable to pulverize, no item in hand.') else player:set_wielded_item(nil) minetest.chat_send_player(name, 'An item was pulverized.') end end, }) -- Key = player name minetest.rollback_punch_callbacks = {} minetest.register_on_punchnode(function(pos, node, puncher) local name = puncher:get_player_name() if minetest.rollback_punch_callbacks[name] then minetest.rollback_punch_callbacks[name](pos, node, puncher) minetest.rollback_punch_callbacks[name] = nil end end) minetest.register_chatcommand("rollback_check", { params = "[] [] [limit]", description = "check who has last touched a node or near it, ".. "max. ago (default range=0, seconds=86400=24h, limit=5)", privs = {rollback=true}, func = function(name, param) local range, seconds, limit = param:match("(%d+) *(%d*) *(%d*)") range = tonumber(range) or 0 seconds = tonumber(seconds) or 86400 limit = tonumber(limit) or 5 if limit > 100 then minetest.chat_send_player(name, "That limit is too high!") return end minetest.chat_send_player(name, "Punch a node (range=".. range..", seconds="..seconds.."s, limit="..limit..")") minetest.rollback_punch_callbacks[name] = function(pos, node, puncher) local name = puncher:get_player_name() minetest.chat_send_player(name, "Checking "..minetest.pos_to_string(pos).."...") local actions = minetest.rollback_get_node_actions(pos, range, seconds, limit) local num_actions = #actions if num_actions == 0 then minetest.chat_send_player(name, "Nobody has touched the ".. "specified location in "..seconds.." seconds") return end local time = os.time() for i = num_actions, 1, -1 do local action = actions[i] minetest.chat_send_player(name, ("%s %s %s -> %s %d seconds ago.") :format( minetest.pos_to_string(action.pos), action.actor, action.oldnode.name, action.newnode.name, time - action.time)) end end end, }) minetest.register_chatcommand("rollback", { params = " [] | : []", description = "revert actions of a player; default for is 60", privs = {rollback=true}, func = function(name, param) local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)") if not target_name then local player_name = nil player_name, seconds = string.match(param, "([^ ]+) *(%d*)") if not player_name then minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check") return end target_name = "player:"..player_name end seconds = tonumber(seconds) or 60 minetest.chat_send_player(name, "Reverting actions of ".. target_name.." since "..seconds.." seconds.") local success, log = minetest.rollback_revert_actions_by( target_name, seconds) if #log > 100 then minetest.chat_send_player(name, "(log is too long to show)") else for _, line in pairs(log) do minetest.chat_send_player(name, line) end end if success then minetest.chat_send_player(name, "Reverting actions succeeded.") else minetest.chat_send_player(name, "Reverting actions FAILED.") end end, }) minetest.register_chatcommand("status", { params = "", description = "print server status line", privs = {}, func = function(name, param) minetest.chat_send_player(name, minetest.get_server_status()) end, }) minetest.register_chatcommand("time", { params = "<0...24000>", description = "set time of day", privs = {settime=true}, func = function(name, param) if param == "" then minetest.chat_send_player(name, "Missing parameter") return end local newtime = tonumber(param) if newtime == nil then minetest.chat_send_player(name, "Invalid time") else minetest.set_timeofday((newtime % 24000) / 24000) minetest.chat_send_player(name, "Time of day changed.") minetest.log("action", name .. " sets time " .. newtime) end end, }) minetest.register_chatcommand("shutdown", { params = "", description = "shutdown server", privs = {server=true}, func = function(name, param) minetest.log("action", name .. " shuts down server") minetest.request_shutdown() minetest.chat_send_all("*** Server shutting down (operator request).") end, }) minetest.register_chatcommand("ban", { params = "", description = "ban IP of player", privs = {ban=true}, func = function(name, param) if param == "" then minetest.chat_send_player(name, "Ban list: " .. minetest.get_ban_list()) return end if not minetest.get_player_by_name(param) then minetest.chat_send_player(name, "No such player") return end if not minetest.ban_player(param) then minetest.chat_send_player(name, "Failed to ban player") else local desc = minetest.get_ban_description(param) minetest.chat_send_player(name, "Banned " .. desc .. ".") minetest.log("action", name .. " bans " .. desc .. ".") end end, }) minetest.register_chatcommand("unban", { params = "", description = "remove IP ban", privs = {ban=true}, func = function(name, param) if not minetest.unban_player_or_ip(param) then minetest.chat_send_player(name, "Failed to unban player/IP") else minetest.chat_send_player(name, "Unbanned " .. param) minetest.log("action", name .. " unbans " .. param) end end, }) minetest.register_chatcommand("clearobjects", { params = "", description = "clear all objects in world", privs = {server=true}, func = function(name, param) minetest.log("action", name .. " clears all objects") minetest.chat_send_all("Clearing all objects. This may take long. You may experience a timeout. (by " .. name .. ")") minetest.clear_objects() minetest.log("action", "object clearing done") minetest.chat_send_all("*** Cleared all objects.") end, }) minetest.register_chatcommand("msg", { params = " ", description = "Send a private message", privs = {shout=true}, func = function(name, param) local found, _, sendto, message = param:find("^([^%s]+)%s(.+)$") if found then if minetest.get_player_by_name(sendto) then minetest.log("action", "PM from "..name.." to "..sendto..": "..message) minetest.chat_send_player(sendto, "PM from "..name..": "..message, false) minetest.chat_send_player(name, "Message sent") else minetest.chat_send_player(name, "The player "..sendto.." is not online") end else minetest.chat_send_player(name, "Invalid usage, see /help msg") end end, }) builtin/item.lua0000644000175000017500000003646212261107224014501 0ustar mquinsonmquinson-- Minetest: builtin/item.lua -- -- Item definition helpers -- function minetest.inventorycube(img1, img2, img3) img2 = img2 or img1 img3 = img3 or img1 return "[inventorycube" .. "{" .. img1:gsub("%^", "&") .. "{" .. img2:gsub("%^", "&") .. "{" .. img3:gsub("%^", "&") end function minetest.get_pointed_thing_position(pointed_thing, above) if pointed_thing.type == "node" then if above then -- The position where a node would be placed return pointed_thing.above else -- The position where a node would be dug return pointed_thing.under end elseif pointed_thing.type == "object" then obj = pointed_thing.ref if obj ~= nil then return obj:getpos() else return nil end else return nil end end function minetest.dir_to_facedir(dir, is6d) --account for y if requested if is6d and math.abs(dir.y) > math.abs(dir.x) and math.abs(dir.y) > math.abs(dir.z) then --from above if dir.y < 0 then if math.abs(dir.x) > math.abs(dir.z) then if dir.x < 0 then return 19 else return 13 end else if dir.z < 0 then return 10 else return 4 end end --from below else if math.abs(dir.x) > math.abs(dir.z) then if dir.x < 0 then return 15 else return 17 end else if dir.z < 0 then return 6 else return 8 end end end --otherwise, place horizontally elseif math.abs(dir.x) > math.abs(dir.z) then if dir.x < 0 then return 3 else return 1 end else if dir.z < 0 then return 2 else return 0 end end end function minetest.facedir_to_dir(facedir) --a table of possible dirs return ({{x=0, y=0, z=1}, {x=1, y=0, z=0}, {x=0, y=0, z=-1}, {x=-1, y=0, z=0}, {x=0, y=-1, z=0}, {x=0, y=1, z=0}}) --indexed into by a table of correlating facedirs [({[0]=1, 2, 3, 4, 5, 2, 6, 4, 6, 2, 5, 4, 1, 5, 3, 6, 1, 6, 3, 5, 1, 4, 3, 2}) --indexed into by the facedir in question [facedir]] end function minetest.dir_to_wallmounted(dir) if math.abs(dir.y) > math.max(math.abs(dir.x), math.abs(dir.z)) then if dir.y < 0 then return 1 else return 0 end elseif math.abs(dir.x) > math.abs(dir.z) then if dir.x < 0 then return 3 else return 2 end else if dir.z < 0 then return 5 else return 4 end end end function minetest.get_node_drops(nodename, toolname) local drop = ItemStack({name=nodename}):get_definition().drop if drop == nil then -- default drop return {nodename} elseif type(drop) == "string" then -- itemstring drop return {drop} elseif drop.items == nil then -- drop = {} to disable default drop return {} end -- Extended drop table local got_items = {} local got_count = 0 local _, item, tool for _, item in ipairs(drop.items) do local good_rarity = true local good_tool = true if item.rarity ~= nil then good_rarity = item.rarity < 1 or math.random(item.rarity) == 1 end if item.tools ~= nil then good_tool = false for _, tool in ipairs(item.tools) do if tool:sub(1, 1) == '~' then good_tool = toolname:find(tool:sub(2)) ~= nil else good_tool = toolname == tool end if good_tool then break end end end if good_rarity and good_tool then got_count = got_count + 1 for _, add_item in ipairs(item.items) do got_items[#got_items+1] = add_item end if drop.max_items ~= nil and got_count == drop.max_items then break end end end return got_items end function minetest.item_place_node(itemstack, placer, pointed_thing, param2) local item = itemstack:peek_item() local def = itemstack:get_definition() if def.type ~= "node" or pointed_thing.type ~= "node" then return itemstack, false end local under = pointed_thing.under local oldnode_under = minetest.get_node_or_nil(under) local above = pointed_thing.above local oldnode_above = minetest.get_node_or_nil(above) if not oldnode_under or not oldnode_above then minetest.log("info", placer:get_player_name() .. " tried to place" .. " node in unloaded position " .. minetest.pos_to_string(above)) return itemstack, false end local olddef_under = ItemStack({name=oldnode_under.name}):get_definition() olddef_under = olddef_under or minetest.nodedef_default local olddef_above = ItemStack({name=oldnode_above.name}):get_definition() olddef_above = olddef_above or minetest.nodedef_default if not olddef_above.buildable_to and not olddef_under.buildable_to then minetest.log("info", placer:get_player_name() .. " tried to place" .. " node in invalid position " .. minetest.pos_to_string(above) .. ", replacing " .. oldnode_above.name) return itemstack, false end -- Place above pointed node local place_to = {x = above.x, y = above.y, z = above.z} -- If node under is buildable_to, place into it instead (eg. snow) if olddef_under.buildable_to then minetest.log("info", "node under is buildable to") place_to = {x = under.x, y = under.y, z = under.z} end if minetest.is_protected(place_to, placer:get_player_name()) then minetest.log("action", placer:get_player_name() .. " tried to place " .. def.name .. " at protected position " .. minetest.pos_to_string(place_to)) minetest.record_protection_violation(place_to, placer:get_player_name()) return itemstack end minetest.log("action", placer:get_player_name() .. " places node " .. def.name .. " at " .. minetest.pos_to_string(place_to)) local oldnode = minetest.get_node(place_to) local newnode = {name = def.name, param1 = 0, param2 = param2} -- Calculate direction for wall mounted stuff like torches and signs if def.paramtype2 == 'wallmounted' and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, z = under.z - above.z } newnode.param2 = minetest.dir_to_wallmounted(dir) -- Calculate the direction for furnaces and chests and stuff elseif def.paramtype2 == 'facedir' and not param2 then local placer_pos = placer:getpos() if placer_pos then local dir = { x = above.x - placer_pos.x, y = above.y - placer_pos.y, z = above.z - placer_pos.z } newnode.param2 = minetest.dir_to_facedir(dir) minetest.log("action", "facedir: " .. newnode.param2) end end -- Check if the node is attached and if it can be placed there if minetest.get_item_group(def.name, "attached_node") ~= 0 and not check_attached_node(place_to, newnode) then minetest.log("action", "attached node " .. def.name .. " can not be placed at " .. minetest.pos_to_string(place_to)) return itemstack, false end -- Add node and update minetest.add_node(place_to, newnode) local take_item = true -- Run callback if def.after_place_node then -- Copy place_to because callback can modify it local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} if def.after_place_node(place_to_copy, placer, itemstack) then take_item = false end end -- Run script hook local _, callback for _, callback in ipairs(minetest.registered_on_placenodes) do -- Copy pos and node because callback can modify them local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} local newnode_copy = {name=newnode.name, param1=newnode.param1, param2=newnode.param2} local oldnode_copy = {name=oldnode.name, param1=oldnode.param1, param2=oldnode.param2} if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack) then take_item = false end end if take_item then itemstack:take_item() end return itemstack, true end function minetest.item_place_object(itemstack, placer, pointed_thing) local pos = minetest.get_pointed_thing_position(pointed_thing, true) if pos ~= nil then local item = itemstack:take_item() minetest.add_item(pos, item) end return itemstack end function minetest.item_place(itemstack, placer, pointed_thing, param2) -- Call on_rightclick if the pointed node defines it if pointed_thing.type == "node" and placer and not placer:get_player_control().sneak then local n = minetest.get_node(pointed_thing.under) local nn = n.name if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n, placer, itemstack) or itemstack, false end end if itemstack:get_definition().type == "node" then return minetest.item_place_node(itemstack, placer, pointed_thing, param2) end return itemstack end function minetest.item_drop(itemstack, dropper, pos) if dropper.get_player_name then local v = dropper:get_look_dir() local p = {x=pos.x+v.x, y=pos.y+1.5+v.y, z=pos.z+v.z} local obj = minetest.add_item(p, itemstack) if obj then v.x = v.x*2 v.y = v.y*2 + 1 v.z = v.z*2 obj:setvelocity(v) end else minetest.add_item(pos, itemstack) end return ItemStack("") end function minetest.item_eat(hp_change, replace_with_item) return function(itemstack, user, pointed_thing) -- closure 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 return itemstack end end function minetest.node_punch(pos, node, puncher) -- Run script hook local _, callback for _, callback in ipairs(minetest.registered_on_punchnodes) do -- 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} callback(pos_copy, node_copy, puncher) end end function minetest.handle_node_drops(pos, drops, digger) -- Add dropped items to object's inventory if digger:get_inventory() then local _, dropped_item for _, dropped_item in ipairs(drops) do local left = digger:get_inventory():add_item("main", dropped_item) if not left:is_empty() then local p = { x = pos.x + math.random()/2-0.25, y = pos.y + math.random()/2-0.25, z = pos.z + math.random()/2-0.25, } minetest.add_item(p, left) end end end end function minetest.node_dig(pos, node, digger) local def = ItemStack({name=node.name}):get_definition() if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then minetest.log("info", digger:get_player_name() .. " tried to dig " .. node.name .. " which is not diggable " .. minetest.pos_to_string(pos)) return end if minetest.is_protected(pos, digger:get_player_name()) then minetest.log("action", digger:get_player_name() .. " tried to dig " .. node.name .. " at protected position " .. minetest.pos_to_string(pos)) minetest.record_protection_violation(pos, digger:get_player_name()) return end minetest.log('action', digger:get_player_name() .. " digs " .. node.name .. " at " .. minetest.pos_to_string(pos)) local wielded = digger:get_wielded_item() local drops = minetest.get_node_drops(node.name, wielded:get_name()) local wdef = wielded:get_definition() local tp = wielded:get_tool_capabilities() local dp = minetest.get_dig_params(def.groups, tp) if wdef and wdef.after_use then wielded = wdef.after_use(wielded, digger, node, dp) or wielded else -- Wear out tool if not minetest.setting_getbool("creative_mode") then wielded:add_wear(dp.wear) end end digger:set_wielded_item(wielded) -- Handle drops minetest.handle_node_drops(pos, drops, digger) local oldmetadata = nil if def.after_dig_node then oldmetadata = minetest.get_meta(pos):to_table() end -- Remove node and update minetest.remove_node(pos) -- Run callback if def.after_dig_node then -- 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} def.after_dig_node(pos_copy, node_copy, oldmetadata, digger) end -- Run script hook local _, callback for _, callback in ipairs(minetest.registered_on_dignodes) do -- 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} callback(pos_copy, node_copy, digger) end end -- This is used to allow mods to redefine minetest.item_place and so on -- NOTE: This is not the preferred way. Preferred way is to provide enough -- callbacks to not require redefining global functions. -celeron55 local function redef_wrapper(table, name) return function(...) return table[name](...) end end -- -- Item definition defaults -- minetest.nodedef_default = { -- Item properties type="node", -- name intentionally not defined here description = "", groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, stack_max = 99, usable = false, liquids_pointable = false, tool_capabilities = nil, node_placement_prediction = nil, -- Interaction callbacks on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop on_use = nil, can_dig = nil, on_punch = redef_wrapper(minetest, 'node_punch'), -- minetest.node_punch on_rightclick = nil, on_dig = redef_wrapper(minetest, 'node_dig'), -- minetest.node_dig on_receive_fields = nil, on_metadata_inventory_move = minetest.node_metadata_inventory_move_allow_all, on_metadata_inventory_offer = minetest.node_metadata_inventory_offer_allow_all, on_metadata_inventory_take = minetest.node_metadata_inventory_take_allow_all, -- Node properties drawtype = "normal", visual_scale = 1.0, -- Don't define these because otherwise the old tile_images and -- special_materials wouldn't be read --tiles ={""}, --special_tiles = { -- {name="", backface_culling=true}, -- {name="", backface_culling=true}, --}, alpha = 255, post_effect_color = {a=0, r=0, g=0, b=0}, paramtype = "none", paramtype2 = "none", is_ground_content = true, sunlight_propagates = false, walkable = true, pointable = true, diggable = true, climbable = false, buildable_to = false, liquidtype = "none", liquid_alternative_flowing = "", liquid_alternative_source = "", liquid_viscosity = 0, drowning = 0, light_source = 0, damage_per_second = 0, selection_box = {type="regular"}, legacy_facedir_simple = false, legacy_wallmounted = false, } minetest.craftitemdef_default = { type="craft", -- name intentionally not defined here description = "", groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, stack_max = 99, liquids_pointable = false, tool_capabilities = nil, -- Interaction callbacks on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop on_use = nil, } minetest.tooldef_default = { type="tool", -- name intentionally not defined here description = "", groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, stack_max = 1, liquids_pointable = false, tool_capabilities = nil, -- Interaction callbacks on_place = redef_wrapper(minetest, 'item_place'), -- minetest.item_place on_drop = redef_wrapper(minetest, 'item_drop'), -- minetest.item_drop on_use = nil, } minetest.noneitemdef_default = { -- This is used for the hand and unknown items type="none", -- name intentionally not defined here description = "", groups = {}, inventory_image = "", wield_image = "", wield_scale = {x=1,y=1,z=1}, stack_max = 99, liquids_pointable = false, tool_capabilities = nil, -- Interaction callbacks on_place = redef_wrapper(minetest, 'item_place'), on_drop = nil, on_use = nil, } builtin/modstore.lua0000644000175000017500000004174112261107224015373 0ustar mquinsonmquinson--Minetest --Copyright (C) 2013 sapier -- --This program is free software; you can redistribute it and/or modify --it under the terms of the GNU Lesser General Public License as published by --the Free Software Foundation; either version 2.1 of the License, or --(at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU Lesser General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -------------------------------------------------------------------------------- --modstore implementation modstore = {} -------------------------------------------------------------------------------- -- @function [parent=#modstore] init function modstore.init() modstore.tabnames = {} table.insert(modstore.tabnames,"dialog_modstore_unsorted") table.insert(modstore.tabnames,"dialog_modstore_search") modstore.modsperpage = 5 modstore.basetexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM modstore.lastmodtitle = "" modstore.last_search = "" modstore.searchlist = filterlist.create( function() if modstore.modlist_unsorted ~= nil and modstore.modlist_unsorted.data ~= nil then return modstore.modlist_unsorted.data end return {} end, function(element,modid) if element.id == modid then return true end return false end, --compare fct nil, --uid match fct function(element,substring) if substring == nil or substring == "" then return false end substring = substring:upper() if element.title ~= nil and element.title:upper():find(substring) ~= nil then return true end if element.details ~= nil and element.details.author ~= nil and element.details.author:upper():find(substring) ~= nil then return true end if element.details ~= nil and element.details.description ~= nil and element.details.description:upper():find(substring) ~= nil then return true end return false end --filter fct ) modstore.current_list = nil end -------------------------------------------------------------------------------- -- @function [parent=#modstore] nametoindex function modstore.nametoindex(name) for i=1,#modstore.tabnames,1 do if modstore.tabnames[i] == name then return i end end return 1 end -------------------------------------------------------------------------------- -- @function [parent=#modstore] getsuccessfuldialog function modstore.getsuccessfuldialog() local retval = "" retval = retval .. "size[6,2]" 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;" .. engine.formspec_escape(modstore.lastmodentry.moddetails.basename) .. "]" end retval = retval .. "button[2.5,1.5;1,0.5;btn_confirm_mod_successfull;" .. fgettext("ok") .. "]" return retval end -------------------------------------------------------------------------------- -- @function [parent=#modstore] gettab function modstore.gettab(tabname) local retval = "" local is_modstore_tab = false if tabname == "dialog_modstore_unsorted" then modstore.modsperpage = 5 retval = modstore.getmodlist(modstore.modlist_unsorted) is_modstore_tab = true end if tabname == "dialog_modstore_search" then retval = modstore.getsearchpage() is_modstore_tab = true end if is_modstore_tab then return modstore.tabheader(tabname) .. retval end if tabname == "modstore_mod_installed" then return modstore.getsuccessfuldialog() end if tabname == "modstore_downloading" then return "size[6,2]label[0.25,0.75;" .. fgettext("Downloading") .. " " .. modstore.lastmodtitle .. " " .. fgettext("please wait...") .. "]" end return "" end -------------------------------------------------------------------------------- -- @function [parent=#modstore] tabheader function modstore.tabheader(tabname) local retval = "size[12,10.25]" retval = retval .. "tabheader[-0.3,-0.99;modstore_tab;" .. "Unsorted,Search;" .. modstore.nametoindex(tabname) .. ";true;false]" .. "button[4,9.9;4,0.5;btn_modstore_close;" .. fgettext("Close modstore") .. "]" return retval end -------------------------------------------------------------------------------- -- @function [parent=#modstore] handle_buttons function modstore.handle_buttons(current_tab,fields) if fields["modstore_tab"] then local index = tonumber(fields["modstore_tab"]) if index > 0 and index <= #modstore.tabnames then if modstore.tabnames[index] == "dialog_modstore_search" then filterlist.set_filtercriteria(modstore.searchlist,modstore.last_search) filterlist.refresh(modstore.searchlist) modstore.modsperpage = 4 modstore.currentlist = { page = 0, pagecount = math.ceil(filterlist.size(modstore.searchlist) / modstore.modsperpage), data = filterlist.get_list(modstore.searchlist), } end return { current_tab = modstore.tabnames[index], is_dialog = true, show_buttons = false } end end if fields["btn_modstore_page_up"] then if modstore.current_list ~= nil and modstore.current_list.page > 0 then modstore.current_list.page = modstore.current_list.page - 1 end end if fields["btn_modstore_page_down"] then if modstore.current_list ~= nil and modstore.current_list.page 1 then local versiony = ypos + 0.05 retval = retval .. "dropdown[9.1," .. versiony .. ";2.48,0.25;dd_version" .. details.id .. ";" local versions = "" for i=1,#details.versions , 1 do if versions ~= "" then versions = versions .. "," end versions = versions .. details.versions[i].date:sub(1,10) end retval = retval .. versions .. ";1]" end if details.basename then --install button local buttony = ypos + 1.2 retval = retval .."button[9.1," .. buttony .. ";2.5,0.5;btn_install_mod_" .. details.id .. ";" if modmgr.mod_exists(details.basename) then retval = retval .. fgettext("re-Install") .."]" else retval = retval .. fgettext("Install") .."]" end end return retval end -------------------------------------------------------------------------------- --@function [parent=#modstore] getmodlist function modstore.getmodlist(list,yoffset) modstore.current_list = list if #list.data == 0 then return "" end if yoffset == nil then yoffset = 0 end local scrollbar = "" scrollbar = scrollbar .. "label[0.1,9.5;" .. fgettext("Page $1 of $2", list.page+1, list.pagecount) .. "]" scrollbar = scrollbar .. "box[11.6," .. (yoffset + 0.35) .. ";0.28," .. (8.6 - yoffset) .. ";#000000]" local scrollbarpos = (yoffset + 0.75) + ((7.7 -yoffset)/(list.pagecount-1)) * list.page scrollbar = scrollbar .. "box[11.6," ..scrollbarpos .. ";0.28,0.5;#32CD32]" scrollbar = scrollbar .. "button[11.6," .. (yoffset + (0.3)) .. ";0.5,0.5;btn_modstore_page_up;^]" scrollbar = scrollbar .. "button[11.6," .. 9.0 .. ";0.5,0.5;btn_modstore_page_down;v]" local retval = "" local endmod = (list.page * modstore.modsperpage) + modstore.modsperpage if (endmod > #list.data) then endmod = #list.data end for i=(list.page * modstore.modsperpage) +1, endmod, 1 do --getmoddetails local details = list.data[i].details if details == nil then details = {} details.title = list.data[i].title details.author = "" details.rating = -1 details.description = "" end if details ~= nil then local screenshot_ypos = yoffset +(i-1 - (list.page * modstore.modsperpage))*1.9 +0.2 retval = retval .. modstore.getshortmodinfo(screenshot_ypos, list.data[i], details) end end return retval .. scrollbar end -------------------------------------------------------------------------------- --@function [parent=#modstore] getsearchpage function modstore.getsearchpage() local retval = "" local search = "" if modstore.last_search ~= nil then search = modstore.last_search end retval = retval .. "button[9.5,0.2;2.5,0.5;btn_modstore_search;".. fgettext("Search") .. "]" .. "field[0.5,0.5;9,0.5;te_modstore_search;;" .. search .. "]" --show 4 mods only modstore.modsperpage = 4 retval = retval .. modstore.getmodlist( modstore.currentlist, 1.75) return retval; end builtin/filterlist.lua0000644000175000017500000002157412261107224015722 0ustar mquinsonmquinson--Minetest --Copyright (C) 2013 sapier -- --This program is free software; you can redistribute it and/or modify --it under the terms of the GNU Lesser General Public License as published by --the Free Software Foundation; either version 2.1 of the License, or --(at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU Lesser General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -------------------------------------------------------------------------------- -- Generic implementation of a filter/sortable list -- -- Usage: -- -- Filterlist needs to be initialized on creation. To achieve this you need to -- -- pass following functions: -- -- raw_fct() (mandatory): -- -- function returning a table containing the elements to be filtered -- -- compare_fct(element1,element2) (mandatory): -- -- function returning true/false if element1 is same element as element2 -- -- uid_match_fct(element1,uid) (optional) -- -- function telling if uid is attached to element1 -- -- filter_fct(element,filtercriteria) (optional) -- -- function returning true/false if filtercriteria met to element -- -- fetch_param (optional) -- -- parameter passed to raw_fct to aquire correct raw data -- -- -- -------------------------------------------------------------------------------- filterlist = {} -------------------------------------------------------------------------------- function filterlist.refresh(this) this.m_raw_list = this.m_raw_list_fct(this.m_fetch_param) filterlist.process(this) end -------------------------------------------------------------------------------- function filterlist.create(raw_fct,compare_fct,uid_match_fct,filter_fct,fetch_param) assert((raw_fct ~= nil) and (type(raw_fct) == "function")) assert((compare_fct ~= nil) and (type(compare_fct) == "function")) local this = {} this.m_raw_list_fct = raw_fct this.m_compare_fct = compare_fct this.m_filter_fct = filter_fct this.m_uid_match_fct = uid_match_fct this.m_filtercriteria = nil this.m_fetch_param = fetch_param this.m_sortmode = "none" this.m_sort_list = {} this.m_processed_list = nil this.m_raw_list = this.m_raw_list_fct(this.m_fetch_param) filterlist.process(this) return this end -------------------------------------------------------------------------------- function filterlist.add_sort_mechanism(this,name,fct) this.m_sort_list[name] = fct end -------------------------------------------------------------------------------- function filterlist.set_filtercriteria(this,criteria) if criteria == this.m_filtercriteria and type(criteria) ~= "table" then return end this.m_filtercriteria = criteria filterlist.process(this) end -------------------------------------------------------------------------------- function filterlist.get_filtercriteria(this) return this.m_filtercriteria end -------------------------------------------------------------------------------- --supported sort mode "alphabetic|none" function filterlist.set_sortmode(this,mode) if (mode == this.m_sortmode) then return end this.m_sortmode = mode filterlist.process(this) end -------------------------------------------------------------------------------- function filterlist.get_list(this) return this.m_processed_list end -------------------------------------------------------------------------------- function filterlist.get_raw_list(this) return this.m_raw_list end -------------------------------------------------------------------------------- function filterlist.get_raw_element(this,idx) if type(idx) ~= "number" then idx = tonumber(idx) end if idx ~= nil and idx > 0 and idx < #this.m_raw_list then return this.m_raw_list[idx] end return nil end -------------------------------------------------------------------------------- function filterlist.get_raw_index(this,listindex) assert(this.m_processed_list ~= nil) if listindex ~= nil and listindex > 0 and listindex <= #this.m_processed_list then local entry = this.m_processed_list[listindex] for i,v in ipairs(this.m_raw_list) do if this.m_compare_fct(v,entry) then return i end end end return 0 end -------------------------------------------------------------------------------- function filterlist.get_current_index(this,listindex) assert(this.m_processed_list ~= nil) if listindex ~= nil and listindex > 0 and listindex <= #this.m_raw_list then local entry = this.m_raw_list[listindex] for i,v in ipairs(this.m_processed_list) do if this.m_compare_fct(v,entry) then return i end end end return 0 end -------------------------------------------------------------------------------- function filterlist.process(this) assert(this.m_raw_list ~= nil) if this.m_sortmode == "none" and this.m_filtercriteria == nil then this.m_processed_list = this.m_raw_list return end this.m_processed_list = {} for k,v in pairs(this.m_raw_list) do if this.m_filtercriteria == nil or this.m_filter_fct(v,this.m_filtercriteria) then table.insert(this.m_processed_list,v) end end if this.m_sortmode == "none" then return end if this.m_sort_list[this.m_sortmode] ~= nil and type(this.m_sort_list[this.m_sortmode]) == "function" then this.m_sort_list[this.m_sortmode](this) end end -------------------------------------------------------------------------------- function filterlist.size(this) if this.m_processed_list == nil then return 0 end return #this.m_processed_list end -------------------------------------------------------------------------------- function filterlist.uid_exists_raw(this,uid) for i,v in ipairs(this.m_raw_list) do if this.m_uid_match_fct(v,uid) then return true end end return false end -------------------------------------------------------------------------------- function filterlist.raw_index_by_uid(this, uid) local elementcount = 0 local elementidx = 0 for i,v in ipairs(this.m_raw_list) do if this.m_uid_match_fct(v,uid) then elementcount = elementcount +1 elementidx = i end end -- If there are more elements than one with same name uid can't decide which -- one is meant. This shouldn't be possible but just for sure. if elementcount > 1 then elementidx=0 end return elementidx end -------------------------------------------------------------------------------- -- COMMON helper functions -- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- function compare_worlds(world1,world2) if world1.path ~= world2.path then return false end if world1.name ~= world2.name then return false end if world1.gameid ~= world2.gameid then return false end return true end -------------------------------------------------------------------------------- function sort_worlds_alphabetic(this) table.sort(this.m_processed_list, function(a, b) --fixes issue #857 (crash due to sorting nil in worldlist) if a == nil or b == nil then if a == nil and b ~= nil then return false end if b == nil and a ~= nil then return true end return false end if a.name:lower() == b.name:lower() then return a.name < b.name end return a.name:lower() < b.name:lower() end) end -------------------------------------------------------------------------------- function sort_mod_list(this) table.sort(this.m_processed_list, function(a, b) -- Show game mods at bottom if a.typ ~= b.typ then return b.typ == "game_mod" end -- If in same or no modpack, sort by name if a.modpack == b.modpack then if a.name:lower() == b.name:lower() then return a.name < b.name end return a.name:lower() < b.name:lower() -- Else compare name to modpack name else -- Always show modpack pseudo-mod on top of modpack mod list if a.name == b.modpack then return true elseif b.name == a.modpack then return false end local name_a = a.modpack or a.name local name_b = b.modpack or b.name if name_a:lower() == name_b:lower() then return name_a < name_b end return name_a:lower() < name_b:lower() end end) end builtin/mm_menubar.lua0000644000175000017500000000463312261107224015660 0ustar mquinsonmquinson--Minetest --Copyright (C) 2013 sapier -- --This program is free software; you can redistribute it and/or modify --it under the terms of the GNU Lesser General Public License as published by --the Free Software Foundation; either version 2.1 of the License, or --(at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU Lesser General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. menubar = {} -------------------------------------------------------------------------------- function menubar.handle_buttons(fields) for i=1,#menubar.buttons,1 do if fields[menubar.buttons[i].btn_name] ~= nil then menu.last_game = menubar.buttons[i].index engine.setting_set("main_menu_last_game_idx",menu.last_game) menu.update_gametype() end end end -------------------------------------------------------------------------------- function menubar.refresh() menubar.formspec = "box[-0.3,5.625;12.4,1.2;#000000]" .. "box[-0.3,5.6;12.4,0.05;#FFFFFF]" menubar.buttons = {} local button_base = -0.08 local maxbuttons = #gamemgr.games if maxbuttons > 11 then maxbuttons = 11 end for i=1,maxbuttons,1 do local btn_name = "menubar_btn_" .. gamemgr.games[i].id local buttonpos = button_base + (i-1) * 1.1 if gamemgr.games[i].menuicon_path ~= nil and gamemgr.games[i].menuicon_path ~= "" then menubar.formspec = menubar.formspec .. "image_button[" .. buttonpos .. ",5.72;1.165,1.175;" .. engine.formspec_escape(gamemgr.games[i].menuicon_path) .. ";" .. btn_name .. ";;true;false]" else local part1 = gamemgr.games[i].id:sub(1,5) local part2 = gamemgr.games[i].id:sub(6,10) local part3 = gamemgr.games[i].id:sub(11) local text = part1 .. "\n" .. part2 if part3 ~= nil and part3 ~= "" then text = text .. "\n" .. part3 end menubar.formspec = menubar.formspec .. "image_button[" .. buttonpos .. ",5.72;1.165,1.175;;" ..btn_name .. ";" .. text .. ";true;true]" end local toadd = { btn_name = btn_name, index = i, } table.insert(menubar.buttons,toadd) end end builtin/falling.lua0000644000175000017500000001324112261107224015145 0ustar mquinsonmquinson-- Minetest: builtin/item.lua -- -- Falling stuff -- minetest.register_entity("__builtin:falling_node", { initial_properties = { physical = true, collide_with_objects = false, collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}, visual = "wielditem", textures = {}, visual_size = {x=0.667, y=0.667}, }, node = {}, set_node = function(self, node) self.node = node local stack = ItemStack(node.name) local itemtable = stack:to_table() local itemname = nil if itemtable then itemname = stack:to_table().name end local item_texture = nil local item_type = "" if minetest.registered_items[itemname] then item_texture = minetest.registered_items[itemname].inventory_image item_type = minetest.registered_items[itemname].type end prop = { is_visible = true, textures = {node.name}, } self.object:set_properties(prop) end, get_staticdata = function(self) return self.node.name end, on_activate = function(self, staticdata) self.object:set_armor_groups({immortal=1}) --self.object:setacceleration({x=0, y=-10, z=0}) self:set_node({name=staticdata}) end, on_step = function(self, dtime) -- Set gravity self.object:setacceleration({x=0, y=-10, z=0}) -- Turn to actual sand when collides to ground or just move local pos = self.object:getpos() local bcp = {x=pos.x, y=pos.y-0.7, z=pos.z} -- Position of bottom center point local bcn = minetest.get_node(bcp) local bcd = minetest.registered_nodes[bcn.name] -- Note: walkable is in the node definition, not in item groups if not bcd or (bcd.walkable or (minetest.get_node_group(self.node.name, "float") ~= 0 and bcd.liquidtype ~= "none")) then if bcd and bcd.leveled and bcn.name == self.node.name then local addlevel = self.node.level if addlevel == nil or addlevel <= 0 then addlevel = bcd.leveled end if minetest.add_node_level(bcp, addlevel) == 0 then self.object:remove() return end elseif bcd and bcd.buildable_to and (minetest.get_node_group(self.node.name, "float") == 0 or bcd.liquidtype == "none") then minetest.remove_node(bcp) return end local np = {x=bcp.x, y=bcp.y+1, z=bcp.z} -- Check what's here local n2 = minetest.get_node(np) -- If it's not air or liquid, remove node and replace it with -- it's drops if n2.name ~= "air" and (not minetest.registered_nodes[n2.name] or minetest.registered_nodes[n2.name].liquidtype == "none") then local drops = minetest.get_node_drops(n2.name, "") minetest.remove_node(np) -- Add dropped items local _, dropped_item for _, dropped_item in ipairs(drops) do minetest.add_item(np, dropped_item) end -- Run script hook local _, callback for _, callback in ipairs(minetest.registered_on_dignodes) do callback(np, n2, nil) end end -- Create node and remove entity minetest.add_node(np, self.node) self.object:remove() nodeupdate(np) else -- Do nothing end end }) function spawn_falling_node(p, node) obj = minetest.add_entity(p, "__builtin:falling_node") obj:get_luaentity():set_node(node) end function drop_attached_node(p) local nn = minetest.get_node(p).name minetest.remove_node(p) for _,item in ipairs(minetest.get_node_drops(nn, "")) do local pos = { x = p.x + math.random()/2 - 0.25, y = p.y + math.random()/2 - 0.25, z = p.z + math.random()/2 - 0.25, } minetest.add_item(pos, item) end end function check_attached_node(p, n) local def = minetest.registered_nodes[n.name] local d = {x=0, y=0, z=0} if def.paramtype2 == "wallmounted" then if n.param2 == 0 then d.y = 1 elseif n.param2 == 1 then d.y = -1 elseif n.param2 == 2 then d.x = 1 elseif n.param2 == 3 then d.x = -1 elseif n.param2 == 4 then d.z = 1 elseif n.param2 == 5 then d.z = -1 end else d.y = -1 end local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z} local nn = minetest.get_node(p2).name local def2 = minetest.registered_nodes[nn] if def2 and not def2.walkable then return false end return true end -- -- Some common functions -- function nodeupdate_single(p, delay) n = minetest.get_node(p) if minetest.get_node_group(n.name, "falling_node") ~= 0 then p_bottom = {x=p.x, y=p.y-1, z=p.z} n_bottom = minetest.get_node(p_bottom) -- Note: walkable is in the node definition, not in item groups if minetest.registered_nodes[n_bottom.name] and (minetest.get_node_group(n.name, "float") == 0 or minetest.registered_nodes[n_bottom.name].liquidtype == "none") and (n.name ~= n_bottom.name or (minetest.registered_nodes[n_bottom.name].leveled and minetest.env:get_node_level(p_bottom) < minetest.env:get_node_max_level(p_bottom))) and (not minetest.registered_nodes[n_bottom.name].walkable or minetest.registered_nodes[n_bottom.name].buildable_to) then if delay then minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false) else n.level = minetest.env:get_node_level(p) minetest.remove_node(p) spawn_falling_node(p, n) nodeupdate(p) end end end if minetest.get_node_group(n.name, "attached_node") ~= 0 then if not check_attached_node(p, n) then drop_attached_node(p) nodeupdate(p) end end end function nodeupdate(p, delay) -- Round p to prevent falling entities to get stuck p.x = math.floor(p.x+0.5) p.y = math.floor(p.y+0.5) p.z = math.floor(p.z+0.5) for x = -1,1 do for y = -1,1 do for z = -1,1 do nodeupdate_single({x=p.x+x, y=p.y+y, z=p.z+z}, delay or not (x==0 and y==0 and z==0)) end end end end -- -- Global callbacks -- function on_placenode(p, node) nodeupdate(p) end minetest.register_on_placenode(on_placenode) function on_dignode(p, node) nodeupdate(p) end minetest.register_on_dignode(on_dignode) builtin/modmgr.lua0000644000175000017500000006766412261107224015040 0ustar mquinsonmquinson--Minetest --Copyright (C) 2013 sapier -- --This program is free software; you can redistribute it and/or modify --it under the terms of the GNU Lesser General Public License as published by --the Free Software Foundation; either version 2.1 of the License, or --(at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU Lesser General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -------------------------------------------------------------------------------- function get_mods(path,retval,modpack) local mods = engine.get_dirlist(path,true) for i=1,#mods,1 do local toadd = {} local modpackfile = nil toadd.name = mods[i] toadd.path = path .. DIR_DELIM .. mods[i] .. DIR_DELIM if modpack ~= nil and modpack ~= "" then toadd.modpack = modpack else local filename = path .. DIR_DELIM .. mods[i] .. DIR_DELIM .. "modpack.txt" local error = nil modpackfile,error = io.open(filename,"r") end if modpackfile ~= nil then modpackfile:close() toadd.is_modpack = true table.insert(retval,toadd) get_mods(path .. DIR_DELIM .. mods[i],retval,mods[i]) else table.insert(retval,toadd) end end end --modmanager implementation modmgr = {} -------------------------------------------------------------------------------- function modmgr.extract(modfile) if modfile.type == "zip" then local tempfolder = os.tempfolder() if tempfolder ~= nil and tempfolder ~= "" then engine.create_dir(tempfolder) if engine.extract_zip(modfile.name,tempfolder) then return tempfolder end end end return nil end ------------------------------------------------------------------------------- function modmgr.getbasefolder(temppath) if temppath == nil then return { type = "invalid", path = "" } end local testfile = io.open(temppath .. DIR_DELIM .. "init.lua","r") if testfile ~= nil then testfile:close() return { type="mod", path=temppath } end testfile = io.open(temppath .. DIR_DELIM .. "modpack.txt","r") if testfile ~= nil then testfile:close() return { type="modpack", path=temppath } end local subdirs = engine.get_dirlist(temppath,true) --only single mod or modpack allowed if #subdirs ~= 1 then return { type = "invalid", path = "" } end testfile = io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."init.lua","r") if testfile ~= nil then testfile:close() return { type="mod", path= temppath .. DIR_DELIM .. subdirs[1] } end testfile = io.open(temppath .. DIR_DELIM .. subdirs[1] ..DIR_DELIM .."modpack.txt","r") if testfile ~= nil then testfile:close() return { type="modpack", path=temppath .. DIR_DELIM .. subdirs[1] } end return { type = "invalid", path = "" } end -------------------------------------------------------------------------------- function modmgr.isValidModname(modpath) if modpath:find("-") ~= nil then return false end return true end -------------------------------------------------------------------------------- function modmgr.parse_register_line(line) local pos1 = line:find("\"") local pos2 = nil if pos1 ~= nil then pos2 = line:find("\"",pos1+1) end if pos1 ~= nil and pos2 ~= nil then local item = line:sub(pos1+1,pos2-1) if item ~= nil and item ~= "" then local pos3 = item:find(":") if pos3 ~= nil then local retval = item:sub(1,pos3-1) if retval ~= nil and retval ~= "" then return retval end end end end return nil end -------------------------------------------------------------------------------- function modmgr.parse_dofile_line(modpath,line) local pos1 = line:find("\"") local pos2 = nil if pos1 ~= nil then pos2 = line:find("\"",pos1+1) end if pos1 ~= nil and pos2 ~= nil then local filename = line:sub(pos1+1,pos2-1) if filename ~= nil and filename ~= "" and filename:find(".lua") then return modmgr.identify_modname(modpath,filename) end end return nil end -------------------------------------------------------------------------------- function modmgr.identify_modname(modpath,filename) local testfile = io.open(modpath .. DIR_DELIM .. filename,"r") if testfile ~= nil then local line = testfile:read() while line~= nil do local modname = nil if line:find("minetest.register_tool") then modname = modmgr.parse_register_line(line) end if line:find("minetest.register_craftitem") then modname = modmgr.parse_register_line(line) end if line:find("minetest.register_node") then modname = modmgr.parse_register_line(line) end if line:find("dofile") then modname = modmgr.parse_dofile_line(modpath,line) end if modname ~= nil then testfile:close() return modname end line = testfile:read() end testfile:close() end return nil end -------------------------------------------------------------------------------- function modmgr.tab() if modmgr.global_mods == nil then modmgr.refresh_globals() end if modmgr.selected_mod == nil then modmgr.selected_mod = 1 end local retval = "vertlabel[0,-0.25;".. fgettext("MODS") .. "]" .. "label[0.8,-0.25;".. fgettext("Installed Mods:") .. "]" .. "textlist[0.75,0.25;4.5,4;modlist;" .. modmgr.render_modlist(modmgr.global_mods) .. ";" .. modmgr.selected_mod .. "]" retval = retval .. "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[2.45,4.85;3.05,0.5;btn_mod_mgr_download;".. fgettext("Online mod repository") .. "]" local selected_mod = nil if filterlist.size(modmgr.global_mods) >= modmgr.selected_mod then selected_mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] end if selected_mod ~= nil then local modscreenshot = nil --check for screenshot beeing available local screenshotfilename = selected_mod.path .. DIR_DELIM .. "screenshot.png" local error = nil screenshotfile,error = io.open(screenshotfilename,"r") if error == nil then screenshotfile:close() modscreenshot = screenshotfilename end if modscreenshot == nil then modscreenshot = modstore.basetexturedir .. "no_screenshot.png" end retval = retval .. "image[5.5,0;3,2;" .. engine.formspec_escape(modscreenshot) .. "]" .. "label[8.25,0.6;" .. selected_mod.name .. "]" local descriptionlines = nil error = nil local descriptionfilename = selected_mod.path .. "description.txt" descriptionfile,error = io.open(descriptionfilename,"r") if error == nil then descriptiontext = descriptionfile:read("*all") descriptionlines = engine.splittext(descriptiontext,42) descriptionfile:close() else descriptionlines = {} table.insert(descriptionlines,fgettext("No mod description available")) end retval = retval .. "label[5.5,1.7;".. fgettext("Mod information:") .. "]" .. "textlist[5.5,2.2;6.2,2.4;description;" for i=1,#descriptionlines,1 do retval = retval .. engine.formspec_escape(descriptionlines[i]) .. "," end if selected_mod.is_modpack then retval = retval .. ";0]" .. "button[10,4.85;2,0.5;btn_mod_mgr_rename_modpack;" .. fgettext("Rename") .. "]" retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;" .. fgettext("Uninstall selected modpack") .. "]" else --show dependencies retval = retval .. ",Depends:," toadd = modmgr.get_dependencies(selected_mod.path) retval = retval .. toadd .. ";0]" retval = retval .. "button[5.5,4.85;4.5,0.5;btn_mod_mgr_delete_mod;" .. fgettext("Uninstall selected mod") .. "]" end end return retval end -------------------------------------------------------------------------------- function modmgr.dialog_rename_modpack() local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] local retval = "label[1.75,1;".. fgettext("Rename Modpack:") .. "]".. "field[4.5,1.4;6,0.5;te_modpack_name;;" .. mod.name .. "]" .. "button[5,4.2;2.6,0.5;dlg_rename_modpack_confirm;".. fgettext("Accept") .. "]" .. "button[7.5,4.2;2.8,0.5;dlg_rename_modpack_cancel;".. fgettext("Cancel") .. "]" return retval end -------------------------------------------------------------------------------- function modmgr.precheck() if modmgr.world_config_selected_world == nil then modmgr.world_config_selected_world = 1 end if modmgr.world_config_selected_mod == nil then modmgr.world_config_selected_mod = 1 end if modmgr.hide_gamemods == nil then modmgr.hide_gamemods = true end if modmgr.hide_modpackcontents == nil then modmgr.hide_modpackcontents = true end end -------------------------------------------------------------------------------- function modmgr.render_modlist(render_list) local retval = "" if render_list == nil then if modmgr.global_mods == nil then modmgr.refresh_globals() end render_list = modmgr.global_mods end local list = filterlist.get_list(render_list) local last_modpack = nil for i,v in ipairs(list) do if retval ~= "" then retval = retval .."," end local color = "" if v.is_modpack then local rawlist = filterlist.get_raw_list(render_list) local all_enabled = true for j=1,#rawlist,1 do if rawlist[j].modpack == list[i].name and rawlist[j].enabled ~= true then all_enabled = false break end end if all_enabled == false then color = mt_color_grey else color = mt_color_dark_green end end if v.typ == "game_mod" then color = mt_color_blue else if v.enabled then color = mt_color_green end end retval = retval .. color if v.modpack ~= nil then retval = retval .. " " end retval = retval .. v.name end return retval end -------------------------------------------------------------------------------- function modmgr.dialog_configure_world() modmgr.precheck() local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] local mod = filterlist.get_list(modmgr.modlist)[modmgr.world_config_selected_mod] local retval = "size[11,6.5]" .. "label[0.5,-0.25;" .. fgettext("World:") .. "]" .. "label[1.75,-0.25;" .. worldspec.name .. "]" if modmgr.hide_gamemods then retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]" else retval = retval .. "checkbox[0,5.75;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]" end if modmgr.hide_modpackcontents then retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]" else retval = retval .. "checkbox[2,5.75;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]" end if mod == nil then mod = {name=""} end retval = retval .. "label[0,0.45;" .. fgettext("Mod:") .. "]" .. "label[0.75,0.45;" .. mod.name .. "]" .. "label[0,1;" .. fgettext("Depends:") .. "]" .. "textlist[0,1.5;5,4.25;world_config_depends;" .. modmgr.get_dependencies(mod.path) .. ";0]" .. "button[9.25,6.35;2,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" .. "button[7.4,6.35;2,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]" if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then if mod.is_modpack then local rawlist = filterlist.get_raw_list(modmgr.modlist) local all_enabled = true for j=1,#rawlist,1 do if rawlist[j].modpack == mod.name and rawlist[j].enabled ~= true then all_enabled = false break end end if all_enabled == false then retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]" else retval = retval .. "button[5.5,-0.125;2,0.5;btn_mp_disable;" .. fgettext("Disable MP") .. "]" end else if mod.enabled then retval = retval .. "checkbox[5.5,-0.375;cb_mod_enable;" .. fgettext("enabled") .. ";true]" else retval = retval .. "checkbox[5.5,-0.375;cb_mod_enable;" .. fgettext("enabled") .. ";false]" end end end retval = retval .. "button[8.5,-0.125;2.5,0.5;btn_all_mods;" .. fgettext("Enable all") .. "]" .. "textlist[5.5,0.5;5.5,5.75;world_config_modlist;" retval = retval .. modmgr.render_modlist(modmgr.modlist) retval = retval .. ";" .. modmgr.world_config_selected_mod .."]" return retval end -------------------------------------------------------------------------------- function modmgr.handle_buttons(tab,fields) local retval = nil if tab == "mod_mgr" then retval = modmgr.handle_modmgr_buttons(fields) end if tab == "dialog_rename_modpack" then retval = modmgr.handle_rename_modpack_buttons(fields) end if tab == "dialog_delete_mod" then retval = modmgr.handle_delete_mod_buttons(fields) end if tab == "dialog_configure_world" then retval = modmgr.handle_configure_world_buttons(fields) end return retval end -------------------------------------------------------------------------------- function modmgr.get_dependencies(modfolder) local toadd = "" if modfolder ~= nil then local filename = modfolder .. DIR_DELIM .. "depends.txt" local dependencyfile = io.open(filename,"r") if dependencyfile then local dependency = dependencyfile:read("*l") while dependency do if toadd ~= "" then toadd = toadd .. "," end toadd = toadd .. dependency dependency = dependencyfile:read() end dependencyfile:close() end end return toadd end -------------------------------------------------------------------------------- function modmgr.get_worldconfig(worldpath) local filename = worldpath .. DIR_DELIM .. "world.mt" local worldfile = Settings(filename) local worldconfig = {} worldconfig.global_mods = {} worldconfig.game_mods = {} for key,value in pairs(worldfile:to_table()) do if key == "gameid" then worldconfig.id = value else worldconfig.global_mods[key] = engine.is_yes(value) end end --read gamemods local gamespec = gamemgr.find_by_gameid(worldconfig.id) gamemgr.get_game_mods(gamespec, worldconfig.game_mods) return worldconfig end -------------------------------------------------------------------------------- function modmgr.handle_modmgr_buttons(fields) local retval = { tab = nil, is_dialog = nil, show_buttons = nil, } if fields["modlist"] ~= nil then local event = explode_textlist_event(fields["modlist"]) modmgr.selected_mod = event.index end if fields["btn_mod_mgr_install_local"] ~= nil then engine.show_file_open_dialog("mod_mgt_open_dlg",fgettext("Select Mod File:")) end if fields["btn_mod_mgr_download"] ~= nil then modstore.update_modlist() retval.current_tab = "dialog_modstore_unsorted" retval.is_dialog = true retval.show_buttons = false return retval end if fields["btn_mod_mgr_rename_modpack"] ~= nil then retval.current_tab = "dialog_rename_modpack" retval.is_dialog = true retval.show_buttons = false return retval end if fields["btn_mod_mgr_delete_mod"] ~= nil then retval.current_tab = "dialog_delete_mod" retval.is_dialog = true retval.show_buttons = false return retval end if fields["mod_mgt_open_dlg_accepted"] ~= nil and fields["mod_mgt_open_dlg_accepted"] ~= "" then modmgr.installmod(fields["mod_mgt_open_dlg_accepted"],nil) end return nil; end -------------------------------------------------------------------------------- function modmgr.installmod(modfilename,basename) local modfile = modmgr.identify_filetype(modfilename) local modpath = modmgr.extract(modfile) if modpath == nil then gamedata.errormessage = fgettext("Install Mod: file: \"$1\"", modfile.name) .. fgettext("\nInstall Mod: unsupported filetype \"$1\" or broken archive", modfile.type) return end local basefolder = modmgr.getbasefolder(modpath) if basefolder.type == "modpack" then local clean_path = nil if basename ~= nil then clean_path = "mp_" .. basename end if clean_path == nil then clean_path = get_last_folder(cleanup_path(basefolder.path)) end if clean_path ~= nil then local targetpath = engine.get_modpath() .. DIR_DELIM .. clean_path if not engine.copy_dir(basefolder.path,targetpath) then gamedata.errormessage = fgettext("Failed to install $1 to $2", basename, targetpath) end else gamedata.errormessage = fgettext("Install Mod: unable to find suitable foldername for modpack $1", modfilename) end end if basefolder.type == "mod" then local targetfolder = basename if targetfolder == nil then targetfolder = modmgr.identify_modname(basefolder.path,"init.lua") end --if heuristic failed try to use current foldername if targetfolder == nil then targetfolder = get_last_folder(basefolder.path) end if targetfolder ~= nil and modmgr.isValidModname(targetfolder) then local targetpath = engine.get_modpath() .. DIR_DELIM .. targetfolder engine.copy_dir(basefolder.path,targetpath) else gamedata.errormessage = fgettext("Install Mod: unable to find real modname for: $1", modfilename) end end engine.delete_dir(modpath) modmgr.refresh_globals() end -------------------------------------------------------------------------------- function modmgr.handle_rename_modpack_buttons(fields) if fields["dlg_rename_modpack_confirm"] ~= nil then local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] local oldpath = engine.get_modpath() .. DIR_DELIM .. mod.name local targetpath = engine.get_modpath() .. DIR_DELIM .. fields["te_modpack_name"] engine.copy_dir(oldpath,targetpath,false) modmgr.refresh_globals() modmgr.selected_mod = filterlist.get_current_index(modmgr.global_mods, filterlist.raw_index_by_uid(modmgr.global_mods, fields["te_modpack_name"])) end return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end -------------------------------------------------------------------------------- function modmgr.handle_configure_world_buttons(fields) if fields["world_config_modlist"] ~= nil then local event = explode_textlist_event(fields["world_config_modlist"]) modmgr.world_config_selected_mod = event.index if event.typ == "DCL" then modmgr.world_config_enable_mod(nil) end end if fields["key_enter"] ~= nil then modmgr.world_config_enable_mod(nil) end if fields["cb_mod_enable"] ~= nil then local toset = engine.is_yes(fields["cb_mod_enable"]) modmgr.world_config_enable_mod(toset) end if fields["btn_mp_enable"] ~= nil or fields["btn_mp_disable"] then local toset = (fields["btn_mp_enable"] ~= nil) modmgr.world_config_enable_mod(toset) end if fields["cb_hide_gamemods"] ~= nil then local current = filterlist.get_filtercriteria(modmgr.modlist) if current == nil then current = {} end if engine.is_yes(fields["cb_hide_gamemods"]) then current.hide_game = true modmgr.hide_gamemods = true else current.hide_game = false modmgr.hide_gamemods = false end filterlist.set_filtercriteria(modmgr.modlist,current) end if fields["cb_hide_mpcontent"] ~= nil then local current = filterlist.get_filtercriteria(modmgr.modlist) if current == nil then current = {} end if engine.is_yes(fields["cb_hide_mpcontent"]) then current.hide_modpackcontents = true modmgr.hide_modpackcontents = true else current.hide_modpackcontents = false modmgr.hide_modpackcontents = false end filterlist.set_filtercriteria(modmgr.modlist,current) end if fields["btn_config_world_save"] then local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] local filename = worldspec.path .. DIR_DELIM .. "world.mt" local worldfile = Settings(filename) local mods = worldfile:to_table() local rawlist = filterlist.get_raw_list(modmgr.modlist) local i,mod for i,mod in ipairs(rawlist) do if not mod.is_modpack and mod.typ ~= "game_mod" then if mod.enabled then worldfile:set("load_mod_"..mod.name, "true") else worldfile:set("load_mod_"..mod.name, "false") end mods["load_mod_"..mod.name] = nil end end -- Remove mods that are not present anymore for key,value in pairs(mods) do if key:sub(1,9) == "load_mod_" then worldfile:remove(key) end end if not worldfile:write() then engine.log("error", "Failed to write world config file") end modmgr.modlist = nil modmgr.worldconfig = nil return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end if fields["btn_config_world_cancel"] then modmgr.worldconfig = nil return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end if fields["btn_all_mods"] then local list = filterlist.get_raw_list(modmgr.modlist) for i=1,#list,1 do if list[i].typ ~= "game_mod" and not list[i].is_modpack then list[i].enabled = true end end end return nil end -------------------------------------------------------------------------------- function modmgr.world_config_enable_mod(toset) local mod = filterlist.get_list(modmgr.modlist) [engine.get_textlist_index("world_config_modlist")] if mod.typ == "game_mod" then -- game mods can't be enabled or disabled elseif not mod.is_modpack then if toset == nil then mod.enabled = not mod.enabled else mod.enabled = toset end else local list = filterlist.get_raw_list(modmgr.modlist) for i=1,#list,1 do if list[i].modpack == mod.name then if toset == nil then toset = not list[i].enabled end list[i].enabled = toset end end end end -------------------------------------------------------------------------------- function modmgr.handle_delete_mod_buttons(fields) local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] if fields["dlg_delete_mod_confirm"] ~= nil then if mod.path ~= nil and mod.path ~= "" and mod.path ~= engine.get_modpath() then if not engine.delete_dir(mod.path) then gamedata.errormessage = fgettext("Modmgr: failed to delete \"$1\"", mod.path) end modmgr.refresh_globals() else gamedata.errormessage = fgettext("Modmgr: invalid modpath \"$1\"", mod.path) end end return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end -------------------------------------------------------------------------------- function modmgr.dialog_delete_mod() local mod = filterlist.get_list(modmgr.global_mods)[modmgr.selected_mod] local retval = "field[1.75,1;10,3;;" .. fgettext("Are you sure you want to delete \"$1\"?", mod.name) .. ";]".. "button[4,4.2;1,0.5;dlg_delete_mod_confirm;" .. fgettext("Yes") .. "]" .. "button[6.5,4.2;3,0.5;dlg_delete_mod_cancel;" .. fgettext("No of course not!") .. "]" return retval end -------------------------------------------------------------------------------- function modmgr.preparemodlist(data) local retval = {} local global_mods = {} local game_mods = {} --read global mods local modpath = engine.get_modpath() if modpath ~= nil and modpath ~= "" then get_mods(modpath,global_mods) end for i=1,#global_mods,1 do global_mods[i].typ = "global_mod" table.insert(retval,global_mods[i]) end --read game mods local gamespec = gamemgr.find_by_gameid(data.gameid) gamemgr.get_game_mods(gamespec, game_mods) for i=1,#game_mods,1 do game_mods[i].typ = "game_mod" table.insert(retval,game_mods[i]) end if data.worldpath == nil then return retval end --read world mod configuration local filename = data.worldpath .. DIR_DELIM .. "world.mt" local worldfile = Settings(filename) for key,value in pairs(worldfile:to_table()) do if key:sub(1, 9) == "load_mod_" then key = key:sub(10) local element = nil for i=1,#retval,1 do if retval[i].name == key then element = retval[i] break end end if element ~= nil then element.enabled = engine.is_yes(value) else engine.log("info", "Mod: " .. key .. " " .. dump(value) .. " but not found") end end end return retval end -------------------------------------------------------------------------------- function modmgr.init_worldconfig() modmgr.precheck() local worldspec = engine.get_worlds()[modmgr.world_config_selected_world] if worldspec ~= nil then --read worldconfig modmgr.worldconfig = modmgr.get_worldconfig(worldspec.path) if modmgr.worldconfig.id == nil or modmgr.worldconfig.id == "" then modmgr.worldconfig = nil return false end modmgr.modlist = filterlist.create( modmgr.preparemodlist, --refresh modmgr.comparemod, --compare function(element,uid) --uid match if element.name == uid then return true end end, function(element,criteria) if criteria.hide_game and element.typ == "game_mod" then return false end if criteria.hide_modpackcontents and element.modpack ~= nil then return false end return true end, --filter { worldpath= worldspec.path, gameid = worldspec.gameid } ) filterlist.set_filtercriteria(modmgr.modlist, { hide_game=modmgr.hide_gamemods, hide_modpackcontents= modmgr.hide_modpackcontents }) filterlist.add_sort_mechanism(modmgr.modlist, "alphabetic", sort_mod_list) filterlist.set_sortmode(modmgr.modlist, "alphabetic") return true end return false end -------------------------------------------------------------------------------- function modmgr.comparemod(elem1,elem2) if elem1 == nil or elem2 == nil then return false end if elem1.name ~= elem2.name then return false end if elem1.is_modpack ~= elem2.is_modpack then return false end if elem1.typ ~= elem2.typ then return false end if elem1.modpack ~= elem2.modpack then return false end if elem1.path ~= elem2.path then return false end return true end -------------------------------------------------------------------------------- function modmgr.gettab(name) local retval = "" if name == "mod_mgr" then retval = retval .. modmgr.tab() end if name == "dialog_rename_modpack" then retval = retval .. modmgr.dialog_rename_modpack() end if name == "dialog_delete_mod" then retval = retval .. modmgr.dialog_delete_mod() end if name == "dialog_configure_world" then retval = retval .. modmgr.dialog_configure_world() end return retval end -------------------------------------------------------------------------------- function modmgr.mod_exists(basename) if modmgr.global_mods == nil then modmgr.refresh_globals() end if filterlist.raw_index_by_uid(modmgr.global_mods,basename) > 0 then return true end return false end -------------------------------------------------------------------------------- function modmgr.get_global_mod(idx) if modmgr.global_mods == nil then return nil end if idx < 1 or idx > filterlist.size(modmgr.global_mods) then return nil end return filterlist.get_list(modmgr.global_mods)[idx] end -------------------------------------------------------------------------------- function modmgr.refresh_globals() modmgr.global_mods = filterlist.create( modmgr.preparemodlist, --refresh modmgr.comparemod, --compare function(element,uid) --uid match if element.name == uid then return true end end, nil, --filter {} ) filterlist.add_sort_mechanism(modmgr.global_mods, "alphabetic", sort_mod_list) filterlist.set_sortmode(modmgr.global_mods, "alphabetic") end -------------------------------------------------------------------------------- function modmgr.identify_filetype(name) if name:sub(-3):lower() == "zip" then return { name = name, type = "zip" } end if name:sub(-6):lower() == "tar.gz" or name:sub(-3):lower() == "tgz"then return { name = name, type = "tgz" } end if name:sub(-6):lower() == "tar.bz2" then return { name = name, type = "tbz" } end if name:sub(-2):lower() == "7z" then return { name = name, type = "7z" } end return { name = name, type = "ukn" } end builtin/misc_helpers.lua0000644000175000017500000002441112261107224016207 0ustar mquinsonmquinson-- Minetest: builtin/misc_helpers.lua -------------------------------------------------------------------------------- function basic_dump2(o) if type(o) == "number" then return tostring(o) elseif type(o) == "string" then return string.format("%q", o) elseif type(o) == "boolean" then return tostring(o) elseif type(o) == "function" then return "" elseif type(o) == "userdata" then return "" elseif type(o) == "nil" then return "nil" else error("cannot dump a " .. type(o)) return nil end end -------------------------------------------------------------------------------- function dump2(o, name, dumped) name = name or "_" dumped = dumped or {} io.write(name, " = ") if type(o) == "number" or type(o) == "string" or type(o) == "boolean" or type(o) == "function" or type(o) == "nil" or type(o) == "userdata" then io.write(basic_dump2(o), "\n") elseif type(o) == "table" then if dumped[o] then io.write(dumped[o], "\n") else dumped[o] = name io.write("{}\n") -- new table for k,v in pairs(o) do local fieldname = string.format("%s[%s]", name, basic_dump2(k)) dump2(v, fieldname, dumped) end end else error("cannot dump a " .. type(o)) return nil end end -------------------------------------------------------------------------------- function dump(o, dumped) dumped = dumped or {} if type(o) == "number" then return tostring(o) elseif type(o) == "string" then return string.format("%q", o) elseif type(o) == "table" then if dumped[o] then return "" end dumped[o] = true local t = {} for k,v in pairs(o) do t[#t+1] = "[" .. dump(k, dumped) .. "] = " .. dump(v, dumped) end return "{" .. table.concat(t, ", ") .. "}" elseif type(o) == "boolean" then return tostring(o) elseif type(o) == "function" then return "" elseif type(o) == "userdata" then return "" elseif type(o) == "nil" then return "nil" else error("cannot dump a " .. type(o)) return nil end end -------------------------------------------------------------------------------- function string:split(sep) local sep, fields = sep or ",", {} local pattern = string.format("([^%s]+)", sep) self:gsub(pattern, function(c) fields[#fields+1] = c end) return fields end -------------------------------------------------------------------------------- function file_exists(filename) local f = io.open(filename, "r") if f==nil then return false else f:close() return true end end -------------------------------------------------------------------------------- function string:trim() return (self:gsub("^%s*(.-)%s*$", "%1")) end assert(string.trim("\n \t\tfoo bar\t ") == "foo bar") -------------------------------------------------------------------------------- function math.hypot(x, y) local t x = math.abs(x) y = math.abs(y) t = math.min(x, y) x = math.max(x, y) if x == 0 then return 0 end t = t / x return x * math.sqrt(1 + t * t) end -------------------------------------------------------------------------------- function explode_textlist_event(text) local retval = {} retval.typ = "INV" local parts = text:split(":") if #parts == 2 then retval.typ = parts[1]:trim() retval.index= tonumber(parts[2]:trim()) if type(retval.index) ~= "number" then retval.typ = "INV" end end return retval end -------------------------------------------------------------------------------- function get_last_folder(text,count) local parts = text:split(DIR_DELIM) if count == nil then return parts[#parts] end local retval = "" for i=1,count,1 do retval = retval .. parts[#parts - (count-i)] .. DIR_DELIM end return retval end -------------------------------------------------------------------------------- function cleanup_path(temppath) local parts = temppath:split("-") temppath = "" for i=1,#parts,1 do if temppath ~= "" then temppath = temppath .. "_" end temppath = temppath .. parts[i] end parts = temppath:split(".") temppath = "" for i=1,#parts,1 do if temppath ~= "" then temppath = temppath .. "_" end temppath = temppath .. parts[i] end parts = temppath:split("'") temppath = "" for i=1,#parts,1 do if temppath ~= "" then temppath = temppath .. "" end temppath = temppath .. parts[i] end parts = temppath:split(" ") temppath = "" for i=1,#parts,1 do if temppath ~= "" then temppath = temppath end temppath = temppath .. parts[i] end return temppath end local tbl = engine or minetest function tbl.formspec_escape(text) if text ~= nil then text = string.gsub(text,"\\","\\\\") text = string.gsub(text,"%]","\\]") text = string.gsub(text,"%[","\\[") text = string.gsub(text,";","\\;") text = string.gsub(text,",","\\,") end return text end function tbl.splittext(text,charlimit) local retval = {} local current_idx = 1 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 stop = nl_stop gotnewline = true end local last_line = "" while start ~= nil do if string.len(last_line) + (stop-start) > charlimit then table.insert(retval,last_line) last_line = "" end if last_line ~= "" then last_line = last_line .. " " end last_line = last_line .. string.sub(text,current_idx,stop -1) if gotnewline then 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) if nl_start ~= nil and (start == nil or nl_start < start) then start = nl_start stop = nl_stop gotnewline = true end end --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)) else last_line = last_line .. " " .. string.sub(text,current_idx) table.insert(retval,last_line) end return retval end -------------------------------------------------------------------------------- if minetest then local dirs1 = { 9, 18, 7, 12 } local dirs2 = { 20, 23, 22, 21 } function minetest.rotate_and_place(itemstack, placer, pointed_thing, infinitestacks, orient_flags) orient_flags = orient_flags or {} local node = minetest.get_node(pointed_thing.under) if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].on_rightclick then local above = pointed_thing.above local under = pointed_thing.under local pitch = placer:get_look_pitch() local pname = minetest.get_node(under).name local node = minetest.get_node(above) local fdir = minetest.dir_to_facedir(placer:get_look_dir()) local wield_name = itemstack:get_name() local reg_node = minetest.registered_nodes[pname] if not reg_node or not reg_node.on_rightclick then local iswall = (above.x ~= under.x) or (above.z ~= under.z) local isceiling = (above.x == under.x) and (above.z == under.z) and (pitch > 0) local pos1 = above if reg_node and reg_node.buildable_to then pos1 = under iswall = false end reg_node = minetest.registered_nodes[minetest.get_node(pos1).name] if not reg_node or not reg_node.buildable_to then return end if orient_flags.force_floor then iswall = false isceiling = false elseif orient_flags.force_ceiling then iswall = false isceiling = true elseif orient_flags.force_wall then iswall = true isceiling = false elseif orient_flags.invert_wall then iswall = not iswall end if iswall then minetest.add_node(pos1, {name = wield_name, param2 = dirs1[fdir+1] }) elseif isceiling then if orient_flags.force_facedir then minetest.add_node(pos1, {name = wield_name, param2 = 20 }) else minetest.add_node(pos1, {name = wield_name, param2 = dirs2[fdir+1] }) end else -- place right side up if orient_flags.force_facedir then minetest.add_node(pos1, {name = wield_name, param2 = 0 }) else minetest.add_node(pos1, {name = wield_name, param2 = fdir }) end end if not infinitestacks then itemstack:take_item() return itemstack end end else minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack) end end -------------------------------------------------------------------------------- --Wrapper for rotate_and_place() to check for sneak and assume Creative mode --implies infinite stacks when performing a 6d rotation. -------------------------------------------------------------------------------- minetest.rotate_node = function(itemstack, placer, pointed_thing) minetest.rotate_and_place(itemstack, placer, pointed_thing, minetest.setting_getbool("creative_mode"), {invert_wall = placer:get_player_control().sneak}) return itemstack end end -------------------------------------------------------------------------------- -- mainmenu only functions -------------------------------------------------------------------------------- if engine ~= nil then engine.get_game = function(index) local games = game.get_games() if index > 0 and index <= #games then return games[index] end return nil end function fgettext(text, ...) text = engine.gettext(text) local arg = {n=select('#', ...), ...} if arg.n >= 1 then -- Insert positional parameters ($1, $2, ...) result = '' pos = 1 while pos <= text:len() do newpos = text:find('[$]', pos) if newpos == nil then result = result .. text:sub(pos) pos = text:len() + 1 else paramindex = tonumber(text:sub(newpos+1, newpos+1)) result = result .. text:sub(pos, newpos-1) .. tostring(arg[paramindex]) pos = newpos + 2 end end text = result end return engine.formspec_escape(text) end end -------------------------------------------------------------------------------- -- core only fct -------------------------------------------------------------------------------- if minetest ~= nil then -------------------------------------------------------------------------------- function minetest.pos_to_string(pos) return "(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")" end end builtin/auth.lua0000644000175000017500000001277212261107224014502 0ustar mquinsonmquinson-- Minetest: builtin/auth.lua -- -- Authentication handler -- function minetest.string_to_privs(str, delim) assert(type(str) == "string") delim = delim or ',' privs = {} for _, priv in pairs(string.split(str, delim)) do privs[priv:trim()] = true end return privs end function minetest.privs_to_string(privs, delim) assert(type(privs) == "table") delim = delim or ',' list = {} for priv, bool in pairs(privs) do if bool then table.insert(list, priv) end end return table.concat(list, delim) end assert(minetest.string_to_privs("a,b").b == true) assert(minetest.privs_to_string({a=true,b=true}) == "a,b") minetest.auth_file_path = minetest.get_worldpath().."/auth.txt" minetest.auth_table = {} local function read_auth_file() local newtable = {} local file, errmsg = io.open(minetest.auth_file_path, 'rb') if not file then minetest.log("info", minetest.auth_file_path.." could not be opened for reading ("..errmsg.."); assuming new world") return end for line in file:lines() do if line ~= "" then local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)") if not name or not password or not privilegestring then error("Invalid line in auth.txt: "..dump(line)) end local privileges = minetest.string_to_privs(privilegestring) newtable[name] = {password=password, privileges=privileges} end end io.close(file) minetest.auth_table = newtable minetest.notify_authentication_modified() end local function save_auth_file() local newtable = {} -- Check table for validness before attempting to save for name, stuff in pairs(minetest.auth_table) do assert(type(name) == "string") assert(name ~= "") assert(type(stuff) == "table") assert(type(stuff.password) == "string") assert(type(stuff.privileges) == "table") end local file, errmsg = io.open(minetest.auth_file_path, 'w+b') if not file then error(minetest.auth_file_path.." could not be opened for writing: "..errmsg) end for name, stuff in pairs(minetest.auth_table) do local privstring = minetest.privs_to_string(stuff.privileges) file:write(name..":"..stuff.password..":"..privstring..'\n') end io.close(file) end read_auth_file() minetest.builtin_auth_handler = { get_auth = function(name) assert(type(name) == "string") -- Figure out what password to use for a new player (singleplayer -- always has an empty password, otherwise use default, which is -- usually empty too) local new_password_hash = "" -- If not in authentication table, return nil if not minetest.auth_table[name] then return nil end -- Figure out what privileges the player should have. -- Take a copy of the privilege table local privileges = {} for priv, _ in pairs(minetest.auth_table[name].privileges) do privileges[priv] = true end -- If singleplayer, give all privileges except those marked as give_to_singleplayer = false if minetest.is_singleplayer() then for priv, def in pairs(minetest.registered_privileges) do if def.give_to_singleplayer then privileges[priv] = true end end -- For the admin, give everything elseif name == minetest.setting_get("name") then for priv, def in pairs(minetest.registered_privileges) do privileges[priv] = true end end -- All done return { password = minetest.auth_table[name].password, privileges = privileges, } end, create_auth = function(name, password) assert(type(name) == "string") assert(type(password) == "string") minetest.log('info', "Built-in authentication handler adding player '"..name.."'") minetest.auth_table[name] = { password = password, privileges = minetest.string_to_privs(minetest.setting_get("default_privs")), } save_auth_file() end, set_password = function(name, password) assert(type(name) == "string") assert(type(password) == "string") if not minetest.auth_table[name] then minetest.builtin_auth_handler.create_auth(name, password) else minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'") minetest.auth_table[name].password = password save_auth_file() end return true end, set_privileges = function(name, privileges) assert(type(name) == "string") assert(type(privileges) == "table") if not minetest.auth_table[name] then minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password"))) end minetest.auth_table[name].privileges = privileges minetest.notify_authentication_modified(name) save_auth_file() end, reload = function() read_auth_file() return true end, } function minetest.register_authentication_handler(handler) if minetest.registered_auth_handler then error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname) end minetest.registered_auth_handler = handler minetest.registered_auth_handler_modname = minetest.get_current_modname() end function minetest.get_auth_handler() if minetest.registered_auth_handler then return minetest.registered_auth_handler end return minetest.builtin_auth_handler end function minetest.set_player_password(name, password) if minetest.get_auth_handler().set_password then minetest.get_auth_handler().set_password(name, password) end end function minetest.set_player_privs(name, privs) if minetest.get_auth_handler().set_privileges then minetest.get_auth_handler().set_privileges(name, privs) end end function minetest.auth_reload() if minetest.get_auth_handler().reload then return minetest.get_auth_handler().reload() end return false end builtin/deprecated.lua0000644000175000017500000000312112261107224015625 0ustar mquinsonmquinson-- Minetest: builtin/deprecated.lua -- -- Default material types -- function digprop_err() minetest.log("info", debug.traceback()) minetest.log("info", "WARNING: The minetest.digprop_* functions are obsolete and need to be replaced by item groups.") end minetest.digprop_constanttime = digprop_err minetest.digprop_stonelike = digprop_err minetest.digprop_dirtlike = digprop_err minetest.digprop_gravellike = digprop_err minetest.digprop_woodlike = digprop_err minetest.digprop_leaveslike = digprop_err minetest.digprop_glasslike = digprop_err minetest.node_metadata_inventory_move_allow_all = function() minetest.log("info", "WARNING: minetest.node_metadata_inventory_move_allow_all is obsolete and does nothing.") end minetest.add_to_creative_inventory = function(itemstring) minetest.log('info', "WARNING: minetest.add_to_creative_inventory: This function is deprecated and does nothing.") end -- -- EnvRef -- minetest.env = {} local envref_deprecation_message_printed = false setmetatable(minetest.env, { __index = function(table, key) if not envref_deprecation_message_printed then minetest.log("info", "WARNING: minetest.env:[...] is deprecated and should be replaced with minetest.[...]") envref_deprecation_message_printed = true end local func = minetest[key] if type(func) == "function" then rawset(table, key, function(self, ...) return func(...) end) else rawset(table, key, nil) end return rawget(table, key) end }) function minetest.rollback_get_last_node_actor(pos, range, seconds) return minetest.rollback_get_node_actions(pos, range, seconds, 1)[1] end builtin/misc.lua0000644000175000017500000000554212261107224014471 0ustar mquinsonmquinson-- Minetest: builtin/misc.lua -- -- Misc. API functions -- minetest.timers_to_add = {} minetest.timers = {} minetest.register_globalstep(function(dtime) for _, timer in ipairs(minetest.timers_to_add) do table.insert(minetest.timers, timer) end minetest.timers_to_add = {} for index, timer in ipairs(minetest.timers) do timer.time = timer.time - dtime if timer.time <= 0 then timer.func(unpack(timer.args or {})) table.remove(minetest.timers,index) end end end) function minetest.after(time, func, ...) table.insert(minetest.timers_to_add, {time=time, func=func, args={...}}) end function minetest.check_player_privs(name, privs) local player_privs = minetest.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 end end if #missing_privileges > 0 then return false, missing_privileges end return true, "" end local player_list = {} minetest.register_on_joinplayer(function(player) player_list[player:get_player_name()] = player end) minetest.register_on_leaveplayer(function(player) player_list[player:get_player_name()] = nil end) function minetest.get_connected_players() local temp_table = {} for index, value in pairs(player_list) do if value:is_player_connected() then table.insert(temp_table, value) end end return temp_table end function minetest.hash_node_position(pos) return (pos.z+32768)*65536*65536 + (pos.y+32768)*65536 + pos.x+32768 end function minetest.get_item_group(name, group) if not minetest.registered_items[name] or not minetest.registered_items[name].groups[group] then return 0 end return minetest.registered_items[name].groups[group] end function minetest.get_node_group(name, group) return minetest.get_item_group(name, group) end function minetest.string_to_pos(value) local p = {} p.x, p.y, p.z = string.match(value, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") if p.x and p.y and p.z then p.x = tonumber(p.x) p.y = tonumber(p.y) p.z = tonumber(p.z) return p end local p = {} p.x, p.y, p.z = string.match(value, "^%( *([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+) *%)$") if p.x and p.y and p.z then p.x = tonumber(p.x) p.y = tonumber(p.y) p.z = tonumber(p.z) return p end return nil end assert(minetest.string_to_pos("10.0, 5, -2").x == 10) assert(minetest.string_to_pos("( 10.0, 5, -2)").z == -2) assert(minetest.string_to_pos("asd, 5, -2)") == nil) function minetest.setting_get_pos(name) local value = minetest.setting_get(name) if not value then return nil end return minetest.string_to_pos(value) end -- To be overriden by protection mods function minetest.is_protected(pos, name) return false end function minetest.record_protection_violation(pos, name) for _, func in pairs(minetest.registered_on_protection_violation) do func(pos, name) end end builtin/item_entity.lua0000644000175000017500000000636612261107224016075 0ustar mquinsonmquinson-- Minetest: builtin/item_entity.lua function minetest.spawn_item(pos, item) -- Take item in any format local stack = ItemStack(item) local obj = minetest.add_entity(pos, "__builtin:item") obj:get_luaentity():set_item(stack:to_string()) return obj end minetest.register_entity("__builtin:item", { initial_properties = { hp_max = 1, physical = true, collide_with_objects = false, collisionbox = {-0.17,-0.17,-0.17, 0.17,0.17,0.17}, visual = "sprite", visual_size = {x=0.5, y=0.5}, textures = {""}, spritediv = {x=1, y=1}, initial_sprite_basepos = {x=0, y=0}, is_visible = false, }, itemstring = '', physical_state = true, set_item = function(self, itemstring) self.itemstring = itemstring local stack = ItemStack(itemstring) local itemtable = stack:to_table() local itemname = nil if itemtable then itemname = stack:to_table().name end local item_texture = nil local item_type = "" if minetest.registered_items[itemname] then item_texture = minetest.registered_items[itemname].inventory_image item_type = minetest.registered_items[itemname].type end prop = { is_visible = true, visual = "sprite", textures = {"unknown_item.png"} } if item_texture and item_texture ~= "" then prop.visual = "sprite" prop.textures = {item_texture} prop.visual_size = {x=0.50, y=0.50} else prop.visual = "wielditem" prop.textures = {itemname} prop.visual_size = {x=0.20, y=0.20} prop.automatic_rotate = math.pi * 0.25 end self.object:set_properties(prop) end, get_staticdata = function(self) --return self.itemstring return minetest.serialize({ itemstring = self.itemstring, always_collect = self.always_collect, }) end, on_activate = function(self, staticdata) if string.sub(staticdata, 1, string.len("return")) == "return" then local data = minetest.deserialize(staticdata) if data and type(data) == "table" then self.itemstring = data.itemstring self.always_collect = data.always_collect end else self.itemstring = staticdata end self.object:set_armor_groups({immortal=1}) self.object:setvelocity({x=0, y=2, z=0}) self.object:setacceleration({x=0, y=-10, z=0}) self:set_item(self.itemstring) end, on_step = function(self, dtime) local p = self.object:getpos() p.y = p.y - 0.3 local nn = minetest.get_node(p).name -- If node is not registered or node is walkably solid and resting on nodebox local v = self.object:getvelocity() if not minetest.registered_nodes[nn] or minetest.registered_nodes[nn].walkable and v.y == 0 then if self.physical_state then 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 }) end else if not self.physical_state then self.object:setvelocity({x=0,y=0,z=0}) self.object:setacceleration({x=0, y=-10, z=0}) self.physical_state = true self.object:set_properties({ physical = true }) end end end, on_punch = function(self, hitter) if self.itemstring ~= '' then local left = hitter:get_inventory():add_item("main", self.itemstring) if not left:is_empty() then self.itemstring = left:to_string() return end end self.object:remove() end, }) builtin/mainmenu.lua0000644000175000017500000012007112261107224015342 0ustar mquinsonmquinsonprint = engine.debug math.randomseed(os.time()) os.setlocale("C", "numeric") local errorfct = error error = function(text) print(debug.traceback("")) errorfct(text) end local scriptpath = engine.get_scriptdir() mt_color_grey = "#AAAAAA" mt_color_blue = "#0000DD" mt_color_green = "#00DD00" mt_color_dark_green = "#003300" --for all other colors ask sfan5 to complete his worK! dofile(scriptpath .. DIR_DELIM .. "misc_helpers.lua") dofile(scriptpath .. DIR_DELIM .. "filterlist.lua") dofile(scriptpath .. DIR_DELIM .. "modmgr.lua") dofile(scriptpath .. DIR_DELIM .. "modstore.lua") dofile(scriptpath .. DIR_DELIM .. "gamemgr.lua") dofile(scriptpath .. DIR_DELIM .. "mm_textures.lua") dofile(scriptpath .. DIR_DELIM .. "mm_menubar.lua") dofile(scriptpath .. DIR_DELIM .. "async_event.lua") menu = {} local tabbuilder = {} local worldlist = nil -------------------------------------------------------------------------------- local function filter_texture_pack_list(list) retval = {"None"} for _,i in ipairs(list) do if i~="base" then table.insert(retval, i) end end return retval end -------------------------------------------------------------------------------- function menu.render_favorite(spec,render_details) local text = "" if spec.name ~= nil then text = text .. engine.formspec_escape(spec.name:trim()) -- if spec.description ~= nil and -- engine.formspec_escape(spec.description):trim() ~= "" then -- text = text .. " (" .. engine.formspec_escape(spec.description) .. ")" -- end else if spec.address ~= nil then text = text .. spec.address:trim() if spec.port ~= nil then text = text .. ":" .. spec.port end end end if not render_details then return text end local details = "" if spec.password == true then details = details .. "*" else details = details .. "_" end if spec.creative then details = details .. "C" else details = details .. "_" end if spec.damage then details = details .. "D" else details = details .. "_" end if spec.pvp then details = details .. "P" else details = details .. "_" end details = details .. " " local playercount = "" if spec.clients ~= nil and spec.clients_max ~= nil then playercount = string.format("%03d",spec.clients) .. "/" .. string.format("%03d",spec.clients_max) .. " " end return playercount .. engine.formspec_escape(details) .. text end -------------------------------------------------------------------------------- os.tempfolder = function() local filetocheck = os.tmpname() os.remove(filetocheck) local randname = "MTTempModFolder_" .. math.random(0,10000) if DIR_DELIM == "\\" then local tempfolder = os.getenv("TEMP") return tempfolder .. filetocheck else local backstring = filetocheck:reverse() return filetocheck:sub(0,filetocheck:len()-backstring:find(DIR_DELIM)+1) ..randname end end -------------------------------------------------------------------------------- function text2textlist(xpos,ypos,width,height,tl_name,textlen,text,transparency) local textlines = engine.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 .. engine.formspec_escape(textlines[i]) .. "," end retval = retval .. ";0;" if transparency then retval = retval .. "true" end retval = retval .. "]" return retval end -------------------------------------------------------------------------------- function init_globals() --init gamedata gamedata.worldindex = 0 worldlist = filterlist.create( engine.get_worlds, compare_worlds, function(element,uid) if element.name == uid then return true end return false end, --unique id compare fct function(element,gameid) if element.gameid == gameid then return true end return false end --filter fct ) filterlist.add_sort_mechanism(worldlist,"alphabetic",sort_worlds_alphabetic) filterlist.set_sortmode(worldlist,"alphabetic") end -------------------------------------------------------------------------------- function update_menu() local formspec -- handle errors if gamedata.errormessage ~= nil then formspec = "size[12,5.2]" .. "field[1,2;10,2;;ERROR: " .. gamedata.errormessage .. ";]".. "button[4.5,4.2;3,0.5;btn_error_confirm;" .. fgettext("Ok") .. "]" else formspec = tabbuilder.gettab() end engine.update_formspec(formspec) end -------------------------------------------------------------------------------- function menu.render_world_list() local retval = "" local current_worldlist = filterlist.get_list(worldlist) for i,v in ipairs(current_worldlist) do if retval ~= "" then retval = retval .."," end retval = retval .. engine.formspec_escape(v.name) .. " \\[" .. engine.formspec_escape(v.gameid) .. "\\]" end return retval end -------------------------------------------------------------------------------- function menu.render_texture_pack_list(list) local retval = "" for i,v in ipairs(list) do if retval ~= "" then retval = retval .."," end retval = retval .. v end return retval end -------------------------------------------------------------------------------- function menu.asyncOnlineFavourites() menu.favorites = {} engine.handle_async( function(param) return engine.get_favorites("online") end, nil, function(result) menu.favorites = result engine.event_handler("Refresh") end ) end -------------------------------------------------------------------------------- function menu.init() --init menu data gamemgr.update_gamelist() menu.last_game = tonumber(engine.setting_get("main_menu_last_game_idx")) if type(menu.last_game) ~= "number" then menu.last_game = 1 end if engine.setting_getbool("public_serverlist") then menu.asyncOnlineFavourites() else menu.favorites = engine.get_favorites("local") end menu.defaulttexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM end -------------------------------------------------------------------------------- function menu.lastgame() if menu.last_game > 0 and menu.last_game <= #gamemgr.games then return gamemgr.games[menu.last_game] end if #gamemgr.games >= 1 then menu.last_game = 1 return gamemgr.games[menu.last_game] end --error case!! return nil end -------------------------------------------------------------------------------- function menu.update_last_game() local current_world = filterlist.get_raw_element(worldlist, engine.setting_get("mainmenu_last_selected_world") ) if current_world == nil then return end local gamespec, i = gamemgr.find_by_gameid(current_world.gameid) if i ~= nil then menu.last_game = i engine.setting_set("main_menu_last_game_idx",menu.last_game) end end -------------------------------------------------------------------------------- function menu.handle_key_up_down(fields,textlist,settingname) if fields["key_up"] then local oldidx = engine.get_textlist_index(textlist) if oldidx > 1 then local newidx = oldidx -1 engine.setting_set(settingname, filterlist.get_raw_index(worldlist,newidx)) end end if fields["key_down"] then local oldidx = engine.get_textlist_index(textlist) if oldidx < filterlist.size(worldlist) then local newidx = oldidx + 1 engine.setting_set(settingname, filterlist.get_raw_index(worldlist,newidx)) end end end -------------------------------------------------------------------------------- function tabbuilder.dialog_create_world() local mapgens = {"v6", "v7", "indev", "singlenode", "math"} local current_seed = engine.setting_get("fixed_map_seed") or "" local current_mg = engine.setting_get("mg_name") local mglist = "" local selindex = 1 local i = 1 for k,v in pairs(mapgens) do if current_mg == v then selindex = i end i = i + 1 mglist = mglist .. v .. "," end mglist = mglist:sub(1, -2) local retval = "label[2,0;" .. fgettext("World name") .. "]".. "field[4.5,0.4;6,0.5;te_world_name;;]" .. "label[2,1;" .. fgettext("Seed") .. "]".. "field[4.5,1.4;6,0.5;te_seed;;".. current_seed .. "]" .. "label[2,2;" .. fgettext("Mapgen") .. "]".. "dropdown[4.2,2;6.3;dd_mapgen;" .. mglist .. ";" .. selindex .. "]" .. "label[2,3;" .. fgettext("Game") .. "]".. "textlist[4.2,3;5.8,2.3;games;" .. gamemgr.gamelist() .. ";" .. menu.last_game .. ";true]" .. "button[5,5.5;2.6,0.5;world_create_confirm;" .. fgettext("Create") .. "]" .. "button[7.5,5.5;2.8,0.5;world_create_cancel;" .. fgettext("Cancel") .. "]" return retval end -------------------------------------------------------------------------------- function tabbuilder.dialog_delete_world() return "label[2,2;" .. fgettext("Delete World \"$1\"?", filterlist.get_raw_list(worldlist)[menu.world_to_del].name) .. "]".. "button[3.5,4.2;2.6,0.5;world_delete_confirm;" .. fgettext("Yes").. "]" .. "button[6,4.2;2.8,0.5;world_delete_cancel;" .. fgettext("No") .. "]" end -------------------------------------------------------------------------------- function tabbuilder.gettab() local tsize = tabbuilder.tabsizes[tabbuilder.current_tab] or {width=12, height=5.2} local retval = "size[" .. tsize.width .. "," .. tsize.height .. "]" if tabbuilder.show_buttons then retval = retval .. tabbuilder.tab_header() end local buildfunc = tabbuilder.tabfuncs[tabbuilder.current_tab] if buildfunc ~= nil then retval = retval .. buildfunc() end retval = retval .. modmgr.gettab(tabbuilder.current_tab) retval = retval .. gamemgr.gettab(tabbuilder.current_tab) retval = retval .. modstore.gettab(tabbuilder.current_tab) return retval end -------------------------------------------------------------------------------- function tabbuilder.handle_create_world_buttons(fields) if fields["world_create_confirm"] or fields["key_enter"] then local worldname = fields["te_world_name"] local gameindex = engine.get_textlist_index("games") if gameindex > 0 and worldname ~= "" then local message = nil if not filterlist.uid_exists_raw(worldlist,worldname) then engine.setting_set("mg_name",fields["dd_mapgen"]) message = engine.create_world(worldname,gameindex) else message = fgettext("A world named \"$1\" already exists", worldname) end engine.setting_set("fixed_map_seed", fields["te_seed"]) if message ~= nil then gamedata.errormessage = message else menu.last_game = gameindex engine.setting_set("main_menu_last_game_idx",gameindex) filterlist.refresh(worldlist) engine.setting_set("mainmenu_last_selected_world", filterlist.raw_index_by_uid(worldlist,worldname)) end else gamedata.errormessage = fgettext("No worldname given or no game selected") end end if fields["games"] then tabbuilder.skipformupdate = true return end --close dialog tabbuilder.is_dialog = false tabbuilder.show_buttons = true tabbuilder.current_tab = engine.setting_get("main_menu_tab") end -------------------------------------------------------------------------------- function tabbuilder.handle_delete_world_buttons(fields) if fields["world_delete_confirm"] then if menu.world_to_del > 0 and menu.world_to_del <= #filterlist.get_raw_list(worldlist) then engine.delete_world(menu.world_to_del) menu.world_to_del = 0 filterlist.refresh(worldlist) end end tabbuilder.is_dialog = false tabbuilder.show_buttons = true tabbuilder.current_tab = engine.setting_get("main_menu_tab") end -------------------------------------------------------------------------------- function tabbuilder.handle_multiplayer_buttons(fields) if fields["te_name"] ~= nil then gamedata.playername = fields["te_name"] engine.setting_set("name", fields["te_name"]) end if fields["favourites"] ~= nil then local event = explode_textlist_event(fields["favourites"]) if event.typ == "DCL" then if event.index <= #menu.favorites then gamedata.address = menu.favorites[event.index].address gamedata.port = menu.favorites[event.index].port gamedata.playername = fields["te_name"] if fields["te_pwd"] ~= nil then gamedata.password = fields["te_pwd"] end gamedata.selected_world = 0 if menu.favorites ~= nil then gamedata.servername = menu.favorites[event.index].name gamedata.serverdescription = menu.favorites[event.index].description end if gamedata.address ~= nil and gamedata.port ~= nil then engine.setting_set("address",gamedata.address) engine.setting_set("remote_port",gamedata.port) engine.start() end end end if event.typ == "CHG" then if event.index <= #menu.favorites then local address = menu.favorites[event.index].address local port = menu.favorites[event.index].port if address ~= nil and port ~= nil then engine.setting_set("address",address) engine.setting_set("remote_port",port) end menu.fav_selected = event.index end end return end if fields["key_up"] ~= nil or fields["key_down"] ~= nil then local fav_idx = engine.get_textlist_index("favourites") if fields["key_up"] ~= nil and fav_idx > 1 then fav_idx = fav_idx -1 else if fields["key_down"] and fav_idx < #menu.favorites then fav_idx = fav_idx +1 end end local address = menu.favorites[fav_idx].address local port = menu.favorites[fav_idx].port if address ~= nil and port ~= nil then engine.setting_set("address",address) engine.setting_set("remote_port",port) end menu.fav_selected = fav_idx return end if fields["cb_public_serverlist"] ~= nil then engine.setting_set("public_serverlist", fields["cb_public_serverlist"]) if engine.setting_getbool("public_serverlist") then menu.asyncOnlineFavourites() else menu.favorites = engine.get_favorites("local") end menu.fav_selected = nil return end if fields["btn_delete_favorite"] ~= nil then local current_favourite = engine.get_textlist_index("favourites") engine.delete_favorite(current_favourite) menu.favorites = engine.get_favorites() menu.fav_selected = nil engine.setting_set("address","") engine.setting_set("remote_port","30000") return end if fields["btn_mp_connect"] ~= nil or fields["key_enter"] ~= nil then gamedata.playername = fields["te_name"] gamedata.password = fields["te_pwd"] gamedata.address = fields["te_address"] gamedata.port = fields["te_port"] local fav_idx = engine.get_textlist_index("favourites") if fav_idx > 0 and fav_idx <= #menu.favorites and menu.favorites[fav_idx].address == fields["te_address"] and menu.favorites[fav_idx].port == fields["te_port"] then gamedata.servername = menu.favorites[fav_idx].name gamedata.serverdescription = menu.favorites[fav_idx].description else gamedata.servername = "" gamedata.serverdescription = "" end gamedata.selected_world = 0 engine.setting_set("address",fields["te_address"]) engine.setting_set("remote_port",fields["te_port"]) engine.start() return end end -------------------------------------------------------------------------------- function tabbuilder.handle_server_buttons(fields) local world_doubleclick = false if fields["srv_worlds"] ~= nil then local event = explode_textlist_event(fields["srv_worlds"]) if event.typ == "DCL" then world_doubleclick = true end if event.typ == "CHG" then engine.setting_set("mainmenu_last_selected_world", filterlist.get_raw_index(worldlist,engine.get_textlist_index("srv_worlds"))) end end menu.handle_key_up_down(fields,"srv_worlds","mainmenu_last_selected_world") if fields["cb_creative_mode"] then engine.setting_set("creative_mode", fields["cb_creative_mode"]) end if fields["cb_enable_damage"] then engine.setting_set("enable_damage", fields["cb_enable_damage"]) end if fields["cb_server_announce"] then engine.setting_set("server_announce", fields["cb_server_announce"]) end if fields["start_server"] ~= nil or world_doubleclick or fields["key_enter"] then local selected = engine.get_textlist_index("srv_worlds") if selected > 0 then gamedata.playername = fields["te_playername"] gamedata.password = fields["te_passwd"] gamedata.port = fields["te_serverport"] gamedata.address = "" gamedata.selected_world = filterlist.get_raw_index(worldlist,selected) engine.setting_set("port",gamedata.port) menu.update_last_game(gamedata.selected_world) engine.start() end end if fields["world_create"] ~= nil then tabbuilder.current_tab = "dialog_create_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false end if fields["world_delete"] ~= nil then local selected = engine.get_textlist_index("srv_worlds") if selected > 0 and selected <= filterlist.size(worldlist) then local world = filterlist.get_list(worldlist)[selected] if world ~= nil and world.name ~= nil and world.name ~= "" then menu.world_to_del = filterlist.get_raw_index(worldlist,selected) tabbuilder.current_tab = "dialog_delete_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false else menu.world_to_del = 0 end end end if fields["world_configure"] ~= nil then selected = engine.get_textlist_index("srv_worlds") if selected > 0 then modmgr.world_config_selected_world = filterlist.get_raw_index(worldlist,selected) if modmgr.init_worldconfig() then tabbuilder.current_tab = "dialog_configure_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false end end end end -------------------------------------------------------------------------------- function tabbuilder.handle_settings_buttons(fields) if fields["cb_fancy_trees"] then engine.setting_set("new_style_leaves", fields["cb_fancy_trees"]) end if fields["cb_smooth_lighting"] then engine.setting_set("smooth_lighting", fields["cb_smooth_lighting"]) end if fields["cb_3d_clouds"] then engine.setting_set("enable_3d_clouds", fields["cb_3d_clouds"]) end if fields["cb_opaque_water"] then engine.setting_set("opaque_water", fields["cb_opaque_water"]) end if fields["cb_mipmapping"] then engine.setting_set("mip_map", fields["cb_mipmapping"]) end if fields["cb_anisotrophic"] then engine.setting_set("anisotropic_filter", fields["cb_anisotrophic"]) end if fields["cb_bilinear"] then engine.setting_set("bilinear_filter", fields["cb_bilinear"]) end if fields["cb_trilinear"] then engine.setting_set("trilinear_filter", fields["cb_trilinear"]) end if fields["cb_shaders"] then if (engine.setting_get("video_driver") == "direct3d8" or engine.setting_get("video_driver") == "direct3d9") then engine.setting_set("enable_shaders", "false") gamedata.errormessage = fgettext("To enable shaders the OpenGL driver needs to be used.") else engine.setting_set("enable_shaders", fields["cb_shaders"]) end end if fields["cb_pre_ivis"] then engine.setting_set("preload_item_visuals", fields["cb_pre_ivis"]) end if fields["cb_particles"] then engine.setting_set("enable_particles", fields["cb_particles"]) end if fields["cb_finite_liquid"] then engine.setting_set("liquid_finite", fields["cb_finite_liquid"]) end if fields["cb_bumpmapping"] then engine.setting_set("enable_bumpmapping", fields["cb_bumpmapping"]) end if fields["cb_parallax"] then engine.setting_set("enable_parallax_occlusion", fields["cb_parallax"]) end if fields["cb_waving_water"] then engine.setting_set("enable_waving_water", fields["cb_waving_water"]) end if fields["cb_waving_leaves"] then engine.setting_set("enable_waving_leaves", fields["cb_waving_leaves"]) end if fields["cb_waving_plants"] then engine.setting_set("enable_waving_plants", fields["cb_waving_plants"]) end if fields["btn_change_keys"] ~= nil then engine.show_keys_menu() end end -------------------------------------------------------------------------------- function tabbuilder.handle_singleplayer_buttons(fields) local world_doubleclick = false if fields["sp_worlds"] ~= nil then local event = explode_textlist_event(fields["sp_worlds"]) if event.typ == "DCL" then world_doubleclick = true end if event.typ == "CHG" then engine.setting_set("mainmenu_last_selected_world", filterlist.get_raw_index(worldlist,engine.get_textlist_index("sp_worlds"))) end end menu.handle_key_up_down(fields,"sp_worlds","mainmenu_last_selected_world") if fields["cb_creative_mode"] then engine.setting_set("creative_mode", fields["cb_creative_mode"]) end if fields["cb_enable_damage"] then engine.setting_set("enable_damage", fields["cb_enable_damage"]) end if fields["play"] ~= nil or world_doubleclick or fields["key_enter"] then local selected = engine.get_textlist_index("sp_worlds") if selected > 0 then gamedata.selected_world = filterlist.get_raw_index(worldlist,selected) gamedata.singleplayer = true menu.update_last_game(gamedata.selected_world) engine.start() end end if fields["world_create"] ~= nil then tabbuilder.current_tab = "dialog_create_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false end if fields["world_delete"] ~= nil then local selected = engine.get_textlist_index("sp_worlds") if selected > 0 and selected <= filterlist.size(worldlist) then local world = filterlist.get_list(worldlist)[selected] if world ~= nil and world.name ~= nil and world.name ~= "" then menu.world_to_del = filterlist.get_raw_index(worldlist,selected) tabbuilder.current_tab = "dialog_delete_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false else menu.world_to_del = 0 end end end if fields["world_configure"] ~= nil then selected = engine.get_textlist_index("sp_worlds") if selected > 0 then modmgr.world_config_selected_world = filterlist.get_raw_index(worldlist,selected) if modmgr.init_worldconfig() then tabbuilder.current_tab = "dialog_configure_world" tabbuilder.is_dialog = true tabbuilder.show_buttons = false end end end end -------------------------------------------------------------------------------- function tabbuilder.handle_texture_pack_buttons(fields) if fields["TPs"] ~= nil then local event = explode_textlist_event(fields["TPs"]) if event.typ == "CHG" or event.typ=="DCL" then local index = engine.get_textlist_index("TPs") engine.setting_set("mainmenu_last_selected_TP", index) local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true)) local current_index = engine.get_textlist_index("TPs") if #list >= current_index then local new_path = engine.get_texturepath()..DIR_DELIM..list[current_index] if list[current_index] == "None" then new_path = "" end engine.setting_set("texture_path", new_path) end end end end -------------------------------------------------------------------------------- function tabbuilder.tab_header() if tabbuilder.last_tab_index == nil then tabbuilder.last_tab_index = 1 end local toadd = "" for i=1,#tabbuilder.current_buttons,1 do if toadd ~= "" then toadd = toadd .. "," end toadd = toadd .. tabbuilder.current_buttons[i].caption end return "tabheader[-0.3,-0.99;main_tab;" .. toadd ..";" .. tabbuilder.last_tab_index .. ";true;false]" end -------------------------------------------------------------------------------- function tabbuilder.handle_tab_buttons(fields) if fields["main_tab"] then local index = tonumber(fields["main_tab"]) tabbuilder.last_tab_index = index tabbuilder.current_tab = tabbuilder.current_buttons[index].name engine.setting_set("main_menu_tab",tabbuilder.current_tab) end --handle tab changes if tabbuilder.current_tab ~= tabbuilder.old_tab then if tabbuilder.current_tab ~= "singleplayer" and not tabbuilder.is_dialog then menu.update_gametype(true) end end if tabbuilder.current_tab == "singleplayer" then menu.update_gametype() end tabbuilder.old_tab = tabbuilder.current_tab end -------------------------------------------------------------------------------- function tabbuilder.tab_multiplayer() local retval = "vertlabel[0,-0.25;".. fgettext("CLIENT") .. "]" .. "label[1,-0.25;".. fgettext("Favorites:") .. "]".. "label[1,4.25;".. fgettext("Address/Port") .. "]".. "label[9,2.75;".. fgettext("Name/Password") .. "]" .. "field[1.25,5.25;5.5,0.5;te_address;;" ..engine.setting_get("address") .."]" .. "field[6.75,5.25;2.25,0.5;te_port;;" ..engine.setting_get("remote_port") .."]" .. "checkbox[1,3.6;cb_public_serverlist;".. fgettext("Public Serverlist") .. ";" .. dump(engine.setting_getbool("public_serverlist")) .. "]" if not engine.setting_getbool("public_serverlist") then retval = retval .. "button[6.45,3.95;2.25,0.5;btn_delete_favorite;".. fgettext("Delete") .. "]" end retval = retval .. "button[9,4.95;2.5,0.5;btn_mp_connect;".. fgettext("Connect") .. "]" .. "field[9.3,3.75;2.5,0.5;te_name;;" ..engine.setting_get("name") .."]" .. "pwdfield[9.3,4.5;2.5,0.5;te_pwd;]" .. "textarea[9.3,0.25;2.5,2.75;;" if menu.fav_selected ~= nil and menu.favorites[menu.fav_selected].description ~= nil then retval = retval .. engine.formspec_escape(menu.favorites[menu.fav_selected].description,true) end retval = retval .. ";]" .. "textlist[1,0.35;7.5,3.35;favourites;" local render_details = engine.setting_getbool("public_serverlist") if #menu.favorites > 0 then retval = retval .. menu.render_favorite(menu.favorites[1],render_details) for i=2,#menu.favorites,1 do retval = retval .. "," .. menu.render_favorite(menu.favorites[i],render_details) end end if menu.fav_selected ~= nil then retval = retval .. ";" .. menu.fav_selected .. "]" else retval = retval .. ";0]" end return retval end -------------------------------------------------------------------------------- function tabbuilder.tab_server() local index = filterlist.get_current_index(worldlist, tonumber(engine.setting_get("mainmenu_last_selected_world")) ) local retval = "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" .. "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" .. "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" .. "button[8.5,4.9;3.25,0.5;start_server;".. fgettext("Start Game") .. "]" .. "label[4,-0.25;".. fgettext("Select World:") .. "]".. "vertlabel[0,-0.25;".. fgettext("START SERVER") .. "]" .. "checkbox[0.5,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. dump(engine.setting_getbool("creative_mode")) .. "]".. "checkbox[0.5,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. dump(engine.setting_getbool("enable_damage")) .. "]".. "checkbox[0.5,1.15;cb_server_announce;".. fgettext("Public") .. ";" .. dump(engine.setting_getbool("server_announce")) .. "]".. "field[0.8,3.2;3,0.5;te_playername;".. fgettext("Name") .. ";" .. engine.setting_get("name") .. "]" .. "pwdfield[0.8,4.2;3,0.5;te_passwd;".. fgettext("Password") .. "]" .. "field[0.8,5.2;3,0.5;te_serverport;".. fgettext("Server Port") .. ";" .. engine.setting_get("port") .."]" .. "textlist[4,0.25;7.5,3.7;srv_worlds;" .. menu.render_world_list() .. ";" .. index .. "]" return retval end -------------------------------------------------------------------------------- function tabbuilder.tab_settings() tab_string = "vertlabel[0,0;" .. fgettext("SETTINGS") .. "]" .. "checkbox[1,0;cb_fancy_trees;".. fgettext("Fancy Trees") .. ";" .. dump(engine.setting_getbool("new_style_leaves")) .. "]".. "checkbox[1,0.5;cb_smooth_lighting;".. fgettext("Smooth Lighting") .. ";".. dump(engine.setting_getbool("smooth_lighting")) .. "]".. "checkbox[1,1;cb_3d_clouds;".. fgettext("3D Clouds") .. ";" .. dump(engine.setting_getbool("enable_3d_clouds")) .. "]".. "checkbox[1,1.5;cb_opaque_water;".. fgettext("Opaque Water") .. ";" .. dump(engine.setting_getbool("opaque_water")) .. "]".. "checkbox[1,2.0;cb_pre_ivis;".. fgettext("Preload item visuals") .. ";" .. dump(engine.setting_getbool("preload_item_visuals")) .. "]".. "checkbox[1,2.5;cb_particles;".. fgettext("Enable Particles") .. ";" .. dump(engine.setting_getbool("enable_particles")) .. "]".. "checkbox[1,3.0;cb_finite_liquid;".. fgettext("Finite Liquid") .. ";" .. dump(engine.setting_getbool("liquid_finite")) .. "]".. "checkbox[4.5,0;cb_mipmapping;".. fgettext("Mip-Mapping") .. ";" .. dump(engine.setting_getbool("mip_map")) .. "]".. "checkbox[4.5,0.5;cb_anisotrophic;".. fgettext("Anisotropic Filtering") .. ";" .. dump(engine.setting_getbool("anisotropic_filter")) .. "]".. "checkbox[4.5,1.0;cb_bilinear;".. fgettext("Bi-Linear Filtering") .. ";" .. dump(engine.setting_getbool("bilinear_filter")) .. "]".. "checkbox[4.5,1.5;cb_trilinear;".. fgettext("Tri-Linear Filtering") .. ";" .. dump(engine.setting_getbool("trilinear_filter")) .. "]".. "checkbox[8,0;cb_shaders;".. fgettext("Shaders") .. ";" .. dump(engine.setting_getbool("enable_shaders")) .. "]".. "button[1,4.5;2.25,0.5;btn_change_keys;".. fgettext("Change keys") .. "]" if engine.setting_getbool("enable_shaders") then tab_string = tab_string .. "checkbox[8,0.5;cb_bumpmapping;".. fgettext("Bumpmapping") .. ";" .. dump(engine.setting_getbool("enable_bumpmapping")) .. "]".. "checkbox[8,1.0;cb_parallax;".. fgettext("Parallax Occlusion") .. ";" .. dump(engine.setting_getbool("enable_parallax_occlusion")) .. "]".. "checkbox[8,1.5;cb_waving_water;".. fgettext("Waving Water") .. ";" .. dump(engine.setting_getbool("enable_waving_water")) .. "]".. "checkbox[8,2.0;cb_waving_leaves;".. fgettext("Waving Leaves") .. ";" .. dump(engine.setting_getbool("enable_waving_leaves")) .. "]".. "checkbox[8,2.5;cb_waving_plants;".. fgettext("Waving Plants") .. ";" .. dump(engine.setting_getbool("enable_waving_plants")) .. "]" else tab_string = tab_string .. "textlist[8.33,0.7;4,1;;#888888" .. fgettext("Bumpmapping") .. ";0;true]" .. "textlist[8.33,1.2;4,1;;#888888" .. fgettext("Parallax Occlusion") .. ";0;true]" .. "textlist[8.33,1.7;4,1;;#888888" .. fgettext("Waving Water") .. ";0;true]" .. "textlist[8.33,2.2;4,1;;#888888" .. fgettext("Waving Leaves") .. ";0;true]" .. "textlist[8.33,2.7;4,1;;#888888" .. fgettext("Waving Plants") .. ";0;true]" end return tab_string end -------------------------------------------------------------------------------- function tabbuilder.tab_singleplayer() local index = filterlist.get_current_index(worldlist, tonumber(engine.setting_get("mainmenu_last_selected_world")) ) return "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" .. "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" .. "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" .. "button[8.5,4.95;3.25,0.5;play;".. fgettext("Play") .. "]" .. "label[4,-0.25;".. fgettext("Select World:") .. "]".. "vertlabel[0,-0.25;".. fgettext("SINGLE PLAYER") .. "]" .. "checkbox[0.5,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. dump(engine.setting_getbool("creative_mode")) .. "]".. "checkbox[0.5,0.7;cb_enable_damage;".. fgettext("Enable Damage") .. ";" .. dump(engine.setting_getbool("enable_damage")) .. "]".. "textlist[4,0.25;7.5,3.7;sp_worlds;" .. menu.render_world_list() .. ";" .. index .. "]" .. menubar.formspec end -------------------------------------------------------------------------------- function tabbuilder.tab_texture_packs() local retval = "label[4,-0.25;".. fgettext("Select texture pack:") .. "]".. "vertlabel[0,-0.25;".. fgettext("TEXTURE PACKS") .. "]" .. "textlist[4,0.25;7.5,5.0;TPs;" local current_texture_path = engine.setting_get("texture_path") local list = filter_texture_pack_list(engine.get_dirlist(engine.get_texturepath(), true)) local index = tonumber(engine.setting_get("mainmenu_last_selected_TP")) if index == nil then index = 1 end if current_texture_path == "" then retval = retval .. menu.render_texture_pack_list(list) .. ";" .. index .. "]" return retval end local infofile = current_texture_path ..DIR_DELIM.."info.txt" local infotext = "" local f = io.open(infofile, "r") if f==nil then infotext = fgettext("No information available") else infotext = f:read("*all") f:close() end local screenfile = current_texture_path..DIR_DELIM.."screenshot.png" local no_screenshot = nil if not file_exists(screenfile) then screenfile = nil no_screenshot = engine.get_texturepath()..DIR_DELIM.. "base"..DIR_DELIM.."pack"..DIR_DELIM.."no_screenshot.png" end return retval .. menu.render_texture_pack_list(list) .. ";" .. index .. "]" .. "image[0.65,0.25;4.0,3.7;"..engine.formspec_escape(screenfile or no_screenshot).."]".. "textarea[1.0,3.25;3.7,1.5;;"..engine.formspec_escape(infotext or "")..";]" end -------------------------------------------------------------------------------- function tabbuilder.tab_credits() local logofile = menu.defaulttexturedir .. "logo.png" return "vertlabel[0,-0.5;CREDITS]" .. "label[0.5,3;Minetest " .. engine.get_version() .. "]" .. "label[0.5,3.3;http://minetest.net]" .. "image[0.5,1;" .. engine.formspec_escape(logofile) .. "]" .. "textlist[3.5,-0.25;8.5,5.8;list_credits;" .. "#FFFF00" .. fgettext("Core Developers") .."," .. "Perttu Ahola (celeron55) ,".. "Ryan Kwolek (kwolekr) ,".. "PilzAdam ," .. "Ilya Zhuravlev (xyz) ,".. "Lisa Milne (darkrose) ,".. "Maciej Kasatkin (RealBadAngel) ,".. "proller ,".. "sfan5 ,".. "kahrl ,".. "sapier,".. "ShadowNinja ,".. "Nathanael Courant (Nore/Novatux) ,".. "BlockMen,".. ",".. "#FFFF00" .. fgettext("Active Contributors") .. "," .. "Vanessa Ezekowitz (VanessaE) ,".. "Jurgen Doser (doserj) ,".. "Jeija ,".. "MirceaKitsune ,".. "dannydark ,".. "0gb.us <0gb.us@0gb.us>,".. "," .. "#FFFF00" .. fgettext("Previous Contributors") .. "," .. "Guiseppe Bilotta (Oblomov) ,".. "Jonathan Neuschafer ,".. "Nils Dagsson Moskopp (erlehmann) ,".. "Constantin Wenger (SpeedProg) ,".. "matttpt ,".. "JacobF ,".. ";0;true]" end -------------------------------------------------------------------------------- function tabbuilder.init() tabbuilder.tabfuncs = { singleplayer = tabbuilder.tab_singleplayer, multiplayer = tabbuilder.tab_multiplayer, server = tabbuilder.tab_server, settings = tabbuilder.tab_settings, texture_packs = tabbuilder.tab_texture_packs, credits = tabbuilder.tab_credits, dialog_create_world = tabbuilder.dialog_create_world, dialog_delete_world = tabbuilder.dialog_delete_world } tabbuilder.tabsizes = { dialog_create_world = {width=12, height=7}, dialog_delete_world = {width=12, height=5.2} } tabbuilder.current_tab = engine.setting_get("main_menu_tab") if tabbuilder.current_tab == nil or tabbuilder.current_tab == "" then tabbuilder.current_tab = "singleplayer" engine.setting_set("main_menu_tab",tabbuilder.current_tab) end --initialize tab buttons tabbuilder.last_tab = nil tabbuilder.show_buttons = true tabbuilder.current_buttons = {} table.insert(tabbuilder.current_buttons,{name="singleplayer", caption=fgettext("Singleplayer")}) table.insert(tabbuilder.current_buttons,{name="multiplayer", caption=fgettext("Client")}) table.insert(tabbuilder.current_buttons,{name="server", caption=fgettext("Server")}) table.insert(tabbuilder.current_buttons,{name="settings", caption=fgettext("Settings")}) table.insert(tabbuilder.current_buttons,{name="texture_packs", caption=fgettext("Texture Packs")}) if engine.setting_getbool("main_menu_game_mgr") then table.insert(tabbuilder.current_buttons,{name="game_mgr", caption=fgettext("Games")}) end if engine.setting_getbool("main_menu_mod_mgr") then table.insert(tabbuilder.current_buttons,{name="mod_mgr", caption=fgettext("Mods")}) end table.insert(tabbuilder.current_buttons,{name="credits", caption=fgettext("Credits")}) for i=1,#tabbuilder.current_buttons,1 do if tabbuilder.current_buttons[i].name == tabbuilder.current_tab then tabbuilder.last_tab_index = i end end if tabbuilder.current_tab ~= "singleplayer" then menu.update_gametype(true) else menu.update_gametype() end end -------------------------------------------------------------------------------- function tabbuilder.checkretval(retval) if retval ~= nil then if retval.current_tab ~= nil then tabbuilder.current_tab = retval.current_tab end if retval.is_dialog ~= nil then tabbuilder.is_dialog = retval.is_dialog end if retval.show_buttons ~= nil then tabbuilder.show_buttons = retval.show_buttons end if retval.skipformupdate ~= nil then tabbuilder.skipformupdate = retval.skipformupdate end if retval.ignore_menu_quit == true then tabbuilder.ignore_menu_quit = true else tabbuilder.ignore_menu_quit = false end end end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- initialize callbacks -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- engine.button_handler = function(fields) --print("Buttonhandler: tab: " .. tabbuilder.current_tab .. " fields: " .. dump(fields)) if fields["btn_error_confirm"] then gamedata.errormessage = nil end local retval = modmgr.handle_buttons(tabbuilder.current_tab,fields) tabbuilder.checkretval(retval) retval = gamemgr.handle_buttons(tabbuilder.current_tab,fields) tabbuilder.checkretval(retval) retval = modstore.handle_buttons(tabbuilder.current_tab,fields) tabbuilder.checkretval(retval) if tabbuilder.current_tab == "dialog_create_world" then tabbuilder.handle_create_world_buttons(fields) end if tabbuilder.current_tab == "dialog_delete_world" then tabbuilder.handle_delete_world_buttons(fields) end if tabbuilder.current_tab == "singleplayer" then tabbuilder.handle_singleplayer_buttons(fields) end if tabbuilder.current_tab == "texture_packs" then tabbuilder.handle_texture_pack_buttons(fields) end if tabbuilder.current_tab == "multiplayer" then tabbuilder.handle_multiplayer_buttons(fields) end if tabbuilder.current_tab == "settings" then tabbuilder.handle_settings_buttons(fields) end if tabbuilder.current_tab == "server" then tabbuilder.handle_server_buttons(fields) end --tab buttons tabbuilder.handle_tab_buttons(fields) --menubar buttons menubar.handle_buttons(fields) if not tabbuilder.skipformupdate then --update menu update_menu() else tabbuilder.skipformupdate = false end end -------------------------------------------------------------------------------- engine.event_handler = function(event) if event == "MenuQuit" then if tabbuilder.is_dialog then if tabbuilder.ignore_menu_quit then return end tabbuilder.is_dialog = false tabbuilder.show_buttons = true tabbuilder.current_tab = engine.setting_get("main_menu_tab") menu.update_gametype() update_menu() else engine.close() end end if event == "Refresh" then update_menu() end end -------------------------------------------------------------------------------- function menu.update_gametype(reset) local game = menu.lastgame() if reset or game == nil then mm_texture.reset() engine.set_topleft_text("") filterlist.set_filtercriteria(worldlist,nil) else mm_texture.update(tabbuilder.current_tab,game) engine.set_topleft_text(game.name) filterlist.set_filtercriteria(worldlist,game.id) end end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- menu startup -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- init_globals() mm_texture.init() menu.init() tabbuilder.init() menubar.refresh() modstore.init() engine.sound_play("main_menu", true) update_menu() builtin/async_event.lua0000644000175000017500000000247712261107224016060 0ustar mquinsonmquinsonlocal tbl = engine or minetest tbl.async_jobs = {} if engine ~= nil then function tbl.async_event_handler(jobid, serialized_retval) local retval = nil if serialized_retval ~= "ERROR" then retval= marshal.decode(serialized_retval) else tbl.log("error","Error fetching async result") end assert(type(tbl.async_jobs[jobid]) == "function") tbl.async_jobs[jobid](retval) tbl.async_jobs[jobid] = nil end else minetest.register_globalstep( function(dtime) local list = tbl.get_finished_jobs() for i=1,#list,1 do local retval = marshal.decode(list[i].retval) assert(type(tbl.async_jobs[jobid]) == "function") tbl.async_jobs[list[i].jobid](retval) tbl.async_jobs[list[i].jobid] = nil end end) end function tbl.handle_async(fct, parameters, callback) --serialize fct local serialized_fct = marshal.encode(fct) assert(marshal.decode(serialized_fct) ~= nil) --serialize parameters local serialized_params = marshal.encode(parameters) if serialized_fct == nil or serialized_params == nil or serialized_fct:len() == 0 or serialized_params:len() == 0 then return false end local jobid = tbl.do_async_callback( serialized_fct, serialized_fct:len(), serialized_params, serialized_params:len()) tbl.async_jobs[jobid] = callback return true end builtin/detached_inventory.lua0000644000175000017500000000103012261107224017400 0ustar mquinsonmquinson-- Minetest: builtin/detached_inventory.lua minetest.detached_inventories = {} function minetest.create_detached_inventory(name, callbacks) local stuff = {} stuff.name = name if callbacks then stuff.allow_move = callbacks.allow_move stuff.allow_put = callbacks.allow_put stuff.allow_take = callbacks.allow_take stuff.on_move = callbacks.on_move stuff.on_put = callbacks.on_put stuff.on_take = callbacks.on_take end minetest.detached_inventories[name] = stuff return minetest.create_detached_inventory_raw(name) end builtin/privileges.lua0000644000175000017500000000331412261107224015702 0ustar mquinsonmquinson-- Minetest: builtin/privileges.lua -- -- Privileges -- minetest.registered_privileges = {} function minetest.register_privilege(name, param) local function fill_defaults(def) if def.give_to_singleplayer == nil then def.give_to_singleplayer = true end if def.description == nil then def.description = "(no description)" end end local def = {} if type(param) == "table" then def = param else def = {description = param} end fill_defaults(def) minetest.registered_privileges[name] = def end minetest.register_privilege("interact", "Can interact with things and modify the world") minetest.register_privilege("teleport", "Can use /teleport command") minetest.register_privilege("bring", "Can teleport other players") minetest.register_privilege("settime", "Can use /time") minetest.register_privilege("privs", "Can modify privileges") minetest.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges") minetest.register_privilege("server", "Can do server maintenance stuff") minetest.register_privilege("shout", "Can speak in chat") minetest.register_privilege("ban", "Can ban and unban players") minetest.register_privilege("give", "Can use /give and /giveme") minetest.register_privilege("password", "Can use /setpassword and /clearpassword") minetest.register_privilege("fly", { description = "Can fly using the free_move mode", give_to_singleplayer = false, }) minetest.register_privilege("fast", { description = "Can walk fast using the fast_move mode", give_to_singleplayer = false, }) minetest.register_privilege("noclip", { description = "Can fly through walls", give_to_singleplayer = false, }) minetest.register_privilege("rollback", "Can use the rollback functionality") builtin/misc_register.lua0000644000175000017500000002766512261107224016407 0ustar mquinsonmquinson-- Minetest: builtin/misc_register.lua -- -- Make raw registration functions inaccessible to anyone except this file -- local register_item_raw = minetest.register_item_raw minetest.register_item_raw = nil local register_alias_raw = minetest.register_alias_raw minetest.register_item_raw = nil -- -- Item / entity / ABM registration functions -- minetest.registered_abms = {} minetest.registered_entities = {} minetest.registered_items = {} minetest.registered_nodes = {} minetest.registered_craftitems = {} minetest.registered_tools = {} minetest.registered_aliases = {} -- For tables that are indexed by item name: -- If table[X] does not exist, default to table[minetest.registered_aliases[X]] local alias_metatable = { __index = function(t, name) return rawget(t, minetest.registered_aliases[name]) end } setmetatable(minetest.registered_items, alias_metatable) setmetatable(minetest.registered_nodes, alias_metatable) setmetatable(minetest.registered_craftitems, alias_metatable) setmetatable(minetest.registered_tools, alias_metatable) -- These item names may not be used because they would interfere -- with legacy itemstrings local forbidden_item_names = { MaterialItem = true, MaterialItem2 = true, MaterialItem3 = true, NodeItem = true, node = true, CraftItem = true, craft = true, MBOItem = true, ToolItem = true, tool = true, } local function check_modname_prefix(name) if name:sub(1,1) == ":" then -- Escape the modname prefix enforcement mechanism return name:sub(2) else -- Modname prefix enforcement local expected_prefix = minetest.get_current_modname() .. ":" if name:sub(1, #expected_prefix) ~= expected_prefix then error("Name " .. name .. " does not follow naming conventions: " .. "\"modname:\" or \":\" prefix required") end local subname = name:sub(#expected_prefix+1) if subname:find("[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_]") then error("Name " .. name .. " does not follow naming conventions: " .. "contains unallowed characters") end return name end end function minetest.register_abm(spec) -- Add to minetest.registered_abms minetest.registered_abms[#minetest.registered_abms+1] = spec end function minetest.register_entity(name, prototype) -- Check name if name == nil then error("Unable to register entity: Name is nil") end name = check_modname_prefix(tostring(name)) prototype.name = name prototype.__index = prototype -- so that it can be used as a metatable -- Add to minetest.registered_entities minetest.registered_entities[name] = prototype end function minetest.register_item(name, itemdef) -- Check name if name == nil then error("Unable to register item: Name is nil") end name = check_modname_prefix(tostring(name)) if forbidden_item_names[name] then error("Unable to register item: Name is forbidden: " .. name) end itemdef.name = name -- Apply defaults and add to registered_* table if itemdef.type == "node" then -- Use the nodebox as selection box if it's not set manually if itemdef.drawtype == "nodebox" and not itemdef.selection_box then itemdef.selection_box = itemdef.node_box elseif itemdef.drawtype == "fencelike" and not itemdef.selection_box then itemdef.selection_box = { type = "fixed", fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8}, } end setmetatable(itemdef, {__index = minetest.nodedef_default}) minetest.registered_nodes[itemdef.name] = itemdef elseif itemdef.type == "craft" then setmetatable(itemdef, {__index = minetest.craftitemdef_default}) minetest.registered_craftitems[itemdef.name] = itemdef elseif itemdef.type == "tool" then setmetatable(itemdef, {__index = minetest.tooldef_default}) minetest.registered_tools[itemdef.name] = itemdef elseif itemdef.type == "none" then setmetatable(itemdef, {__index = minetest.noneitemdef_default}) else error("Unable to register item: Type is invalid: " .. dump(itemdef)) end -- Flowing liquid uses param2 if itemdef.type == "node" and itemdef.liquidtype == "flowing" then itemdef.paramtype2 = "flowingliquid" end -- BEGIN Legacy stuff if itemdef.cookresult_itemstring ~= nil and itemdef.cookresult_itemstring ~= "" then minetest.register_craft({ type="cooking", output=itemdef.cookresult_itemstring, recipe=itemdef.name, cooktime=itemdef.furnace_cooktime }) end if itemdef.furnace_burntime ~= nil and itemdef.furnace_burntime >= 0 then minetest.register_craft({ type="fuel", recipe=itemdef.name, burntime=itemdef.furnace_burntime }) end -- END Legacy stuff -- Disable all further modifications getmetatable(itemdef).__newindex = {} --minetest.log("Registering item: " .. itemdef.name) minetest.registered_items[itemdef.name] = itemdef minetest.registered_aliases[itemdef.name] = nil register_item_raw(itemdef) end function minetest.register_node(name, nodedef) nodedef.type = "node" minetest.register_item(name, nodedef) end function minetest.register_craftitem(name, craftitemdef) craftitemdef.type = "craft" -- BEGIN Legacy stuff if craftitemdef.inventory_image == nil and craftitemdef.image ~= nil then craftitemdef.inventory_image = craftitemdef.image end -- END Legacy stuff minetest.register_item(name, craftitemdef) end function minetest.register_tool(name, tooldef) tooldef.type = "tool" tooldef.stack_max = 1 -- BEGIN Legacy stuff if tooldef.inventory_image == nil and tooldef.image ~= nil then tooldef.inventory_image = tooldef.image end if tooldef.tool_capabilities == nil and (tooldef.full_punch_interval ~= nil or tooldef.basetime ~= nil or tooldef.dt_weight ~= nil or tooldef.dt_crackiness ~= nil or tooldef.dt_crumbliness ~= nil or tooldef.dt_cuttability ~= nil or tooldef.basedurability ~= nil or tooldef.dd_weight ~= nil or tooldef.dd_crackiness ~= nil or tooldef.dd_crumbliness ~= nil or tooldef.dd_cuttability ~= nil) then tooldef.tool_capabilities = { full_punch_interval = tooldef.full_punch_interval, basetime = tooldef.basetime, dt_weight = tooldef.dt_weight, dt_crackiness = tooldef.dt_crackiness, dt_crumbliness = tooldef.dt_crumbliness, dt_cuttability = tooldef.dt_cuttability, basedurability = tooldef.basedurability, dd_weight = tooldef.dd_weight, dd_crackiness = tooldef.dd_crackiness, dd_crumbliness = tooldef.dd_crumbliness, dd_cuttability = tooldef.dd_cuttability, } end -- END Legacy stuff minetest.register_item(name, tooldef) end function minetest.register_alias(name, convert_to) if forbidden_item_names[name] then error("Unable to register alias: Name is forbidden: " .. name) end if minetest.registered_items[name] ~= nil then minetest.log("WARNING: Not registering alias, item with same name" .. " is already defined: " .. name .. " -> " .. convert_to) else --minetest.log("Registering alias: " .. name .. " -> " .. convert_to) minetest.registered_aliases[name] = convert_to register_alias_raw(name, convert_to) end end local register_biome_raw = minetest.register_biome minetest.registered_biomes = {} function minetest.register_biome(biome) minetest.registered_biomes[biome.name] = biome register_biome_raw(biome) end function minetest.on_craft(itemstack, player, old_craft_list, craft_inv) for _, func in ipairs(minetest.registered_on_crafts) do itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack end return itemstack end function minetest.craft_predict(itemstack, player, old_craft_list, craft_inv) for _, func in ipairs(minetest.registered_craft_predicts) do itemstack = func(itemstack, player, old_craft_list, craft_inv) or itemstack end return itemstack end -- Alias the forbidden item names to "" so they can't be -- created via itemstrings (e.g. /give) local name for name in pairs(forbidden_item_names) do minetest.registered_aliases[name] = "" register_alias_raw(name, "") end -- Deprecated: -- Aliases for minetest.register_alias (how ironic...) --minetest.alias_node = minetest.register_alias --minetest.alias_tool = minetest.register_alias --minetest.alias_craftitem = minetest.register_alias -- -- Built-in node definitions. Also defined in C. -- minetest.register_item(":unknown", { type = "none", description = "Unknown Item", inventory_image = "unknown_item.png", on_place = minetest.item_place, on_drop = minetest.item_drop, groups = {not_in_creative_inventory=1}, diggable = true, }) minetest.register_node(":air", { description = "Air (you hacker you!)", inventory_image = "unknown_node.png", wield_image = "unknown_node.png", drawtype = "airlike", paramtype = "light", sunlight_propagates = true, walkable = false, pointable = false, diggable = false, buildable_to = true, air_equivalent = true, drop = "", groups = {not_in_creative_inventory=1}, }) minetest.register_node(":ignore", { description = "Ignore (you hacker you!)", inventory_image = "unknown_node.png", wield_image = "unknown_node.png", drawtype = "airlike", paramtype = "none", sunlight_propagates = false, walkable = false, pointable = false, diggable = false, buildable_to = true, -- A way to remove accidentally placed ignores air_equivalent = true, drop = "", groups = {not_in_creative_inventory=1}, }) -- The hand (bare definition) minetest.register_item(":", { type = "none", groups = {not_in_creative_inventory=1}, }) function minetest.run_callbacks(callbacks, mode, ...) assert(type(callbacks) == "table") local cb_len = #callbacks if cb_len == 0 then if mode == 2 or mode == 3 then return true elseif mode == 4 or mode == 5 then return false end end local ret = nil for i = 1, cb_len do local cb_ret = callbacks[i](...) if mode == 0 and i == 1 then ret = cb_ret elseif mode == 1 and i == cb_len then ret = cb_ret elseif mode == 2 then if not cb_ret or i == 1 then ret = cb_ret end elseif mode == 3 then if cb_ret then return cb_ret end ret = cb_ret elseif mode == 4 then if (cb_ret and not ret) or i == 1 then ret = cb_ret end elseif mode == 5 and cb_ret then return cb_ret end end return ret end -- -- Callback registration -- local function make_registration() local t = {} local registerfunc = function(func) table.insert(t, func) end return t, registerfunc end local function make_registration_reverse() local t = {} local registerfunc = function(func) table.insert(t, 1, func) end return t, registerfunc end minetest.registered_on_chat_messages, minetest.register_on_chat_message = make_registration() minetest.registered_globalsteps, minetest.register_globalstep = make_registration() minetest.registered_on_mapgen_inits, minetest.register_on_mapgen_init = make_registration() minetest.registered_on_shutdown, minetest.register_on_shutdown = make_registration() minetest.registered_on_punchnodes, minetest.register_on_punchnode = make_registration() minetest.registered_on_placenodes, minetest.register_on_placenode = make_registration() minetest.registered_on_dignodes, minetest.register_on_dignode = make_registration() minetest.registered_on_generateds, minetest.register_on_generated = make_registration() minetest.registered_on_newplayers, minetest.register_on_newplayer = make_registration() minetest.registered_on_dieplayers, minetest.register_on_dieplayer = make_registration() minetest.registered_on_respawnplayers, minetest.register_on_respawnplayer = make_registration() minetest.registered_on_prejoinplayers, minetest.register_on_prejoinplayer = make_registration() minetest.registered_on_joinplayers, minetest.register_on_joinplayer = make_registration() minetest.registered_on_leaveplayers, minetest.register_on_leaveplayer = make_registration() minetest.registered_on_player_receive_fields, minetest.register_on_player_receive_fields = make_registration_reverse() minetest.registered_on_cheats, minetest.register_on_cheat = make_registration() minetest.registered_on_crafts, minetest.register_on_craft = make_registration() minetest.registered_craft_predicts, minetest.register_craft_predict = make_registration() minetest.registered_on_protection_violation, minetest.register_on_protection_violation = make_registration() builtin/mm_textures.lua0000644000175000017500000000774412261107224016120 0ustar mquinsonmquinson--Minetest --Copyright (C) 2013 sapier -- --This program is free software; you can redistribute it and/or modify --it under the terms of the GNU Lesser General Public License as published by --the Free Software Foundation; either version 2.1 of the License, or --(at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU Lesser General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. mm_texture = {} -------------------------------------------------------------------------------- function mm_texture.init() mm_texture.defaulttexturedir = engine.get_texturepath() .. DIR_DELIM .. "base" .. DIR_DELIM .. "pack" .. DIR_DELIM mm_texture.basetexturedir = mm_texture.defaulttexturedir mm_texture.texturepack = engine.setting_get("texture_path") mm_texture.gameid = nil end -------------------------------------------------------------------------------- function mm_texture.update(tab,gamedetails) if tab ~= "singleplayer" then mm_texture.reset() return end if gamedetails == nil then return end mm_texture.update_game(gamedetails) end -------------------------------------------------------------------------------- function mm_texture.reset() mm_texture.gameid = nil local have_bg = false local have_overlay = mm_texture.set_generic("overlay") if not have_overlay then have_bg = mm_texture.set_generic("background") end mm_texture.clear("header") mm_texture.clear("footer") engine.set_clouds(false) mm_texture.set_generic("footer") mm_texture.set_generic("header") if not have_bg and engine.setting_getbool("enable_clouds") then engine.set_clouds(true) end end -------------------------------------------------------------------------------- function mm_texture.update_game(gamedetails) if mm_texture.gameid == gamedetails.id then return end local have_bg = false local have_overlay = mm_texture.set_game("overlay",gamedetails) if not have_overlay then have_bg = mm_texture.set_game("background",gamedetails) end mm_texture.clear("header") mm_texture.clear("footer") engine.set_clouds(false) if not have_bg and engine.setting_getbool("enable_clouds") then engine.set_clouds(true) end mm_texture.set_game("footer",gamedetails) mm_texture.set_game("header",gamedetails) mm_texture.gameid = gamedetails.id end -------------------------------------------------------------------------------- function mm_texture.clear(identifier) engine.set_background(identifier,"") end -------------------------------------------------------------------------------- function mm_texture.set_generic(identifier) --try texture pack first if mm_texture.texturepack ~= nil then local path = mm_texture.texturepack .. DIR_DELIM .."menu_" .. identifier .. ".png" if engine.set_background(identifier,path) then return true end end if mm_texture.defaulttexturedir ~= nil then local path = mm_texture.defaulttexturedir .. DIR_DELIM .."menu_" .. identifier .. ".png" if engine.set_background(identifier,path) then return true end end return false end -------------------------------------------------------------------------------- function mm_texture.set_game(identifier,gamedetails) if gamedetails == nil then return false end if mm_texture.texturepack ~= nil then local path = mm_texture.texturepack .. DIR_DELIM .. gamedetails.id .. "_menu_" .. identifier .. ".png" if engine.set_background(identifier,path) then return true end end local path = gamedetails.path .. DIR_DELIM .."menu" .. DIR_DELIM .. identifier .. ".png" if engine.set_background(identifier,path) then return true end return false end builtin/gamemgr.lua0000644000175000017500000002176212261107224015157 0ustar mquinsonmquinson--Minetest --Copyright (C) 2013 sapier -- --This program is free software; you can redistribute it and/or modify --it under the terms of the GNU Lesser General Public License as published by --the Free Software Foundation; either version 2.1 of the License, or --(at your option) any later version. -- --This program is distributed in the hope that it will be useful, --but WITHOUT ANY WARRANTY; without even the implied warranty of --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --GNU Lesser General Public License for more details. -- --You should have received a copy of the GNU Lesser General Public License along --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. gamemgr = {} -------------------------------------------------------------------------------- function gamemgr.dialog_new_game() local retval = "label[2,2;" .. fgettext("Game Name") .. "]".. "field[4.5,2.4;6,0.5;te_game_name;;]" .. "button[5,4.2;2.6,0.5;new_game_confirm;" .. fgettext("Create") .. "]" .. "button[7.5,4.2;2.8,0.5;new_game_cancel;" .. fgettext("Cancel") .. "]" return retval end -------------------------------------------------------------------------------- function gamemgr.handle_games_buttons(fields) if fields["gamelist"] ~= nil then local event = explode_textlist_event(fields["gamelist"]) gamemgr.selected_game = event.index end if fields["btn_game_mgr_edit_game"] ~= nil then return { is_dialog = true, show_buttons = false, current_tab = "dialog_edit_game" } end if fields["btn_game_mgr_new_game"] ~= nil then return { is_dialog = true, show_buttons = false, current_tab = "dialog_new_game" } end return nil end -------------------------------------------------------------------------------- function gamemgr.handle_new_game_buttons(fields) if fields["new_game_confirm"] and fields["te_game_name"] ~= nil and fields["te_game_name"] ~= "" then local gamepath = engine.get_gamepath() if gamepath ~= nil and gamepath ~= "" then local gamefolder = cleanup_path(fields["te_game_name"]) --TODO check for already existing first engine.create_dir(gamepath .. DIR_DELIM .. gamefolder) engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "mods") engine.create_dir(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "menu") local gameconf = io.open(gamepath .. DIR_DELIM .. gamefolder .. DIR_DELIM .. "game.conf","w") if gameconf then gameconf:write("name = " .. fields["te_game_name"]) gameconf:close() end end end return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end -------------------------------------------------------------------------------- function gamemgr.handle_edit_game_buttons(fields) local current_game = gamemgr.get_game(gamemgr.selected_game) if fields["btn_close_edit_game"] ~= nil or current_game == nil then return { is_dialog = false, show_buttons = true, current_tab = engine.setting_get("main_menu_tab") } end if fields["btn_remove_mod_from_game"] ~= nil then gamemgr.delete_mod(current_game,engine.get_textlist_index("mods_current")) end if fields["btn_add_mod_to_game"] ~= nil then local modindex = engine.get_textlist_index("mods_available") local mod = modmgr.get_global_mod(modindex) if mod ~= nil then local sourcepath = mod.path if not gamemgr.add_mod(current_game,sourcepath) then gamedata.errormessage = fgettext("Gamemgr: Unable to copy mod \"$1\" to game \"$2\"", mod.name, current_game.id) end end end return nil end -------------------------------------------------------------------------------- function gamemgr.add_mod(gamespec,sourcepath) if gamespec.gamemods_path ~= nil and gamespec.gamemods_path ~= "" then local modname = get_last_folder(sourcepath) return engine.copy_dir(sourcepath,gamespec.gamemods_path .. DIR_DELIM .. modname); end return false end -------------------------------------------------------------------------------- function gamemgr.delete_mod(gamespec,modindex) if gamespec.gamemods_path ~= nil and gamespec.gamemods_path ~= "" then local game_mods = {} get_mods(gamespec.gamemods_path,game_mods) if modindex > 0 and #game_mods >= modindex then if game_mods[modindex].path:sub(0,gamespec.gamemods_path:len()) == gamespec.gamemods_path then engine.delete_dir(game_mods[modindex].path) end end end end -------------------------------------------------------------------------------- function gamemgr.find_by_gameid(gameid) for i=1,#gamemgr.games,1 do if gamemgr.games[i].id == gameid then return gamemgr.games[i], i end end return nil, nil end -------------------------------------------------------------------------------- function gamemgr.get_game_mods(gamespec, retval) if gamespec ~= nil and gamespec.gamemods_path ~= nil and gamespec.gamemods_path ~= "" then get_mods(gamespec.gamemods_path, retval) end end -------------------------------------------------------------------------------- function gamemgr.get_game_modlist(gamespec) local retval = "" local game_mods = {} gamemgr.get_game_mods(gamespec, game_mods) for i=1,#game_mods,1 do if retval ~= "" then retval = retval.."," end retval = retval .. game_mods[i].name end return retval end -------------------------------------------------------------------------------- function gamemgr.gettab(name) local retval = "" if name == "dialog_edit_game" then retval = retval .. gamemgr.dialog_edit_game() end if name == "dialog_new_game" then retval = retval .. gamemgr.dialog_new_game() end if name == "game_mgr" then retval = retval .. gamemgr.tab() end return retval end -------------------------------------------------------------------------------- function gamemgr.tab() if gamemgr.selected_game == nil then gamemgr.selected_game = 1 end local retval = "vertlabel[0,-0.25;" .. fgettext("GAMES") .. "]" .. "label[1,-0.25;" .. fgettext("Games") .. ":]" .. "textlist[1,0.25;4.5,4.4;gamelist;" .. gamemgr.gamelist() .. ";" .. gamemgr.selected_game .. "]" local current_game = gamemgr.get_game(gamemgr.selected_game) if current_game ~= nil then if current_game.menuicon_path ~= nil and current_game.menuicon_path ~= "" then retval = retval .. "image[5.8,-0.25;2,2;" .. engine.formspec_escape(current_game.menuicon_path) .. "]" end retval = retval .. "field[8,-0.25;6,2;;" .. current_game.name .. ";]".. "label[6,1.4;" .. fgettext("Mods:") .."]" .. "button[9.7,1.5;2,0.2;btn_game_mgr_edit_game;" .. fgettext("edit game") .. "]" .. "textlist[6,2;5.5,3.3;game_mgr_modlist;" .. gamemgr.get_game_modlist(current_game) ..";0]" .. "button[1,4.75;3.2,0.5;btn_game_mgr_new_game;" .. fgettext("new game") .. "]" end return retval end -------------------------------------------------------------------------------- function gamemgr.dialog_edit_game() local current_game = gamemgr.get_game(gamemgr.selected_game) if current_game ~= nil then local retval = "vertlabel[0,-0.25;" .. fgettext("EDIT GAME") .."]" .. "label[0,-0.25;" .. current_game.name .. "]" .. "button[11.55,-0.2;0.75,0.5;btn_close_edit_game;x]" if current_game.menuicon_path ~= nil and current_game.menuicon_path ~= "" then retval = retval .. "image[5.25,0;2,2;" .. engine.formspec_escape(current_game.menuicon_path) .. "]" end retval = retval .. "textlist[0.5,0.5;4.5,4.3;mods_current;" .. gamemgr.get_game_modlist(current_game) ..";0]" retval = retval .. "textlist[7,0.5;4.5,4.3;mods_available;" .. modmgr.render_modlist() .. ";0]" retval = retval .. "button[0.55,4.95;4.7,0.5;btn_remove_mod_from_game;" .. fgettext("Remove selected mod") .."]" retval = retval .. "button[7.05,4.95;4.7,0.5;btn_add_mod_to_game;" .. fgettext("<<-- Add mod") .."]" return retval end end -------------------------------------------------------------------------------- function gamemgr.handle_buttons(tab,fields) local retval = nil if tab == "dialog_edit_game" then retval = gamemgr.handle_edit_game_buttons(fields) end if tab == "dialog_new_game" then retval = gamemgr.handle_new_game_buttons(fields) end if tab == "game_mgr" then retval = gamemgr.handle_games_buttons(fields) end return retval end -------------------------------------------------------------------------------- function gamemgr.get_game(index) if index > 0 and index <= #gamemgr.games then return gamemgr.games[index] end return nil end -------------------------------------------------------------------------------- function gamemgr.update_gamelist() gamemgr.games = engine.get_games() end -------------------------------------------------------------------------------- function gamemgr.gamelist() local retval = "" if #gamemgr.games > 0 then retval = retval .. gamemgr.games[1].id for i=2,#gamemgr.games,1 do retval = retval .. "," .. gamemgr.games[i].name end end return retval end builtin/builtin.lua0000644000175000017500000000177312261107224015206 0ustar mquinsonmquinson-- -- This file contains built-in stuff in Minetest implemented in Lua. -- -- It is always loaded and executed after registration of the C API, -- before loading and running any mods. -- -- Initialize some very basic things print = minetest.debug math.randomseed(os.time()) os.setlocale("C", "numeric") local errorfct = error error = function(text) print(debug.traceback("")) errorfct(text) end -- Load other files local modpath = minetest.get_modpath("__builtin") dofile(modpath.."/serialize.lua") dofile(modpath.."/misc_helpers.lua") dofile(modpath.."/item.lua") dofile(modpath.."/misc_register.lua") dofile(modpath.."/item_entity.lua") dofile(modpath.."/deprecated.lua") dofile(modpath.."/misc.lua") dofile(modpath.."/privileges.lua") dofile(modpath.."/auth.lua") dofile(modpath.."/chatcommands.lua") dofile(modpath.."/static_spawn.lua") dofile(modpath.."/detached_inventory.lua") dofile(modpath.."/falling.lua") dofile(modpath.."/features.lua") dofile(modpath.."/voxelarea.lua") dofile(modpath.."/vector.lua") builtin/voxelarea.lua0000644000175000017500000000444012261107224015520 0ustar mquinsonmquinsonVoxelArea = { MinEdge = {x=1, y=1, z=1}, MaxEdge = {x=0, y=0, z=0}, ystride = 0, zstride = 0, } function VoxelArea:new(o) o = o or {} setmetatable(o, self) self.__index = self local e = o:getExtent() o.ystride = e.x o.zstride = e.x * e.y return o end function VoxelArea:getExtent() return { x = self.MaxEdge.x - self.MinEdge.x + 1, y = self.MaxEdge.y - self.MinEdge.y + 1, z = self.MaxEdge.z - self.MinEdge.z + 1, } end function VoxelArea:getVolume() local e = self:getExtent() return e.x * e.y * e.z end function VoxelArea:index(x, y, z) local i = (z - self.MinEdge.z) * self.zstride + (y - self.MinEdge.y) * self.ystride + (x - self.MinEdge.x) + 1 return math.floor(i) end function VoxelArea:indexp(p) local i = (p.z - self.MinEdge.z) * self.zstride + (p.y - self.MinEdge.y) * self.ystride + (p.x - self.MinEdge.x) + 1 return math.floor(i) end function VoxelArea:position(i) local p = {} i = i - 1 p.z = math.floor(i / self.zstride) + self.MinEdge.z i = i % self.zstride p.y = math.floor(i / self.ystride) + self.MinEdge.y i = i % self.ystride p.x = math.floor(i) + self.MinEdge.x return p end function VoxelArea:contains(x, y, z) return (x >= self.MinEdge.x) and (x <= self.MaxEdge.x) and (y >= self.MinEdge.y) and (y <= self.MaxEdge.y) and (z >= self.MinEdge.z) and (z <= self.MaxEdge.z) end function VoxelArea:containsp(p) return (p.x >= self.MinEdge.x) and (p.x <= self.MaxEdge.x) and (p.y >= self.MinEdge.y) and (p.y <= self.MaxEdge.y) and (p.z >= self.MinEdge.z) and (p.z <= self.MaxEdge.z) end function VoxelArea:containsi(i) return (i >= 1) and (i <= self:getVolume()) end function VoxelArea:iter(minx, miny, minz, maxx, maxy, maxz) local i = self:index(minx, miny, minz) - 1 local last = self:index(maxx, maxy, maxz) local ystride = self.ystride local zstride = self.zstride local yoff = (last+1) % ystride local zoff = (last+1) % zstride local ystridediff = (i - last) % ystride local zstridediff = (i - last) % zstride return function() i = i + 1 if i % zstride == zoff then i = i + zstridediff elseif i % ystride == yoff then i = i + ystridediff end if i <= last then return i end end end function VoxelArea:iterp(minp, maxp) return self:iter(minp.x, minp.y, minp.z, maxp.x, maxp.y, maxp.z) end builtin/serialize.lua0000644000175000017500000001732212261107224015524 0ustar mquinsonmquinson-- Minetest: builtin/serialize.lua -- https://github.com/fab13n/metalua/blob/no-dll/src/lib/serialize.lua -- Copyright (c) 2006-2997 Fabien Fleutot -- License: MIT -------------------------------------------------------------------------------- -- Serialize an object into a source code string. This string, when passed as -- an argument to deserialize(), returns an object structurally identical -- to the original one. The following are currently supported: -- * strings, numbers, booleans, nil -- * tables thereof. Tables can have shared part, but can't be recursive yet. -- Caveat: metatables and environments aren't saved. -------------------------------------------------------------------------------- local no_identity = { number=1, boolean=1, string=1, ['nil']=1 } function minetest.serialize(x) local gensym_max = 0 -- index of the gensym() symbol generator local seen_once = { } -- element->true set of elements seen exactly once in the table local multiple = { } -- element->varname set of elements seen more than once local nested = { } -- transient, set of elements currently being traversed local nest_points = { } local nest_patches = { } local function gensym() gensym_max = gensym_max + 1 ; return gensym_max end ----------------------------------------------------------------------------- -- nest_points are places where a table appears within itself, directly or not. -- for instance, all of these chunks create nest points in table x: -- "x = { }; x[x] = 1", "x = { }; x[1] = x", "x = { }; x[1] = { y = { x } }". -- To handle those, two tables are created by mark_nest_point: -- * nest_points [parent] associates all keys and values in table parent which -- create a nest_point with boolean `true' -- * nest_patches contain a list of { parent, key, value } tuples creating -- a nest point. They're all dumped after all the other table operations -- have been performed. -- -- mark_nest_point (p, k, v) fills tables nest_points and nest_patches with -- informations required to remember that key/value (k,v) create a nest point -- in table parent. It also marks `parent' as occuring multiple times, since -- several references to it will be required in order to patch the nest -- points. ----------------------------------------------------------------------------- local function mark_nest_point (parent, k, v) local nk, nv = nested[k], nested[v] assert (not nk or seen_once[k] or multiple[k]) assert (not nv or seen_once[v] or multiple[v]) local mode = (nk and nv and "kv") or (nk and "k") or ("v") local parent_np = nest_points [parent] local pair = { k, v } if not parent_np then parent_np = { }; nest_points [parent] = parent_np end parent_np [k], parent_np [v] = nk, nv table.insert (nest_patches, { parent, k, v }) seen_once [parent], multiple [parent] = nil, true end ----------------------------------------------------------------------------- -- First pass, list the tables and functions which appear more than once in x ----------------------------------------------------------------------------- local function mark_multiple_occurences (x) if no_identity [type(x)] then return end if seen_once [x] then seen_once [x], multiple [x] = nil, true elseif multiple [x] then -- pass else seen_once [x] = true end if type (x) == 'table' then nested [x] = true for k, v in pairs (x) do if nested[k] or nested[v] then mark_nest_point (x, k, v) else mark_multiple_occurences (k) mark_multiple_occurences (v) end end nested [x] = nil end end local dumped = { } -- multiply occuring values already dumped in localdefs local localdefs = { } -- already dumped local definitions as source code lines -- mutually recursive functions: local dump_val, dump_or_ref_val -------------------------------------------------------------------- -- if x occurs multiple times, dump the local var rather than the -- value. If it's the first time it's dumped, also dump the content -- in localdefs. -------------------------------------------------------------------- function dump_or_ref_val (x) if nested[x] then return 'false' end -- placeholder for recursive reference if not multiple[x] then return dump_val (x) end local var = dumped [x] if var then return "_[" .. var .. "]" end -- already referenced local val = dump_val(x) -- first occurence, create and register reference var = gensym() table.insert(localdefs, "_["..var.."]="..val) dumped [x] = var return "_[" .. var .. "]" end ----------------------------------------------------------------------------- -- Second pass, dump the object; subparts occuring multiple times are dumped -- in local variables which can be referenced multiple times; -- care is taken to dump locla vars in asensible order. ----------------------------------------------------------------------------- function dump_val(x) local t = type(x) if x==nil then return 'nil' elseif t=="number" then return tostring(x) elseif t=="string" then return string.format("%q", x) elseif t=="boolean" then return x and "true" or "false" elseif t=="table" then local acc = { } local idx_dumped = { } local np = nest_points [x] for i, v in ipairs(x) do if np and np[v] then table.insert (acc, 'false') -- placeholder else table.insert (acc, dump_or_ref_val(v)) end idx_dumped[i] = true end for k, v in pairs(x) do if np and (np[k] or np[v]) then --check_multiple(k); check_multiple(v) -- force dumps in localdefs elseif not idx_dumped[k] then table.insert (acc, "[" .. dump_or_ref_val(k) .. "] = " .. dump_or_ref_val(v)) end end return "{ "..table.concat(acc,", ").." }" else error ("Can't serialize data of type "..t) end end local function dump_nest_patches() for _, entry in ipairs(nest_patches) do local p, k, v = unpack (entry) assert (multiple[p]) local set = dump_or_ref_val (p) .. "[" .. dump_or_ref_val (k) .. "] = " .. dump_or_ref_val (v) .. " -- rec " table.insert (localdefs, set) end end mark_multiple_occurences (x) local toplevel = dump_or_ref_val (x) dump_nest_patches() if next (localdefs) then return "local _={ }\n" .. table.concat (localdefs, "\n") .. "\nreturn " .. toplevel else return "return " .. toplevel end end -- Deserialization. -- http://stackoverflow.com/questions/5958818/loading-serialized-data-into-a-table -- local function stringtotable(sdata) if sdata:byte(1) == 27 then return nil, "binary bytecode prohibited" end local f, message = assert(loadstring(sdata)) if not f then return nil, message end setfenv(f, table) return f() end function minetest.deserialize(sdata) local table = {} local okay,results = pcall(stringtotable, sdata) if okay then return results end minetest.log('error', 'minetest.deserialize(): '.. results) return nil end -- Run some unit tests local function unit_test() function unitTest(name, success) if not success then error(name .. ': failed') end end unittest_input = {cat={sound="nyan", speed=400}, dog={sound="woof"}} unittest_output = minetest.deserialize(minetest.serialize(unittest_input)) unitTest("test 1a", unittest_input.cat.sound == unittest_output.cat.sound) unitTest("test 1b", unittest_input.cat.speed == unittest_output.cat.speed) unitTest("test 1c", unittest_input.dog.sound == unittest_output.dog.sound) unittest_input = {escapechars="\n\r\t\v\\\"\'", noneuropean="θשׁ٩∂"} unittest_output = minetest.deserialize(minetest.serialize(unittest_input)) unitTest("test 3a", unittest_input.escapechars == unittest_output.escapechars) unitTest("test 3b", unittest_input.noneuropean == unittest_output.noneuropean) end unit_test() -- Run it unit_test = nil -- Hide it client/0000755000175000017500000000000012261107224012635 5ustar mquinsonmquinsonclient/shaders/0000755000175000017500000000000012261107224014266 5ustar mquinsonmquinsonclient/shaders/leaves_shader/0000755000175000017500000000000012261107224017073 5ustar mquinsonmquinsonclient/shaders/leaves_shader/base.txt0000644000175000017500000000002212261107224020540 0ustar mquinsonmquinsontrans_alphach_ref client/shaders/leaves_shader/opengl_fragment.glsl0000644000175000017500000000275012261107224023131 0ustar mquinsonmquinsonuniform sampler2D baseTexture; uniform sampler2D normalTexture; uniform sampler2D useNormalmap; uniform vec4 skyBgColor; uniform float fogDistance; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; const float e = 2.718281828459; void main (void) { vec3 color; vec2 uv = gl_TexCoord[0].st; #ifdef USE_NORMALMAPS float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; #endif #ifdef ENABLE_BUMPMAPPING if (use_normalmap > 0.0) { vec3 base = texture2D(baseTexture, uv).rgb; vec3 vVec = normalize(eyeVec); vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); vec3 R = reflect(-vVec, bump); vec3 lVec = normalize(vVec); float diffuse = max(dot(lVec, bump), 0.0); float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; } else { color = texture2D(baseTexture, uv).rgb; } #else color = texture2D(baseTexture, uv).rgb; #endif float alpha = texture2D(baseTexture, uv).a; vec4 col = vec4(color.r, color.g, color.b, alpha); col *= gl_Color; col = col * col; // SRGB -> Linear col *= 1.8; col.r = 1.0 - exp(1.0 - col.r) / e; col.g = 1.0 - exp(1.0 - col.g) / e; col.b = 1.0 - exp(1.0 - col.b) / e; col = sqrt(col); // Linear -> SRGB 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.r, col.g, col.b, alpha); } client/shaders/leaves_shader/opengl_vertex.glsl0000644000175000017500000000445112261107224022643 0ustar mquinsonmquinsonuniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; uniform float animationTimer; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; #ifdef ENABLE_WAVING_LEAVES 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 smoothTriangleWave( float x ) { return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0; } #endif void main(void) { gl_TexCoord[0] = gl_MultiTexCoord0; #ifdef ENABLE_WAVING_LEAVES vec4 pos = gl_Vertex; vec4 pos2 = mTransWorld*gl_Vertex; pos.x += (smoothTriangleWave(animationTimer*10.0 + pos2.x * 0.01 + pos2.z * 0.01) * 2.0 - 1.0) * 0.4; pos.y += (smoothTriangleWave(animationTimer*15.0 + pos2.x * -0.01 + pos2.z * -0.01) * 2.0 - 1.0) * 0.2; pos.z += (smoothTriangleWave(animationTimer*10.0 + pos2.x * -0.01 + pos2.z * -0.01) * 2.0 - 1.0) * 0.4; gl_Position = mWorldViewProj * pos; #else gl_Position = mWorldViewProj * gl_Vertex; #endif vPosition = (mWorldViewProj * gl_Vertex).xyz; eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); float day = gl_Color.r; float night = gl_Color.g; float light_source = gl_Color.b; /*color.r = mix(night, day, dayNightRatio); color.g = color.r; color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue b += (day - night) / 13.0; rg -= (day - night) / 13.0; // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); // Artificial light is yellow-ish // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); color.r = clamp(rg,0.0,1.0); color.g = clamp(rg,0.0,1.0); color.b = clamp(b,0.0,1.0); // Make sides and bottom darker than the top color = color * color; // SRGB -> Linear if(gl_Normal.y <= 0.5) color *= 0.6; //color *= 0.7; color = sqrt(color); // Linear -> SRGB color.a = gl_Color.a; gl_FrontColor = gl_BackColor = color; gl_TexCoord[0] = gl_MultiTexCoord0; } client/shaders/solids_shader/0000755000175000017500000000000012261107224017111 5ustar mquinsonmquinsonclient/shaders/solids_shader/base.txt0000644000175000017500000000002212261107224020556 0ustar mquinsonmquinsontrans_alphach_ref client/shaders/solids_shader/opengl_fragment.glsl0000644000175000017500000000454012261107224023146 0ustar mquinsonmquinsonuniform sampler2D baseTexture; uniform sampler2D normalTexture; uniform sampler2D useNormalmap; uniform vec4 skyBgColor; uniform float fogDistance; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; #ifdef ENABLE_PARALLAX_OCCLUSION varying vec3 tsEyeVec; #endif const float e = 2.718281828459; void main (void) { vec3 color; vec2 uv = gl_TexCoord[0].st; #ifdef USE_NORMALMAPS float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; #endif #ifdef ENABLE_PARALLAX_OCCLUSION float height; vec2 tsEye = vec2(tsEyeVec.x,-tsEyeVec.y); if (use_normalmap > 0.0) { float map_height = texture2D(normalTexture, uv).a; if (map_height < 1.0){ float height = PARALLAX_OCCLUSION_SCALE * map_height - PARALLAX_OCCLUSION_BIAS; uv = uv + height * tsEye; } } #endif /* Steep parallax code, for future use if ((parallaxMappingMode == 2.0) && (use_normalmap > 0.0)) { const float numSteps = 40.0; float height = 1.0; float step = 1.0 / numSteps; vec4 NB = texture2D(normalTexture, uv); vec2 delta = tsEye * parallaxMappingScale / numSteps; for (float i = 0.0; i < numSteps; i++) { if (NB.a < height) { height -= step; uv += delta; NB = texture2D(normalTexture, uv); } else { break; } } } */ #ifdef ENABLE_BUMPMAPPING if (use_normalmap > 0.0) { vec3 base = texture2D(baseTexture, uv).rgb; vec3 vVec = normalize(eyeVec); vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); vec3 R = reflect(-vVec, bump); vec3 lVec = normalize(vVec); float diffuse = max(dot(lVec, bump), 0.0); float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; } else { color = texture2D(baseTexture, uv).rgb; } #else color = texture2D(baseTexture, uv).rgb; #endif float alpha = texture2D(baseTexture, uv).a; vec4 col = vec4(color.r, color.g, color.b, alpha); col *= gl_Color; col = col * col; // SRGB -> Linear col *= 1.8; col.r = 1.0 - exp(1.0 - col.r) / e; col.g = 1.0 - exp(1.0 - col.g) / e; col.b = 1.0 - exp(1.0 - col.b) / e; col = sqrt(col); // Linear -> SRGB 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.r, col.g, col.b, alpha); } client/shaders/solids_shader/opengl_vertex.glsl0000644000175000017500000000575412261107224022670 0ustar mquinsonmquinsonuniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; #ifdef ENABLE_PARALLAX_OCCLUSION varying vec3 tsEyeVec; #endif void main(void) { gl_Position = mWorldViewProj * gl_Vertex; vPosition = (mWorldViewProj * gl_Vertex).xyz; eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; #ifdef ENABLE_PARALLAX_OCCLUSION 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); tsEyeVec = normalize(eyeVec * tbnMatrix); #endif vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); float day = gl_Color.r; float night = gl_Color.g; float light_source = gl_Color.b; /*color.r = mix(night, day, dayNightRatio); color.g = color.r; color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue b += (day - night) / 13.0; rg -= (day - night) / 13.0; // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); // Artificial light is yellow-ish // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); color.r = clamp(rg,0.0,1.0); color.g = clamp(rg,0.0,1.0); color.b = clamp(b,0.0,1.0); // Make sides and bottom darker than the top color = color * color; // SRGB -> Linear if(gl_Normal.y <= 0.5) color *= 0.6; //color *= 0.7; color = sqrt(color); // Linear -> SRGB color.a = gl_Color.a; gl_FrontColor = gl_BackColor = color; gl_TexCoord[0] = gl_MultiTexCoord0; } client/shaders/liquids_shader/0000755000175000017500000000000012261107224017266 5ustar mquinsonmquinsonclient/shaders/liquids_shader/base.txt0000644000175000017500000000001612261107224020736 0ustar mquinsonmquinsontrans_alphach client/shaders/liquids_shader/opengl_fragment.glsl0000644000175000017500000000274412261107224023327 0ustar mquinsonmquinsonuniform sampler2D baseTexture; uniform sampler2D normalTexture; uniform sampler2D useNormalmap; uniform vec4 skyBgColor; uniform float fogDistance; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; const float e = 2.718281828459; void main (void) { vec3 color; vec2 uv = gl_TexCoord[0].st; #ifdef USE_NORMALMAPS float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; #endif #ifdef ENABLE_BUMPMAPPING if (use_normalmap > 0.0) { vec3 base = texture2D(baseTexture, uv).rgb; vec3 vVec = normalize(eyeVec); vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); vec3 R = reflect(-vVec, bump); vec3 lVec = normalize(vVec); float diffuse = max(dot(vec3(-1.0, -0.4, 0.5), bump), 0.0); float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; } else { color = texture2D(baseTexture, uv).rgb; } #else color = texture2D(baseTexture, uv).rgb; #endif float alpha = gl_Color.a; vec4 col = vec4(color.r, color.g, color.b, alpha); col *= gl_Color; col = col * col; // SRGB -> Linear col *= 1.8; col.r = 1.0 - exp(1.0 - col.r) / e; col.g = 1.0 - exp(1.0 - col.g) / e; col.b = 1.0 - exp(1.0 - col.b) / e; col = sqrt(col); // Linear -> SRGB 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); } gl_FragColor = vec4(col.r, col.g, col.b, alpha); } client/shaders/liquids_shader/opengl_vertex.glsl0000644000175000017500000000327112261107224023035 0ustar mquinsonmquinsonuniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; uniform float animationTimer; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; void main(void) { #ifdef ENABLE_WAVING_WATER vec4 pos2 = gl_Vertex; pos2.y -= 2.0; pos2.y -= sin (pos2.z/WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH) * WATER_WAVE_HEIGHT + sin ((pos2.z/WATER_WAVE_LENGTH + animationTimer * WATER_WAVE_SPEED * WATER_WAVE_LENGTH) / 7.0) * WATER_WAVE_HEIGHT; gl_Position = mWorldViewProj * pos2; #else gl_Position = mWorldViewProj * gl_Vertex; #endif eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; vPosition = (mWorldViewProj * gl_Vertex).xyz; vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); float day = gl_Color.r; float night = gl_Color.g; float light_source = gl_Color.b; /*color.r = mix(night, day, dayNightRatio); color.g = color.r; color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue b += (day - night) / 13.0; rg -= (day - night) / 13.0; // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); // Artificial light is yellow-ish // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); color.r = clamp(rg,0.0,1.0); color.g = clamp(rg,0.0,1.0); color.b = clamp(b,0.0,1.0); color.a = gl_Color.a; gl_FrontColor = gl_BackColor = color; gl_TexCoord[0] = gl_MultiTexCoord0; } client/shaders/alpha_shader/0000755000175000017500000000000012261107224016701 5ustar mquinsonmquinsonclient/shaders/alpha_shader/base.txt0000644000175000017500000000001612261107224020351 0ustar mquinsonmquinsontrans_alphach client/shaders/alpha_shader/opengl_fragment.glsl0000644000175000017500000000360312261107224022735 0ustar mquinsonmquinsonuniform sampler2D baseTexture; uniform sampler2D normalTexture; uniform sampler2D useNormalmap; uniform vec4 skyBgColor; uniform float fogDistance; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; #ifdef ENABLE_PARALLAX_OCCLUSION varying vec3 tsEyeVec; #endif const float e = 2.718281828459; void main (void) { vec3 color; vec2 uv = gl_TexCoord[0].st; #ifdef USE_NORMALMAPS float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; #endif #ifdef ENABLE_PARALLAX_OCCLUSION float height; vec2 tsEye = vec2(tsEyeVec.x,-tsEyeVec.y); if (use_normalmap > 0.0) { float map_height = texture2D(normalTexture, uv).a; if (map_height < 1.0){ float height = PARALLAX_OCCLUSION_SCALE * map_height - PARALLAX_OCCLUSION_BIAS; uv = uv + height * tsEye; } } #endif #ifdef ENABLE_BUMPMAPPING if (use_normalmap > 0.0) { vec3 base = texture2D(baseTexture, uv).rgb; vec3 vVec = normalize(eyeVec); vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); vec3 R = reflect(-vVec, bump); vec3 lVec = normalize(vVec); float diffuse = max(dot(lVec, bump), 0.0); float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; } else { color = texture2D(baseTexture, uv).rgb; } #else color = texture2D(baseTexture, uv).rgb; #endif float alpha = texture2D(baseTexture, uv).a; vec4 col = vec4(color.r, color.g, color.b, alpha); col *= gl_Color; col = col * col; // SRGB -> Linear col *= 1.8; col.r = 1.0 - exp(1.0 - col.r) / e; col.g = 1.0 - exp(1.0 - col.g) / e; col.b = 1.0 - exp(1.0 - col.b) / e; col = sqrt(col); // Linear -> SRGB 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.r, col.g, col.b, alpha); } client/shaders/alpha_shader/opengl_vertex.glsl0000644000175000017500000000575412261107224022460 0ustar mquinsonmquinsonuniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; #ifdef ENABLE_PARALLAX_OCCLUSION varying vec3 tsEyeVec; #endif void main(void) { gl_Position = mWorldViewProj * gl_Vertex; vPosition = (mWorldViewProj * gl_Vertex).xyz; eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; #ifdef ENABLE_PARALLAX_OCCLUSION 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); tsEyeVec = normalize(eyeVec * tbnMatrix); #endif vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); float day = gl_Color.r; float night = gl_Color.g; float light_source = gl_Color.b; /*color.r = mix(night, day, dayNightRatio); color.g = color.r; color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue b += (day - night) / 13.0; rg -= (day - night) / 13.0; // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); // Artificial light is yellow-ish // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); color.r = clamp(rg,0.0,1.0); color.g = clamp(rg,0.0,1.0); color.b = clamp(b,0.0,1.0); // Make sides and bottom darker than the top color = color * color; // SRGB -> Linear if(gl_Normal.y <= 0.5) color *= 0.6; //color *= 0.7; color = sqrt(color); // Linear -> SRGB color.a = gl_Color.a; gl_FrontColor = gl_BackColor = color; gl_TexCoord[0] = gl_MultiTexCoord0; } client/shaders/plants_shader/0000755000175000017500000000000012261107224017115 5ustar mquinsonmquinsonclient/shaders/plants_shader/base.txt0000644000175000017500000000002212261107224020562 0ustar mquinsonmquinsontrans_alphach_ref client/shaders/plants_shader/opengl_fragment.glsl0000644000175000017500000000275012261107224023153 0ustar mquinsonmquinsonuniform sampler2D baseTexture; uniform sampler2D normalTexture; uniform sampler2D useNormalmap; uniform vec4 skyBgColor; uniform float fogDistance; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; const float e = 2.718281828459; void main (void) { vec3 color; vec2 uv = gl_TexCoord[0].st; #ifdef USE_NORMALMAPS float use_normalmap = texture2D(useNormalmap,vec2(1.0,1.0)).r; #endif #ifdef ENABLE_BUMPMAPPING if (use_normalmap > 0.0) { vec3 base = texture2D(baseTexture, uv).rgb; vec3 vVec = normalize(eyeVec); vec3 bump = normalize(texture2D(normalTexture, uv).xyz * 2.0 - 1.0); vec3 R = reflect(-vVec, bump); vec3 lVec = normalize(vVec); float diffuse = max(dot(lVec, bump), 0.0); float specular = pow(clamp(dot(R, lVec), 0.0, 1.0),1.0); color = mix (base,diffuse*base,1.0) + 0.1 * specular * diffuse; } else { color = texture2D(baseTexture, uv).rgb; } #else color = texture2D(baseTexture, uv).rgb; #endif float alpha = texture2D(baseTexture, uv).a; vec4 col = vec4(color.r, color.g, color.b, alpha); col *= gl_Color; col = col * col; // SRGB -> Linear col *= 1.8; col.r = 1.0 - exp(1.0 - col.r) / e; col.g = 1.0 - exp(1.0 - col.g) / e; col.b = 1.0 - exp(1.0 - col.b) / e; col = sqrt(col); // Linear -> SRGB 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.r, col.g, col.b, alpha); } client/shaders/plants_shader/opengl_vertex.glsl0000644000175000017500000000430012261107224022656 0ustar mquinsonmquinsonuniform mat4 mWorldViewProj; uniform mat4 mInvWorld; uniform mat4 mTransWorld; uniform float dayNightRatio; uniform float animationTimer; uniform vec3 eyePosition; varying vec3 vPosition; varying vec3 eyeVec; #ifdef ENABLE_WAVING_PLANTS 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 smoothTriangleWave( float x ) { return smoothCurve( triangleWave( x ) ) * 2.0 - 1.0; } #endif void main(void) { gl_TexCoord[0] = gl_MultiTexCoord0; #ifdef ENABLE_WAVING_PLANTS vec4 pos = gl_Vertex; vec4 pos2 = mTransWorld * gl_Vertex; if (gl_TexCoord[0].y < 0.05) { pos.x += (smoothTriangleWave(animationTimer * 20.0 + pos2.x * 0.1 + pos2.z * 0.1) * 2.0 - 1.0) * 0.8; pos.y -= (smoothTriangleWave(animationTimer * 10.0 + pos2.x * -0.5 + pos2.z * -0.5) * 2.0 - 1.0) * 0.4; } gl_Position = mWorldViewProj * pos; #else gl_Position = mWorldViewProj * gl_Vertex; #endif vPosition = (mWorldViewProj * gl_Vertex).xyz; eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; vec4 color; //color = vec4(1.0, 1.0, 1.0, 1.0); float day = gl_Color.r; float night = gl_Color.g; float light_source = gl_Color.b; /*color.r = mix(night, day, dayNightRatio); color.g = color.r; color.b = color.r;*/ float rg = mix(night, day, dayNightRatio); rg += light_source * 2.5; // Make light sources brighter float b = rg; // Moonlight is blue b += (day - night) / 13.0; rg -= (day - night) / 13.0; // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); // Artificial light is yellow-ish // See C++ implementation in mapblock_mesh.cpp finalColorBlend() rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); color.r = clamp(rg,0.0,1.0); color.g = clamp(rg,0.0,1.0); color.b = clamp(b,0.0,1.0); // Make sides and bottom darker than the top color = color * color; // SRGB -> Linear if(gl_Normal.y <= 0.5) color *= 0.6; //color *= 0.7; color = sqrt(color); // Linear -> SRGB color.a = gl_Color.a; gl_FrontColor = gl_BackColor = color; } client/serverlist/0000755000175000017500000000000012261107224015037 5ustar mquinsonmquinsonclient/serverlist/.gitignore0000644000175000017500000000001612261107224017024 0ustar mquinsonmquinson* !.gitignore cmake/0000755000175000017500000000000012261107224012437 5ustar mquinsonmquinsoncmake/Modules/0000755000175000017500000000000012261107224014047 5ustar mquinsonmquinsoncmake/Modules/FindSqlite3.cmake0000644000175000017500000000111612261107224017175 0ustar mquinsonmquinson# Look for sqlite3, use our own if not found FIND_PATH(SQLITE3_INCLUDE_DIR sqlite3.h) FIND_LIBRARY(SQLITE3_LIBRARY NAMES sqlite3) IF(SQLITE3_LIBRARY AND SQLITE3_INCLUDE_DIR) SET( SQLITE3_FOUND TRUE ) ENDIF(SQLITE3_LIBRARY AND SQLITE3_INCLUDE_DIR) IF(SQLITE3_FOUND) MESSAGE(STATUS "Found system sqlite3 header file in ${SQLITE3_INCLUDE_DIR}") MESSAGE(STATUS "Found system sqlite3 library ${SQLITE3_LIBRARY}") ELSE(SQLITE3_FOUND) SET(SQLITE3_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/sqlite) SET(SQLITE3_LIBRARY sqlite3) MESSAGE(STATUS "Using project sqlite3 library") ENDIF(SQLITE3_FOUND) cmake/Modules/FindOpenGLES2.cmake0000644000175000017500000000660612261107224017320 0ustar mquinsonmquinson#------------------------------------------------------------------- # This file is stolen from part of the CMake build system for OGRE (Object-oriented Graphics Rendering Engine) http://www.ogre3d.org/ # # The contents of this file are placed in the public domain. Feel # free to make use of it in any way you like. #------------------------------------------------------------------- # - Try to find OpenGLES and EGL # Once done this will define # # OPENGLES2_FOUND - system has OpenGLES # OPENGLES2_INCLUDE_DIR - the GL include directory # OPENGLES2_LIBRARIES - Link these to use OpenGLES # # EGL_FOUND - system has EGL # 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) 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 ) FIND_LIBRARY(EGL_egl_LIBRARY NAMES EGL PATHS /opt/graphics/OpenGL/lib /usr/openwin/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 ) IF(OPENGLES2_FOUND) MESSAGE(STATUS "Found system opengles2 library ${OPENGLES2_LIBRARIES}") ELSE () SET(OPENGLES2_LIBRARIES "") ENDIF () cmake/Modules/FindVorbis.cmake0000644000175000017500000000331612261107224017121 0ustar mquinsonmquinson# - Find vorbis # Find the native vorbis includes and libraries # # VORBIS_INCLUDE_DIR - where to find vorbis.h, etc. # OGG_INCLUDE_DIR - where to find ogg/ogg.h, etc. # VORBIS_LIBRARIES - List of libraries when using vorbis(file). # VORBIS_FOUND - True if vorbis found. if(NOT GP2XWIZ) if(VORBIS_INCLUDE_DIR) # Already in cache, be silent set(VORBIS_FIND_QUIETLY TRUE) endif(VORBIS_INCLUDE_DIR) find_path(OGG_INCLUDE_DIR ogg/ogg.h) find_path(VORBIS_INCLUDE_DIR vorbis/vorbisfile.h) # MSVC built ogg/vorbis may be named ogg_static and vorbis_static find_library(OGG_LIBRARY NAMES ogg ogg_static) find_library(VORBIS_LIBRARY NAMES vorbis vorbis_static) find_library(VORBISFILE_LIBRARY NAMES vorbisfile vorbisfile_static) # Handle the QUIETLY and REQUIRED arguments and set VORBIS_FOUND # to TRUE if all listed variables are TRUE. include(FindPackageHandleStandardArgs) find_package_handle_standard_args(VORBIS DEFAULT_MSG OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) else(NOT GP2XWIZ) find_path(VORBIS_INCLUDE_DIR tremor/ivorbisfile.h) find_library(VORBIS_LIBRARY NAMES vorbis_dec) find_package_handle_standard_args(VORBIS DEFAULT_MSG VORBIS_INCLUDE_DIR VORBIS_LIBRARY) endif(NOT GP2XWIZ) if(VORBIS_FOUND) if(NOT GP2XWIZ) set(VORBIS_LIBRARIES ${VORBISFILE_LIBRARY} ${VORBIS_LIBRARY} ${OGG_LIBRARY}) else(NOT GP2XWIZ) set(VORBIS_LIBRARIES ${VORBIS_LIBRARY}) endif(NOT GP2XWIZ) else(VORBIS_FOUND) set(VORBIS_LIBRARIES) endif(VORBIS_FOUND) mark_as_advanced(OGG_INCLUDE_DIR VORBIS_INCLUDE_DIR) mark_as_advanced(OGG_LIBRARY VORBIS_LIBRARY VORBISFILE_LIBRARY) cmake/Modules/misc.cmake0000644000175000017500000000110212261107224015776 0ustar mquinsonmquinson# # 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) cmake/Modules/GenerateVersion.cmake0000644000175000017500000000112112261107224020144 0ustar mquinsonmquinson# Always run during 'make' if(VERSION_EXTRA) set(VERSION_GITHASH "${VERSION_STRING}") else(VERSION_EXTRA) execute_process(COMMAND git describe --always --tag --dirty 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}") endif() endif() configure_file( ${GENERATE_VERSION_SOURCE_DIR}/cmake_config_githash.h.in ${GENERATE_VERSION_BINARY_DIR}/cmake_config_githash.h) cmake/Modules/FindCURL.cmake0000644000175000017500000000304412261107224016420 0ustar mquinsonmquinson# - 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. if( UNIX ) FIND_PATH(CURL_INCLUDE_DIR NAMES curl.h PATHS /usr/local/include/curl /usr/include/curl ) 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 ) 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 ) MESSAGE(STATUS "CURL_INCLUDE_DIR = ${CURL_INCLUDE_DIR}") MESSAGE(STATUS "CURL_LIBRARY = ${CURL_LIBRARY}") MESSAGE(STATUS "CURL_DLL = ${CURL_DLL}") cmake/Modules/FindJson.cmake0000644000175000017500000000104612261107224016564 0ustar mquinsonmquinson# Look for json, use our own if not found #FIND_PATH(JSON_INCLUDE_DIR json.h) #FIND_LIBRARY(JSON_LIBRARY NAMES jsoncpp) #IF(JSON_LIBRARY AND JSON_INCLUDE_DIR) # SET( JSON_FOUND TRUE ) #ENDIF(JSON_LIBRARY AND JSON_INCLUDE_DIR) #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) cmake/Modules/FindIrrlicht.cmake0000644000175000017500000000447012261107224017437 0ustar mquinsonmquinson#FindIrrlicht.cmake set(IRRLICHT_SOURCE_DIR "" CACHE PATH "Path to irrlicht source directory (optional)") if( UNIX ) # Unix else( UNIX ) # Windows endif( UNIX ) # Find include directory if(NOT IRRLICHT_SOURCE_DIR STREQUAL "") set(IRRLICHT_SOURCE_DIR_INCLUDE "${IRRLICHT_SOURCE_DIR}/include" ) set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a Irrlicht Irrlicht.lib) if(WIN32) if(MSVC) set(IRRLICHT_SOURCE_DIR_LIBS "${IRRLICHT_SOURCE_DIR}/lib/Win32-visualstudio") set(IRRLICHT_LIBRARY_NAMES Irrlicht.lib) else() set(IRRLICHT_SOURCE_DIR_LIBS "${IRRLICHT_SOURCE_DIR}/lib/Win32-gcc") set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a libIrrlicht.dll.a) endif() else() set(IRRLICHT_SOURCE_DIR_LIBS "${IRRLICHT_SOURCE_DIR}/lib/Linux") set(IRRLICHT_LIBRARY_NAMES libIrrlicht.a) endif() FIND_PATH(IRRLICHT_INCLUDE_DIR NAMES irrlicht.h PATHS ${IRRLICHT_SOURCE_DIR_INCLUDE} NO_DEFAULT_PATH ) 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 PATHS /usr/local/include/irrlicht /usr/include/irrlicht ) FIND_LIBRARY(IRRLICHT_LIBRARY NAMES 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 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)" ) else() FIND_FILE(IRRLICHT_DLL NAMES Irrlicht.dll PATHS "${IRRLICHT_SOURCE_DIR}/bin/Win32-gcc" DOC "Path of the Irrlicht dll (for installation)" ) endif() MESSAGE(STATUS "IRRLICHT_DLL = ${IRRLICHT_DLL}") 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) cmake/Modules/FindGettextLib.cmake0000644000175000017500000000445712261107224017737 0ustar mquinsonmquinson# Package finder for gettext libs and include files 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 NAMES libintl.h PATHS "${CUSTOM_GETTEXT_PATH}/include" DOC "gettext include directory") FIND_PROGRAM(GETTEXT_MSGFMT NAMES msgfmt PATHS "${CUSTOM_GETTEXT_PATH}/bin" DOC "path to msgfmt") # 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 libintl.lib intl.lib libintl3.lib intl3.lib) ELSE() SET(GETTEXT_LIB_NAMES libintl.dll.a intl.dll.a libintl3.dll.a intl3.dll.a) ENDIF() FIND_LIBRARY(GETTEXT_LIBRARY NAMES ${GETTEXT_LIB_NAMES} PATHS "${CUSTOM_GETTEXT_PATH}/lib" DOC "gettext *intl*.lib") 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 NAMES libiconv2.dll PATHS "${CUSTOM_GETTEXT_PATH}/bin" "${CUSTOM_GETTEXT_PATH}/lib" DOC "gettext *iconv*.lib") ENDIF(WIN32) 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() IF(GETTEXT_FOUND) SET(GETTEXT_PO_PATH ${CMAKE_SOURCE_DIR}/po) SET(GETTEXT_MO_BUILD_PATH ${CMAKE_BINARY_DIR}/locale//LC_MESSAGES) SET(GETTEXT_MO_DEST_PATH ${LOCALEDIR}//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} ${_buildvar} ${GETTEXT_MO_BUILD_PATH}) STRING(REPLACE "" ${_locale} ${_destvar} ${GETTEXT_MO_DEST_PATH}) ENDMACRO(SET_MO_PATHS) ELSE() SET(GETTEXT_INCLUDE_DIR "") SET(GETTEXT_LIBRARY "") ENDIF() doc/0000755000175000017500000000000012261107224012124 5ustar mquinsonmquinsondoc/mapformat.txt0000644000175000017500000003433612261107224014664 0ustar mquinsonmquinson============================= Minetest World Format 22...25 ============================= This applies to a world format carrying the block serialization version 22...25, used at least in - 0.4.dev-20120322 ... 0.4.dev-20120606 (22...23) - 0.4.0 (23) - 24 was never released as stable and existed for ~2 days The block serialization version does not fully specify every aspect of this format; if compliance with this format is to be checked, it needs to be done by detecting if the files and data indeed follows it. Legacy stuff ============= Data can, in theory, be contained in the flat file directory structure described below in Version 17, but it is not officially supported. Also you may stumble upon all kinds of oddities in not-so-recent formats. Files ====== Everything is contained in a directory, the name of which is freeform, but often serves as the name of the world. Currently the authentication and ban data is stored on a per-world basis. It can be copied over from an old world to a newly created world. World |-- auth.txt ----- Authentication data |-- env_meta.txt - Environment metadata |-- ipban.txt ---- Banned ips/users |-- map_meta.txt - Map metadata |-- map.sqlite --- Map data |-- players ------ Player directory | |-- player1 -- Player file | '-- Foo ------ Player file `-- world.mt ----- World metadata auth.txt --------- Contains authentication data, player per line. :: Format of password hash is SHA1'd, in the base64 encoding. Example lines: - Player "celeron55", no password, privileges "interact" and "shout": celeron55::interact,shout - Player "Foo", password "bar", privilege "shout": foo:iEPX+SQWIR3p67lj/0zigSWTKHg:shout - Player "bar", no password, no privileges: bar:: env_meta.txt ------------- Simple global environment variables. Example content (added indentation): game_time = 73471 time_of_day = 19118 EnvArgsEnd ipban.txt ---------- Banned IP addresses and usernames. Example content (added indentation): 123.456.78.9|foo 123.456.78.10|bar map_meta.txt ------------- Simple global map variables. Example content (added indentation): seed = 7980462765762429666 [end_of_params] map.sqlite ----------- Map data. See Map File Format below. player1, Foo ------------- Player data. Filename can be anything. See Player File Format below. world.mt --------- World metadata. Example content (added indentation): gameid = mesetint Player File Format =================== - Should be pretty self-explanatory. - Note: position is in nodes * 10 Example content (added indentation): hp = 11 name = celeron55 pitch = 39.77 position = (-5231.97,15,1961.41) version = 1 yaw = 101.37 PlayerArgsEnd List main 32 Item default:torch 13 Item default:pick_steel 1 50112 Item experimental:tnt Item default:cobble 99 Item default:pick_stone 1 13104 Item default:shovel_steel 1 51838 Item default:dirt 61 Item default:rail 78 Item default:coal_lump 3 Item default:cobble 99 Item default:leaves 22 Item default:gravel 52 Item default:axe_steel 1 2045 Item default:cobble 98 Item default:sand 61 Item default:water_source 94 Item default:glass 2 Item default:mossycobble Item default:pick_steel 1 64428 Item animalmaterials:bone Item default:sword_steel Item default:sapling Item default:sword_stone 1 10647 Item default:dirt 99 Empty Empty Empty Empty Empty Empty Empty Empty EndInventoryList List craft 9 Empty Empty Empty Empty Empty Empty Empty Empty Empty EndInventoryList List craftpreview 1 Empty EndInventoryList List craftresult 1 Empty EndInventoryList EndInventory Map File Format ================ Minetest maps consist of MapBlocks, chunks of 16x16x16 nodes. In addition to the bulk node data, MapBlocks stored on disk also contain other things. History -------- We need a bit of history in here. Initially Minetest stored maps in a format called the "sectors" format. It was a directory/file structure like this: sectors2/XXX/ZZZ/YYYY For example, the MapBlock at (0,1,-2) was this file: sectors2/000/ffd/0001 Eventually Minetest outgrow this directory structure, as filesystems were struggling under the amount of files and directories. Large servers seriously needed a new format, and thus the base of the current format was invented, suggested by celeron55 and implemented by JacobF. SQLite3 was slammed in, and blocks files were directly inserted as blobs in a single table, indexed by integer primary keys, oddly mangled from coordinates. Today we know that SQLite3 allows multiple primary keys (which would allow storing coordinates separately), but the format has been kept unchanged for that part. So, this is where it has come. So here goes ------------- map.sqlite is an sqlite3 database, containg a single table, called "blocks". It looks like this: CREATE TABLE `blocks` (`pos` INT NOT NULL PRIMARY KEY,`data` BLOB); The key -------- "pos" is created from the three coordinates of a MapBlock using this algorithm, defined here in Python: def getBlockAsInteger(p): return int64(p[2]*16777216 + p[1]*4096 + p[0]) def int64(u): while u >= 2**63: u -= 2**64 while u <= -2**63: u += 2**64 return u It can be converted the other way by using this code: def getIntegerAsBlock(i): x = unsignedToSigned(i % 4096, 2048) i = int((i - x) / 4096) y = unsignedToSigned(i % 4096, 2048) i = int((i - y) / 4096) z = unsignedToSigned(i % 4096, 2048) return x,y,z def unsignedToSigned(i, max_positive): if i < max_positive: return i else: return i - 2*max_positive The blob --------- The blob is the data that would have otherwise gone into the file. See below for description. MapBlock serialization format ============================== NOTE: Byte order is MSB first (big-endian). NOTE: Zlib data is in such a format that Python's zlib at least can directly decompress. u8 version - map format version number, this one is version 22 u8 flags - Flag bitmasks: - 0x01: is_underground: Should be set to 0 if there will be no light obstructions above the block. If/when sunlight of a block is updated and there is no block above it, this value is checked for determining whether sunlight comes from the top. - 0x02: day_night_differs: Whether the lighting of the block is different on day and night. Only blocks that have this bit set are updated when day transforms to night. - 0x04: lighting_expired: If true, lighting is invalid and should be updated. If you can't calculate lighting in your generator properly, you could try setting this 1 to everything and setting the uppermost block in every sector as is_underground=0. I am quite sure it doesn't work properly, though. - 0x08: generated: True if the block has been generated. If false, block is mostly filled with CONTENT_IGNORE and is likely to contain eg. parts of trees of neighboring blocks. u8 content_width - Number of bytes in the content (param0) fields of nodes if map format version <= 23: - Always 1 if map format version >= 24: - Always 2 u8 params_width - Number of bytes used for parameters per node - Always 2 zlib-compressed node data: if content_width == 1: - content: u8[4096]: param0 fields u8[4096]: param1 fields u8[4096]: param2 fields if content_width == 2: - content: u16[4096]: param0 fields u8[4096]: param1 fields u8[4096]: param2 fields - The location of a node in each of those arrays is (z*16*16 + y*16 + x). zlib-compressed node metadata list - content: u16 version (=1) u16 count of metadata foreach count: u16 position (p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X) u16 type_id u16 content_size u8[content_size] (content of metadata) - Node timers if map format version == 23: u8 unused version (always 0) if map format version == 24: (NOTE: Not released as stable) u8 nodetimer_version if nodetimer_version == 0: (nothing else) if nodetimer_version == 1: u16 num_of_timers foreach num_of_timers: u16 timer position (z*16*16 + y*16 + x) s32 timeout*1000 s32 elapsed*1000 u8 static object version: - Always 0 u16 static_object_count foreach static_object_count: u8 type (object type-id) s32 pos_x_nodes * 10000 s32 pos_y_nodes * 10000 s32 pos_z_nodes * 10000 u16 data_size u8[data_size] data u32 timestamp - Timestamp when last saved, as seconds from starting the game. - 0xffffffff = invalid/unknown timestamp, nothing should be done with the time difference when loaded u8 name-id-mapping version - Always 0 u16 num_name_id_mappings foreach num_name_id_mappings u16 id u16 name_len u8[name_len] name - Node timers if map format version == 25: u8 length of the data of a single timer (always 2+4+4=10) u16 num_of_timers foreach num_of_timers: u16 timer position (z*16*16 + y*16 + x) s32 timeout*1000 s32 elapsed*1000 EOF. Format of nodes ---------------- A node is composed of the u8 fields param0, param1 and param2. if map format version <= 23: The content id of a node is determined as so: - If param0 < 0x80, content_id = param0 - Otherwise content_id = (param0<<4) + (param2>>4) if map format version >= 24: The content id of a node is param0. The purpose of param1 and param2 depend on the definition of the node. The name-id-mapping -------------------- The mapping maps node content ids to node names. Node metadata format --------------------- 1: Generic metadata serialized inventory u32 len u8[len] text u16 len u8[len] owner u16 len u8[len] infotext u16 len u8[len] inventory drawspec u8 allow_text_input (bool) u8 removal_disabled (bool) u8 enforce_owner (bool) u32 num_vars foreach num_vars u16 len u8[len] name u32 len u8[len] value 14: Sign metadata u16 text_len u8[text_len] text 15: Chest metadata serialized inventory 16: Furnace metadata TBD 17: Locked Chest metadata u16 len u8[len] owner serialized inventory Static objects --------------- Static objects are persistent freely moving objects in the world. Object types: 1: Test object 2: Item 3: Rat (deprecated) 4: Oerkki (deprecated) 5: Firefly (deprecated) 6: MobV2 (deprecated) 7: LuaEntity 1: Item: u8 version version 0: u16 len u8[len] itemstring 7: LuaEntity: u8 version version 1: u16 len u8[len] entity name u32 len u8[len] static data s16 hp s32 velocity.x * 10000 s32 velocity.y * 10000 s32 velocity.z * 10000 s32 yaw * 1000 Itemstring format ------------------ eg. 'default:dirt 5' eg. 'default:pick_wood 21323' eg. '"default:apple" 2' eg. 'default:apple' - The wear value in tools is 0...65535 - There are also a number of older formats that you might stumble upon: eg. 'node "default:dirt" 5' eg. 'NodeItem default:dirt 5' eg. 'ToolItem WPick 21323' Inventory serialization format ------------------------------- - The inventory serialization format is line-based - The newline character used is "\n" - The end condition of a serialized inventory is always "EndInventory\n" - All the slots in a list must always be serialized. Example (format does not include "---"): --- List foo 4 Item default:sapling Item default:sword_stone 1 10647 Item default:dirt 99 Empty EndInventoryList List bar 9 Empty Empty Empty Empty Empty Empty Empty Empty Empty EndInventoryList EndInventory --- ============================================== Minetest World Format used as of 2011-05 or so ============================================== Map data serialization format version 17. 0.3.1 does not use this format, but a more recent one. This exists here for historical reasons. Directory structure: sectors/XXXXZZZZ or sectors2/XXX/ZZZ XXXX, ZZZZ, XXX and ZZZ being the hexadecimal X and Z coordinates. Under these, the block files are stored, called YYYY. There also exists files map_meta.txt and chunk_meta, that are used by the generator. If they are not found or invalid, the generator will currently behave quite strangely. The MapBlock file format (sectors2/XXX/ZZZ/YYYY): ------------------------------------------------- NOTE: Byte order is MSB first. u8 version - map format version number, this one is version 17 u8 flags - Flag bitmasks: - 0x01: is_underground: Should be set to 0 if there will be no light obstructions above the block. If/when sunlight of a block is updated and there is no block above it, this value is checked for determining whether sunlight comes from the top. - 0x02: day_night_differs: Whether the lighting of the block is different on day and night. Only blocks that have this bit set are updated when day transforms to night. - 0x04: lighting_expired: If true, lighting is invalid and should be updated. If you can't calculate lighting in your generator properly, you could try setting this 1 to everything and setting the uppermost block in every sector as is_underground=0. I am quite sure it doesn't work properly, though. zlib-compressed map data: - content: u8[4096]: content types u8[4096]: param1 values u8[4096]: param2 values zlib-compressed node metadata - content: u16 version (=1) u16 count of metadata foreach count: u16 position (= p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X) u16 type_id u16 content_size u8[content_size] misc. stuff contained in the metadata u16 mapblockobject_count - always write as 0. - if read != 0, just fail. foreach mapblockobject_count: - deprecated, should not be used. Length of this data can only be known by properly parsing it. Just hope not to run into any of this. u8 static object version: - currently 0 u16 static_object_count foreach static_object_count: u8 type (object type-id) s32 pos_x * 1000 s32 pos_y * 1000 s32 pos_z * 1000 u16 data_size u8[data_size] data u32 timestamp - Timestamp when last saved, as seconds from starting the game. - 0xffffffff = invalid/unknown timestamp, nothing will be done with the time difference when loaded (recommended) Node metadata format: --------------------- Sign metadata: u16 string_len u8[string_len] string Furnace metadata: TBD Chest metadata: TBD Locking Chest metadata: u16 string_len u8[string_len] string TBD // END doc/minetestserver.60000644000175000017500000000304412261107224015273 0ustar mquinsonmquinson.\" Minetestserver man page .TH minetestserver 6 "10 September 2013" "" "" .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 Load configuration from specified file .TP \-\-disable\-unittests Disable unit tests .TP \-\-enable\-unittests Enable unit tests .TP \-\-gameid Set gameid .TP \-\-help Show allowed options .TP \-\-version Show version information .TP \-\-logfile Set logfile path (debug.txt) .TP \-\-map\-dir Same as \-\-world (deprecated) .TP \-\-port 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 Set world path .TP \-\-migrate Migrate from current map backend to another. Possible values are sqlite3 and leveldb. .SH BUGS Please report all bugs to Perttu Ahola . .SH AUTHOR .PP Perttu Ahola and contributors. .PP This man page was originally written by Juhani Numminen . .SH WWW http://www.minetest.net/ .SH "SEE ALSO" .BR minetest(6) doc/menu_lua_api.txt0000644000175000017500000001505312261107224015327 0ustar mquinsonmquinsonMinetest Lua Mainmenu API Reference 0.4.9 ======================================== Introduction ------------- The main menu is defined as a formspec by Lua in builtin/mainmenu.lua Description of formspec language to show your menu is in lua_api.txt Callbacks --------- engine.buttonhandler(fields): called when a button is pressed. ^ fields = {name1 = value1, name2 = value2, ...} engine.event_handler(event) ^ event: "MenuQuit", "KeyEnter", "ExitButton" or "EditBoxEnter" Gamedata -------- The "gamedata" table is read when calling engine.start(). It should contain: { playername = , password = , address = , port = , selected_world = , -- 0 for client mode singleplayer = , } Functions --------- engine.start() engine.close() Filesystem: engine.get_scriptdir() ^ returns directory of script engine.get_modpath() (possible in async calls) ^ returns path to global modpath engine.get_modstore_details(modid) (possible in async calls) ^ modid numeric id of mod in modstore ^ returns { id = , title = , basename = , description = , author = , download_url= , license = , rating = } engine.get_modstore_list() (possible in async calls) ^ returns { [1] = { id = , title = , basename = } } engine.get_gamepath() (possible in async calls) ^ returns path to global gamepath engine.get_texturepath() (possible in async calls) ^ returns path to default textures engine.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 engine.create_dir(absolute_path) (possible in async calls) ^ absolute_path to directory to create (needs to be absolute) ^ returns true/false engine.delete_dir(absolute_path) (possible in async calls) ^ absolute_path to directory to delete (needs to be absolute) ^ returns true/false engine.copy_dir(source,destination,keep_soure) (possible in async calls) ^ source folder ^ destination folder ^ keep_source DEFAULT true --> if set to false source is deleted after copying ^ returns true/false engine.extract_zip(zipfile,destination) [unzip within path required] ^ zipfile to extract ^ destination folder to extract to ^ returns true/false engine.download_file(url,target) (possible in async calls) ^ url to download ^ target to store to ^ returns true/false engine.get_version() (possible in async calls) ^ returns current minetest version engine.sound_play(spec, looped) -> handle ^ spec = SimpleSoundSpec (see lua-api.txt) ^ looped = bool engine.sound_stop(handle) GUI: engine.update_formspec(formspec) - engine.set_background(type, texturepath) ^ type: "background", "overlay", "header" or "footer" engine.set_clouds() engine.set_topleft_text(text) Games: engine.get_game(index) ^ returns { id = , path = , gamemods_path = , name = , menuicon_path = , DEPRECATED: addon_mods_paths = {[1] = ,}, } engine.get_games() -> table of all games in upper format (possible in async calls) Favorites: engine.get_favorites(location) -> list of favorites (possible in async calls) ^ location: "local" or "online" ^ returns { [1] = { clients = , clients_max = , version = , password = , creative = , damage = , pvp = , description = , name = , address =
, port = }, } engine.delete_favorite(id, location) -> success Logging: engine.debug(line) (possible in async calls) ^ Always printed to stderr and logfile (print() is redirected here) engine.log(line) (possible in async calls) engine.log(loglevel, line) (possible in async calls) ^ loglevel one of "error", "action", "info", "verbose" Settings: engine.setting_set(name, value) engine.setting_get(name) -> string or nil (possible in async calls) engine.setting_setbool(name, value) engine.setting_getbool(name) -> bool or nil (possible in async calls) engine.setting_save() -> nil, save all settings to config file Worlds: engine.get_worlds() -> list of worlds (possible in async calls) ^ returns { [1] = { path = , name = , gameid = , }, } engine.create_world(worldname, gameid) engine.delete_world(index) UI: engine.get_textlist_index(textlistname) -> index engine.show_keys_menu() engine.file_open_dialog(formname,caption) ^ shows a file open dialog ^ formname is base name of dialog response returned in fields ^ -if dialog was accepted "_accepted" ^^ will be added to fieldname containing the path ^ -if dialog was canceled "_cancelled" ^ will be added to fieldname value is set to formname itself ^ returns nil or selected file/folder Helpers: engine.formspec_escape(string) -> string ^ escapes characters [ ] \ , ; that can not be used in formspecs engine.gettext(string) -> string ^ look up the translation of a string in the gettext message catalog fgettext(string, ...) -> string ^ call engine.gettext(string), replace "$1"..."$9" with the given ^ extra arguments, call engine.formspec_escape and return the result engine.parse_json(string[, nullvalue]) -> something (possible in async calls) ^ see minetest.parse_json (lua_api.txt) dump(obj, dumped={}) ^ Return object serialized as a string string:split(separator) ^ eg. string:split("a,b", ",") == {"a","b"} string:trim() ^ eg. string.trim("\n \t\tfoo bar\t ") == "foo bar" minetest.is_yes(arg) (possible in async calls) ^ returns whether arg can be interpreted as yes Async: engine.handle_async(async_job,parameters,finished) ^ execute a function asynchronously ^ async_job is a function receiving one parameter and returning one parameter ^ parameters parameter table passed to async_job ^ finished function to be called once async_job has finished ^ the result of async_job is passed to this function Limitations of Async operations -No access to global lua variables, don't even try -Limited set of available functions e.g. No access to functions modifying menu like engine.start,engine.close, engine.file_open_dialog Class reference ---------------- Settings: see lua_api.txt doc/lua_api.txt0000644000175000017500000030531112261107224014302 0ustar mquinsonmquinsonMinetest Lua Modding API Reference 0.4.9 ======================================== More information at http://www.minetest.net/ Developer Wiki: http://dev.minetest.net/ Introduction ------------- Content and functionality can be added to Minetest 0.4 by using Lua scripting in run-time loaded mods. A mod is a self-contained bunch of scripts, textures and other related things that is loaded by and interfaces with Minetest. Mods are contained and ran solely on the server side. Definitions and media files are automatically transferred to the client. If you see a deficiency in the API, feel free to attempt to add the functionality in the engine and API. You can send such improvements as source code patches to . Programming in Lua ------------------- If you have any difficulty in understanding this, please read: http://www.lua.org/pil/ Startup -------- Mods are loaded during server startup from the mod load paths by running the init.lua scripts in a shared environment. Paths ----- RUN_IN_PLACE=1: (Windows release, local build) $path_user: Linux: Windows: $path_share: Linux: Windows: RUN_IN_PLACE=0: (Linux release) $path_share: Linux: /usr/share/minetest Windows: /minetest-0.4.x $path_user: Linux: ~/.minetest Windows: C:/users//AppData/minetest (maybe) Games ----- Games are looked up from: $path_share/games/gameid/ $path_user/games/gameid/ where gameid is unique to each game. The game directory contains the file game.conf, which contains these fields: name = eg. name = Minetest The game directory can contain the file minetest.conf, which will be used to set default settings when running the particular game. Mod load path ------------- Generic: $path_share/games/gameid/mods/ $path_share/mods/ $path_user/games/gameid/mods/ $path_user/mods/ <-- User-installed mods $worldpath/worldmods/ In a run-in-place version (eg. the distributed windows version): minetest-0.4.x/games/gameid/mods/ minetest-0.4.x/mods/gameid/ <-- User-installed mods minetest-0.4.x/worlds/worldname/worldmods/ On an installed version on linux: /usr/share/minetest/games/gameid/mods/ ~/.minetest/mods/gameid/ <-- User-installed mods ~/.minetest/worlds/worldname/worldmods Mod load path for world-specific games -------------------------------------- It is possible to include a game in a world; in this case, no mods or games are loaded or checked from anywhere else. This is useful for eg. adventure worlds. This happens if the following directory exists: $world/game/ Mods should be then be placed in: $world/game/mods/ Modpack support ---------------- Mods can be put in a subdirectory, if the parent directory, which otherwise should be a mod, contains a file named modpack.txt. This file shall be empty, except for lines starting with #, which are comments. Mod directory structure ------------------------ mods |-- modname | |-- depends.txt | |-- screenshot.png | |-- description.txt | |-- init.lua | |-- textures | | |-- modname_stuff.png | | `-- modname_something_else.png | |-- sounds | |-- media | `-- `-- another modname: The location of this directory can be fetched by using minetest.get_modpath(modname) depends.txt: List of mods that have to be loaded before loading this mod. A single line contains a single modname. Optional dependencies can be defined by appending a question mark to a single modname. Their meaning is that if the specified mod is missing, that does not prevent this mod from being loaded. screenshot.png: A screenshot shown in modmanager within mainmenu. description.txt: File containing desctiption to be shown within mainmenu. init.lua: The main Lua script. Running this script should register everything it wants to register. Subsequent execution depends on minetest calling the registered callbacks. minetest.setting_get(name) and minetest.setting_getbool(name) can be used to read custom or existing settings at load time, if necessary. textures, sounds, media: Media files (textures, sounds, whatever) that will be transferred to the client and will be available for use by the mod. Naming convention for registered textual names ---------------------------------------------- Registered names should generally be in this format: "modname:" ( can have characters a-zA-Z0-9_) This is to prevent conflicting names from corrupting maps and is enforced by the mod loader. Example: mod "experimental", ideal item/node/entity name "tnt": -> the name should be "experimental:tnt". Enforcement can be overridden by prefixing the name with ":". This can be used for overriding the registrations of some other mod. Example: Any mod can redefine experimental:tnt by using the name ":experimental:tnt" when registering it. (also that mod is required to have "experimental" as a dependency) The ":" prefix can also be used for maintaining backwards compatibility. Aliases ------- Aliases can be added by using minetest.register_alias(name, convert_to) This will make Minetest to convert things called name to things called convert_to. This can be used for maintaining backwards compatibility. This can be also used for setting quick access names for things, eg. if you have an item called epiclylongmodname:stuff, you could do minetest.register_alias("stuff", "epiclylongmodname:stuff") and be able to use "/giveme stuff". Textures -------- Mods should generally prefix their textures with modname_, eg. given the mod name "foomod", a texture could be called "foomod_foothing.png" Textures are referred to by their complete name, or alternatively by stripping out the file extension: eg. foomod_foothing.png eg. foomod_foothing Sounds ------- Only OGG files are supported. For positional playing of sounds, only single-channel (mono) files are supported. Otherwise OpenAL will play them non-positionally. Mods should generally prefix their sounds with modname_, eg. given the mod name "foomod", a sound could be called "foomod_foosound.ogg" Sounds are referred to by their name with a dot, a single digit and the file extension stripped out. When a sound is played, the actual sound file is chosen randomly from the matching sounds. When playing the sound "foomod_foosound", the sound is chosen randomly from the available ones of the following files: foomod_foosound.ogg foomod_foosound.0.ogg foomod_foosound.1.ogg ... foomod_foosound.9.ogg Examples of sound parameter tables: -- Play locationless on all clients { gain = 1.0, -- default } -- Play locationless to a player { to_player = name, gain = 1.0, -- default } -- Play in a location { pos = {x=1,y=2,z=3}, gain = 1.0, -- default max_hear_distance = 32, -- default } -- Play connected to an object, looped { object = , gain = 1.0, -- default max_hear_distance = 32, -- default loop = true, -- only sounds connected to objects can be looped } SimpleSoundSpec: eg. "" eg. "default_place_node" eg. {} eg. {name="default_place_node"} eg. {name="default_place_node", gain=1.0} Registered definitions of stuff -------------------------------- Anything added using certain minetest.register_* functions get added to the global minetest.registered_* tables. minetest.register_entity(name, prototype table) -> minetest.registered_entities[name] minetest.register_node(name, node definition) -> minetest.registered_items[name] -> minetest.registered_nodes[name] minetest.register_tool(name, item definition) -> minetest.registered_items[name] minetest.register_craftitem(name, item definition) -> minetest.registered_items[name] Note that in some cases you will stumble upon things that are not contained in these tables (eg. when a mod has been removed). Always check for existence before trying to access the fields. Example: If you want to check the drawtype of a node, you could do: local function get_nodedef_field(nodename, fieldname) if not minetest.registered_nodes[nodename] then return nil end return minetest.registered_nodes[nodename][fieldname] end local drawtype = get_nodedef_field(nodename, "drawtype") Example: minetest.get_item_group(name, group) has been implemented as: function minetest.get_item_group(name, group) if not minetest.registered_items[name] or not minetest.registered_items[name].groups[group] then return 0 end return minetest.registered_items[name].groups[group] end Nodes ------ Nodes are the bulk data of the world: cubes and other things that take the space of a cube. Huge amounts of them are handled efficiently, but they are quite static. The definition of a node is stored and can be accessed by name in minetest.registered_nodes[node.name] See "Registered definitions of stuff". Nodes are passed by value between Lua and the engine. They are represented by a table: {name="name", param1=num, param2=num} param1 and param2 are 8 bit integers. The engine uses them for certain automated functions. If you don't use these functions, you can use them to store arbitrary values. The functions of param1 and param2 are determined by certain fields in the node definition: param1 is reserved for the engine when paramtype != "none": paramtype = "light" ^ The value stores light with and without sun in it's upper and lower 4 bits. param2 is reserved for the engine when any of these are used: liquidtype == "flowing" ^ The level and some flags of the liquid is stored in param2 drawtype == "flowingliquid" ^ The drawn liquid level is read from param2 drawtype == "torchlike" drawtype == "signlike" paramtype2 == "wallmounted" ^ The rotation of the node is stored in param2. You can make this value by using minetest.dir_to_wallmounted(). paramtype2 == "facedir" ^ The rotation of the node is stored in param2. Furnaces and chests are rotated this way. Can be made by using minetest.dir_to_facedir(). Values range 0 - 23 facedir modulo 4 = axisdir 0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y- facedir's two less significant bits are rotation around the axis paramtype2 == "leveled" ^ The drawn node level is read from param2, like flowingliquid Nodes can also contain extra data. See "Node Metadata". Node drawtypes --------------- There are a bunch of different looking node types. These are mostly just copied from Minetest 0.3; more may be made in the future. Look for examples in games/minimal or games/minetest_game. - normal - airlike - liquid - flowingliquid - glasslike - glasslike_framed - allfaces - allfaces_optional - torchlike - signlike - plantlike - fencelike - raillike - nodebox -- See below. EXPERIMENTAL Node boxes ----------- Node selection boxes are defined using "node boxes" The "nodebox" node drawtype allows defining visual of nodes consisting of arbitrary number of boxes. It allows defining stuff like stairs. Only the "fixed" and "leveled" box type is supported for these. ^ Please note that this is still experimental, and may be incompatibly changed in the future. A nodebox is defined as any of: { -- A normal cube; the default in most things type = "regular" } { -- A fixed box (facedir param2 is used, if applicable) type = "fixed", fixed = box OR {box1, box2, ...} } { -- A box like the selection box for torches -- (wallmounted param2 is used, if applicable) type = "wallmounted", wall_top = box, wall_bottom = box, wall_side = box } A box is defined as: {x1, y1, z1, x2, y2, z2} A box of a regular node would look like: {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, type = "leveled" is same as "fixed", but y2 will be automaticaly setted to level from param2 Ore types --------------- These tell in what manner the ore is generated. All default ores are of the uniformly-distributed scatter type. - scatter 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_threshhold, 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. IMPORTANT: The noise is not transformed by offset or scale when comparing against the noise threshhold, 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. - claylike - NOT YET IMPLEMENTED Places ore if there are no more than clust_scarcity number of specified nodes within a Von Neumann neighborhood of clust_size radius. Ore attributes ------------------- Currently supported flags: absheight - absheight Also produce this same ore between the height range of -height_max and -height_min. Useful for having ore in sky realms without having to duplicate ore entries. Decoration types ------------------- The varying types of decorations that can be placed. The default value is simple, and is currently the only type supported. - simple Creates a 1xHx1 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, papyrus, 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. 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. Schematic attributes --------------------- Currently supported flags: place_center_x, place_center_y, place_center_z - 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. 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. 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. 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. Fractional values can be used. The offset field specifies a pixel offset from the position. Contrary to position, the offset is not scaled to screen size. This allows for some precisely-positioned 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. - image Displays an image on the HUD. - scale: The scale of the image, with 1 being the original texture size. Only the X coordinate scale is used (positive values) Negative values represent that percentage of the screen it should take; e.g. x=-100 means 100% (width) - text: The name of the texture that is displayed. - alignment: The alignment of the image. - offset: offset in pixels from position. - text Displays text on the HUD. - scale: Defines the bounding rectangle of the text. A value such as {x=100, y=100} should work. - text: The text to be displayed in the HUD element. - number: An integer containing the RGB value of the color used to draw the text. Specify 0xFFFFFF for white text, 0xFF0000 for red, and so on. - alignment: The alignment of the text. - offset: offset in pixels from position. - statbar Displays a horizontal bar made up of half-images. - text: The name of the texture that is used. - number: The number of half-textures that are displayed. If odd, will end with a vertically center-split texture. - direction - offset: offset in pixels from position. - inventory - text: The name of the inventory list to be displayed. - number: Number of items in the inventory to be displayed. - item: Position of item that is selected. - direction Representations of simple things -------------------------------- Position/vector: {x=num, y=num, z=num} For helper functions see "Vector helpers". pointed_thing: {type="nothing"} {type="node", under=pos, above=pos} {type="object", ref=ObjectRef} Items ------ Node (register_node): A node from the world Tool (register_tool): A tool/weapon that can dig and damage things according to tool_capabilities Craftitem (register_craftitem): A miscellaneous item Items and item stacks can exist in three formats: Serialized; This is called stackstring or itemstring: eg. 'default:dirt 5' eg. 'default:pick_wood 21323' eg. 'default:apple' Table format: eg. {name="default:dirt", count=5, wear=0, metadata=""} ^ 5 dirt nodes eg. {name="default:pick_wood", count=1, wear=21323, metadata=""} ^ a wooden pick about 1/3 weared out eg. {name="default:apple", count=1, wear=0, metadata=""} ^ an apple. ItemStack: C++ native format with many helper methods. Useful for converting between formats. See the Class reference section for details. When an item must be passed to a function, it can usually be in any of these formats. Groups ------- In a number of places, there is a group table. Groups define the properties of a thing (item, node, armor of entity, capabilities of tool) in such a way that the engine and other mods can can interact with the thing without actually knowing what the thing is. Usage: - Groups are stored in a table, having the group names with keys and the group ratings as values. For example: groups = {crumbly=3, soil=1} ^ Default dirt groups = {crumbly=2, soil=1, level=2, outerspace=1} ^ A more special dirt-kind of thing - Groups always have a rating associated with them. If there is no useful meaning for a rating for an enabled group, it shall be 1. - When not defined, the rating of a group defaults to 0. Thus when you read groups, you must interpret nil and 0 as the same value, 0. You can read the rating of a group for an item or a node by using minetest.get_item_group(itemname, groupname) Groups of items ---------------- Groups of items can define what kind of an item it is (eg. wool). Groups of nodes ---------------- In addition to the general item things, groups are used to define whether a node is destroyable and how long it takes to destroy by a tool. Groups of entities ------------------- For entities, groups are, as of now, used only for calculating damage. The rating is the percentage of damage caused by tools with this damage group. See "Entity damage mechanism". object.get_armor_groups() -> a group-rating table (eg. {fleshy=100}) object.set_armor_groups({fleshy=30, cracky=80}) Groups of tools ---------------- Groups in tools define which groups of nodes and entities they are effective towards. Groups in crafting recipes --------------------------- An example: Make meat soup from any meat, any water and any bowl { output = 'food:meat_soup_raw', recipe = { {'group:meat'}, {'group:water'}, {'group:bowl'}, }, -- preserve = {'group:bowl'}, -- Not implemented yet (TODO) } An another example: Make red wool from white wool and red dye { type = 'shapeless', output = 'wool:red', recipe = {'wool:white', 'group:dye,basecolor_red'}, } Special groups --------------- - immortal: Disables the group damage system for an entity - level: Can be used to give an additional sense of progression in the game. - A larger level will cause eg. a weapon of a lower level make much less damage, and get weared out much faster, or not be able to get drops from destroyed nodes. - 0 is something that is directly accessible at the start of gameplay - There is no upper limit - dig_immediate: (player can always pick up node without tool wear) - 2: node is removed without tool wear after 0.5 seconds or so (rail, sign) - 3: node is removed without tool wear immediately (torch) - disable_jump: Player (and possibly other things) cannot jump from node - fall_damage_add_percent: damage speed = speed * (1 + value/100) - bouncy: value is bounce speed in percent - falling_node: if there is no walkable block under the node it will fall - attached_node: if the node under it is not a walkable block the node will be 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 Known damage and digging time defining groups ---------------------------------------------- - crumbly: dirt, sand - cracky: tough but crackable stuff like stone. - snappy: something that can be cut using fine tools; eg. leaves, small plants, wire, sheets of metal - choppy: something that can be cut using force; eg. trees, wooden planks - fleshy: Living things like animals and the player. This could imply some blood effects when hitting. - explody: Especially prone to explosions - oddly_breakable_by_hand: Can be added to nodes that shouldn't logically be breakable by the hand but are. Somewhat similar to dig_immediate, but times are more like {[1]=3.50,[2]=2.00,[3]=0.70} and this does not override the speed of a tool if the tool can dig at a faster speed than this suggests for the hand. Examples of custom groups -------------------------- Item groups are often used for defining, well, //groups of items//. - meat: any meat-kind of a thing (rating might define the size or healing ability or be irrelevant - it is not defined as of yet) - eatable: anything that can be eaten. Rating might define HP gain in half hearts. - flammable: can be set on fire. Rating might define the intensity of the fire, affecting eg. the speed of the spreading of an open fire. - wool: any wool (any origin, any color) - metal: any metal - weapon: any weapon - heavy: anything considerably heavy Digging time calculation specifics ----------------------------------- Groups such as **crumbly**, **cracky** and **snappy** are used for this purpose. Rating is 1, 2 or 3. A higher rating for such a group implies faster digging time. The **level** group is used to limit the toughness of nodes a tool can dig and to scale the digging times / damage to a greater extent. ^ PLEASE DO UNDERSTAND THIS, otherwise you cannot use the system to it's full potential. Tools define their properties by a list of parameters for groups. They cannot dig other groups; thus it is important to use a standard bunch of groups to enable interaction with tools. **Tools define:** * Full punch interval * Maximum drop level * For an arbitrary list of groups: * Uses (until the tool breaks) * Maximum level (usually 0, 1, 2 or 3) * Digging times * Damage groups **Full punch interval**: When used as a weapon, the tool will do full damage if this time is spent between punches. If eg. half the time is spent, the tool will do half damage. **Maximum drop level** Suggests the maximum level of node, when dug with the tool, that will drop it's useful item. (eg. iron ore to drop a lump of iron). - This is not automated; it is the responsibility of the node definition to implement this **Uses** Determines how many uses the tool has when it is used for digging a node, of this group, of the maximum level. For lower leveled nodes, the use count is multiplied by 3^leveldiff. - uses=10, leveldiff=0 -> actual uses: 10 - uses=10, leveldiff=1 -> actual uses: 30 - uses=10, leveldiff=2 -> actual uses: 90 **Maximum level** Tells what is the maximum level of a node of this group that the tool will be able to dig. **Digging times** List of digging times for different ratings of the group, for nodes of the maximum level. * For example, as a lua table, ''times={2=2.00, 3=0.70}''. This would result in the tool to be able to dig nodes that have a rating of 2 or 3 for this group, and unable to dig the rating 1, which is the toughest. Unless there is a matching group that enables digging otherwise. **Damage groups** List of damage for groups of entities. See "Entity damage mechanism". Example definition of the capabilities of a tool ------------------------------------------------- tool_capabilities = { full_punch_interval=1.5, max_drop_level=1, groupcaps={ crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} } damage_groups = {fleshy=2}, } This makes the tool be able to dig nodes that fullfill both of these: - Have the **crumbly** group - Have a **level** group less or equal to 2 Table of resulting digging times: crumbly 0 1 2 3 4 <- level -> 0 - - - - - 1 0.80 1.60 1.60 - - 2 0.60 1.20 1.20 - - 3 0.40 0.80 0.80 - - level diff: 2 1 0 -1 -2 Table of resulting tool uses: -> 0 - - - - - 1 180 60 20 - - 2 180 60 20 - - 3 180 60 20 - - Notes: - At crumbly=0, the node is not diggable. - At crumbly=3, the level difference digging time divider kicks in and makes easy nodes to be quickly breakable. - At level > 2, the node is not diggable, because it's level > maxlevel Entity damage mechanism ------------------------ 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) * (object.armor_groups[group] / 100.0) -- Where object.armor_groups[group] is 0 for inexisting values return damage 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 (eg. by defining a sprite animation) (not implemented; TODO). - Currently a smoke puff will appear when an entity dies. The group **immortal** completely disables normal damage. Entities can define a special armor group, which is **punch_operable**. This group disables the regular damage mechanism for players punching it by hand or a non-tool item, so that it can do something else than take damage. 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. * ''puncher'' is the object performing the punch. Can be nil. Should never be accessed unless absolutely required, to encourage interoperability. * ''time_from_last_punch'' is time from last punch (by puncher) or nil. * ''tool_capabilities'' can be nil. * ''direction'' is a unit vector, pointing from the source of the punch to the punched object. To punch an entity/object in Lua, call ''object:punch(puncher, time_from_last_punch, tool_capabilities, direction)''. * Return value is tool wear. * Parameters are equal to the above callback. * If ''direction'' is nil and ''puncher'' is not nil, ''direction'' will be automatically filled in based on the location of ''puncher''. Node Metadata ------------- The instance of a node in the world normally only contains the three values mentioned in "Nodes". However, it is possible to insert extra data into a node. It is called "node metadata"; See "NodeMetaRef". Metadata contains two things: - A key-value store - An inventory Some of the values in the key-value store are handled specially: - formspec: Defines a right-click inventory menu. See "Formspec". - infotext: Text shown on the screen when the node is pointed at Example stuff: local meta = minetest.get_meta(pos) meta:set_string("formspec", "invsize[8,9;]".. "list[context;main;0,0;8,4;]".. "list[current_player;main;0,5;8,4;]") meta:set_string("infotext", "Chest"); local inv = meta:get_inventory() inv:set_size("main", 8*4) 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] = ""} }, fields = { formspec = "invsize[8,9;]list[context;main;0,0;8,4;]list[current_player;main;0,5;8,4;]", infotext = "Chest" } }) Formspec -------- Formspec defines a menu. Currently not much else than inventories are supported. It is a string, with a somewhat strange format. Spaces and newlines can be inserted between the blocks, as is used in the examples. Examples: - Chest: invsize[8,9;] list[context;main;0,0;8,4;] list[current_player;main;0,5;8,4;] - Furnace: invsize[8,9;] list[context;fuel;2,3;1,1;] list[context;src;2,1;1,1;] list[context;dst;5,1;2,2;] list[current_player;main;0,5;8,4;] - Minecraft-like player inventory invsize[8,7.5;] 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;] list[current_player;craftpreview;7,1;1,1;] Elements: size[,] ^ Define the size of the menu in inventory slots ^ deprecated: invsize[,;] list[;;,;,;] list[;;,;,;] ^ Show an inventory list listcolors[;] ^ Sets background color of slots in HEX-Color format ^ Sets background color of slots on mouse hovering listcolors[;;] ^ Sets background color of slots in HEX-Color format ^ Sets background color of slots on mouse hovering ^ Sets color of slots border listcolors[;;;;] ^ Sets background color of slots in HEX-Color format ^ Sets background color of slots on mouse hovering ^ Sets color of slots border ^ Sets background color of tooltips ^ Sets font color of tooltips image[,;,;] ^ Show an image ^ Position and size units are inventory slots item_image[,;,;] ^ Show an inventory image of registered item/node ^ Position and size units are inventory slots bgcolor[;] ^ Sets background color of formspec in HEX-Color format ^ If true the background color is drawn fullscreen (does not effect the size of the formspec) background[,;,;] ^ Use a background. Inventory rectangles are not drawn then. ^ Position and size units are inventory slots ^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px background[,;,;;] ^ Use a background. Inventory rectangles are not drawn then. ^ Position and size units are inventory slots ^ Example for formspec 8x4 in 16x resolution: image shall be sized 8*16px x 4*16px ^ If true the background is clipped to formspec size (x and y are used as offset values, w and h are ignored) pwdfield[,;,;;