From d5ca3b721e049fc4f59718a55c932a06533d4b3c Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Tue, 14 May 2013 12:29:30 +0200 Subject: [PATCH 01/41] Fix memory leak in run_tests() --- src/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test.cpp b/src/test.cpp index d1f53f88..e1dbfa6b 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1772,6 +1772,10 @@ void run_tests() TEST(TestConnection); dout_con<<"=== END RUNNING UNIT TESTS FOR CONNECTION ==="< Date: Thu, 16 May 2013 02:19:32 +0200 Subject: [PATCH 02/41] Dont drop fonts with ENABLE_FREETYPE=0 --- src/guiChatConsole.cpp | 4 +++- src/main.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index f31e599d..3dfd0090 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -121,7 +121,9 @@ GUIChatConsole::GUIChatConsole( GUIChatConsole::~GUIChatConsole() { - delete m_font; +#if USE_FREETYPE + m_font->drop(); +#endif } void GUIChatConsole::openConsole(f32 height) diff --git a/src/main.cpp b/src/main.cpp index 7f9ec1ac..d28ba9b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2145,7 +2145,9 @@ int main(int argc, char *argv[]) */ device->drop(); - delete font; +#if USE_FREETYPE + font->drop(); +#endif #endif // !SERVER From f9b5771274f662ed12c209c3f1b563eb29d99ca7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 17 May 2013 23:38:23 +0300 Subject: [PATCH 03/41] Fix Problem with uk Translation --- po/uk/minetest.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/po/uk/minetest.po b/po/uk/minetest.po index 84a8a870..d9823b5e 100644 --- a/po/uk/minetest.po +++ b/po/uk/minetest.po @@ -51,7 +51,7 @@ msgstr "" msgid "" "\n" "Check debug.txt for details." -msgstr "Деталі у файлі debug.txt." +msgstr "\nДеталі у файлі debug.txt." #: src/guiConfigureWorld.cpp:123 msgid "" From fedf644635456b85d6168ca98f6dc712b1ce7f2e Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Fri, 17 May 2013 23:15:10 +0200 Subject: [PATCH 04/41] Move kahrl to core devs in credits tab --- src/guiMainMenu.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index 36685db8..223bba93 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -54,11 +54,11 @@ const wchar_t *contrib_core_strs[] = { L"Lisa Milne (darkrose) ", L"Maciej Kasatkin (RealBadAngel) ", L"proller ", - L"sfan5 " + L"sfan5 ", + L"kahrl " }; const wchar_t *contrib_active_strs[] = { - L"kahrl ", L"sapier ", L"Vanessa Ezekowitz (VanessaE) ", L"Jurgen Doser (doserj) ", From 1369503aba1ae0c64e6c25b32cc5552bbdead8fb Mon Sep 17 00:00:00 2001 From: Aaron Suen Date: Fri, 17 May 2013 17:10:39 -0400 Subject: [PATCH 05/41] Fix math for isBlockInSight. Fixes #718 (client-side). --- src/camera.cpp | 3 +-- src/util/numeric.cpp | 33 +++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 6224a2b8..0dd0a767 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -342,8 +342,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize, m_fov_y = fov_degrees * M_PI / 180.0; // Increase vertical FOV on lower aspect ratios (<16:10) m_fov_y *= MYMAX(1.0, MYMIN(1.4, sqrt(16./10. / m_aspect))); - // WTF is this? It can't be right - m_fov_x = 2 * atan(0.5 * m_aspect * tan(m_fov_y)); + m_fov_x = 2 * atan(m_aspect * tan(0.5 * m_fov_y)); m_cameranode->setAspectRatio(m_aspect); m_cameranode->setFOV(m_fov_y); diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index ed83df7d..0e2772c3 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -159,40 +159,41 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // Block position relative to camera v3f blockpos_relative = blockpos - camera_pos; - // Distance in camera direction (+=front, -=back) - f32 dforward = blockpos_relative.dotProduct(camera_dir); - // Total distance f32 d = blockpos_relative.getLength(); if(distance_ptr) *distance_ptr = d; - // If block is very close, it is always in sight - if(d < 1.44*1.44*MAP_BLOCKSIZE*BS/2) - return true; - // If block is far away, it's not in sight if(d > range) return false; - // Maximum radius of a block - f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS; + // Maximum radius of a block. The magic number is + // sqrt(3.0) / 2.0 in literal form. + f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS; // If block is (nearly) touching the camera, don't // bother validating further (that is, render it anyway) if(d < block_max_radius) return true; - + + // Adjust camera position, for purposes of computing the angle, + // such that a block that has any portion visible with the + // current camera position will have the center visible at the + // adjusted postion + f32 adjdist = block_max_radius / cos((M_PI - camera_fov) / 2); + + // Block position relative to adjusted camera + v3f blockpos_adj = blockpos - (camera_pos - camera_dir * adjdist); + + // Distance in camera direction (+=front, -=back) + f32 dforward = blockpos_adj.dotProduct(camera_dir); + // Cosine of the angle between the camera direction // and the block direction (camera_dir is an unit vector) - f32 cosangle = dforward / d; + f32 cosangle = dforward / blockpos_adj.getLength(); - // Compensate for the size of the block - // (as the block has to be shown even if it's a bit off FOV) - // This is an estimate, plus an arbitary factor - cosangle += block_max_radius / d * 0.5; - // If block is not in the field of view, skip it if(cosangle < cos(camera_fov / 2)) return false; From 9397b5de083fabe2e93c55de5bf90e5c75bbe9c1 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sat, 18 May 2013 01:52:18 +0200 Subject: [PATCH 06/41] Fix memory leak in MeshUpdateThread --- src/client.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 929ed0ea..533c40ab 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -350,6 +350,11 @@ Client::~Client() m_mesh_update_thread.setRun(false); while(m_mesh_update_thread.IsRunning()) sleep_ms(100); + while(!m_mesh_update_thread.m_queue_out.empty()) { + MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); + delete r.mesh; + } + delete m_inventory_from_server; @@ -757,6 +762,8 @@ void Client::step(float dtime) // Replace with the new mesh block->mesh = r.mesh; + } else { + delete r.mesh; } if(r.ack_block_to_server) { From 714ecc5e27c2c01b0f3ce0ba626abb6baca9af13 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sat, 18 May 2013 13:01:47 +0200 Subject: [PATCH 07/41] Only create SoundManager in main menu if USE_SOUND is true --- src/main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index d28ba9b9..e5c8a55d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1845,7 +1845,9 @@ int main(int argc, char *argv[]) MenuMusicFetcher soundfetcher; ISoundManager *sound = NULL; +#if USE_SOUND sound = createOpenALSoundManager(&soundfetcher); +#endif if(!sound) sound = &dummySoundManager; SimpleSoundSpec spec; From b76ec317c9eb69c8f4864f5d467e6a1e0ac7e81e Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sat, 18 May 2013 16:13:32 +0200 Subject: [PATCH 08/41] Remove common from CMakeLists.txt, README.txt, lua-api.txt and buildwin.sh --- CMakeLists.txt | 5 ----- README.txt | 14 +++----------- doc/lua_api.txt | 4 ---- util/buildbot/buildwin32.sh | 11 ----------- 4 files changed, 3 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba273a0d..7c101923 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,11 +133,6 @@ 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(COMMON_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/common") -if(EXISTS ${COMMON_SOURCE} AND IS_DIRECTORY ${COMMON_SOURCE}) - install(FILES ${COMMON_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/common/") - install(DIRECTORY ${COMMON_SOURCE}/mods DESTINATION "${SHAREDIR}/games/common") -endif() 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/") diff --git a/README.txt b/README.txt index ae5003f7..f264ba6b 100644 --- a/README.txt +++ b/README.txt @@ -9,10 +9,9 @@ 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 these projects too: - https://github.com/minetest/common/ +contained, you probably want to download the minetest_game project too: https://github.com/minetest/minetest_game/ -See the README.txt in them. +See the README.txt in it. Further documentation ---------------------- @@ -92,13 +91,6 @@ $ wget https://github.com/minetest/minetest/tarball/master -O master.tar.gz $ tar xf master.tar.gz $ cd minetest-minetest-286edd4 (or similar) -Download common (needed for minetest_game and some others) -$ cd games/ -$ wget https://github.com/minetest/common/tarball/master -O common.tar.gz -$ tar xf common.tar.gz -$ mv minetest-common-* common -$ cd .. - 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 @@ -124,7 +116,7 @@ $ ./minetest Compiling on Windows: --------------------- - This section is outdated. In addition to what is described here: - - In addition to minetest, you need to download common and minetest_game. + - 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: diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 02ca7cba..cd90b5aa 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -50,12 +50,8 @@ where gameid is unique to each game. The game directory contains the file game.conf, which contains these fields: name = - common_mods = eg. name = Minetest - common_mods = bucket, default, doors, fire, stairs - -Common mods are loaded from the pseudo-game "common". The game directory can contain the file minetest.conf, which will be used to set default settings when running the particular game. diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index 0c0d7cf2..fc42db8a 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -53,9 +53,6 @@ cd $builddir wget http://github.com/minetest/minetest/zipball/master \ -c -O $packagedir/minetest.zip --tries=3 || (echo "Please download http://github.com/minetest/minetest/zipball/master manually and save it as $packagedir/minetest.zip"; read -s) [ -e $packagedir/minetest.zip ] || (echo "minetest.zip not found"; exit 1) -wget http://github.com/minetest/common/zipball/master \ - -c -O $packagedir/common.zip --tries=3 || (echo "Please download http://github.com/minetest/common/zipball/master manually and save it as $packagedir/common.zip"; read -s) -[ -e $packagedir/common.zip ] || (echo "common.zip not found"; exit 1) wget http://github.com/minetest/minetest_game/zipball/master \ -c -O $packagedir/minetest_game.zip --tries=3 || (echo "Please download http://github.com/minetest/minetest_game/zipball/master manually and save it as $packagedir/minetest_game.zip"; read -s) [ -e $packagedir/minetest_game.zip ] || (echo "minetest_game.zip not found"; exit 1) @@ -69,7 +66,6 @@ wget http://github.com/minetest/minetest_game/zipball/master \ minetestdirname=`unzip -l $packagedir/minetest.zip | head -n 7 | tail -n 1 | sed -e 's/^[^m]*//' -e 's/\/.*$//'` minetestdir=$builddir/$minetestdirname || exit 1 git_hash=`echo $minetestdirname | sed -e 's/minetest-minetest-//'` -commondirname=`unzip -l $packagedir/common.zip | head -n 7 | tail -n 1 | sed -e 's/^[^m]*//' -e 's/\/.*$//'` minetest_gamedirname=`unzip -l $packagedir/minetest_game.zip | head -n 7 | tail -n 1 | sed -e 's/^[^m]*//' -e 's/\/.*$//'` # Extract stuff @@ -90,13 +86,6 @@ unzip -o $packagedir/minetest.zip || exit 1 rm -rf $builddir/minetest ln -s $minetestdir $builddir/minetest -# Extract common -cd $minetestdir/games || exit 1 -rm -rf common || exit 1 -unzip -o $packagedir/common.zip || exit 1 -commondir=$minetestdir/games/$commondirname || exit 1 -mv $commondir $minetestdir/games/common || exit 1 - # Extract minetest_game cd $minetestdir/games || exit 1 rm -rf minetest_game || exit 1 From 6074163bf358ba8bf3a012e8093e24ecab5b6d61 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sat, 18 May 2013 16:14:06 +0200 Subject: [PATCH 09/41] Remove survival and build from CMakeLists.txt --- CMakeLists.txt | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c101923..82fd23a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,18 +140,6 @@ if(EXISTS ${MINETEST_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_GAME_SOURCE}) install(DIRECTORY ${MINETEST_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/minetest_game") install(DIRECTORY ${MINETEST_GAME_SOURCE}/menu DESTINATION "${SHAREDIR}/games/minetest_game") endif() -set(MINETEST_BUILD_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/build") -if(EXISTS ${MINETEST_BUILD_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_BUILD_GAME_SOURCE}) - install(FILES ${MINETEST_BUILD_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/build/") - install(FILES ${MINETEST_BUILD_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/build/") - install(DIRECTORY ${MINETEST_BUILD_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/build") -endif() -set(MINETEST_SURVIVAL_GAME_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/games/survival") -if(EXISTS ${MINETEST_SURVIVAL_GAME_SOURCE} AND IS_DIRECTORY ${MINETEST_SURVIVAL_GAME_SOURCE}) - install(FILES ${MINETEST_SURVIVAL_GAME_SOURCE}/game.conf DESTINATION "${SHAREDIR}/games/survival/") - install(FILES ${MINETEST_SURVIVAL_GAME_SOURCE}/README.txt DESTINATION "${SHAREDIR}/games/survival/") - install(DIRECTORY ${MINETEST_SURVIVAL_GAME_SOURCE}/mods DESTINATION "${SHAREDIR}/games/survival") -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") From 45fcc9de29de762e8dde20a50db4a3184c830e42 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sat, 18 May 2013 17:00:47 +0200 Subject: [PATCH 10/41] New modsystem Mods are placed in $path_/mods They can be enabled per world in world.mt or the configure world window --- doc/lua_api.txt | 4 +-- mods/minetest/mods_here.txt | 1 - mods/mods_here.txt | 4 +++ src/guiConfigureWorld.cpp | 59 +++++++------------------------------ src/guiConfigureWorld.h | 3 -- src/subgame.cpp | 4 +-- 6 files changed, 19 insertions(+), 56 deletions(-) delete mode 100644 mods/minetest/mods_here.txt create mode 100644 mods/mods_here.txt diff --git a/doc/lua_api.txt b/doc/lua_api.txt index cd90b5aa..b7be5020 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -60,9 +60,9 @@ Mod load path ------------- Generic: $path_share/games/gameid/mods/ - $path_share/mods/gameid/ + $path_share/mods/ $path_user/games/gameid/mods/ - $path_user/mods/gameid/ <-- User-installed mods + $path_user/mods/ <-- User-installed mods $worldpath/worldmods/ In a run-in-place version (eg. the distributed windows version): diff --git a/mods/minetest/mods_here.txt b/mods/minetest/mods_here.txt deleted file mode 100644 index 5135cb94..00000000 --- a/mods/minetest/mods_here.txt +++ /dev/null @@ -1 +0,0 @@ -You can install Minetest mods by copying (and extracting) them into this folder. diff --git a/mods/mods_here.txt b/mods/mods_here.txt new file mode 100644 index 00000000..e105fbd1 --- /dev/null +++ b/mods/mods_here.txt @@ -0,0 +1,4 @@ +You can install Minetest mods by copying (and extracting) them into this folder. +To enable them, go to the configure world window in the main menu or write + load_mod_ = true +in world.mt in the world directory. diff --git a/src/guiConfigureWorld.cpp b/src/guiConfigureWorld.cpp index f94ed7d1..e33e87ef 100644 --- a/src/guiConfigureWorld.cpp +++ b/src/guiConfigureWorld.cpp @@ -116,40 +116,18 @@ GUIConfigureWorld::GUIConfigureWorld(gui::IGUIEnvironment* env, // mod_names if(!mod.is_modpack && mod_names.count(modname) == 0) - m_new_mod_names.insert(modname); + m_settings.setBool("load_mod_"+modname, false); } - if(!m_new_mod_names.empty()) - { - wchar_t* text = wgettext("Warning: Some mods are not configured yet.\n" - "They will be enabled by default when you save the configuration. "); - GUIMessageMenu *menu = - new GUIMessageMenu(Environment, Parent, -1, m_menumgr, text); - menu->drop(); - delete[] text; - } - - // find missing mods (mentioned in world.mt, but not installed) - std::set missing_mods; for(std::set::iterator it = mod_names.begin(); it != mod_names.end(); ++it) { std::string modname = *it; if(m_addonmods.count(modname) == 0) - missing_mods.insert(modname); + m_settings.remove("load_mod_"+modname); } - if(!missing_mods.empty()) - { - wchar_t* text = wgettext("Warning: Some configured mods are missing.\n" - "Their setting will be removed when you save the configuration. "); - GUIMessageMenu *menu = - new GUIMessageMenu(Environment, Parent, -1, m_menumgr, text); - delete[] text; - for(std::set::iterator it = missing_mods.begin(); - it != missing_mods.end(); ++it) - m_settings.remove("load_mod_"+(*it)); - menu->drop(); - } + std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt"; + m_settings.updateConfigFile(worldmtfile.c_str()); } void GUIConfigureWorld::drawMenu() @@ -388,11 +366,6 @@ bool GUIConfigureWorld::OnEvent(const SEvent& event) return true; } case GUI_ID_SAVE: { - for(std::set::iterator it = m_new_mod_names.begin(); - it!= m_new_mod_names.end(); ++it) - { - m_settings.setBool("load_mod_"+(*it),true); - } std::string worldmtfile = m_wspec.path+DIR_DELIM+"world.mt"; m_settings.updateConfigFile(worldmtfile.c_str()); @@ -558,22 +531,14 @@ void GUIConfigureWorld::buildTreeView(std::map mods, buildTreeView(mod.modpack_content, new_node); else { - // set icon for node: ? for new mods, x for disabled mods, - // checkmark for enabled mods - if(m_new_mod_names.count(modname) > 0) - { - new_node->setIcon(QUESTIONMARK_STR); - } + // set icon for node: x for disabled mods, checkmark for enabled mods + bool mod_enabled = false; + if(m_settings.exists("load_mod_"+modname)) + mod_enabled = m_settings.getBool("load_mod_"+modname); + if(mod_enabled) + new_node->setIcon(CHECKMARK_STR); else - { - bool mod_enabled = true; - if(m_settings.exists("load_mod_"+modname)) - mod_enabled = m_settings.getBool("load_mod_"+modname); - if(mod_enabled) - new_node->setIcon(CHECKMARK_STR); - else - new_node->setIcon(CROSS_STR); - } + new_node->setIcon(CROSS_STR); } } } @@ -690,7 +655,6 @@ void GUIConfigureWorld::enableMod(std::string modname) m_nodes.find(modname); if(it != m_nodes.end()) (*it).second->setIcon(CHECKMARK_STR); - m_new_mod_names.erase(modname); //also enable all dependencies for(std::set::iterator it=mspec.depends.begin(); it != mspec.depends.end(); ++it) @@ -715,7 +679,6 @@ void GUIConfigureWorld::disableMod(std::string modname) m_nodes.find(modname); if(it != m_nodes.end()) (*it).second->setIcon(CROSS_STR); - m_new_mod_names.erase(modname); //also disable all mods that depend on this one std::pair::iterator, std::multimap::iterator > rdep = diff --git a/src/guiConfigureWorld.h b/src/guiConfigureWorld.h index 8a77c5f8..23ebac66 100644 --- a/src/guiConfigureWorld.h +++ b/src/guiConfigureWorld.h @@ -69,9 +69,6 @@ private: // the settings in the world.mt file Settings m_settings; - // mods that are installed but not mentioned in world.mt file - std::set m_new_mod_names; - // maps modnames to nodes in m_treeview std::map m_nodes; diff --git a/src/subgame.cpp b/src/subgame.cpp index cdb54661..7fee3899 100644 --- a/src/subgame.cpp +++ b/src/subgame.cpp @@ -91,9 +91,9 @@ SubgameSpec findSubgame(const std::string &id) // Find mod directories std::set mods_paths; if(!user_game) - mods_paths.insert(share + DIR_DELIM + "mods" + DIR_DELIM + id); + mods_paths.insert(share + DIR_DELIM + "mods"); if(user != share || user_game) - mods_paths.insert(user + DIR_DELIM + "mods" + DIR_DELIM + id); + mods_paths.insert(user + DIR_DELIM + "mods"); std::string game_name = getGameName(game_path); if(game_name == "") game_name = id; From f577facf79729a03bb274a27c9f50f0092cd3a40 Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 19 May 2013 12:07:00 -0400 Subject: [PATCH 11/41] Fix regression of world loading caused by 81c863ac --- src/game.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index f63a4a40..746769ae 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1042,7 +1042,7 @@ void the_game( u32 lasttime = device->getTimer()->getTime(); while(device->run()) { - f32 dtime=0; // in seconds + f32 dtime = 0.033; // in seconds if (cloud_menu_background) { u32 time = device->getTimer()->getTime(); if(time > lasttime) @@ -1136,7 +1136,7 @@ void the_game( u32 lasttime = device->getTimer()->getTime(); while(device->run()) { - f32 dtime=0; // in seconds + f32 dtime = 0.033; // in seconds if (cloud_menu_background) { u32 time = device->getTimer()->getTime(); if(time > lasttime) From 93474c4218eee621a96e24324b1b41a55571f0df Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sat, 18 May 2013 23:26:27 -0400 Subject: [PATCH 12/41] Remove no virtual dtor warnings, make MapgenParams contain actual NoiseParams --- src/mapgen.cpp | 52 ++++++++++++------------- src/mapgen.h | 10 ++++- src/mapgen_indev.cpp | 84 +++++++++++++++++++---------------------- src/mapgen_indev.h | 59 +++++++++++++++-------------- src/mapgen_singlenode.h | 4 +- src/mapgen_v6.cpp | 22 +++++------ src/mapgen_v6.h | 47 ++++++++++++----------- src/mapgen_v7.cpp | 12 +++--- src/mapgen_v7.h | 26 +++++++------ src/noise.h | 6 +-- src/settings.h | 37 +++++++++--------- 11 files changed, 182 insertions(+), 177 deletions(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 0646a882..835c14be 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -71,6 +71,12 @@ Ore *createOre(OreType type) { } +Ore::~Ore() { + delete np; + delete noise; +} + + void Ore::resolveNodeNames(INodeDefManager *ndef) { if (ore == CONTENT_IGNORE) { ore = ndef->getId(ore_name); @@ -347,23 +353,18 @@ bool MapgenV6Params::readParams(Settings *settings) { freq_desert = settings->getFloat("mgv6_freq_desert"); freq_beach = settings->getFloat("mgv6_freq_beach"); - np_terrain_base = settings->getNoiseParams("mgv6_np_terrain_base"); - np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher"); - np_steepness = settings->getNoiseParams("mgv6_np_steepness"); - np_height_select = settings->getNoiseParams("mgv6_np_height_select"); - np_mud = settings->getNoiseParams("mgv6_np_mud"); - np_beach = settings->getNoiseParams("mgv6_np_beach"); - np_biome = settings->getNoiseParams("mgv6_np_biome"); - np_cave = settings->getNoiseParams("mgv6_np_cave"); - np_humidity = settings->getNoiseParams("mgv6_np_humidity"); - np_trees = settings->getNoiseParams("mgv6_np_trees"); - np_apple_trees = settings->getNoiseParams("mgv6_np_apple_trees"); - - bool success = - np_terrain_base && np_terrain_higher && np_steepness && - np_height_select && np_trees && np_mud && - np_beach && np_biome && np_cave && - np_humidity && np_apple_trees; + bool success = + settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base) && + settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher) && + settings->getNoiseParams("mgv6_np_steepness", np_steepness) && + settings->getNoiseParams("mgv6_np_height_select", np_height_select) && + settings->getNoiseParams("mgv6_np_mud", np_mud) && + settings->getNoiseParams("mgv6_np_beach", np_beach) && + settings->getNoiseParams("mgv6_np_biome", np_biome) && + settings->getNoiseParams("mgv6_np_cave", np_cave) && + settings->getNoiseParams("mgv6_np_humidity", np_humidity) && + settings->getNoiseParams("mgv6_np_trees", np_trees) && + settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees); return success; } @@ -387,16 +388,13 @@ void MapgenV6Params::writeParams(Settings *settings) { bool MapgenV7Params::readParams(Settings *settings) { - np_terrain_base = settings->getNoiseParams("mgv7_np_terrain_base"); - np_terrain_alt = settings->getNoiseParams("mgv7_np_terrain_alt"); - np_terrain_mod = settings->getNoiseParams("mgv7_np_terrain_mod"); - np_terrain_persist = settings->getNoiseParams("mgv7_np_terrain_persist"); - np_height_select = settings->getNoiseParams("mgv7_np_height_select"); - np_ridge = settings->getNoiseParams("mgv7_np_ridge"); - - bool success = - np_terrain_base && np_terrain_alt && np_terrain_mod && - np_terrain_persist && np_height_select && np_ridge; + bool success = + settings->getNoiseParams("mgv7_np_terrain_base", np_terrain_base) && + settings->getNoiseParams("mgv7_np_terrain_alt", np_terrain_alt) && + settings->getNoiseParams("mgv7_np_terrain_mod", np_terrain_mod) && + settings->getNoiseParams("mgv7_np_terrain_persist", np_terrain_persist) && + settings->getNoiseParams("mgv7_np_height_select", np_height_select) && + settings->getNoiseParams("mgv7_np_ridge", np_ridge); return success; } diff --git a/src/mapgen.h b/src/mapgen.h index 17136a13..5d1e3bdf 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -74,7 +74,8 @@ struct MapgenParams { } virtual bool readParams(Settings *settings) = 0; - virtual void writeParams(Settings *settings) {}; + virtual void writeParams(Settings *settings) = 0; + virtual ~MapgenParams() {} }; class Mapgen { @@ -86,6 +87,8 @@ public: ManualMapVoxelManipulator *vm; INodeDefManager *ndef; + virtual ~Mapgen() {} + void updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nmax); void setLighting(v3s16 nmin, v3s16 nmax, u8 light); void lightSpread(VoxelArea &a, v3s16 p, u8 light); @@ -105,6 +108,7 @@ struct MapgenFactory { virtual Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) = 0; virtual MapgenParams *createMapgenParams() = 0; + virtual ~MapgenFactory() {} }; enum OreType { @@ -140,6 +144,8 @@ public: noise = NULL; } + virtual ~Ore(); + void resolveNodeNames(INodeDefManager *ndef); void placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); virtual void generate(ManualMapVoxelManipulator *vm, int seed, @@ -147,11 +153,13 @@ public: }; class OreScatter : public Ore { + ~OreScatter() {} virtual void generate(ManualMapVoxelManipulator *vm, int seed, u32 blockseed, v3s16 nmin, v3s16 nmax); }; class OreSheet : public Ore { + ~OreSheet() {} virtual void generate(ManualMapVoxelManipulator *vm, int seed, u32 blockseed, v3s16 nmin, v3s16 nmax); }; diff --git a/src/mapgen_indev.cpp b/src/mapgen_indev.cpp index 2eeaf13e..430359be 100644 --- a/src/mapgen_indev.cpp +++ b/src/mapgen_indev.cpp @@ -84,17 +84,14 @@ void NoiseIndev::transformNoiseMapFarScale(float xx, float yy, float zz) { MapgenIndev::MapgenIndev(int mapgenid, MapgenIndevParams *params, EmergeManager *emerge) : MapgenV6(mapgenid, params, emerge) { - noiseindev_terrain_base = new NoiseIndev(params->npindev_terrain_base, seed, csize.X, csize.Z); - noiseindev_terrain_higher = new NoiseIndev(params->npindev_terrain_higher, seed, csize.X, csize.Z); - noiseindev_steepness = new NoiseIndev(params->npindev_steepness, seed, csize.X, csize.Z); -// noise_height_select = new Noise(params->np_height_select, seed, csize.X, csize.Y); -// noise_trees = new Noise(params->np_trees, seed, csize.X, csize.Y); - noiseindev_mud = new NoiseIndev(params->npindev_mud, seed, csize.X, csize.Z); -// noise_beach = new Noise(params->np_beach, seed, csize.X, csize.Y); - noiseindev_float_islands1 = new NoiseIndev(params->npindev_float_islands1, seed, csize.X, csize.Y, csize.Z); - noiseindev_float_islands2 = new NoiseIndev(params->npindev_float_islands2, seed, csize.X, csize.Y, csize.Z); - noiseindev_float_islands3 = new NoiseIndev(params->npindev_float_islands3, seed, csize.X, csize.Z); - noiseindev_biome = new NoiseIndev(params->npindev_biome, seed, csize.X, csize.Z); + noiseindev_terrain_base = new NoiseIndev(¶ms->npindev_terrain_base, seed, csize.X, csize.Z); + noiseindev_terrain_higher = new NoiseIndev(¶ms->npindev_terrain_higher, seed, csize.X, csize.Z); + noiseindev_steepness = new NoiseIndev(¶ms->npindev_steepness, seed, csize.X, csize.Z); + noiseindev_mud = new NoiseIndev(¶ms->npindev_mud, seed, csize.X, csize.Z); + noiseindev_float_islands1 = new NoiseIndev(¶ms->npindev_float_islands1, seed, csize.X, csize.Y, csize.Z); + noiseindev_float_islands2 = new NoiseIndev(¶ms->npindev_float_islands2, seed, csize.X, csize.Y, csize.Z); + noiseindev_float_islands3 = new NoiseIndev(¶ms->npindev_float_islands3, seed, csize.X, csize.Z); + noiseindev_biome = new NoiseIndev(¶ms->npindev_biome, seed, csize.X, csize.Z); } MapgenIndev::~MapgenIndev() { @@ -170,46 +167,41 @@ void MapgenIndev::calculateNoise() { } bool MapgenIndevParams::readParams(Settings *settings) { - freq_desert = settings->getFloat("mgv6_freq_desert"); - freq_beach = settings->getFloat("mgv6_freq_beach"); + freq_desert = settings->getFloat("mgv6_freq_desert"); + freq_beach = settings->getFloat("mgv6_freq_beach"); - npindev_terrain_base = settings->getNoiseIndevParams("mgindev_np_terrain_base"); - npindev_terrain_higher = settings->getNoiseIndevParams("mgindev_np_terrain_higher"); - npindev_steepness = settings->getNoiseIndevParams("mgindev_np_steepness"); - np_height_select = settings->getNoiseParams("mgv6_np_height_select"); - np_trees = settings->getNoiseParams("mgv6_np_trees"); - npindev_mud = settings->getNoiseIndevParams("mgindev_np_mud"); - np_beach = settings->getNoiseParams("mgv6_np_beach"); - npindev_biome = settings->getNoiseIndevParams("mgindev_np_biome"); - np_cave = settings->getNoiseParams("mgv6_np_cave"); - npindev_float_islands1 = settings->getNoiseIndevParams("mgindev_np_float_islands1"); - npindev_float_islands2 = settings->getNoiseIndevParams("mgindev_np_float_islands2"); - npindev_float_islands3 = settings->getNoiseIndevParams("mgindev_np_float_islands3"); - - bool success = - npindev_terrain_base && npindev_terrain_higher && npindev_steepness && - np_height_select && np_trees && npindev_mud && - np_beach && np_biome && np_cave && - npindev_float_islands1 && npindev_float_islands2 && npindev_float_islands3; - return success; + bool success = + settings->getNoiseIndevParams("mgindev_np_terrain_base", npindev_terrain_base) && + settings->getNoiseIndevParams("mgindev_np_terrain_higher", npindev_terrain_higher) && + settings->getNoiseIndevParams("mgindev_np_steepness", npindev_steepness) && + settings->getNoiseParams("mgv6_np_height_select", np_height_select) && + settings->getNoiseParams("mgv6_np_trees", np_trees) && + settings->getNoiseIndevParams("mgindev_np_mud", npindev_mud) && + settings->getNoiseParams("mgv6_np_beach", np_beach) && + settings->getNoiseIndevParams("mgindev_np_biome", npindev_biome) && + settings->getNoiseParams("mgv6_np_cave", np_cave) && + settings->getNoiseIndevParams("mgindev_np_float_islands1", npindev_float_islands1) && + settings->getNoiseIndevParams("mgindev_np_float_islands2", npindev_float_islands2) && + settings->getNoiseIndevParams("mgindev_np_float_islands3", npindev_float_islands3); + return success; } void MapgenIndevParams::writeParams(Settings *settings) { - settings->setFloat("mgv6_freq_desert", freq_desert); - settings->setFloat("mgv6_freq_beach", freq_beach); + settings->setFloat("mgv6_freq_desert", freq_desert); + settings->setFloat("mgv6_freq_beach", freq_beach); - settings->setNoiseIndevParams("mgindev_np_terrain_base", npindev_terrain_base); - settings->setNoiseIndevParams("mgindev_np_terrain_higher", npindev_terrain_higher); - settings->setNoiseIndevParams("mgindev_np_steepness", npindev_steepness); - settings->setNoiseParams("mgv6_np_height_select", np_height_select); - settings->setNoiseParams("mgv6_np_trees", np_trees); - settings->setNoiseIndevParams("mgindev_np_mud", npindev_mud); - settings->setNoiseParams("mgv6_np_beach", np_beach); - settings->setNoiseIndevParams("mgindev_np_biome", npindev_biome); - settings->setNoiseParams("mgv6_np_cave", np_cave); - settings->setNoiseIndevParams("mgindev_np_float_islands1", npindev_float_islands1); - settings->setNoiseIndevParams("mgindev_np_float_islands2", npindev_float_islands2); - settings->setNoiseIndevParams("mgindev_np_float_islands3", npindev_float_islands3); + settings->setNoiseIndevParams("mgindev_np_terrain_base", npindev_terrain_base); + settings->setNoiseIndevParams("mgindev_np_terrain_higher", npindev_terrain_higher); + settings->setNoiseIndevParams("mgindev_np_steepness", npindev_steepness); + settings->setNoiseParams("mgv6_np_height_select", np_height_select); + settings->setNoiseParams("mgv6_np_trees", np_trees); + settings->setNoiseIndevParams("mgindev_np_mud", npindev_mud); + settings->setNoiseParams("mgv6_np_beach", np_beach); + settings->setNoiseIndevParams("mgindev_np_biome", npindev_biome); + settings->setNoiseParams("mgv6_np_cave", np_cave); + settings->setNoiseIndevParams("mgindev_np_float_islands1", npindev_float_islands1); + settings->setNoiseIndevParams("mgindev_np_float_islands2", npindev_float_islands2); + settings->setNoiseIndevParams("mgindev_np_float_islands3", npindev_float_islands3); } diff --git a/src/mapgen_indev.h b/src/mapgen_indev.h index 87ae39d0..cfef5904 100644 --- a/src/mapgen_indev.h +++ b/src/mapgen_indev.h @@ -32,30 +32,33 @@ struct NoiseIndevParams : public NoiseParams { float farscale; float farspread; - NoiseIndevParams(){} - NoiseIndevParams(float offset_, float scale_, v3f spread_, int seed_, int octaves_, float persist_, float farscale_ = 1, float farspread_ = 1) + NoiseIndevParams() {} + NoiseIndevParams(float offset_, float scale_, v3f spread_, + int seed_, int octaves_, float persist_, + float farscale_ = 1, float farspread_ = 1) { - offset = offset_; - scale = scale_; - spread = spread_; - seed = seed_; + offset = offset_; + scale = scale_; + spread = spread_; + seed = seed_; octaves = octaves_; persist = persist_; - farscale = farscale_; + farscale = farscale_; farspread = farspread_; } - + + ~NoiseIndevParams() {} }; -#define getNoiseIndevParams(x) getStruct((x), "f,f,v3,s32,s32,f,f,f") -#define setNoiseIndevParams(x, y) setStruct((x), "f,f,v3,s32,s32,f,f,f", (y)) +#define getNoiseIndevParams(x, y) getStruct((x), "f,f,v3,s32,s32,f,f,f", &(y), sizeof(y)) +#define setNoiseIndevParams(x, y) setStruct((x), "f,f,v3,s32,s32,f,f,f", &(y)) class NoiseIndev : public Noise { - public: +public: NoiseIndevParams *npindev; - //NoiseIndev() {}; + virtual ~NoiseIndev() {}; NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy); NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy, int sz); void init(NoiseIndevParams *np, int seed, int sx, int sy, int sz); @@ -77,34 +80,34 @@ extern NoiseIndevParams nparams_indev_def_float_islands; */ struct MapgenIndevParams : public MapgenV6Params { - NoiseIndevParams *npindev_terrain_base; - NoiseIndevParams *npindev_terrain_higher; - NoiseIndevParams *npindev_steepness; + NoiseIndevParams npindev_terrain_base; + NoiseIndevParams npindev_terrain_higher; + NoiseIndevParams npindev_steepness; //NoiseParams *np_height_select; //NoiseParams *np_trees; - NoiseIndevParams *npindev_mud; + NoiseIndevParams npindev_mud; //NoiseParams *np_beach; - NoiseIndevParams *npindev_biome; + NoiseIndevParams npindev_biome; //NoiseParams *np_cave; - NoiseIndevParams *npindev_float_islands1; - NoiseIndevParams *npindev_float_islands2; - NoiseIndevParams *npindev_float_islands3; + NoiseIndevParams npindev_float_islands1; + NoiseIndevParams npindev_float_islands2; + NoiseIndevParams npindev_float_islands3; MapgenIndevParams() { //freq_desert = 0.45; //freq_beach = 0.15; - npindev_terrain_base = &nparams_indev_def; //&nparams_indev_def_terrain_base; - npindev_terrain_higher = &nparams_indev_def; //&nparams_indev_def_terrain_higher; - npindev_steepness = &nparams_indev_def; //&nparams_indev_def_steepness; + npindev_terrain_base = nparams_indev_def; //&nparams_indev_def_terrain_base; + npindev_terrain_higher = nparams_indev_def; //&nparams_indev_def_terrain_higher; + npindev_steepness = nparams_indev_def; //&nparams_indev_def_steepness; //np_height_select = &nparams_v6_def_height_select; //np_trees = &nparams_v6_def_trees; - npindev_mud = &nparams_indev_def; //&nparams_indev_def_mud; + npindev_mud = nparams_indev_def; //&nparams_indev_def_mud; //np_beach = &nparams_v6_def_beach; - npindev_biome = &nparams_indev_def; //&nparams_indev_def_biome; + npindev_biome = nparams_indev_def; //&nparams_indev_def_biome; //np_cave = &nparams_v6_def_cave; - npindev_float_islands1 = &nparams_indev_def; //&nparams_indev_def_float_islands; - npindev_float_islands2 = &nparams_indev_def; //&nparams_indev_def_float_islands; - npindev_float_islands3 = &nparams_indev_def; //&nparams_indev_def_float_islands; + npindev_float_islands1 = nparams_indev_def; //&nparams_indev_def_float_islands; + npindev_float_islands2 = nparams_indev_def; //&nparams_indev_def_float_islands; + npindev_float_islands3 = nparams_indev_def; //&nparams_indev_def_float_islands; } diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h index b86c9a77..8b816aa2 100644 --- a/src/mapgen_singlenode.h +++ b/src/mapgen_singlenode.h @@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., struct MapgenSinglenodeParams : public MapgenParams { - MapgenSinglenodeParams() { - } + MapgenSinglenodeParams() {} + ~MapgenSinglenodeParams() {} bool readParams(Settings *settings); void writeParams(Settings *settings); diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index d90d142d..e5ff72a6 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -80,18 +80,18 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) this->ystride = csize.X; //////fix this - np_cave = params->np_cave; - np_humidity = params->np_humidity; - np_trees = params->np_trees; - np_apple_trees = params->np_apple_trees; + np_cave = ¶ms->np_cave; + np_humidity = ¶ms->np_humidity; + np_trees = ¶ms->np_trees; + np_apple_trees = ¶ms->np_apple_trees; - noise_terrain_base = new Noise(params->np_terrain_base, seed, csize.X, csize.Y); - noise_terrain_higher = new Noise(params->np_terrain_higher, seed, csize.X, csize.Y); - noise_steepness = new Noise(params->np_steepness, seed, csize.X, csize.Y); - noise_height_select = new Noise(params->np_height_select, seed, csize.X, csize.Y); - noise_mud = new Noise(params->np_mud, seed, csize.X, csize.Y); - noise_beach = new Noise(params->np_beach, seed, csize.X, csize.Y); - noise_biome = new Noise(params->np_biome, seed, csize.X, csize.Y); + noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Y); + noise_terrain_higher = new Noise(¶ms->np_terrain_higher, seed, csize.X, csize.Y); + noise_steepness = new Noise(¶ms->np_steepness, seed, csize.X, csize.Y); + noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Y); + noise_mud = new Noise(¶ms->np_mud, seed, csize.X, csize.Y); + noise_beach = new Noise(¶ms->np_beach, seed, csize.X, csize.Y); + noise_biome = new Noise(¶ms->np_biome, seed, csize.X, csize.Y); } diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index a0276fb5..8f456fd3 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -45,35 +45,36 @@ extern NoiseParams nparams_v6_def_apple_trees; struct MapgenV6Params : public MapgenParams { float freq_desert; float freq_beach; - NoiseParams *np_terrain_base; - NoiseParams *np_terrain_higher; - NoiseParams *np_steepness; - NoiseParams *np_height_select; - NoiseParams *np_mud; - NoiseParams *np_beach; - NoiseParams *np_biome; - NoiseParams *np_cave; - NoiseParams *np_humidity; - NoiseParams *np_trees; - NoiseParams *np_apple_trees; + NoiseParams np_terrain_base; + NoiseParams np_terrain_higher; + NoiseParams np_steepness; + NoiseParams np_height_select; + NoiseParams np_mud; + NoiseParams np_beach; + NoiseParams np_biome; + NoiseParams np_cave; + NoiseParams np_humidity; + NoiseParams np_trees; + NoiseParams np_apple_trees; MapgenV6Params() { freq_desert = 0.45; freq_beach = 0.15; - np_terrain_base = &nparams_v6_def_terrain_base; - np_terrain_higher = &nparams_v6_def_terrain_higher; - np_steepness = &nparams_v6_def_steepness; - np_height_select = &nparams_v6_def_height_select; - np_mud = &nparams_v6_def_mud; - np_beach = &nparams_v6_def_beach; - np_biome = &nparams_v6_def_biome; - np_cave = &nparams_v6_def_cave; - np_humidity = &nparams_v6_def_humidity; - np_trees = &nparams_v6_def_trees; - np_apple_trees = &nparams_v6_def_apple_trees; - + np_terrain_base = nparams_v6_def_terrain_base; + np_terrain_higher = nparams_v6_def_terrain_higher; + np_steepness = nparams_v6_def_steepness; + np_height_select = nparams_v6_def_height_select; + np_mud = nparams_v6_def_mud; + np_beach = nparams_v6_def_beach; + np_biome = nparams_v6_def_biome; + np_cave = nparams_v6_def_cave; + np_humidity = nparams_v6_def_humidity; + np_trees = nparams_v6_def_trees; + np_apple_trees = nparams_v6_def_apple_trees; } + ~MapgenV6Params() {} + bool readParams(Settings *settings); void writeParams(Settings *settings); }; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 4bfc866e..2fb5d7b9 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -85,12 +85,12 @@ MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge) this->ridge_heightmap = new s16[csize.X * csize.Z]; // Terrain noise - noise_terrain_base = new Noise(params->np_terrain_base, seed, csize.X, csize.Z); - noise_terrain_alt = new Noise(params->np_terrain_alt, seed, csize.X, csize.Z); - noise_terrain_mod = new Noise(params->np_terrain_mod, seed, csize.X, csize.Z); - noise_terrain_persist = new Noise(params->np_terrain_persist, seed, csize.X, csize.Z); - noise_height_select = new Noise(params->np_height_select, seed, csize.X, csize.Z); - noise_ridge = new Noise(params->np_ridge, seed, csize.X, csize.Y, csize.Z); + noise_terrain_base = new Noise(¶ms->np_terrain_base, seed, csize.X, csize.Z); + noise_terrain_alt = new Noise(¶ms->np_terrain_alt, seed, csize.X, csize.Z); + noise_terrain_mod = new Noise(¶ms->np_terrain_mod, seed, csize.X, csize.Z); + noise_terrain_persist = new Noise(¶ms->np_terrain_persist, seed, csize.X, csize.Z); + noise_height_select = new Noise(¶ms->np_height_select, seed, csize.X, csize.Z); + noise_ridge = new Noise(¶ms->np_ridge, seed, csize.X, csize.Y, csize.Z); // Biome noise noise_heat = new Noise(bmgr->np_heat, seed, csize.X, csize.Z); diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index 3391098e..b6b03689 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -30,22 +30,24 @@ extern NoiseParams nparams_v7_def_height_select; extern NoiseParams nparams_v7_def_ridge; struct MapgenV7Params : public MapgenParams { - NoiseParams *np_terrain_base; - NoiseParams *np_terrain_alt; - NoiseParams *np_terrain_mod; - NoiseParams *np_terrain_persist; - NoiseParams *np_height_select; - NoiseParams *np_ridge; + NoiseParams np_terrain_base; + NoiseParams np_terrain_alt; + NoiseParams np_terrain_mod; + NoiseParams np_terrain_persist; + NoiseParams np_height_select; + NoiseParams np_ridge; MapgenV7Params() { - np_terrain_base = &nparams_v7_def_terrain_base; - np_terrain_alt = &nparams_v7_def_terrain_alt; - np_terrain_mod = &nparams_v7_def_terrain_mod; - np_terrain_persist = &nparams_v7_def_terrain_persist; - np_height_select = &nparams_v7_def_height_select; - np_ridge = &nparams_v7_def_ridge; + np_terrain_base = nparams_v7_def_terrain_base; + np_terrain_alt = nparams_v7_def_terrain_alt; + np_terrain_mod = nparams_v7_def_terrain_mod; + np_terrain_persist = nparams_v7_def_terrain_persist; + np_height_select = nparams_v7_def_height_select; + np_ridge = nparams_v7_def_ridge; } + ~MapgenV7Params() {} + bool readParams(Settings *settings); void writeParams(Settings *settings); }; diff --git a/src/noise.h b/src/noise.h index ace6d7eb..0bf1a2f1 100644 --- a/src/noise.h +++ b/src/noise.h @@ -72,8 +72,8 @@ struct NoiseParams { // Convenience macros for getting/setting NoiseParams in Settings -#define getNoiseParams(x) getStruct((x), "f,f,v3,s32,s32,f") -#define setNoiseParams(x, y) setStruct((x), "f,f,v3,s32,s32,f", (y)) +#define getNoiseParams(x, y) getStruct((x), "f,f,v3,s32,s32,f", &(y), sizeof(y)) +#define setNoiseParams(x, y) setStruct((x), "f,f,v3,s32,s32,f", &(y)) class Noise { public: @@ -88,7 +88,7 @@ public: Noise(NoiseParams *np, int seed, int sx, int sy); Noise(NoiseParams *np, int seed, int sx, int sy, int sz); - ~Noise(); + virtual ~Noise(); virtual void init(NoiseParams *np, int seed, int sx, int sy, int sz); void setSize(int sx, int sy); diff --git a/src/settings.h b/src/settings.h index 1b7e3cb0..e7b49b6d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -577,15 +577,15 @@ public: return (isdigit(val[0])) ? stoi(val) : readFlagString(val, flagdesc); } - template T *getStruct(std::string name, std::string format) + bool getStruct(std::string name, std::string format, void *out, size_t olen) { - size_t len = sizeof(T); + size_t len = olen; std::vector strs_alloced; std::string *str; std::string valstr = get(name); char *s = &valstr[0]; - T *buf = new T; - char *bufpos = (char *)buf; + char *buf = new char[len]; + char *bufpos = buf; char *f, *snext; size_t pos; @@ -608,7 +608,7 @@ public: case 'i': if (width == 16) { bufpos += PADDING(bufpos, u16); - if ((bufpos - (char *)buf) + sizeof(u16) <= len) { + if ((bufpos - buf) + sizeof(u16) <= len) { if (is_unsigned) *(u16 *)bufpos = (u16)strtoul(s, &s, 10); else @@ -617,7 +617,7 @@ public: bufpos += sizeof(u16); } else if (width == 32) { bufpos += PADDING(bufpos, u32); - if ((bufpos - (char *)buf) + sizeof(u32) <= len) { + if ((bufpos - buf) + sizeof(u32) <= len) { if (is_unsigned) *(u32 *)bufpos = (u32)strtoul(s, &s, 10); else @@ -626,7 +626,7 @@ public: bufpos += sizeof(u32); } else if (width == 64) { bufpos += PADDING(bufpos, u64); - if ((bufpos - (char *)buf) + sizeof(u64) <= len) { + if ((bufpos - buf) + sizeof(u64) <= len) { if (is_unsigned) *(u64 *)bufpos = (u64)strtoull(s, &s, 10); else @@ -642,7 +642,7 @@ public: *snext++ = 0; bufpos += PADDING(bufpos, bool); - if ((bufpos - (char *)buf) + sizeof(bool) <= len) + if ((bufpos - buf) + sizeof(bool) <= len) *(bool *)bufpos = is_yes(std::string(s)); bufpos += sizeof(bool); @@ -650,7 +650,7 @@ public: break; case 'f': bufpos += PADDING(bufpos, float); - if ((bufpos - (char *)buf) + sizeof(float) <= len) + if ((bufpos - buf) + sizeof(float) <= len) *(float *)bufpos = strtof(s, &s); bufpos += sizeof(float); @@ -674,7 +674,7 @@ public: while ((pos = str->find("\\\"", pos)) != std::string::npos) str->erase(pos, 1); - if ((bufpos - (char *)buf) + sizeof(std::string *) <= len) + if ((bufpos - buf) + sizeof(std::string *) <= len) *(std::string **)bufpos = str; bufpos += sizeof(std::string *); strs_alloced.push_back(str); @@ -690,7 +690,7 @@ public: if (width == 2) { bufpos += PADDING(bufpos, v2f); - if ((bufpos - (char *)buf) + sizeof(v2f) <= len) { + if ((bufpos - buf) + sizeof(v2f) <= len) { v2f *v = (v2f *)bufpos; v->X = strtof(s, &s); s++; @@ -700,7 +700,7 @@ public: bufpos += sizeof(v2f); } else if (width == 3) { bufpos += PADDING(bufpos, v3f); - if ((bufpos - (char *)buf) + sizeof(v3f) <= len) { + if ((bufpos - buf) + sizeof(v3f) <= len) { v3f *v = (v3f *)bufpos; v->X = strtof(s, &s); s++; @@ -720,20 +720,21 @@ public: if (s && *s == ',') s++; - if ((size_t)(bufpos - (char *)buf) > len) //error, buffer too small + if ((size_t)(bufpos - buf) > len) //error, buffer too small goto fail; } if (f && *f) { //error, mismatched number of fields and values fail: - for (unsigned int i = 0; i != strs_alloced.size(); i++) + for (size_t i = 0; i != strs_alloced.size(); i++) delete strs_alloced[i]; - delete buf; - //delete[] buf; - buf = NULL; + delete[] buf; + return false; } - return buf; + memcpy(out, buf, olen); + delete[] buf; + return true; } bool setStruct(std::string name, std::string format, void *value) From b2577b1f27a081a9148e7f9cf6b365b54adb80d2 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sun, 19 May 2013 18:47:58 +0200 Subject: [PATCH 13/41] Install mods/mods_here.txt instead of mods/minetest/mods_here.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 82fd23a0..446fff07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,7 +145,7 @@ if(BUILD_CLIENT) 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/minetest/mods_here.txt" DESTINATION "${SHAREDIR}/mods/minetest") + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/mods/mods_here.txt" DESTINATION "${SHAREDIR}/mods") install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/textures/all/textures_here.txt" DESTINATION "${SHAREDIR}/textures/all") endif() From dcd0b63f645bccf497a030b6b1cec97178542eb1 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sun, 19 May 2013 19:46:50 +0200 Subject: [PATCH 14/41] Dont load mods that have no entry in world.mt --- src/mods.cpp | 11 +++++------ src/server.cpp | 10 ++-------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/mods.cpp b/src/mods.cpp index 64c31999..a0a37afb 100644 --- a/src/mods.cpp +++ b/src/mods.cpp @@ -220,7 +220,7 @@ ModConfiguration::ModConfiguration(std::string worldpath) Settings worldmt_settings; worldmt_settings.readConfigFile(worldmt.c_str()); std::vector names = worldmt_settings.getNames(); - std::set exclude_mod_names; + std::set include_mod_names; for(std::vector::iterator it = names.begin(); it != names.end(); ++it) { @@ -229,14 +229,13 @@ ModConfiguration::ModConfiguration(std::string worldpath) // explicitely excluded. if mod is not mentioned at all, it is // enabled. So by default, all installed mods are enabled. if (name.compare(0,9,"load_mod_") == 0 && - !worldmt_settings.getBool(name)) + worldmt_settings.getBool(name)) { - exclude_mod_names.insert(name.substr(9)); + include_mod_names.insert(name.substr(9)); } } - // Collect all mods in gamespec.addon_mods_paths, - // excluding those in the set exclude_mod_names + // Collect all mods that are also in include_mod_names std::vector addon_mods; for(std::set::const_iterator it_path = gamespec.addon_mods_paths.begin(); it_path != gamespec.addon_mods_paths.end(); ++it_path) @@ -246,7 +245,7 @@ ModConfiguration::ModConfiguration(std::string worldpath) it != addon_mods_in_path.end(); ++it) { ModSpec& mod = *it; - if(exclude_mod_names.count(mod.name) == 0) + if(include_mod_names.count(mod.name) != 0) addon_mods.push_back(mod); } } diff --git a/src/server.cpp b/src/server.cpp index 40a4f8a0..0d724b1c 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -727,19 +727,13 @@ Server::Server( std::string worldmt = m_path_world + DIR_DELIM + "world.mt"; worldmt_settings.readConfigFile(worldmt.c_str()); std::vector names = worldmt_settings.getNames(); - std::set exclude_mod_names; std::set load_mod_names; for(std::vector::iterator it = names.begin(); it != names.end(); ++it) { std::string name = *it; - if (name.compare(0,9,"load_mod_")==0) - { - if(worldmt_settings.getBool(name)) - load_mod_names.insert(name.substr(9)); - else - exclude_mod_names.insert(name.substr(9)); - } + if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name)) + load_mod_names.insert(name.substr(9)); } // complain about mods declared to be loaded, but not found for(std::vector::iterator it = m_mods.begin(); From b2253e5b42aed74365cb6d10b90fea4a30dee5fa Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sun, 19 May 2013 19:52:29 +0200 Subject: [PATCH 15/41] Write mods that are not in world.mt into it at world startup --- src/mods.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mods.cpp b/src/mods.cpp index a0a37afb..9097f570 100644 --- a/src/mods.cpp +++ b/src/mods.cpp @@ -247,8 +247,11 @@ ModConfiguration::ModConfiguration(std::string worldpath) ModSpec& mod = *it; if(include_mod_names.count(mod.name) != 0) addon_mods.push_back(mod); + else + worldmt_settings.setBool("load_mod_" + mod.name, false); } } + worldmt_settings.updateConfigFile(worldmt.c_str()); addMods(addon_mods); From cc92e45b78dd8955e43b2828eafb4b4e3a0a1b99 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Sun, 19 May 2013 19:58:01 +0200 Subject: [PATCH 16/41] Fix EmergeThread names in log Concatenate the thread id as string instead of adding it to the pointer --- src/emerge.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/emerge.cpp b/src/emerge.cpp index 2d8b0181..b31a942e 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -349,7 +349,7 @@ bool EmergeThread::getBlockOrStartGen(v3s16 p, MapBlock **b, void *EmergeThread::Thread() { ThreadStarted(); - log_register_thread("EmergeThread" + id); + log_register_thread("EmergeThread" + itos(id)); DSTACK(__FUNCTION_NAME); BEGIN_DEBUG_EXCEPTION_HANDLER From bc66f4632372e62be53f3b61cd5cc4a82ee44741 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sun, 19 May 2013 20:56:25 +0200 Subject: [PATCH 17/41] Dont teleport back if holding sneak when detaching/turn freemove off --- src/localplayer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 13117d88..f8dfca05 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -73,6 +73,7 @@ void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d, if(isAttached) { setPosition(overridePosition); + m_sneak_node_exists = false; return; } @@ -85,6 +86,7 @@ void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d, { position += m_speed * dtime; setPosition(position); + m_sneak_node_exists = false; return; } From 3e2efdf18ab95d3c88f66c7a1f6caf6b1c7231e5 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Mon, 20 May 2013 00:20:42 +0200 Subject: [PATCH 18/41] Add a little animation when changing the wielded item --- src/camera.cpp | 61 +++++++++++++++++++++++++++++++++++++++++--------- src/camera.h | 8 ++++++- src/game.cpp | 2 +- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/src/camera.cpp b/src/camera.cpp index 0dd0a767..f227bd98 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -71,7 +71,12 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_digging_anim(0), m_digging_button(-1), - m_dummymesh(createCubeMesh(v3f(1,1,1))) + m_dummymesh(createCubeMesh(v3f(1,1,1))), + + m_wield_change_timer(0.125), + m_wield_mesh_next(NULL), + m_previous_playeritem(-1), + m_previous_itemname("") { //dstream<<__FUNCTION_NAME< 0.125) + m_wield_change_timer = 0.125; + + if(m_wield_change_timer >= 0 && m_wield_change_timer - dtime < 0) { + if(m_wield_mesh_next) { + m_wieldnode->setMesh(m_wield_mesh_next); + m_wieldnode->setVisible(true); + } else { + m_wieldnode->setVisible(false); + } + m_wield_mesh_next = NULL; + } + if (m_view_bobbing_state != 0) { //f32 offset = dtime * m_view_bobbing_speed * 0.035; @@ -351,6 +371,10 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize, v3f wield_position = v3f(55, -35, 65); //v3f wield_rotation = v3f(-100, 120, -100); v3f wield_rotation = v3f(-100, 120, -100); + if(m_wield_change_timer < 0) + wield_position.Y -= 40 + m_wield_change_timer*320; + else + wield_position.Y -= 40 - m_wield_change_timer*320; if(m_digging_anim < 0.05 || m_digging_anim > 0.5){ f32 frac = 1.0; if(m_digging_anim > 0.5) @@ -559,18 +583,33 @@ void Camera::setDigging(s32 button) m_digging_button = button; } -void Camera::wield(const ItemStack &item) +void Camera::wield(const ItemStack &item, u16 playeritem) { IItemDefManager *idef = m_gamedef->idef(); - scene::IMesh *wield_mesh = idef->getWieldMesh(item.getDefinition(idef).name, m_gamedef); - if(wield_mesh) - { - m_wieldnode->setMesh(wield_mesh); - m_wieldnode->setVisible(true); - } - else - { - m_wieldnode->setVisible(false); + std::string itemname = item.getDefinition(idef).name; + m_wield_mesh_next = idef->getWieldMesh(itemname, m_gamedef); + if(playeritem != m_previous_playeritem) { + m_previous_playeritem = playeritem; + m_previous_itemname = itemname; + if(m_wield_change_timer >= 0.125) + m_wield_change_timer = -0.125; + else if(m_wield_change_timer > 0) { + m_wield_change_timer = -m_wield_change_timer; + } + } else { + if(m_wield_mesh_next) { + m_wieldnode->setMesh(m_wield_mesh_next); + m_wieldnode->setVisible(true); + } else { + m_wieldnode->setVisible(false); + } + m_wield_mesh_next = NULL; + if(m_previous_itemname != itemname) { + m_previous_itemname = itemname; + m_wield_change_timer = 0; + } + else + m_wield_change_timer = 0.125; } } diff --git a/src/camera.h b/src/camera.h index 0ed5c20d..c8a6e0bb 100644 --- a/src/camera.h +++ b/src/camera.h @@ -117,7 +117,7 @@ public: void setDigging(s32 button); // Replace the wielded item mesh - void wield(const ItemStack &item); + void wield(const ItemStack &item, u16 playeritem); // Draw the wielded tool. // This has to happen *after* the main scene is drawn. @@ -178,6 +178,12 @@ private: //dummymesh for camera irr::scene::IAnimatedMesh* m_dummymesh; + + // Animation when changing wielded item + f32 m_wield_change_timer; + scene::IMesh *m_wield_mesh_next; + u16 m_previous_playeritem; + std::string m_previous_itemname; }; #endif diff --git a/src/game.cpp b/src/game.cpp index 746769ae..185f42ea 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3084,7 +3084,7 @@ void the_game( ItemStack item; if(mlist != NULL) item = mlist->getItem(client.getPlayerItem()); - camera.wield(item); + camera.wield(item, client.getPlayerItem()); } /* From 55a97f4605a263f4b670b447cd4af9ffa1a8d472 Mon Sep 17 00:00:00 2001 From: sapier Date: Sun, 5 May 2013 01:44:55 +0200 Subject: [PATCH 19/41] Allow nil as puncher e.g. to do damage by tnt --- src/content_sao.cpp | 15 +++++++++++++-- src/scriptapi_object.cpp | 23 ++++++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 24a9186f..47b94a5d 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -673,8 +673,14 @@ int LuaEntitySAO::punch(v3f dir, { setHP(getHP() - result.damage); + + std::string punchername = "nil"; + + if ( puncher != 0 ) + punchername = puncher->getDescription(); + actionstream<getDescription()<<", damage "<getDescription(); + actionstream<<"Player "<getName()<<" punched by " - <getDescription()<<", damage "<getBasePosition() - puncher->getBasePosition(); - else - dir = read_v3f(L, 5); + } + float time_from_last_punch = 1000000; if(lua_isnumber(L, 3)) time_from_last_punch = lua_tonumber(L, 3); + ToolCapabilities toolcap = read_tool_capabilities(L, 4); + + if(lua_type(L, 5) == LUA_TTABLE) + dir = read_v3f(L, 5); dir.normalize(); + // Do it co->punch(dir, &toolcap, puncher, time_from_last_punch); return 0; From d00e8bd31a185a4000807de6d21eccce9f86671e Mon Sep 17 00:00:00 2001 From: kwolekr Date: Sun, 19 May 2013 21:26:08 -0400 Subject: [PATCH 20/41] Fix nearly all warnings --- src/collision.cpp | 2 +- src/emerge.h | 4 ++-- src/environment.cpp | 6 ------ src/guiConfigureWorld.cpp | 2 -- src/guiCreateWorld.cpp | 2 -- src/guiPasswordChange.cpp | 1 - src/guiVolumeChange.cpp | 1 - src/map.cpp | 10 +++++----- src/mapgen_v6.cpp | 2 -- src/mapgen_v7.cpp | 2 -- src/player.h | 2 +- src/scriptapi_env.cpp | 3 +-- src/scriptapi_types.cpp | 2 +- src/test.cpp | 2 +- src/voxel.cpp | 2 +- 15 files changed, 13 insertions(+), 30 deletions(-) diff --git a/src/collision.cpp b/src/collision.cpp index 673966b6..3058426e 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -298,7 +298,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, f32 distance = speed_f.getLength(); std::vector clientobjects; c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects); - for (int i=0; i < clientobjects.size(); i++) + for (size_t i=0; i < clientobjects.size(); i++) { if ((self == 0) || (self != clientobjects[i].obj)) { objects.push_back((ActiveObject*)clientobjects[i].obj); diff --git a/src/emerge.h b/src/emerge.h index fdca93c0..b42e82d3 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -31,8 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., infostream << "EmergeThread: " x << std::endl; } class Mapgen; -class MapgenParams; -class MapgenFactory; +struct MapgenParams; +struct MapgenFactory; class Biome; class BiomeDefManager; class EmergeThread; diff --git a/src/environment.cpp b/src/environment.cpp index e06b032f..03b8ef7f 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -910,7 +910,6 @@ void ServerEnvironment::clearAllObjects() if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) continue; u16 id = i->first; - v3f objectpos = obj->getBasePosition(); // Delete static object if block is loaded if(obj->m_static_exists){ MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); @@ -1031,8 +1030,6 @@ void ServerEnvironment::step(float dtime) // Ignore disconnected players if(player->peer_id == 0) continue; - - v3f playerpos = player->getPosition(); // Move player->move(dtime, *m_map, 100*BS); @@ -2072,8 +2069,6 @@ void ClientEnvironment::step(float dtime) */ { - v3f lplayerpos = lplayer->getPosition(); - // Apply physics if(free_move == false && is_climbing == false) { @@ -2197,7 +2192,6 @@ void ClientEnvironment::step(float dtime) i != m_players.end(); ++i) { Player *player = *i; - v3f playerpos = player->getPosition(); /* Handle non-local players diff --git a/src/guiConfigureWorld.cpp b/src/guiConfigureWorld.cpp index e33e87ef..8f5ef937 100644 --- a/src/guiConfigureWorld.cpp +++ b/src/guiConfigureWorld.cpp @@ -165,8 +165,6 @@ void GUIConfigureWorld::regenerateGui(v2u32 screensize) DesiredRect = rect; recalculateAbsolutePosition(false); - v2s32 size = rect.getSize(); - v2s32 topleft = v2s32(10, 10); /* diff --git a/src/guiCreateWorld.cpp b/src/guiCreateWorld.cpp index caa884bc..9fc76421 100644 --- a/src/guiCreateWorld.cpp +++ b/src/guiCreateWorld.cpp @@ -112,8 +112,6 @@ void GUICreateWorld::regenerateGui(v2u32 screensize) DesiredRect = rect; recalculateAbsolutePosition(false); - v2s32 size = rect.getSize(); - v2s32 topleft = v2s32(10+80, 10+70); /* diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp index a4d16444..8b55234c 100644 --- a/src/guiPasswordChange.cpp +++ b/src/guiPasswordChange.cpp @@ -95,7 +95,6 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize) v2s32 size = rect.getSize(); v2s32 topleft_client(40, 0); - v2s32 size_client = size - v2s32(40, 0); /* Add stuff diff --git a/src/guiVolumeChange.cpp b/src/guiVolumeChange.cpp index 4e92b82c..c272b132 100644 --- a/src/guiVolumeChange.cpp +++ b/src/guiVolumeChange.cpp @@ -96,7 +96,6 @@ void GUIVolumeChange::regenerateGui(v2u32 screensize) v2s32 size = rect.getSize(); v2s32 topleft_client(40, 0); - v2s32 size_client = size - v2s32(40, 0); int volume=(int)(g_settings->getFloat("sound_volume")*100); /* Add stuff diff --git a/src/map.cpp b/src/map.cpp index 821805d4..d5ab8eb1 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -288,10 +288,10 @@ void Map::unspreadLight(enum LightBank bank, continue; // Calculate relative position in block - v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE; + //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE; // Get node straight from the block - MapNode n = block->getNode(relpos); + //MapNode n = block->getNode(relpos); u8 oldlight = j->second; @@ -937,7 +937,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, */ v3s16 toppos = p + v3s16(0,1,0); - v3s16 bottompos = p + v3s16(0,-1,0); + //v3s16 bottompos = p + v3s16(0,-1,0); bool node_under_sunlight = true; std::set light_sources; @@ -1246,7 +1246,7 @@ void Map::removeNodeAndUpdate(v3s16 p, // Get the brightest neighbour node and propagate light from it v3s16 n2p = getBrightestNeighbour(bank, p); try{ - MapNode n2 = getNode(n2p); + //MapNode n2 = getNode(n2p); lightNeighbors(bank, n2p, modified_blocks); } catch(InvalidPositionException &e) @@ -2831,7 +2831,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d) sector = new ServerMapSector(this, p2d, m_gamedef); // Sector position on map in nodes - v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; + //v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; /* Insert to container diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index e5ff72a6..eaca3398 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -375,8 +375,6 @@ void MapgenV6::makeChunk(BlockMakeData *data) { v3s16 blockpos = data->blockpos_requested; v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; - v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1); - v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1); // Area of central chunk node_min = blockpos_min*MAP_BLOCKSIZE; diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 2fb5d7b9..6daa5fc6 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -152,8 +152,6 @@ void MapgenV7::makeChunk(BlockMakeData *data) { v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; - v3s16 blockpos_full_min = blockpos_min - v3s16(1, 1, 1); - v3s16 blockpos_full_max = blockpos_max + v3s16(1, 1, 1); node_min = blockpos_min * MAP_BLOCKSIZE; node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1); full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE; diff --git a/src/player.h b/src/player.h index fade6de0..63e166b4 100644 --- a/src/player.h +++ b/src/player.h @@ -87,7 +87,7 @@ class Map; class IGameDef; struct CollisionInfo; class PlayerSAO; -class HudElement; +struct HudElement; class Player { diff --git a/src/scriptapi_env.cpp b/src/scriptapi_env.cpp index efed58e6..a9d9391a 100644 --- a/src/scriptapi_env.cpp +++ b/src/scriptapi_env.cpp @@ -227,7 +227,6 @@ int EnvRef::l_get_node_light(lua_State *L) time_of_day = 24000.0 * lua_tonumber(L, 3); time_of_day %= 24000; u32 dnr = time_to_daynight_ratio(time_of_day, true); - MapNode n = env->getMap().getNodeNoEx(pos); try{ MapNode n = env->getMap().getNode(pos); INodeDefManager *ndef = env->getGameDef()->ndef(); @@ -373,7 +372,7 @@ int EnvRef::l_add_item(lua_State *L) ServerEnvironment *env = o->m_env; if(env == NULL) return 0; // pos - v3f pos = checkFloatPos(L, 2); + //v3f pos = checkFloatPos(L, 2); // item ItemStack item = read_item(L, 3); if(item.empty() || !item.isKnown(get_server(L)->idef())) diff --git a/src/scriptapi_types.cpp b/src/scriptapi_types.cpp index f3045110..f2f6b6bd 100644 --- a/src/scriptapi_types.cpp +++ b/src/scriptapi_types.cpp @@ -147,7 +147,7 @@ v3s16 check_v3s16(lua_State *L, int index) video::SColor readARGB8(lua_State *L, int index) { - video::SColor color; + video::SColor color(0); luaL_checktype(L, index, LUA_TTABLE); lua_getfield(L, index, "a"); if(lua_isnumber(L, -1)) diff --git a/src/test.cpp b/src/test.cpp index e1dbfa6b..3a0316e1 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -464,7 +464,7 @@ struct TestCompress: public TestBase std::string str_decompressed = os_decompressed.str(); UTEST(str_decompressed.size() == data_in.size(), "Output size not" " equal (output: %u, input: %u)", - str_decompressed.size(), data_in.size()); + (unsigned int)str_decompressed.size(), (unsigned int)data_in.size()); for(u32 i=0; i Date: Sun, 19 May 2013 21:29:49 -0400 Subject: [PATCH 21/41] Fix lava damage on player's upper body --- src/environment.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/environment.cpp b/src/environment.cpp index 03b8ef7f..9f87b8c9 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2169,7 +2169,7 @@ void ClientEnvironment::step(float dtime) v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); MapNode n2 = m_map->getNodeNoEx(p2); v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); - MapNode n3 = m_map->getNodeNoEx(p2); + MapNode n3 = m_map->getNodeNoEx(p3); u32 damage_per_second = 0; damage_per_second = MYMAX(damage_per_second, From 127c48835573f9e7bd89137ecb2176c2a8e5f783 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sun, 14 Apr 2013 03:01:27 -0400 Subject: [PATCH 22/41] Use the group "soil" for nodes that saplings grow on --- doc/lua_api.txt | 3 ++- games/minimal/mods/default/init.lua | 6 +++--- src/content_abm.cpp | 9 ++------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index b7be5020..e2abcbfe 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -510,7 +510,7 @@ 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 (soil group actually currently not defined; TODO) + ^ 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 @@ -583,6 +583,7 @@ Special groups - 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 Known damage and digging time defining groups ---------------------------------------------- diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index d8067d21..b375cda1 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -741,7 +741,7 @@ minetest.register_node("default:dirt_with_grass", { description = "Dirt with grass", tiles ={"default_grass.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"}, is_ground_content = true, - groups = {crumbly=3}, + groups = {crumbly=3, soil=1}, drop = 'default:dirt', sounds = default.node_sound_dirt_defaults({ footstep = {name="default_grass_footstep", gain=0.4}, @@ -752,7 +752,7 @@ minetest.register_node("default:dirt_with_grass_footsteps", { description = "Dirt with grass and footsteps", tiles ={"default_grass_footsteps.png", "default_dirt.png", "default_dirt.png^default_grass_side.png"}, is_ground_content = true, - groups = {crumbly=3}, + groups = {crumbly=3, soil=1}, drop = 'default:dirt', sounds = default.node_sound_dirt_defaults({ footstep = {name="default_grass_footstep", gain=0.4}, @@ -763,7 +763,7 @@ minetest.register_node("default:dirt", { description = "Dirt", tiles ={"default_dirt.png"}, is_ground_content = true, - groups = {crumbly=3}, + groups = {crumbly=3, soil=1}, sounds = default.node_sound_dirt_defaults(), }) diff --git a/src/content_abm.cpp b/src/content_abm.cpp index e20201dc..f19caf74 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -99,14 +99,10 @@ class MakeTreesFromSaplingsABM : public ActiveBlockModifier { private: content_t c_junglesapling; - content_t c_dirt; - content_t c_dirt_with_grass; public: MakeTreesFromSaplingsABM(ServerEnvironment *env, INodeDefManager *nodemgr) { - c_junglesapling = nodemgr->getId("junglesapling"); - c_dirt = nodemgr->getId("mapgen_dirt"); - c_dirt_with_grass = nodemgr->getId("mapgen_dirt_with_grass"); + c_junglesapling = nodemgr->getId("junglesapling"); } virtual std::set getTriggerContents() @@ -127,8 +123,7 @@ public: ServerMap *map = &env->getServerMap(); MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0)); - if (n_below.getContent() != c_dirt && - n_below.getContent() != c_dirt_with_grass) + if (!((ItemGroupList) ndef->get(n_below).groups)["soil"]) return; bool is_jungle_tree = n.getContent() == c_junglesapling; From 1ba1fb42a431404ca559481420025f3e9f96191d Mon Sep 17 00:00:00 2001 From: Russ Date: Thu, 28 Feb 2013 16:43:27 -0800 Subject: [PATCH 23/41] Fix link in minetest.conf.example --- minetest.conf.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minetest.conf.example b/minetest.conf.example index 7d56f99d..1f9780f4 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -12,7 +12,7 @@ # # NOTE: This file might not be up-to-date, refer to the # defaultsettings.cpp file for an up-to-date list: -# https://bitbucket.org/celeron55/minetest/src/tip/src/defaultsettings.cpp +# https://github.com/minetest/minetest/blob/master/src/defaultsettings.cpp # # A vim command to convert most of defaultsettings.cpp to conf file format: # :'<,'>s/\tsettings->setDefault("\([^"]*\)", "\([^"]*\)");.*/#\1 = \2/g From c135e9c7d4bfaf038a2236f17f6e246d03461b8a Mon Sep 17 00:00:00 2001 From: elagin Date: Thu, 31 Jan 2013 21:03:14 +0400 Subject: [PATCH 24/41] Save settings after dropping main menu --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e5c8a55d..eda99279 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1942,6 +1942,9 @@ int main(int argc, char *argv[]) sound = NULL; } + // Save controls status + menu->readInput(&menudata); + infostream<<"Dropping main menu"<drop(); From 5e80df46b8c42e71c376da50ffb0ef06636073f2 Mon Sep 17 00:00:00 2001 From: MetaDucky Date: Mon, 20 May 2013 16:09:11 +0200 Subject: [PATCH 25/41] Fix typo in particle spawning --- src/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index 533c40ab..e4ca52dd 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1987,7 +1987,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.spawn_particle.expirationtime = expirationtime; event.spawn_particle.size = size; - event.add_particlespawner.collisiondetection = + event.spawn_particle.collisiondetection = collisiondetection; event.spawn_particle.texture = new std::string(texture); From c67f72890c3f9154dd9124310c61ba55bc2806bd Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Mon, 20 May 2013 16:57:22 +0200 Subject: [PATCH 26/41] Add menu_header.png and remove old, unused textures --- textures/base/pack/menu_header.png | Bin 0 -> 56279 bytes textures/base/pack/menubg.png | Bin 1586 -> 0 bytes textures/base/pack/menufooter.png | Bin 356 -> 0 bytes textures/base/pack/menuheader.png | Bin 578 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 textures/base/pack/menu_header.png delete mode 100644 textures/base/pack/menubg.png delete mode 100644 textures/base/pack/menufooter.png delete mode 100644 textures/base/pack/menuheader.png diff --git a/textures/base/pack/menu_header.png b/textures/base/pack/menu_header.png new file mode 100644 index 0000000000000000000000000000000000000000..b95facbdf0e5511fd53044e2d80d0a3c9aa30704 GIT binary patch literal 56279 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≫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 literal 0 HcmV?d00001 diff --git a/textures/base/pack/menubg.png b/textures/base/pack/menubg.png deleted file mode 100644 index 7cb9c89a637584b4e53c82a7c6eb1ec7109befae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1586 zcmZuw2~d+q6kb75sRsk0q?k5{zaXLY3W9(a3WY_b(ZE_-}~PC4=Mb- zCz@KD0stmLAJ5hNHZ$yTM*K|HggXOZ3=}K;y|~K;HygYd_n2W(N(mttV}uZdP!I$P z00964fB=912s~@VCrSY!0Aqj04-!kfDGG<2RUGpVzRQivgMAj}(tFLMbMMCqjgH1f~=b0x{+`qLjbS4M-_J z1vY{`BaC875Wx^5&KEO^D1ij?IuN6f5?&utYaJ`E5I?++&lCPA6vozrkn!cK!dARW;7ZAqYmil;Xk8x zOex()Ljl;>&otgTV{MB@b^GU|kY#+M@6QhfTqKscG$EO}~h?Y0SX~b!+=X-vW2P^CC zOE1gUWNA;A{T|`yqmErDI$N!&=)c?jWchZ_!nMl>&VLnMmSVE1;auPnn|(q-;%E9DAfn*VT5@WtYVs_RuFy`_69T&H;7zq$%~PLRE~nZ{ai_oo0qY?o}pv zkx_0ntu5BM^m2$(uI=?IY4OK7d;Vx^iKuUnpIhX(Q?OX->^C>e_eEm+nW#%geM*X3 zOHoAh%n6^m_38d?NK2AgJM`c5va@ghJwKDMqkkYKL6vbCr9@%nFQnfi|&H}rRuSHf>1##()ElxLbrMJId zpVWIcqWS5hyFCZ5_|9(3mo~82t0k>9i@%W^D{WTTtUK&jvi`KGJSuF}X1pZ4Lf6^< zV{gGVrQ1uJBfEk0s5W! A1poj5 diff --git a/textures/base/pack/menufooter.png b/textures/base/pack/menufooter.png deleted file mode 100644 index fd431846eb4cfa923dcf938ec9c28fca216e9467..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 356 zcmeAS@N?(olHy`uVBq!ia0y~yU~~YnS(t&O7XPaGKuRvaC&ZP3;XgwIh|ty5_3`m( zaA0U?XgF}-!2kaZ|NsAwUSVPZRLNQ35n0T@z;^_M8K-LVNdpDhOFVsD+3zy*G05?K z`?MHDbuKfxSNuP08`fTP-)px(Q7KKz(x6`_d!}Xg8`T$Exr$1s-BgI% z;K#c5`#jfFKduKd1`?94J#(68FMY$`;8C%bHA9j4)`~Mt5<4AEO*?Yjx?6_vyjo}E^7sIj_HXN=m)HC1uH#OS^-z ymP=pZwYEUVj56b`boOkx>iB*fZ1^j`YWbkzLb6Mw<&;$S|uY~CU diff --git a/textures/base/pack/menuheader.png b/textures/base/pack/menuheader.png deleted file mode 100644 index 66fd6def577f6c8862469c37f5382f91d184001d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 578 zcmeAS@N?(olHy`uVBq!ia0y~yV4M$RyKt}p$+#SgGeC+h$=lt9;Xep2*t>i(P=vF< zBeIx*fm;}a85w5HkpSv@d1rw6D*%J%8^1(`@F3b1nP~hu9e& zGQ-J+Uw!;_&psZ%KL6 zLJfm}9ST{Xl)U}+)w_PboFyt=akR{0M2~{wEzGB From e57dc4ef1899fd172b94dc67c3a70be49803145e Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Mon, 20 May 2013 20:31:38 +0200 Subject: [PATCH 27/41] Dont animate changing wielditem from hand to hand --- src/camera.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/camera.cpp b/src/camera.cpp index f227bd98..acefff7e 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -588,7 +588,8 @@ void Camera::wield(const ItemStack &item, u16 playeritem) IItemDefManager *idef = m_gamedef->idef(); std::string itemname = item.getDefinition(idef).name; m_wield_mesh_next = idef->getWieldMesh(itemname, m_gamedef); - if(playeritem != m_previous_playeritem) { + if(playeritem != m_previous_playeritem && + !(m_previous_itemname == "" && itemname == "")) { m_previous_playeritem = playeritem; m_previous_itemname = itemname; if(m_wield_change_timer >= 0.125) From 4a9fe1a1d5d19565bca67c0dcb3dbf43bd06f278 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Tue, 21 May 2013 14:56:43 +0200 Subject: [PATCH 28/41] Log non-fatal mod name conflict to actionstream instead of errorstream --- src/mods.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mods.cpp b/src/mods.cpp index 9097f570..9bcf73aa 100644 --- a/src/mods.cpp +++ b/src/mods.cpp @@ -309,7 +309,7 @@ void ModConfiguration::addMods(std::vector new_mods) // BAD CASE: name conflict in different levels. u32 oldindex = existing_mods[mod.name]; const ModSpec &oldmod = m_unsatisfied_mods[oldindex]; - errorstream<<"WARNING: Mod name conflict detected: \"" + actionstream<<"WARNING: Mod name conflict detected: \"" < Date: Tue, 21 May 2013 20:16:22 +0200 Subject: [PATCH 29/41] Fix disappearing of wielditem --- src/camera.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/camera.cpp b/src/camera.cpp index acefff7e..3d8713d6 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -147,12 +147,13 @@ void Camera::step(f32 dtime) m_view_bobbing_fall = -1; // Mark the effect as finished } + bool was_under_zero = m_wield_change_timer < 0; if(m_wield_change_timer < 0.125) m_wield_change_timer += dtime; if(m_wield_change_timer > 0.125) m_wield_change_timer = 0.125; - if(m_wield_change_timer >= 0 && m_wield_change_timer - dtime < 0) { + if(m_wield_change_timer >= 0 && was_under_zero) { if(m_wield_mesh_next) { m_wieldnode->setMesh(m_wield_mesh_next); m_wieldnode->setVisible(true); From 3abbe7efd1d1da7c24725ae49c2df1013a7afb09 Mon Sep 17 00:00:00 2001 From: Jeija Date: Wed, 6 Feb 2013 19:36:14 +0100 Subject: [PATCH 30/41] Make raillike nodes connect to any other raillike nodes if both are in the group connect_to_raillike --- doc/lua_api.txt | 2 ++ src/content_mapblock.cpp | 76 ++++++++++++++++++++++++++++++++-------- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e2abcbfe..bee247ca 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -584,6 +584,8 @@ Special groups 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 ---------------------------------------------- diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 155b39ab..84408e77 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -1188,34 +1188,81 @@ void mapblock_mesh_generate_special(MeshMakeData *data, MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1)); MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1)); MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1)); - + content_t thiscontent = n.getContent(); - if(n_minus_x.getContent() == thiscontent) + std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind + bool self_connect_to_raillike = ((ItemGroupList) nodedef->get(n).groups)[groupname] != 0; + + if ((nodedef->get(n_minus_x).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_minus_x).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_minus_x.getContent() == thiscontent) is_rail_x[0] = true; - if (n_minus_x_minus_y.getContent() == thiscontent) + + if ((nodedef->get(n_minus_x_minus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_minus_x_minus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_minus_x_minus_y.getContent() == thiscontent) is_rail_x_minus_y[0] = true; - if(n_minus_x_plus_y.getContent() == thiscontent) + + if ((nodedef->get(n_minus_x_plus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_minus_x_plus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_minus_x_plus_y.getContent() == thiscontent) is_rail_x_plus_y[0] = true; - if(n_plus_x.getContent() == thiscontent) + if ((nodedef->get(n_plus_x).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_plus_x).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_plus_x.getContent() == thiscontent) is_rail_x[1] = true; - if (n_plus_x_minus_y.getContent() == thiscontent) + + if ((nodedef->get(n_plus_x_minus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_plus_x_minus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_plus_x_minus_y.getContent() == thiscontent) is_rail_x_minus_y[1] = true; - if(n_plus_x_plus_y.getContent() == thiscontent) + + if ((nodedef->get(n_plus_x_plus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_plus_x_plus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_plus_x_plus_y.getContent() == thiscontent) is_rail_x_plus_y[1] = true; - if(n_minus_z.getContent() == thiscontent) + if ((nodedef->get(n_minus_z).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_minus_z).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_minus_z.getContent() == thiscontent) is_rail_z[0] = true; - if (n_minus_z_minus_y.getContent() == thiscontent) + + if ((nodedef->get(n_minus_z_minus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_minus_z_minus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_minus_z_minus_y.getContent() == thiscontent) is_rail_z_minus_y[0] = true; - if(n_minus_z_plus_y.getContent() == thiscontent) + + if ((nodedef->get(n_minus_z_plus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_minus_z_plus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_minus_z_plus_y.getContent() == thiscontent) is_rail_z_plus_y[0] = true; - if(n_plus_z.getContent() == thiscontent) + if ((nodedef->get(n_plus_z).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_plus_z).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_plus_z.getContent() == thiscontent) is_rail_z[1] = true; - if (n_plus_z_minus_y.getContent() == thiscontent) + + if ((nodedef->get(n_plus_z_minus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_plus_z_minus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_plus_z_minus_y.getContent() == thiscontent) is_rail_z_minus_y[1] = true; - if(n_plus_z_plus_y.getContent() == thiscontent) + + if ((nodedef->get(n_plus_z_plus_y).drawtype == NDT_RAILLIKE + && ((ItemGroupList) nodedef->get(n_plus_z_plus_y).groups)[groupname] != 0 + && self_connect_to_raillike) + || n_plus_z_plus_y.getContent() == thiscontent) is_rail_z_plus_y[1] = true; bool is_rail_x_all[] = {false, false}; @@ -1255,7 +1302,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(is_rail_x_all[0] && is_rail_x_all[1]) angle = 90; if(is_rail_z_all[0] && is_rail_z_all[1]){ - if (n_minus_z_plus_y.getContent() == thiscontent) angle = 180; + if (is_rail_z_plus_y[0]) + angle = 180; } else if(is_rail_x_all[0] && is_rail_z_all[0]) angle = 270; From 865f380c91ced850d1a499c91aa2ab5489624802 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Fri, 24 May 2013 01:38:57 +0200 Subject: [PATCH 31/41] Predict param2 of facedir nodes and attachment of attached_node nodes --- src/game.cpp | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 185f42ea..921cce32 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -847,7 +847,7 @@ void nodePlacementPrediction(Client &client, <<") - Name not known"<get(id).param_type_2 == CPT2_WALLMOUNTED){ v3s16 dir = nodepos - neighbourpos; @@ -859,8 +859,33 @@ void nodePlacementPrediction(Client &client, param2 = dir.Z < 0 ? 5 : 4; } } - // TODO: Facedir prediction - // TODO: If predicted node is in attached_node group, check attachment + if(nodedef->get(id).param_type_2 == CPT2_FACEDIR){ + v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS); + if(abs(dir.X) > abs(dir.Z)){ + param2 = dir.X < 0 ? 3 : 1; + } else { + param2 = dir.Z < 0 ? 2 : 0; + } + } + assert(param2 >= 0 && param2 <= 5); + //Check attachment if node is in group attached_node + if(((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0){ + static v3s16 wallmounted_dirs[8] = { + v3s16(0,1,0), + v3s16(0,-1,0), + v3s16(1,0,0), + v3s16(-1,0,0), + v3s16(0,0,1), + v3s16(0,0,-1), + }; + v3s16 pp; + if(nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) + pp = p + wallmounted_dirs[param2]; + else + pp = p + v3s16(0,-1,0); + if(!nodedef->get(map.getNode(pp)).walkable) + return; + } // Add node to client map MapNode n(id, 0, param2); try{ From ab433775777c4f5055bcf4d2a1cffc506c4f9961 Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 25 May 2013 00:51:02 +0200 Subject: [PATCH 32/41] Move scriptapi to separate folder (by sapier) On the lua side, notably minetest.env:() should now be replaced by minetest.(). The old way is and will stay supported for a long time. Also: Update and clean up lua_api.txt (by celeron55) Move EnvRef to lua and remove add_rat and add_firefly (by kahrl) Add separate src/util/CMakeLists.txt, other minor fixes (by kahrl) --- .gitignore | 5 + builtin/chatcommands.lua | 32 +- builtin/deprecated.lua | 22 + builtin/falling.lua | 28 +- builtin/item.lua | 22 +- builtin/item_entity.lua | 4 +- builtin/misc.lua | 2 +- doc/lua_api.txt | 268 ++-- games/minimal/mods/bucket/init.lua | 10 +- games/minimal/mods/default/init.lua | 40 +- games/minimal/mods/default/mapgen.lua | 16 +- games/minimal/mods/experimental/init.lua | 52 +- games/minimal/mods/legacy/init.lua | 4 - src/CMakeLists.txt | 29 +- src/content_sao.cpp | 24 +- src/emerge.cpp | 5 +- src/environment.cpp | 30 +- src/environment.h | 10 +- src/hud.h | 2 +- src/inventorymanager.cpp | 84 +- src/jthread/jmutex.h | 9 + src/script.cpp | 126 -- src/script/CMakeLists.txt | 9 + src/script/common/CMakeLists.txt | 6 + src/script/common/c_content.cpp | 923 +++++++++++++ src/script/common/c_content.h | 146 ++ .../common/c_converter.cpp} | 53 +- .../common/c_converter.h} | 58 +- src/script/common/c_internal.cpp | 65 + src/script/common/c_internal.h | 60 + .../common/c_types.cpp} | 35 +- src/script/common/c_types.h | 90 ++ src/script/cpp_api/CMakeLists.txt | 11 + src/script/cpp_api/s_base.cpp | 264 ++++ src/script/cpp_api/s_base.h | 167 +++ .../cpp_api/s_entity.cpp} | 114 +- src/script/cpp_api/s_entity.h | 50 + src/script/cpp_api/s_env.cpp | 132 ++ src/{script.h => script/cpp_api/s_env.h} | 35 +- src/script/cpp_api/s_inventory.cpp | 260 ++++ src/script/cpp_api/s_inventory.h | 71 + src/script/cpp_api/s_item.cpp | 164 +++ src/script/cpp_api/s_item.h | 55 + src/script/cpp_api/s_node.cpp | 233 ++++ src/script/cpp_api/s_node.h | 62 + src/script/cpp_api/s_nodemeta.cpp | 261 ++++ src/script/cpp_api/s_nodemeta.h | 67 + src/script/cpp_api/s_player.cpp | 113 ++ src/script/cpp_api/s_player.h | 44 + src/script/cpp_api/scriptapi.cpp | 291 ++++ src/script/cpp_api/scriptapi.h | 82 ++ src/script/lua_api/CMakeLists.txt | 13 + src/script/lua_api/l_base.cpp | 63 + src/script/lua_api/l_base.h | 63 + .../lua_api/l_craft.cpp} | 80 +- src/script/lua_api/l_craft.h | 52 + src/script/lua_api/l_env.cpp | 687 ++++++++++ .../lua_api/l_env.h} | 148 ++- .../lua_api/l_inventory.cpp} | 417 ++---- .../lua_api/l_inventory.h} | 68 +- .../lua_api/l_item.cpp} | 488 ++----- .../lua_api/l_item.h} | 47 +- .../lua_api/l_nodemeta.cpp} | 257 +--- .../lua_api/l_nodemeta.h} | 38 +- .../lua_api/l_nodetimer.cpp} | 7 +- .../lua_api/l_nodetimer.h} | 8 +- .../lua_api/l_noise.cpp} | 45 +- .../lua_api/l_noise.h} | 9 +- .../lua_api/l_object.cpp} | 307 ++--- .../lua_api/l_object.h} | 32 +- .../lua_api/l_particles.cpp} | 46 +- .../lua_api/l_particles.h} | 21 +- src/script/lua_api/luaapi.cpp | 653 +++++++++ src/script/lua_api/luaapi.h | 140 ++ src/scriptapi.cpp | 1181 ----------------- src/scriptapi.h | 75 -- src/scriptapi_common.cpp | 311 ----- src/scriptapi_common.h | 112 -- src/scriptapi_content.cpp | 327 ----- src/scriptapi_craft.h | 51 - src/scriptapi_entity.h | 54 - src/scriptapi_env.cpp | 975 -------------- src/scriptapi_node.cpp | 244 ---- src/scriptapi_node.h | 55 - src/server.cpp | 144 +- src/server.h | 14 +- src/util/CMakeLists.txt | 8 + 87 files changed, 6401 insertions(+), 5584 deletions(-) delete mode 100644 src/script.cpp create mode 100644 src/script/CMakeLists.txt create mode 100644 src/script/common/CMakeLists.txt create mode 100644 src/script/common/c_content.cpp create mode 100644 src/script/common/c_content.h rename src/{scriptapi_types.cpp => script/common/c_converter.cpp} (86%) rename src/{scriptapi_types.h => script/common/c_converter.h} (59%) create mode 100644 src/script/common/c_internal.cpp create mode 100644 src/script/common/c_internal.h rename src/{scriptapi_content.h => script/common/c_types.cpp} (60%) create mode 100644 src/script/common/c_types.h create mode 100644 src/script/cpp_api/CMakeLists.txt create mode 100644 src/script/cpp_api/s_base.cpp create mode 100644 src/script/cpp_api/s_base.h rename src/{scriptapi_entity.cpp => script/cpp_api/s_entity.cpp} (72%) create mode 100644 src/script/cpp_api/s_entity.h create mode 100644 src/script/cpp_api/s_env.cpp rename src/{script.h => script/cpp_api/s_env.h} (59%) create mode 100644 src/script/cpp_api/s_inventory.cpp create mode 100644 src/script/cpp_api/s_inventory.h create mode 100644 src/script/cpp_api/s_item.cpp create mode 100644 src/script/cpp_api/s_item.h create mode 100644 src/script/cpp_api/s_node.cpp create mode 100644 src/script/cpp_api/s_node.h create mode 100644 src/script/cpp_api/s_nodemeta.cpp create mode 100644 src/script/cpp_api/s_nodemeta.h create mode 100644 src/script/cpp_api/s_player.cpp create mode 100644 src/script/cpp_api/s_player.h create mode 100644 src/script/cpp_api/scriptapi.cpp create mode 100644 src/script/cpp_api/scriptapi.h create mode 100644 src/script/lua_api/CMakeLists.txt create mode 100644 src/script/lua_api/l_base.cpp create mode 100644 src/script/lua_api/l_base.h rename src/{scriptapi_craft.cpp => script/lua_api/l_craft.cpp} (86%) create mode 100644 src/script/lua_api/l_craft.h create mode 100644 src/script/lua_api/l_env.cpp rename src/{scriptapi_env.h => script/lua_api/l_env.h} (51%) rename src/{scriptapi_inventory.cpp => script/lua_api/l_inventory.cpp} (52%) rename src/{scriptapi_inventory.h => script/lua_api/l_inventory.h} (60%) rename src/{scriptapi_item.cpp => script/lua_api/l_item.cpp} (55%) rename src/{scriptapi_item.h => script/lua_api/l_item.h} (69%) rename src/{scriptapi_nodemeta.cpp => script/lua_api/l_nodemeta.cpp} (54%) rename src/{scriptapi_nodemeta.h => script/lua_api/l_nodemeta.h} (58%) rename src/{scriptapi_nodetimer.cpp => script/lua_api/l_nodetimer.cpp} (97%) rename src/{scriptapi_nodetimer.h => script/lua_api/l_nodetimer.h} (94%) rename src/{scriptapi_noise.cpp => script/lua_api/l_noise.cpp} (92%) rename src/{scriptapi_noise.h => script/lua_api/l_noise.h} (96%) rename src/{scriptapi_object.cpp => script/lua_api/l_object.cpp} (82%) rename src/{scriptapi_object.h => script/lua_api/l_object.h} (84%) rename src/{scriptapi_particles.cpp => script/lua_api/l_particles.cpp} (79%) rename src/{scriptapi_particles.h => script/lua_api/l_particles.h} (71%) create mode 100644 src/script/lua_api/luaapi.cpp create mode 100644 src/script/lua_api/luaapi.h delete mode 100644 src/scriptapi.cpp delete mode 100644 src/scriptapi.h delete mode 100644 src/scriptapi_common.cpp delete mode 100644 src/scriptapi_common.h delete mode 100644 src/scriptapi_content.cpp delete mode 100644 src/scriptapi_craft.h delete mode 100644 src/scriptapi_entity.h delete mode 100644 src/scriptapi_env.cpp delete mode 100644 src/scriptapi_node.cpp delete mode 100644 src/scriptapi_node.h create mode 100644 src/util/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 50d7c43e..c8f21e50 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,11 @@ src/CMakeFiles/* src/Makefile src/cmake_config.h src/cmake_install.cmake +src/script/CMakeFiles/* +src/script/common/CMakeFiles/* +src/script/cpp_api/CMakeFiles/* +src/script/lua_api/CMakeFiles/* +src/util/CMakeFiles/* src/jthread/CMakeFiles/* src/jthread/Makefile src/jthread/cmake_config.h diff --git a/builtin/chatcommands.lua b/builtin/chatcommands.lua index f548fb01..6a3b29e7 100644 --- a/builtin/chatcommands.lua +++ b/builtin/chatcommands.lua @@ -261,7 +261,7 @@ minetest.register_chatcommand("teleport", { } 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.env:get_node(p) + local n = minetest.get_node(p) if not minetest.registered_nodes[n.name].walkable then return p, true end @@ -272,7 +272,7 @@ minetest.register_chatcommand("teleport", { local teleportee = nil local p = {} p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - teleportee = minetest.env:get_player_by_name(name) + 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) @@ -283,9 +283,9 @@ minetest.register_chatcommand("teleport", { local p = nil local target_name = nil target_name = string.match(param, "^([^ ]+)$") - teleportee = minetest.env:get_player_by_name(name) + teleportee = minetest.get_player_by_name(name) if target_name then - local target = minetest.env:get_player_by_name(target_name) + local target = minetest.get_player_by_name(target_name) if target then p = target:getpos() end @@ -303,7 +303,7 @@ minetest.register_chatcommand("teleport", { local teleportee_name = nil teleportee_name, p.x, p.y, p.z = string.match(param, "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") if teleportee_name then - teleportee = minetest.env:get_player_by_name(teleportee_name) + 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..")") @@ -317,10 +317,10 @@ minetest.register_chatcommand("teleport", { local target_name = nil teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$") if teleportee_name then - teleportee = minetest.env:get_player_by_name(teleportee_name) + teleportee = minetest.get_player_by_name(teleportee_name) end if target_name then - local target = minetest.env:get_player_by_name(target_name) + local target = minetest.get_player_by_name(target_name) if target then p = target:getpos() end @@ -402,7 +402,7 @@ local function handle_give_command(cmd, giver, receiver, stackstring) minetest.chat_send_player(giver, 'error: cannot give an unknown item') return end - local receiverref = minetest.env:get_player_by_name(receiver) + local receiverref = minetest.get_player_by_name(receiver) if receiverref == nil then minetest.chat_send_player(giver, receiver..' is not a known player') return @@ -466,14 +466,14 @@ minetest.register_chatcommand("spawnentity", { return end print('/spawnentity invoked, entityname="'..entityname..'"') - local player = minetest.env:get_player_by_name(name) + local player = minetest.get_player_by_name(name) if player == nil then print("Unable to spawn entity, player is nil") return true -- Handled chat message end local p = player:getpos() p.y = p.y + 1 - minetest.env:add_entity(p, entityname) + minetest.add_entity(p, entityname) minetest.chat_send_player(name, '"'..entityname ..'" spawned.'); end, @@ -483,7 +483,7 @@ minetest.register_chatcommand("pulverize", { description = "delete item in hand", privs = {}, func = function(name, param) - local player = minetest.env:get_player_by_name(name) + local player = minetest.get_player_by_name(name) if player == nil then print("Unable to pulverize, player is nil") return true -- Handled chat message @@ -533,7 +533,7 @@ minetest.register_chatcommand("rollback_check", { if act_p.x ~= pos.x or act_p.y ~= pos.y or act_p.z ~= pos.z then nodedesc = minetest.pos_to_string(act_p) end - local nodename = minetest.env:get_node(act_p).name + local nodename = minetest.get_node(act_p).name minetest.chat_send_player(name, "Last actor on "..nodedesc.. " was "..actor..", "..dump(act_seconds).. "s ago (node is now "..nodename..")") @@ -598,7 +598,7 @@ minetest.register_chatcommand("time", { if newtime == nil then minetest.chat_send_player(name, "Invalid time") else - minetest.env:set_timeofday((newtime % 24000) / 24000) + minetest.set_timeofday((newtime % 24000) / 24000) minetest.chat_send_player(name, "Time of day changed.") minetest.log("action", name .. " sets time " .. newtime) end @@ -625,7 +625,7 @@ minetest.register_chatcommand("ban", { minetest.chat_send_player(name, "Ban list: " .. minetest.get_ban_list()) return end - if not minetest.env:get_player_by_name(param) then + if not minetest.get_player_by_name(param) then minetest.chat_send_player(name, "No such player") return end @@ -660,7 +660,7 @@ minetest.register_chatcommand("clearobjects", { 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.env:clear_objects() + minetest.clear_objects() minetest.log("action", "object clearing done") minetest.chat_send_all("*** Cleared all objects.") end, @@ -673,7 +673,7 @@ minetest.register_chatcommand("msg", { func = function(name, param) local found, _, sendto, message = param:find("^([^%s]+)%s(.+)$") if found then - if minetest.env:get_player_by_name(sendto) 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") diff --git a/builtin/deprecated.lua b/builtin/deprecated.lua index 405ce3b0..23ab3867 100644 --- a/builtin/deprecated.lua +++ b/builtin/deprecated.lua @@ -24,3 +24,25 @@ 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(unpack({...})) + end) + else + rawset(table, key, nil) + end + return rawget(table, key) + end +}) diff --git a/builtin/falling.lua b/builtin/falling.lua index f6491991..5ee69329 100644 --- a/builtin/falling.lua +++ b/builtin/falling.lua @@ -53,27 +53,27 @@ minetest.register_entity("__builtin:falling_node", { -- 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.env:get_node(bcp) + local bcn = minetest.get_node(bcp) -- Note: walkable is in the node definition, not in item groups if minetest.registered_nodes[bcn.name] and minetest.registered_nodes[bcn.name].walkable then if minetest.registered_nodes[bcn.name].buildable_to then - minetest.env:remove_node(bcp) + 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.env:get_node(np) + 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.env:remove_node(np) + minetest.remove_node(np) -- Add dropped items local _, dropped_item for _, dropped_item in ipairs(drops) do - minetest.env:add_item(np, dropped_item) + minetest.add_item(np, dropped_item) end -- Run script hook local _, callback @@ -82,7 +82,7 @@ minetest.register_entity("__builtin:falling_node", { end end -- Create node and remove entity - minetest.env:add_node(np, {name=self.nodename}) + minetest.add_node(np, {name=self.nodename}) self.object:remove() nodeupdate(np) else @@ -92,20 +92,20 @@ minetest.register_entity("__builtin:falling_node", { }) function spawn_falling_node(p, nodename) - obj = minetest.env:add_entity(p, "__builtin:falling_node") + obj = minetest.add_entity(p, "__builtin:falling_node") obj:get_luaentity():set_node(nodename) end function drop_attached_node(p) - local nn = minetest.env:get_node(p).name - minetest.env:remove_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.env:add_item(pos, item) + minetest.add_item(pos, item) end end @@ -130,7 +130,7 @@ function check_attached_node(p, n) d.y = -1 end local p2 = {x=p.x+d.x, y=p.y+d.y, z=p.z+d.z} - local nn = minetest.env:get_node(p2).name + local nn = minetest.get_node(p2).name local def2 = minetest.registered_nodes[nn] if def2 and not def2.walkable then return false @@ -143,10 +143,10 @@ end -- function nodeupdate_single(p, delay) - n = minetest.env:get_node(p) + 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.env:get_node(p_bottom) + 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 (not minetest.registered_nodes[n_bottom.name].walkable or @@ -154,7 +154,7 @@ function nodeupdate_single(p, delay) if delay then minetest.after(0.1, nodeupdate_single, {x=p.x, y=p.y, z=p.z}, false) else - minetest.env:remove_node(p) + minetest.remove_node(p) spawn_falling_node(p, n.name) nodeupdate(p) end diff --git a/builtin/item.lua b/builtin/item.lua index 51a17106..85b4cc0e 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -129,9 +129,9 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) end local under = pointed_thing.under - local oldnode_under = minetest.env:get_node_or_nil(under) + local oldnode_under = minetest.get_node_or_nil(under) local above = pointed_thing.above - local oldnode_above = minetest.env:get_node_or_nil(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" @@ -163,7 +163,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) minetest.log("action", placer:get_player_name() .. " places node " .. def.name .. " at " .. minetest.pos_to_string(place_to)) - local oldnode = minetest.env:get_node(place_to) + local oldnode = minetest.get_node(place_to) local newnode = {name = def.name, param1 = 0, param2 = 0} -- Calculate direction for wall mounted stuff like torches and signs @@ -197,7 +197,7 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) end -- Add node and update - minetest.env:add_node(place_to, newnode) + minetest.add_node(place_to, newnode) local take_item = true @@ -232,7 +232,7 @@ 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.env:add_item(pos, item) + minetest.add_item(pos, item) end return itemstack end @@ -241,7 +241,7 @@ function minetest.item_place(itemstack, placer, pointed_thing) -- 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.env:get_node(pointed_thing.under) + 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 @@ -258,7 +258,7 @@ 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.env:add_item(p, itemstack) + local obj = minetest.add_item(p, itemstack) if obj then v.x = v.x*2 v.y = v.y*2 + 1 @@ -266,7 +266,7 @@ function minetest.item_drop(itemstack, dropper, pos) obj:setvelocity(v) end else - minetest.env:add_item(pos, itemstack) + minetest.add_item(pos, itemstack) end return ItemStack("") end @@ -304,7 +304,7 @@ function minetest.handle_node_drops(pos, drops, digger) y = pos.y + math.random()/2-0.25, z = pos.z + math.random()/2-0.25, } - minetest.env:add_item(p, left) + minetest.add_item(p, left) end end end @@ -340,11 +340,11 @@ function minetest.node_dig(pos, node, digger) local oldmetadata = nil if def.after_dig_node then - oldmetadata = minetest.env:get_meta(pos):to_table() + oldmetadata = minetest.get_meta(pos):to_table() end -- Remove node and update - minetest.env:remove_node(pos) + minetest.remove_node(pos) -- Run callback if def.after_dig_node then diff --git a/builtin/item_entity.lua b/builtin/item_entity.lua index 50ce7eaf..c682db2f 100644 --- a/builtin/item_entity.lua +++ b/builtin/item_entity.lua @@ -3,7 +3,7 @@ function minetest.spawn_item(pos, item) -- Take item in any format local stack = ItemStack(item) - local obj = minetest.env:add_entity(pos, "__builtin:item") + local obj = minetest.add_entity(pos, "__builtin:item") obj:get_luaentity():set_item(stack:to_string()) return obj end @@ -83,7 +83,7 @@ minetest.register_entity("__builtin:item", { on_step = function(self, dtime) local p = self.object:getpos() p.y = p.y - 0.3 - local nn = minetest.env:get_node(p).name + 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 diff --git a/builtin/misc.lua b/builtin/misc.lua index 8308b3d6..f4e7dbca 100644 --- a/builtin/misc.lua +++ b/builtin/misc.lua @@ -43,7 +43,7 @@ end function minetest.get_connected_players() -- This could be optimized a bit, but leave that for later local list = {} - for _, obj in pairs(minetest.env:get_objects_inside_radius({x=0,y=0,z=0}, 1000000)) do + for _, obj in pairs(minetest.get_objects_inside_radius({x=0,y=0,z=0}, 1000000)) do if obj:is_player() then table.insert(list, obj) end diff --git a/doc/lua_api.txt b/doc/lua_api.txt index bee247ca..260a6276 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -768,7 +768,7 @@ Some of the values in the key-value store are handled specially: Example stuff: -local meta = minetest.env:get_meta(pos) +local meta = minetest.get_meta(pos) meta:set_string("formspec", "invsize[8,9;]".. "list[context;main;0,0;8,4;]".. @@ -914,6 +914,7 @@ minetest.formspec_escape(string) -> string minetest namespace reference ----------------------------- +Utilities: minetest.get_current_modname() -> string minetest.get_modpath(modname) -> eg. "/home/user/.minetest/usermods/modname" ^ Useful for loading additional .lua modules or static data from mod @@ -928,6 +929,7 @@ minetest.has_feature(arg) -> bool, missing_features ^ arg: string or table in format {foo=true, bar=true} ^ missing_features: {foo=true, bar=true} +Logging: minetest.debug(line) ^ Always printed to stderr and logfile (print() is redirected here) minetest.log(line) @@ -955,10 +957,12 @@ minetest.register_on_shutdown(func()) minetest.register_on_placenode(func(pos, newnode, placer, oldnode, itemstack)) ^ Called when a node has been placed ^ If return true no item is taken from itemstack -^ Deprecated: Use on_construct or after_place_node in node definition instead +^ Not recommended; use on_construct or after_place_node in node definition +^ whenever possible minetest.register_on_dignode(func(pos, oldnode, digger)) ^ Called when a node has been dug. -^ Deprecated: Use on_destruct or after_dig_node in node definition instead +^ Not recommended: Use on_destruct or after_dig_node in node definition +^ whenever possible minetest.register_on_punchnode(func(pos, node, puncher)) ^ Called when a node is punched minetest.register_on_generated(func(minp, maxp, blockseed)) @@ -1025,6 +1029,64 @@ minetest.chat_send_all(text) minetest.chat_send_player(name, text, prepend) ^ prepend: optional, if it is set to false "Server -!- " will not be prepended to the message +Environment access: + +minetest.set_node(pos, node) +minetest.add_node(pos, node): alias set_node(pos, node) +^ Set node at position (node = {name="foo", param1=0, param2=0}) +minetest.remove_node(pos) +^ Equivalent to set_node(pos, "air") +minetest.get_node(pos) +^ Returns {name="ignore", ...} for unloaded area +minetest.get_node_or_nil(pos) +^ Returns nil for unloaded area +minetest.get_node_light(pos, timeofday) -> 0...15 or nil +^ timeofday: nil = current time, 0 = night, 0.5 = day + +minetest.place_node(pos, node) +^ Place node with the same effects that a player would cause +minetest.dig_node(pos) +^ Dig node with the same effects that a player would cause +minetest.punch_node(pos) +^ Punch node with the same effects that a player would cause + +minetest.get_meta(pos) -- Get a NodeMetaRef at that position +minetest.get_node_timer(pos) -- Get NodeTimerRef + +minetest.add_entity(pos, name): Spawn Lua-defined entity at position +^ Returns ObjectRef, or nil if failed +minetest.add_item(pos, item): Spawn item +^ Returns ObjectRef, or nil if failed +minetest.get_player_by_name(name) -- Get an ObjectRef to a player +minetest.get_objects_inside_radius(pos, radius) +minetest.set_timeofday(val): val: 0...1; 0 = midnight, 0.5 = midday +minetest.get_timeofday() +minetest.find_node_near(pos, radius, nodenames) -> pos or nil +^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +minetest.find_nodes_in_area(minp, maxp, nodenames) -> list of positions +^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +minetest.get_perlin(seeddiff, octaves, persistence, scale) +^ Return world-specific perlin noise (int(worldseed)+seeddiff) +minetest.clear_objects() +^ clear all objects in the environments +minetest.line_of_sight(pos1,pos2,stepsize) ->true/false +^ checkif there is a direct line of sight between pos1 and pos2 +^ pos1 First position +^ pos2 Second position +^ stepsize smaller gives more accurate results but requires more computing + time. Default is 1. +minetest.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm) +^ -> table containing path +^ returns a table of 3d points representing a path from pos1 to pos2 or nil +^ pos1: start position +^ pos2: end position +^ searchdistance: number of blocks to search in each direction +^ max_jump: maximum height difference to consider walkable +^ max_drop: maximum height difference to consider droppable +^ algorithm: A*_noprefetch(default), A*, Dijkstra +minetest.spawn_tree (pos, {treedef}) +^ spawns L-System tree at given pos with definition in treedef table + Inventory: minetest.get_inventory(location) -> InvRef ^ location = eg. {type="player", name="celeron55"} @@ -1195,7 +1257,11 @@ minetest.deserialize(string) -> table Global objects: minetest.env - EnvRef of the server environment and world. -^ Using this you can access nodes and entities +^ Any function in the minetest namespace can be called using the syntax + minetest.env:somefunction(somearguments) + instead of + minetest.somefunction(somearguments) +^ Deprecated, but support is not to be dropped soon Global tables: minetest.registered_items @@ -1224,129 +1290,9 @@ minetest.digprop_glasslike(toughness) Class reference ---------------- -EnvRef: basically ServerEnvironment and ServerMap combined. -methods: -- set_node(pos, node) -- add_node(pos, node): alias set_node(pos, node) - ^ Set node at position (node = {name="foo", param1=0, param2=0}) -- remove_node(pos) - ^ Equivalent to set_node(pos, "air") -- get_node(pos) - ^ Returns {name="ignore", ...} for unloaded area -- get_node_or_nil(pos) - ^ Returns nil for unloaded area -- get_node_light(pos, timeofday) -> 0...15 or nil - ^ timeofday: nil = current time, 0 = night, 0.5 = day - -- place_node(pos, node) - ^ Place node with the same effects that a player would cause -- dig_node(pos) - ^ Dig node with the same effects that a player would cause -- punch_node(pos) - ^ Punch node with the same effects that a player would cause - -- get_meta(pos) -- Get a NodeMetaRef at that position -- get_node_timer(pos) -- Get NodeTimerRef - -- add_entity(pos, name): Spawn Lua-defined entity at position - ^ Returns ObjectRef, or nil if failed -- add_item(pos, item): Spawn item - ^ Returns ObjectRef, or nil if failed -- get_player_by_name(name) -- Get an ObjectRef to a player -- get_objects_inside_radius(pos, radius) -- set_timeofday(val): val: 0...1; 0 = midnight, 0.5 = midday -- get_timeofday() -- find_node_near(pos, radius, nodenames) -> pos or nil - ^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt" -- find_nodes_in_area(minp, maxp, nodenames) -> list of positions - ^ nodenames: eg. {"ignore", "group:tree"} or "default:dirt" -- get_perlin(seeddiff, octaves, persistence, scale) - ^ Return world-specific perlin noise (int(worldseed)+seeddiff) -- clear_objects() - ^ clear all objects in the environments -- line_of_sight(pos1,pos2,stepsize) ->true/false - ^ checkif there is a direct line of sight between pos1 and pos2 - ^ pos1 First position - ^ pos2 Second position - ^ stepsize smaller gives more accurate results but requires more computing - time. Default is 1. --find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm) -> table containing path - ^ returns a table of 3d points representing a path from pos1 to pos2 or nil - ^ pos1: start position - ^ pos2: end position - ^ searchdistance: number of blocks to search in each direction - ^ max_jump: maximum height difference to consider walkable - ^ max_drop: maximum height difference to consider droppable - ^ algorithm: A*_noprefetch(default), A*, Dijkstra -- spawn_tree (pos, {treedef}) - ^ spawns L-System tree at given pos with definition in treedef table -treedef={ - axiom, - string initial tree axiom - rules_a, - string rules set A - rules_b, - string rules set B - rules_c, - string rules set C - rules_d, - string rules set D - trunk, - string trunk node name - leaves, - string leaves node name - leaves2, - string secondary leaves node name - leaves2_chance,- num chance (0-100) to replace leaves with leaves2 - angle, - num angle in deg - iterations, - num max # of iterations, usually 2 -5 - random_level, - num factor to lower nr of iterations, usually 0 - 3 - trunk_type, - string single/double/crossed) type of trunk: 1 node, 2x2 nodes or 3x3 in cross shape - thin_branches, - boolean true -> use thin (1 node) branches - fruit, - string fruit node name - fruit_chance, - num chance (0-100) to replace leaves with fruit node - seed, - num random seed - } - -Key for Special L-System Symbols used in Axioms - G - move forward one unit with the pen up - F - move forward one unit with the pen down drawing trunks and branches - f - move forward one unit with the pen down drawing leaves (100% chance) - T - move forward one unit with the pen down drawing trunks only - R - move forward one unit with the pen down placing fruit - A - replace with rules set A - B - replace with rules set B - C - replace with rules set C - D - replace with rules set D - a - replace with rules set A, chance 90% - b - replace with rules set B, chance 80% - c - replace with rules set C, chance 70% - d - replace with rules set D, chance 60% - + - yaw the turtle right by angle parameter - - - yaw the turtle left by angle parameter - & - pitch the turtle down by angle parameter - ^ - pitch the turtle up by angle parameter - / - roll the turtle to the right by angle parameter - * - roll the turtle to the left by angle parameter - [ - save in stack current state info - ] - recover from stack state info - -Example usage: spawn small apple tree -apple_tree={ - axiom="FFFFFAFFBF", - rules_a="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]", - rules_b="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]", - trunk="default:tree", - leaves="default:leaves", - angle=30, - iterations=2, - random_level=0, - trunk_type="single", - thin_branches=true, - fruit_chance=10, - fruit="default:apple" - } -minetest.env:spawn_tree(pos,apple_tree) - -Deprecated: -- add_rat(pos): Add C++ rat object (no-op) -- add_firefly(pos): Add C++ firefly object (no-op) - NodeMetaRef: Node metadata - reference extra data and functionality stored in a node -- Can be gotten via minetest.env:get_nodemeta(pos) +- Can be gotten via minetest.get_nodemeta(pos) methods: - set_string(name, value) - get_string(name) @@ -1360,7 +1306,7 @@ methods: ^ See "Node Metadata" NodeTimerRef: Node Timers - a high resolution persistent per-node timer -- Can be gotten via minetest.env:get_node_timer(pos) +- Can be gotten via minetest.get_node_timer(pos) methods: - set(timeout,elapsed) ^ set a timer's state @@ -1515,7 +1461,7 @@ methods: PerlinNoise: A perlin noise generator - Can be created via PerlinNoise(seed, octaves, persistence, scale) -- Also minetest.env:get_perlin(seeddiff, octaves, persistence, scale) +- Also minetest.get_perlin(seeddiff, octaves, persistence, scale) methods: - get2d(pos) -> 2d noise value at pos={x=,y=} - get3d(pos) -> 3d noise value at pos={x=,y=,z=} @@ -1545,6 +1491,68 @@ Registered entities ^ Should return a string that will be passed to on_activate when the object is instantiated the next time. +L-system trees +--------------- +treedef={ + axiom, - string initial tree axiom + rules_a, - string rules set A + rules_b, - string rules set B + rules_c, - string rules set C + rules_d, - string rules set D + trunk, - string trunk node name + leaves, - string leaves node name + leaves2, - string secondary leaves node name + leaves2_chance,- num chance (0-100) to replace leaves with leaves2 + angle, - num angle in deg + iterations, - num max # of iterations, usually 2 -5 + random_level, - num factor to lower nr of iterations, usually 0 - 3 + trunk_type, - string single/double/crossed) type of trunk: 1 node, 2x2 nodes or 3x3 in cross shape + thin_branches, - boolean true -> use thin (1 node) branches + fruit, - string fruit node name + fruit_chance, - num chance (0-100) to replace leaves with fruit node + seed, - num random seed + } + +Key for Special L-System Symbols used in Axioms + G - move forward one unit with the pen up + F - move forward one unit with the pen down drawing trunks and branches + f - move forward one unit with the pen down drawing leaves (100% chance) + T - move forward one unit with the pen down drawing trunks only + R - move forward one unit with the pen down placing fruit + A - replace with rules set A + B - replace with rules set B + C - replace with rules set C + D - replace with rules set D + a - replace with rules set A, chance 90% + b - replace with rules set B, chance 80% + c - replace with rules set C, chance 70% + d - replace with rules set D, chance 60% + + - yaw the turtle right by angle parameter + - - yaw the turtle left by angle parameter + & - pitch the turtle down by angle parameter + ^ - pitch the turtle up by angle parameter + / - roll the turtle to the right by angle parameter + * - roll the turtle to the left by angle parameter + [ - save in stack current state info + ] - recover from stack state info + +Example usage: spawn small apple tree +apple_tree={ + axiom="FFFFFAFFBF", + rules_a="[&&&FFFFF&&FFFF][&&&++++FFFFF&&FFFF][&&&----FFFFF&&FFFF]", + rules_b="[&&&++FFFFF&&FFFF][&&&--FFFFF&&FFFF][&&&------FFFFF&&FFFF]", + trunk="default:tree", + leaves="default:leaves", + angle=30, + iterations=2, + random_level=0, + trunk_type="single", + thin_branches=true, + fruit_chance=10, + fruit="default:apple" + } +minetest.spawn_tree(pos,apple_tree) + Definition tables ------------------ @@ -1714,13 +1722,13 @@ Node definition (register_node) after_place_node = func(pos, placer, itemstack), ^ Called after constructing node when node was placed using - minetest.item_place_node / minetest.env:place_node + minetest.item_place_node / minetest.place_node ^ If return true no item is taken from itemstack ^ default: nil after_dig_node = func(pos, oldnode, oldmetadata, digger), ^ oldmetadata is in table format ^ Called after destructing node when node was dug using - minetest.node_dig / minetest.env:dig_node + minetest.node_dig / minetest.dig_node ^ default: nil can_dig = function(pos,player) ^ returns true if node can be dug, or false if not @@ -1739,7 +1747,7 @@ Node definition (register_node) on_timer = function(pos,elapsed), ^ default: nil - ^ called by NodeTimers, see EnvRef and NodeTimerRef + ^ called by NodeTimers, see minetest.get_node_timer and NodeTimerRef ^ elapsed is the total time passed since the timer was started ^ return true to run the timer for another cycle with the same timeout value diff --git a/games/minimal/mods/bucket/init.lua b/games/minimal/mods/bucket/init.lua index 6aa0921d..dcd59ed3 100644 --- a/games/minimal/mods/bucket/init.lua +++ b/games/minimal/mods/bucket/init.lua @@ -41,13 +41,13 @@ function bucket.register_liquid(source, flowing, itemname, inventory_image) return end -- Check if pointing to a liquid - n = minetest.env:get_node(pointed_thing.under) + n = minetest.get_node(pointed_thing.under) if bucket.liquids[n.name] == nil then -- Not a liquid - minetest.env:add_node(pointed_thing.above, {name=source}) + minetest.add_node(pointed_thing.above, {name=source}) elseif n.name ~= source then -- It's a liquid - minetest.env:add_node(pointed_thing.under, {name=source}) + minetest.add_node(pointed_thing.under, {name=source}) end return {name="bucket:bucket_empty"} end @@ -65,10 +65,10 @@ minetest.register_craftitem("bucket:bucket_empty", { return end -- Check if pointing to a liquid source - n = minetest.env:get_node(pointed_thing.under) + n = minetest.get_node(pointed_thing.under) liquiddef = bucket.liquids[n.name] if liquiddef ~= nil and liquiddef.source == n.name and liquiddef.itemname ~= nil then - minetest.env:add_node(pointed_thing.under, {name="air"}) + minetest.add_node(pointed_thing.under, {name="air"}) return {name=liquiddef.itemname} end end, diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index b375cda1..9f9db5ac 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1133,14 +1133,14 @@ minetest.register_node("default:sign_wall", { legacy_wallmounted = true, sounds = default.node_sound_defaults(), on_construct = function(pos) - --local n = minetest.env:get_node(pos) - local meta = minetest.env:get_meta(pos) + --local n = minetest.get_node(pos) + local meta = minetest.get_meta(pos) meta:set_string("formspec", "field[text;;${text}]") meta:set_string("infotext", "\"\"") end, on_receive_fields = function(pos, formname, fields, sender) --print("Sign at "..minetest.pos_to_string(pos).." got "..dump(fields)) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) fields.text = fields.text or "" print((sender:get_player_name() or "").." wrote \""..fields.text.. "\" to sign at "..minetest.pos_to_string(pos)) @@ -1158,7 +1158,7 @@ minetest.register_node("default:chest", { legacy_facedir_simple = true, sounds = default.node_sound_wood_defaults(), on_construct = function(pos) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_string("formspec", "size[8,9]".. "list[current_name;main;0,0;8,4;]".. @@ -1168,7 +1168,7 @@ minetest.register_node("default:chest", { inv:set_size("main", 8*4) end, can_dig = function(pos,player) - local meta = minetest.env:get_meta(pos); + local meta = minetest.get_meta(pos); local inv = meta:get_inventory() return inv:is_empty("main") end, @@ -1190,13 +1190,13 @@ minetest.register_node("default:chest_locked", { legacy_facedir_simple = true, sounds = default.node_sound_wood_defaults(), after_place_node = function(pos, placer) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_string("owner", placer:get_player_name() or "") meta:set_string("infotext", "Locked Chest (owned by ".. meta:get_string("owner")..")") end, on_construct = function(pos) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_string("formspec", "size[8,9]".. "list[current_name;main;0,0;8,4;]".. @@ -1207,12 +1207,12 @@ minetest.register_node("default:chest_locked", { inv:set_size("main", 8*4) end, can_dig = function(pos,player) - local meta = minetest.env:get_meta(pos); + local meta = minetest.get_meta(pos); local inv = meta:get_inventory() return inv:is_empty("main") end, allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) if not has_locked_chest_privilege(meta, player) then minetest.log("action", player:get_player_name().. " tried to access a locked chest belonging to ".. @@ -1223,7 +1223,7 @@ minetest.register_node("default:chest_locked", { return count end, allow_metadata_inventory_put = function(pos, listname, index, stack, player) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) if not has_locked_chest_privilege(meta, player) then minetest.log("action", player:get_player_name().. " tried to access a locked chest belonging to ".. @@ -1234,7 +1234,7 @@ minetest.register_node("default:chest_locked", { return stack:get_count() end, allow_metadata_inventory_take = function(pos, listname, index, stack, player) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) if not has_locked_chest_privilege(meta, player) then minetest.log("action", player:get_player_name().. " tried to access a locked chest belonging to ".. @@ -1275,7 +1275,7 @@ minetest.register_node("default:furnace", { legacy_facedir_simple = true, sounds = default.node_sound_stone_defaults(), on_construct = function(pos) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_string("formspec", default.furnace_inactive_formspec) meta:set_string("infotext", "Furnace") local inv = meta:get_inventory() @@ -1284,7 +1284,7 @@ minetest.register_node("default:furnace", { inv:set_size("dst", 4) end, can_dig = function(pos,player) - local meta = minetest.env:get_meta(pos); + local meta = minetest.get_meta(pos); local inv = meta:get_inventory() if not inv:is_empty("fuel") then return false @@ -1308,7 +1308,7 @@ minetest.register_node("default:furnace_active", { legacy_facedir_simple = true, sounds = default.node_sound_stone_defaults(), on_construct = function(pos) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_string("formspec", default.furnace_inactive_formspec) meta:set_string("infotext", "Furnace"); local inv = meta:get_inventory() @@ -1317,7 +1317,7 @@ minetest.register_node("default:furnace_active", { inv:set_size("dst", 4) end, can_dig = function(pos,player) - local meta = minetest.env:get_meta(pos); + local meta = minetest.get_meta(pos); local inv = meta:get_inventory() if not inv:is_empty("fuel") then return false @@ -1331,16 +1331,16 @@ minetest.register_node("default:furnace_active", { }) function hacky_swap_node(pos,name) - local node = minetest.env:get_node(pos) - local meta = minetest.env:get_meta(pos) + local node = minetest.get_node(pos) + local meta = minetest.get_meta(pos) local meta0 = meta:to_table() if node.name == name then return end node.name = name local meta0 = meta:to_table() - minetest.env:set_node(pos,node) - meta = minetest.env:get_meta(pos) + minetest.set_node(pos,node) + meta = minetest.get_meta(pos) meta:from_table(meta0) end @@ -1349,7 +1349,7 @@ minetest.register_abm({ interval = 1.0, chance = 1, action = function(pos, node, active_object_count, active_object_count_wider) - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) for i, name in ipairs({ "fuel_totaltime", "fuel_time", diff --git a/games/minimal/mods/default/mapgen.lua b/games/minimal/mods/default/mapgen.lua index 478567d0..7209cb6b 100644 --- a/games/minimal/mods/default/mapgen.lua +++ b/games/minimal/mods/default/mapgen.lua @@ -104,17 +104,17 @@ minetest.register_on_generated(function(minp, maxp, seed) for divz=0+1,divs-1-1 do local cx = minp.x + math.floor((divx+0.5)*divlen) local cz = minp.z + math.floor((divz+0.5)*divlen) - if minetest.env:get_node({x=cx,y=1,z=cz}).name == "default:water_source" and - minetest.env:get_node({x=cx,y=0,z=cz}).name == "default:sand" then + if minetest.get_node({x=cx,y=1,z=cz}).name == "default:water_source" and + minetest.get_node({x=cx,y=0,z=cz}).name == "default:sand" then local is_shallow = true local num_water_around = 0 - if minetest.env:get_node({x=cx-divlen*2,y=1,z=cz+0}).name == "default:water_source" then + if minetest.get_node({x=cx-divlen*2,y=1,z=cz+0}).name == "default:water_source" then num_water_around = num_water_around + 1 end - if minetest.env:get_node({x=cx+divlen*2,y=1,z=cz+0}).name == "default:water_source" then + if minetest.get_node({x=cx+divlen*2,y=1,z=cz+0}).name == "default:water_source" then num_water_around = num_water_around + 1 end - if minetest.env:get_node({x=cx+0,y=1,z=cz-divlen*2}).name == "default:water_source" then + if minetest.get_node({x=cx+0,y=1,z=cz-divlen*2}).name == "default:water_source" then num_water_around = num_water_around + 1 end - if minetest.env:get_node({x=cx+0,y=1,z=cz+divlen*2}).name == "default:water_source" then + if minetest.get_node({x=cx+0,y=1,z=cz+divlen*2}).name == "default:water_source" then num_water_around = num_water_around + 1 end if num_water_around >= 2 then is_shallow = false @@ -122,8 +122,8 @@ minetest.register_on_generated(function(minp, maxp, seed) if is_shallow then for x1=-divlen,divlen do for z1=-divlen,divlen do - if minetest.env:get_node({x=cx+x1,y=0,z=cz+z1}).name == "default:sand" then - minetest.env:set_node({x=cx+x1,y=0,z=cz+z1}, {name="default:clay"}) + if minetest.get_node({x=cx+x1,y=0,z=cz+z1}).name == "default:sand" then + minetest.set_node({x=cx+x1,y=0,z=cz+z1}, {name="default:clay"}) end end end diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 6fce9698..3e2e7787 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -66,7 +66,7 @@ test_sound() function on_step(dtime) -- print("experimental on_step") --[[ - objs = minetest.env:get_objects_inside_radius({x=0,y=0,z=0}, 10) + objs = minetest.get_objects_inside_radius({x=0,y=0,z=0}, 10) for k, obj in pairs(objs) do name = obj:get_player_name() if name then @@ -86,17 +86,17 @@ function on_step(dtime) experimental.t1 = experimental.t1 + dtime if experimental.t1 >= 2 then experimental.t1 = experimental.t1 - 2 - minetest.log("time of day is "..minetest.env:get_timeofday()) + minetest.log("time of day is "..minetest.get_timeofday()) if experimental.day then minetest.log("forcing day->night") experimental.day = false - minetest.env:set_timeofday(0.0) + minetest.set_timeofday(0.0) else minetest.log("forcing night->day") experimental.day = true - minetest.env:set_timeofday(0.5) + minetest.set_timeofday(0.5) end - minetest.log("time of day is "..minetest.env:get_timeofday()) + minetest.log("time of day is "..minetest.get_timeofday()) end --]] end @@ -133,8 +133,8 @@ minetest.register_node("experimental:tnt", { minetest.register_on_punchnode(function(p, node) if node.name == "experimental:tnt" then - minetest.env:remove_node(p) - minetest.env:add_entity(p, "experimental:tnt") + minetest.remove_node(p) + minetest.add_entity(p, "experimental:tnt") nodeupdate(p) end end) @@ -262,7 +262,7 @@ minetest.register_on_chat_message(function(name, message) minetest.chat_send_player(name, "you don't have permission to interact") return true -- Handled chat message end - local player = minetest.env:get_player_by_name(name) + local player = minetest.get_player_by_name(name) if player == nil then print("Unable to spawn entity, player is nil") return true -- Handled chat message @@ -270,7 +270,7 @@ minetest.register_on_chat_message(function(name, message) local entityname = "experimental:dummyball" local p = player:getpos() p.y = p.y + 1 - minetest.env:add_entity(p, entityname) + minetest.add_entity(p, entityname) minetest.chat_send_player(name, '"'..entityname ..'" spawned.'); return true -- Handled chat message @@ -323,7 +323,7 @@ end) minetest.register_on_generated(function(minp, maxp) --print("on_generated: minp="..dump(minp).." maxp="..dump(maxp)) --cp = {x=(minp.x+maxp.x)/2, y=(minp.y+maxp.y)/2, z=(minp.z+maxp.z)/2} - --minetest.env:add_node(cp, {name="sand"}) + --minetest.add_node(cp, {name="sand"}) end) -- Example setting get @@ -353,7 +353,7 @@ end) action = function(pos, node, active_object_count, active_object_count_wider) print("TNT ABM action") pos.y = pos.y + 1 - minetest.env:add_node(pos, {name="papyrus"}) + minetest.add_node(pos, {name="papyrus"}) end, })]] @@ -364,7 +364,7 @@ end) chance = 1, action = function(pos, node, active_object_count, active_object_count_wider) print("ABM: Sign text changed") - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_text("foo") end, })]] @@ -395,7 +395,7 @@ minetest.register_abm({ end pos.y = pos.y + 1 - n = minetest.env:get_node(pos) + n = minetest.get_node(pos) print(dump(n)) if n.name ~= "air" then return @@ -404,7 +404,7 @@ minetest.register_abm({ pos.y = pos.y + 2 ncpos = pos nctime = os.clock() - minetest.env:add_node(ncpos, {name="nyancat"}) + minetest.add_node(ncpos, {name="nyancat"}) end }) @@ -431,12 +431,12 @@ minetest.register_abm({ p2 = {x = p1.x + s1[2], y = p1.y, z = p1.z + s1[3]} table.insert(ncold, 1, p0) while #ncold >= 10 do - minetest.env:add_node(ncold[#ncold], {name="air"}) + minetest.add_node(ncold[#ncold], {name="air"}) table.remove(ncold, #ncold) end - minetest.env:add_node(p0, {name="nyancat_rainbow"}) - minetest.env:add_node(p1, {name="nyancat", param1=s0[4]}) - minetest.env:add_node(p2, {name="air"}) + minetest.add_node(p0, {name="nyancat_rainbow"}) + minetest.add_node(p1, {name="nyancat", param1=s0[4]}) + minetest.add_node(p2, {name="air"}) ncpos = p1 end end, @@ -448,20 +448,20 @@ minetest.register_node("experimental:tester_node_1", { groups = {oddly_breakable_by_hand=2}, sounds = default.node_sound_wood_defaults(), -- This was known to cause a bug in minetest.item_place_node() when used - -- via minetest.env:place_node(), causing a placer with no position + -- via minetest.place_node(), causing a placer with no position paramtype2 = "facedir", on_construct = function(pos) experimental.print_to_everything("experimental:tester_node_1:on_construct("..minetest.pos_to_string(pos)..")") - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) meta:set_string("mine", "test") - local timer = minetest.env:get_node_timer(pos) + local timer = minetest.get_node_timer(pos) timer:start(4, 3) end, after_place_node = function(pos, placer) experimental.print_to_everything("experimental:tester_node_1:after_place_node("..minetest.pos_to_string(pos)..")") - local meta = minetest.env:get_meta(pos) + local meta = minetest.get_meta(pos) if meta:get_string("mine") == "test" then experimental.print_to_everything("correct metadata found") else @@ -493,14 +493,14 @@ minetest.register_craftitem("experimental:tester_tool_1", { on_use = function(itemstack, user, pointed_thing) --print(dump(pointed_thing)) if pointed_thing.type == "node" then - if minetest.env:get_node(pointed_thing.under).name == "experimental:tester_node_1" then + if minetest.get_node(pointed_thing.under).name == "experimental:tester_node_1" then local p = pointed_thing.under minetest.log("action", "Tester tool used at "..minetest.pos_to_string(p)) - minetest.env:dig_node(p) + minetest.dig_node(p) else local p = pointed_thing.above minetest.log("action", "Tester tool used at "..minetest.pos_to_string(p)) - minetest.env:place_node(p, {name="experimental:tester_node_1"}) + minetest.place_node(p, {name="experimental:tester_node_1"}) end end end, @@ -556,7 +556,7 @@ minetest.register_chatcommand("test1", { params = "", description = "Test 1: Modify player's inventory view", func = function(name, param) - local player = minetest.env:get_player_by_name(name) + local player = minetest.get_player_by_name(name) if not player then return end diff --git a/games/minimal/mods/legacy/init.lua b/games/minimal/mods/legacy/init.lua index 7f9088ce..98ad69be 100644 --- a/games/minimal/mods/legacy/init.lua +++ b/games/minimal/mods/legacy/init.lua @@ -79,14 +79,12 @@ minetest.register_craftitem(":rat", { description = "Rat", inventory_image = "rat.png", on_drop = function(item, dropper, pos) - minetest.env:add_rat(pos) item:take_item() return item end, on_place = function(item, dropped, pointed) pos = minetest.get_pointed_thing_position(pointed, true) if pos ~= nil then - minetest.env:add_rat(pos) item:take_item() return item end @@ -103,14 +101,12 @@ minetest.register_craftitem(":firefly", { description = "Firefly", inventory_image = "firefly.png", on_drop = function(item, dropper, pos) - minetest.env:add_firefly(pos) item:take_item() return item end, on_place = function(item, dropped, pointed) pos = minetest.get_pointed_thing_position(pointed, true) if pos ~= nil then - minetest.env:add_firefly(pos) item:take_item() return item end diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 74c5fabb..51ed2783 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -206,6 +206,9 @@ configure_file( "${PROJECT_BINARY_DIR}/cmake_config.h" ) +add_subdirectory(script) +add_subdirectory(util) + set(common_SRCS rollback_interface.cpp rollback.cpp @@ -222,22 +225,6 @@ set(common_SRCS itemdef.cpp nodedef.cpp object_properties.cpp - scriptapi_types.cpp - scriptapi_common.cpp - scriptapi_content.cpp - scriptapi_craft.cpp - scriptapi_node.cpp - scriptapi_item.cpp - scriptapi_env.cpp - scriptapi_nodetimer.cpp - scriptapi_noise.cpp - scriptapi_entity.cpp - scriptapi_object.cpp - scriptapi_nodemeta.cpp - scriptapi_inventory.cpp - scriptapi_particles.cpp - scriptapi.cpp - script.cpp log.cpp content_sao.cpp emerge.cpp @@ -283,12 +270,8 @@ set(common_SRCS staticobject.cpp serverlist.cpp pathfinder.cpp - util/serialize.cpp - util/directiontables.cpp - util/numeric.cpp - util/pointedthing.cpp - util/string.cpp - util/timetaker.cpp + ${SCRIPT_SRCS} + ${UTIL_SRCS} ) # This gives us the icon @@ -365,6 +348,7 @@ set(minetestserver_SRCS include_directories( ${PROJECT_BINARY_DIR} + ${PROJECT_SOURCE_DIR} ${IRRLICHT_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR} ${CMAKE_BUILD_TYPE} @@ -375,6 +359,7 @@ include_directories( ${SQLITE3_INCLUDE_DIR} ${LUA_INCLUDE_DIR} ${JSON_INCLUDE_DIR} + ${PROJECT_SOURCE_DIR}/script ) if(USE_FREETYPE) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 47b94a5d..cc02a743 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" // For ToolCapabilities #include "gamedef.h" #include "player.h" -#include "scriptapi.h" +#include "cpp_api/scriptapi.h" #include "genericobject.h" #include "util/serialize.h" @@ -387,8 +387,7 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, LuaEntitySAO::~LuaEntitySAO() { if(m_registered){ - lua_State *L = m_env->getLua(); - scriptapi_luaentity_rm(L, m_id); + ENV_TO_SA(m_env)->luaentity_Remove(m_id); } } @@ -397,16 +396,15 @@ void LuaEntitySAO::addedToEnvironment(u32 dtime_s) ServerActiveObject::addedToEnvironment(dtime_s); // Create entity from name - lua_State *L = m_env->getLua(); - m_registered = scriptapi_luaentity_add(L, m_id, m_init_name.c_str()); + m_registered = ENV_TO_SA(m_env)->luaentity_Add(m_id, m_init_name.c_str()); if(m_registered){ // Get properties - scriptapi_luaentity_get_properties(L, m_id, &m_prop); + ENV_TO_SA(m_env)->luaentity_GetProperties(m_id, &m_prop); // Initialize HP from properties m_hp = m_prop.hp_max; // Activate entity, supplying serialized state - scriptapi_luaentity_activate(L, m_id, m_init_state.c_str(), dtime_s); + ENV_TO_SA(m_env)->luaentity_Activate(m_id, m_init_state.c_str(), dtime_s); } } @@ -515,8 +513,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) } if(m_registered){ - lua_State *L = m_env->getLua(); - scriptapi_luaentity_step(L, m_id, dtime); + ENV_TO_SA(m_env)->luaentity_Step(m_id, dtime); } if(send_recommended == false) @@ -626,8 +623,7 @@ std::string LuaEntitySAO::getStaticData() os<getLua(); - std::string state = scriptapi_luaentity_get_staticdata(L, m_id); + std::string state = ENV_TO_SA(m_env)->luaentity_GetStaticdata(m_id); os<getLua(); - scriptapi_luaentity_punch(L, m_id, puncher, + ENV_TO_SA(m_env)->luaentity_Punch(m_id, puncher, time_from_last_punch, toolcap, dir); return result.wear; @@ -708,8 +703,7 @@ void LuaEntitySAO::rightClick(ServerActiveObject *clicker) // It's best that attachments cannot be clicked if(isAttached()) return; - lua_State *L = m_env->getLua(); - scriptapi_luaentity_rightclick(L, m_id, clicker); + ENV_TO_SA(m_env)->luaentity_Rightclick(m_id, clicker); } void LuaEntitySAO::setPos(v3f pos) diff --git a/src/emerge.cpp b/src/emerge.cpp index b31a942e..fd6c0e91 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -32,8 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapblock.h" #include "serverobject.h" #include "settings.h" -#include "script.h" -#include "scriptapi.h" +#include "cpp_api/scriptapi.h" #include "profiler.h" #include "log.h" #include "nodedef.h" @@ -418,7 +417,7 @@ void *EmergeThread::Thread() { ign(&m_server->m_ignore_map_edit_events_area, VoxelArea(minp, maxp)); { // takes about 90ms with -O1 on an e3-1230v2 - scriptapi_environment_on_generated(m_server->m_lua, + SERVER_TO_SA(m_server)->environment_OnGenerated( minp, maxp, emerge->getBlockSeed(minp)); } diff --git a/src/environment.cpp b/src/environment.cpp index 9f87b8c9..83ae5901 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "log.h" #include "profiler.h" -#include "scriptapi.h" +#include "cpp_api/scriptapi.h" #include "nodedef.h" #include "nodemetadata.h" #include "main.h" // For g_settings, g_profiler @@ -320,10 +320,10 @@ void ActiveBlockList::update(std::list &active_positions, ServerEnvironment */ -ServerEnvironment::ServerEnvironment(ServerMap *map, lua_State *L, +ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface, IGameDef *gamedef, IBackgroundBlockEmerger *emerger): m_map(map), - m_lua(L), + m_script(scriptIface), m_gamedef(gamedef), m_emerger(emerger), m_random_spawn_timer(3), @@ -826,7 +826,7 @@ void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) i != elapsed_timers.end(); i++){ n = block->getNodeNoEx(i->first); v3s16 p = i->first + block->getPosRelative(); - if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed)) + if(m_script->node_on_timer(p,n,i->second.elapsed)) block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); } } @@ -847,17 +847,17 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) MapNode n_old = m_map->getNodeNoEx(p); // Call destructor if(ndef->get(n_old).has_on_destruct) - scriptapi_node_on_destruct(m_lua, p, n_old); + m_script->node_on_destruct(p, n_old); // Replace node bool succeeded = m_map->addNodeWithEvent(p, n); if(!succeeded) return false; // Call post-destructor if(ndef->get(n_old).has_after_destruct) - scriptapi_node_after_destruct(m_lua, p, n_old); + m_script->node_after_destruct(p, n_old); // Call constructor if(ndef->get(n).has_on_construct) - scriptapi_node_on_construct(m_lua, p, n); + m_script->node_on_construct(p, n); return true; } @@ -867,7 +867,7 @@ bool ServerEnvironment::removeNode(v3s16 p) MapNode n_old = m_map->getNodeNoEx(p); // Call destructor if(ndef->get(n_old).has_on_destruct) - scriptapi_node_on_destruct(m_lua, p, n_old); + m_script->node_on_destruct(p, n_old); // Replace with air // This is slightly optimized compared to addNodeWithEvent(air) bool succeeded = m_map->removeNodeWithEvent(p); @@ -875,7 +875,7 @@ bool ServerEnvironment::removeNode(v3s16 p) return false; // Call post-destructor if(ndef->get(n_old).has_after_destruct) - scriptapi_node_after_destruct(m_lua, p, n_old); + m_script->node_after_destruct(p, n_old); // Air doesn't require constructor return true; } @@ -930,7 +930,7 @@ void ServerEnvironment::clearAllObjects() // Tell the object about removal obj->removingFromEnvironment(); // Deregister in scripting api - scriptapi_rm_object_reference(m_lua, obj); + m_script->removeObjectReference(obj); // Delete active object if(obj->environmentDeletes()) @@ -1159,7 +1159,7 @@ void ServerEnvironment::step(float dtime) i != elapsed_timers.end(); i++){ n = block->getNodeNoEx(i->first); p = i->first + block->getPosRelative(); - if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed)) + if(m_script->node_on_timer(p,n,i->second.elapsed)) block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0)); } } @@ -1213,7 +1213,7 @@ void ServerEnvironment::step(float dtime) /* Step script environment (run global on_step()) */ - scriptapi_environment_step(m_lua, dtime); + m_script->environment_Step(dtime); /* Step active objects @@ -1507,7 +1507,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, <addObjectReference(object); // Post-initialize object object->addedToEnvironment(dtime_s); @@ -1595,7 +1595,7 @@ void ServerEnvironment::removeRemovedObjects() // Tell the object about removal obj->removingFromEnvironment(); // Deregister in scripting api - scriptapi_rm_object_reference(m_lua, obj); + m_script->removeObjectReference(obj); // Delete if(obj->environmentDeletes()) @@ -1901,7 +1901,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // Tell the object about removal obj->removingFromEnvironment(); // Deregister in scripting api - scriptapi_rm_object_reference(m_lua, obj); + m_script->removeObjectReference(obj); // Delete active object if(obj->environmentDeletes()) diff --git a/src/environment.h b/src/environment.h index a3e43dbb..a62173a1 100644 --- a/src/environment.h +++ b/src/environment.h @@ -50,6 +50,7 @@ class IGameDef; class Map; class ServerMap; class ClientMap; +class ScriptApi; class Environment { @@ -190,7 +191,7 @@ public: class ServerEnvironment : public Environment { public: - ServerEnvironment(ServerMap *map, lua_State *L, IGameDef *gamedef, + ServerEnvironment(ServerMap *map, ScriptApi *iface, IGameDef *gamedef, IBackgroundBlockEmerger *emerger); ~ServerEnvironment(); @@ -198,8 +199,9 @@ public: ServerMap & getServerMap(); - lua_State* getLua() - { return m_lua; } + //TODO find way to remove this fct! + ScriptApi* getScriptIface() + { return m_script; } IGameDef *getGameDef() { return m_gamedef; } @@ -348,7 +350,7 @@ private: // The map ServerMap *m_map; // Lua state - lua_State *m_lua; + ScriptApi* m_script; // Game definition IGameDef *m_gamedef; // Background block emerger (the server, in practice) diff --git a/src/hud.h b/src/hud.h index 00e44dd8..54b7e7f3 100644 --- a/src/hud.h +++ b/src/hud.h @@ -46,7 +46,7 @@ enum HudElementType { }; enum HudElementStat { - HUD_STAT_POS, + HUD_STAT_POS = 0, HUD_STAT_NAME, HUD_STAT_SCALE, HUD_STAT_TEXT, diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 08cae6d4..6187675d 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "log.h" #include "environment.h" -#include "scriptapi.h" +#include "cpp_api/scriptapi.h" #include "serverobject.h" #include "main.h" // for g_settings #include "settings.h" @@ -226,9 +226,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame to_inv.type == InventoryLocation::DETACHED && from_inv.name == to_inv.name) { - lua_State *L = player->getEnv()->getLua(); - src_can_take_count = scriptapi_detached_inventory_allow_move( - L, from_inv.name, from_list, from_i, + src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove( + from_inv.name, from_list, from_i, to_list, to_i, try_take_count, player); dst_can_put_count = src_can_take_count; } @@ -237,20 +236,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // Destination is detached if(to_inv.type == InventoryLocation::DETACHED) { - lua_State *L = player->getEnv()->getLua(); ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; - dst_can_put_count = scriptapi_detached_inventory_allow_put( - L, to_inv.name, to_list, to_i, src_item, player); + dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut( + to_inv.name, to_list, to_i, src_item, player); } // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { - lua_State *L = player->getEnv()->getLua(); ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; - src_can_take_count = scriptapi_detached_inventory_allow_take( - L, from_inv.name, from_list, from_i, src_item, player); + src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( + from_inv.name, from_list, from_i, src_item, player); } } @@ -262,9 +259,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { - lua_State *L = player->getEnv()->getLua(); - src_can_take_count = scriptapi_nodemeta_inventory_allow_move( - L, from_inv.p, from_list, from_i, + src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove( + from_inv.p, from_list, from_i, to_list, to_i, try_take_count, player); dst_can_put_count = src_can_take_count; } @@ -273,20 +269,18 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // Destination is nodemeta if(to_inv.type == InventoryLocation::NODEMETA) { - lua_State *L = player->getEnv()->getLua(); ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; - dst_can_put_count = scriptapi_nodemeta_inventory_allow_put( - L, to_inv.p, to_list, to_i, src_item, player); + dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut( + to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { - lua_State *L = player->getEnv()->getLua(); ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; - src_can_take_count = scriptapi_nodemeta_inventory_allow_take( - L, from_inv.p, from_list, from_i, src_item, player); + src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( + from_inv.p, from_list, from_i, src_item, player); } } @@ -412,9 +406,8 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame to_inv.type == InventoryLocation::DETACHED && from_inv.name == to_inv.name) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_detached_inventory_on_move( - L, from_inv.name, from_list, from_i, + PLAYER_TO_SA(player)->detached_inventory_OnMove( + from_inv.name, from_list, from_i, to_list, to_i, count, player); } else @@ -422,16 +415,14 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // Destination is detached if(to_inv.type == InventoryLocation::DETACHED) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_detached_inventory_on_put( - L, to_inv.name, to_list, to_i, src_item, player); + PLAYER_TO_SA(player)->detached_inventory_OnPut( + to_inv.name, to_list, to_i, src_item, player); } // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_detached_inventory_on_take( - L, from_inv.name, from_list, from_i, src_item, player); + PLAYER_TO_SA(player)->detached_inventory_OnTake( + from_inv.name, from_list, from_i, src_item, player); } } @@ -442,25 +433,22 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_nodemeta_inventory_on_move( - L, from_inv.p, from_list, from_i, + PLAYER_TO_SA(player)->nodemeta_inventory_OnMove( + from_inv.p, from_list, from_i, to_list, to_i, count, player); } else{ // Destination is nodemeta if(to_inv.type == InventoryLocation::NODEMETA) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_nodemeta_inventory_on_put( - L, to_inv.p, to_list, to_i, src_item, player); + PLAYER_TO_SA(player)->nodemeta_inventory_OnPut( + to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta else if(from_inv.type == InventoryLocation::NODEMETA) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_nodemeta_inventory_on_take( - L, from_inv.p, from_list, from_i, src_item, player); + PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( + from_inv.p, from_list, from_i, src_item, player); } } @@ -563,21 +551,19 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { - lua_State *L = player->getEnv()->getLua(); ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; - src_can_take_count = scriptapi_detached_inventory_allow_take( - L, from_inv.name, from_list, from_i, src_item, player); + src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( + from_inv.name, from_list, from_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { - lua_State *L = player->getEnv()->getLua(); ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; - src_can_take_count = scriptapi_nodemeta_inventory_allow_take( - L, from_inv.p, from_list, from_i, src_item, player); + src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( + from_inv.p, from_list, from_i, src_item, player); } if(src_can_take_count != -1 && src_can_take_count < take_count) @@ -590,7 +576,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // Drop the item ItemStack item1 = list_from->getItem(from_i); item1.count = take_count; - if(scriptapi_item_on_drop(player->getEnv()->getLua(), item1, player, + if(PLAYER_TO_SA(player)->item_OnDrop(item1, player, player->getBasePosition() + v3f(0,1,0))) { actually_dropped_count = take_count - item1.count; @@ -627,17 +613,15 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_detached_inventory_on_take( - L, from_inv.name, from_list, from_i, src_item, player); + PLAYER_TO_SA(player)->detached_inventory_OnTake( + from_inv.name, from_list, from_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { - lua_State *L = player->getEnv()->getLua(); - scriptapi_nodemeta_inventory_on_take( - L, from_inv.p, from_list, from_i, src_item, player); + PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( + from_inv.p, from_list, from_i, src_item, player); } /* diff --git a/src/jthread/jmutex.h b/src/jthread/jmutex.h index e528aeb4..383150f3 100644 --- a/src/jthread/jmutex.h +++ b/src/jthread/jmutex.h @@ -57,6 +57,7 @@ public: int Lock(); int Unlock(); bool IsInitialized() { return initialized; } + private: #if (defined(WIN32) || defined(_WIN32_WCE)) #ifdef JMUTEX_CRITICALSECTION @@ -66,6 +67,14 @@ private: #endif // JMUTEX_CRITICALSECTION #else // pthread mutex pthread_mutex_t mutex; + + bool IsLocked() { + if (pthread_mutex_trylock(&mutex)) { + pthread_mutex_unlock(&mutex); + return true; + } + return false; + } #endif // WIN32 bool initialized; }; diff --git a/src/script.cpp b/src/script.cpp deleted file mode 100644 index 4619fa63..00000000 --- a/src/script.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* -Minetest -Copyright (C) 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. -*/ - -#include "script.h" -#include -#include -#include -#include -#include "log.h" -#include - -extern "C" { -#include -#include -#include -} - -LuaError::LuaError(lua_State *L, const std::string &s) -{ - m_s = "LuaError: "; - m_s += s + "\n"; - m_s += script_get_backtrace(L); -} - -std::string script_get_backtrace(lua_State *L) -{ - std::string s; - lua_getfield(L, LUA_GLOBALSINDEX, "debug"); - if(lua_istable(L, -1)){ - lua_getfield(L, -1, "traceback"); - if(lua_isfunction(L, -1)){ - lua_call(L, 0, 1); - if(lua_isstring(L, -1)){ - s += lua_tostring(L, -1); - } - lua_pop(L, 1); - } - else{ - lua_pop(L, 1); - } - } - lua_pop(L, 1); - return s; -} - -void script_error(lua_State *L, const char *fmt, ...) -{ - va_list argp; - va_start(argp, fmt); - char buf[10000]; - vsnprintf(buf, 10000, fmt, argp); - va_end(argp); - //errorstream<<"SCRIPT ERROR: "< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "common/c_content.h" +#include "common/c_converter.h" +#include "common/c_types.h" +#include "nodedef.h" +#include "itemdef.h" +#include "object_properties.h" +#include "cpp_api/s_node.h" +#include "lua_api/l_object.h" +#include "lua_api/l_item.h" +#include "common/c_internal.h" +#include "server.h" +#include "log.h" +#include "tool.h" +#include "server.h" + +struct EnumString es_TileAnimationType[] = +{ + {TAT_NONE, "none"}, + {TAT_VERTICAL_FRAMES, "vertical_frames"}, + {0, NULL}, +}; + +/******************************************************************************/ +ItemDefinition read_item_definition(lua_State* L,int index, + ItemDefinition default_def) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + // Read the item definition + ItemDefinition def = default_def; + + def.type = (ItemType)getenumfield(L, index, "type", + es_ItemType, ITEM_NONE); + getstringfield(L, index, "name", def.name); + getstringfield(L, index, "description", def.description); + getstringfield(L, index, "inventory_image", def.inventory_image); + getstringfield(L, index, "wield_image", def.wield_image); + + lua_getfield(L, index, "wield_scale"); + if(lua_istable(L, -1)){ + def.wield_scale = check_v3f(L, -1); + } + lua_pop(L, 1); + + def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max); + if(def.stack_max == 0) + def.stack_max = 1; + + lua_getfield(L, index, "on_use"); + def.usable = lua_isfunction(L, -1); + lua_pop(L, 1); + + getboolfield(L, index, "liquids_pointable", def.liquids_pointable); + + warn_if_field_exists(L, index, "tool_digging_properties", + "deprecated: use tool_capabilities"); + + lua_getfield(L, index, "tool_capabilities"); + if(lua_istable(L, -1)){ + def.tool_capabilities = new ToolCapabilities( + read_tool_capabilities(L, -1)); + } + + // If name is "" (hand), ensure there are ToolCapabilities + // because it will be looked up there whenever any other item has + // no ToolCapabilities + if(def.name == "" && def.tool_capabilities == NULL){ + def.tool_capabilities = new ToolCapabilities(); + } + + lua_getfield(L, index, "groups"); + read_groups(L, -1, def.groups); + lua_pop(L, 1); + + lua_getfield(L, index, "sounds"); + if(lua_istable(L, -1)){ + lua_getfield(L, -1, "place"); + read_soundspec(L, -1, def.sound_place); + lua_pop(L, 1); + } + lua_pop(L, 1); + + // Client shall immediately place this node when player places the item. + // Server will update the precise end result a moment later. + // "" = no prediction + getstringfield(L, index, "node_placement_prediction", + def.node_placement_prediction); + + return def; +} + +/******************************************************************************/ +void read_object_properties(lua_State *L, int index, + ObjectProperties *prop) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + if(!lua_istable(L, index)) + return; + + prop->hp_max = getintfield_default(L, -1, "hp_max", 10); + + getboolfield(L, -1, "physical", prop->physical); + + getfloatfield(L, -1, "weight", prop->weight); + + lua_getfield(L, -1, "collisionbox"); + if(lua_istable(L, -1)) + prop->collisionbox = read_aabb3f(L, -1, 1.0); + lua_pop(L, 1); + + getstringfield(L, -1, "visual", prop->visual); + + getstringfield(L, -1, "mesh", prop->mesh); + + lua_getfield(L, -1, "visual_size"); + if(lua_istable(L, -1)) + prop->visual_size = read_v2f(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "textures"); + if(lua_istable(L, -1)){ + prop->textures.clear(); + int table = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + if(lua_isstring(L, -1)) + prop->textures.push_back(lua_tostring(L, -1)); + else + prop->textures.push_back(""); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + lua_getfield(L, -1, "colors"); + if(lua_istable(L, -1)){ + prop->colors.clear(); + int table = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + if(lua_isstring(L, -1)) + prop->colors.push_back(readARGB8(L, -1)); + else + prop->colors.push_back(video::SColor(255, 255, 255, 255)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + + lua_getfield(L, -1, "spritediv"); + if(lua_istable(L, -1)) + prop->spritediv = read_v2s16(L, -1); + lua_pop(L, 1); + + lua_getfield(L, -1, "initial_sprite_basepos"); + if(lua_istable(L, -1)) + prop->initial_sprite_basepos = read_v2s16(L, -1); + lua_pop(L, 1); + + getboolfield(L, -1, "is_visible", prop->is_visible); + getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound); + getfloatfield(L, -1, "automatic_rotate", prop->automatic_rotate); +} + +/******************************************************************************/ +TileDef read_tiledef(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + TileDef tiledef; + + // key at index -2 and value at index + if(lua_isstring(L, index)){ + // "default_lava.png" + tiledef.name = lua_tostring(L, index); + } + else if(lua_istable(L, index)) + { + // {name="default_lava.png", animation={}} + tiledef.name = ""; + getstringfield(L, index, "name", tiledef.name); + getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. + tiledef.backface_culling = getboolfield_default( + L, index, "backface_culling", true); + // animation = {} + lua_getfield(L, index, "animation"); + if(lua_istable(L, -1)){ + // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} + tiledef.animation.type = (TileAnimationType) + getenumfield(L, -1, "type", es_TileAnimationType, + TAT_NONE); + tiledef.animation.aspect_w = + getintfield_default(L, -1, "aspect_w", 16); + tiledef.animation.aspect_h = + getintfield_default(L, -1, "aspect_h", 16); + tiledef.animation.length = + getfloatfield_default(L, -1, "length", 1.0); + } + lua_pop(L, 1); + } + + return tiledef; +} + +/******************************************************************************/ +ContentFeatures read_content_features(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + ContentFeatures f; + + /* Cache existence of some callbacks */ + lua_getfield(L, index, "on_construct"); + if(!lua_isnil(L, -1)) f.has_on_construct = true; + lua_pop(L, 1); + lua_getfield(L, index, "on_destruct"); + if(!lua_isnil(L, -1)) f.has_on_destruct = true; + lua_pop(L, 1); + lua_getfield(L, index, "after_destruct"); + if(!lua_isnil(L, -1)) f.has_after_destruct = true; + lua_pop(L, 1); + + lua_getfield(L, index, "on_rightclick"); + f.rightclickable = lua_isfunction(L, -1); + lua_pop(L, 1); + + /* Name */ + getstringfield(L, index, "name", f.name); + + /* Groups */ + lua_getfield(L, index, "groups"); + read_groups(L, -1, f.groups); + lua_pop(L, 1); + + /* Visual definition */ + + f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", + ScriptApiNode::es_DrawType,NDT_NORMAL); + getfloatfield(L, index, "visual_scale", f.visual_scale); + + // tiles = {} + lua_getfield(L, index, "tiles"); + // If nil, try the deprecated name "tile_images" instead + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + warn_if_field_exists(L, index, "tile_images", + "Deprecated; new name is \"tiles\"."); + lua_getfield(L, index, "tile_images"); + } + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + int i = 0; + while(lua_next(L, table) != 0){ + // Read tiledef from value + f.tiledef[i] = read_tiledef(L, -1); + // removes value, keeps key for next iteration + lua_pop(L, 1); + i++; + if(i==6){ + lua_pop(L, 1); + break; + } + } + // Copy last value to all remaining textures + if(i >= 1){ + TileDef lasttile = f.tiledef[i-1]; + while(i < 6){ + f.tiledef[i] = lasttile; + i++; + } + } + } + lua_pop(L, 1); + + // special_tiles = {} + lua_getfield(L, index, "special_tiles"); + // If nil, try the deprecated name "special_materials" instead + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + warn_if_field_exists(L, index, "special_materials", + "Deprecated; new name is \"special_tiles\"."); + lua_getfield(L, index, "special_materials"); + } + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + int i = 0; + while(lua_next(L, table) != 0){ + // Read tiledef from value + f.tiledef_special[i] = read_tiledef(L, -1); + // removes value, keeps key for next iteration + lua_pop(L, 1); + i++; + if(i==6){ + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); + + f.alpha = getintfield_default(L, index, "alpha", 255); + + bool usealpha = getboolfield_default(L, index, + "use_texture_alpha", false); + if (usealpha) + f.alpha = 0; + + /* Other stuff */ + + lua_getfield(L, index, "post_effect_color"); + if(!lua_isnil(L, -1)) + f.post_effect_color = readARGB8(L, -1); + lua_pop(L, 1); + + f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", + ScriptApiNode::es_ContentParamType, CPT_NONE); + f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", + ScriptApiNode::es_ContentParamType2, CPT2_NONE); + + // Warn about some deprecated fields + warn_if_field_exists(L, index, "wall_mounted", + "deprecated: use paramtype2 = 'wallmounted'"); + warn_if_field_exists(L, index, "light_propagates", + "deprecated: determined from paramtype"); + warn_if_field_exists(L, index, "dug_item", + "deprecated: use 'drop' field"); + warn_if_field_exists(L, index, "extra_dug_item", + "deprecated: use 'drop' field"); + warn_if_field_exists(L, index, "extra_dug_item_rarity", + "deprecated: use 'drop' field"); + warn_if_field_exists(L, index, "metadata_name", + "deprecated: use on_add and metadata callbacks"); + + // True for all ground-like things like stone and mud, false for eg. trees + getboolfield(L, index, "is_ground_content", f.is_ground_content); + f.light_propagates = (f.param_type == CPT_LIGHT); + getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates); + // This is used for collision detection. + // Also for general solidness queries. + getboolfield(L, index, "walkable", f.walkable); + // Player can point to these + getboolfield(L, index, "pointable", f.pointable); + // Player can dig these + getboolfield(L, index, "diggable", f.diggable); + // Player can climb these + getboolfield(L, index, "climbable", f.climbable); + // Player can build on these + getboolfield(L, index, "buildable_to", f.buildable_to); + // Whether the node is non-liquid, source liquid or flowing liquid + f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", + ScriptApiNode::es_LiquidType, LIQUID_NONE); + // If the content is liquid, this is the flowing version of the liquid. + getstringfield(L, index, "liquid_alternative_flowing", + f.liquid_alternative_flowing); + // If the content is liquid, this is the source version of the liquid. + getstringfield(L, index, "liquid_alternative_source", + f.liquid_alternative_source); + // Viscosity for fluid flow, ranging from 1 to 7, with + // 1 giving almost instantaneous propagation and 7 being + // the slowest possible + f.liquid_viscosity = getintfield_default(L, index, + "liquid_viscosity", f.liquid_viscosity); + getboolfield(L, index, "liquid_renewable", f.liquid_renewable); + // Amount of light the node emits + f.light_source = getintfield_default(L, index, + "light_source", f.light_source); + f.damage_per_second = getintfield_default(L, index, + "damage_per_second", f.damage_per_second); + + lua_getfield(L, index, "node_box"); + if(lua_istable(L, -1)) + f.node_box = read_nodebox(L, -1); + lua_pop(L, 1); + + lua_getfield(L, index, "selection_box"); + if(lua_istable(L, -1)) + f.selection_box = read_nodebox(L, -1); + lua_pop(L, 1); + + // Set to true if paramtype used to be 'facedir_simple' + getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); + // Set to true if wall_mounted used to be set to true + getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); + + // Sound table + lua_getfield(L, index, "sounds"); + if(lua_istable(L, -1)){ + lua_getfield(L, -1, "footstep"); + read_soundspec(L, -1, f.sound_footstep); + lua_pop(L, 1); + lua_getfield(L, -1, "dig"); + read_soundspec(L, -1, f.sound_dig); + lua_pop(L, 1); + lua_getfield(L, -1, "dug"); + read_soundspec(L, -1, f.sound_dug); + lua_pop(L, 1); + } + lua_pop(L, 1); + + return f; +} + +/******************************************************************************/ +void read_server_sound_params(lua_State *L, int index, + ServerSoundParams ¶ms) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + // Clear + params = ServerSoundParams(); + if(lua_istable(L, index)){ + getfloatfield(L, index, "gain", params.gain); + getstringfield(L, index, "to_player", params.to_player); + lua_getfield(L, index, "pos"); + if(!lua_isnil(L, -1)){ + v3f p = read_v3f(L, -1)*BS; + params.pos = p; + params.type = ServerSoundParams::SSP_POSITIONAL; + } + lua_pop(L, 1); + lua_getfield(L, index, "object"); + if(!lua_isnil(L, -1)){ + ObjectRef *ref = ObjectRef::checkobject(L, -1); + ServerActiveObject *sao = ObjectRef::getobject(ref); + if(sao){ + params.object = sao->getId(); + params.type = ServerSoundParams::SSP_OBJECT; + } + } + lua_pop(L, 1); + params.max_hear_distance = BS*getfloatfield_default(L, index, + "max_hear_distance", params.max_hear_distance/BS); + getboolfield(L, index, "loop", params.loop); + } +} + +/******************************************************************************/ +void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + if(lua_isnil(L, index)){ + } else if(lua_istable(L, index)){ + getstringfield(L, index, "name", spec.name); + getfloatfield(L, index, "gain", spec.gain); + } else if(lua_isstring(L, index)){ + spec.name = lua_tostring(L, index); + } +} + +/******************************************************************************/ +NodeBox read_nodebox(lua_State *L, int index) +{ + NodeBox nodebox; + if(lua_istable(L, -1)){ + nodebox.type = (NodeBoxType)getenumfield(L, index, "type", + ScriptApiNode::es_NodeBoxType, NODEBOX_REGULAR); + + lua_getfield(L, index, "fixed"); + if(lua_istable(L, -1)) + nodebox.fixed = read_aabb3f_vector(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_top"); + if(lua_istable(L, -1)) + nodebox.wall_top = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_bottom"); + if(lua_istable(L, -1)) + nodebox.wall_bottom = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, index, "wall_side"); + if(lua_istable(L, -1)) + nodebox.wall_side = read_aabb3f(L, -1, BS); + lua_pop(L, 1); + } + return nodebox; +} + +/******************************************************************************/ +MapNode readnode(lua_State *L, int index, INodeDefManager *ndef) +{ + lua_getfield(L, index, "name"); + const char *name = luaL_checkstring(L, -1); + lua_pop(L, 1); + u8 param1; + lua_getfield(L, index, "param1"); + if(lua_isnil(L, -1)) + param1 = 0; + else + param1 = lua_tonumber(L, -1); + lua_pop(L, 1); + u8 param2; + lua_getfield(L, index, "param2"); + if(lua_isnil(L, -1)) + param2 = 0; + else + param2 = lua_tonumber(L, -1); + lua_pop(L, 1); + return MapNode(ndef, name, param1, param2); +} + +/******************************************************************************/ +void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef) +{ + lua_newtable(L); + lua_pushstring(L, ndef->get(n).name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushnumber(L, n.getParam1()); + lua_setfield(L, -2, "param1"); + lua_pushnumber(L, n.getParam2()); + lua_setfield(L, -2, "param2"); +} + +/******************************************************************************/ +void warn_if_field_exists(lua_State *L, int table, + const char *fieldname, const std::string &message) +{ + lua_getfield(L, table, fieldname); + if(!lua_isnil(L, -1)){ +//TODO find way to access backtrace fct from here + // infostream<str){ + if(str == std::string(esp->str)){ + result = esp->num; + return true; + } + esp++; + } + return false; +} + +/******************************************************************************/ +ItemStack read_item(lua_State* L, int index,Server* srv) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(lua_isnil(L, index)) + { + return ItemStack(); + } + else if(lua_isuserdata(L, index)) + { + // Convert from LuaItemStack + LuaItemStack *o = LuaItemStack::checkobject(L, index); + return o->getItem(); + } + else if(lua_isstring(L, index)) + { + // Convert from itemstring + std::string itemstring = lua_tostring(L, index); + IItemDefManager *idef = srv->idef(); + try + { + ItemStack item; + item.deSerialize(itemstring, idef); + return item; + } + catch(SerializationError &e) + { + infostream<<"WARNING: unable to create item from itemstring" + <<": "<idef(); + std::string name = getstringfield_default(L, index, "name", ""); + int count = getintfield_default(L, index, "count", 1); + int wear = getintfield_default(L, index, "wear", 0); + std::string metadata = getstringfield_default(L, index, "metadata", ""); + return ItemStack(name, count, wear, metadata, idef); + } + else + { + throw LuaError(L, "Expecting itemstack, itemstring, table or nil"); + } +} + +/******************************************************************************/ +void push_tool_capabilities(lua_State *L, + const ToolCapabilities &toolcap) +{ + lua_newtable(L); + setfloatfield(L, -1, "full_punch_interval", toolcap.full_punch_interval); + setintfield(L, -1, "max_drop_level", toolcap.max_drop_level); + // Create groupcaps table + lua_newtable(L); + // For each groupcap + for(std::map::const_iterator + i = toolcap.groupcaps.begin(); i != toolcap.groupcaps.end(); i++){ + // Create groupcap table + lua_newtable(L); + const std::string &name = i->first; + const ToolGroupCap &groupcap = i->second; + // Create subtable "times" + lua_newtable(L); + for(std::map::const_iterator + i = groupcap.times.begin(); i != groupcap.times.end(); i++){ + int rating = i->first; + float time = i->second; + lua_pushinteger(L, rating); + lua_pushnumber(L, time); + lua_settable(L, -3); + } + // Set subtable "times" + lua_setfield(L, -2, "times"); + // Set simple parameters + setintfield(L, -1, "maxlevel", groupcap.maxlevel); + setintfield(L, -1, "uses", groupcap.uses); + // Insert groupcap table into groupcaps table + lua_setfield(L, -2, name.c_str()); + } + // Set groupcaps table + lua_setfield(L, -2, "groupcaps"); + //Create damage_groups table + lua_newtable(L); + // For each damage group + for(std::map::const_iterator + i = toolcap.damageGroups.begin(); i != toolcap.damageGroups.end(); i++){ + // Create damage group table + lua_pushinteger(L, i->second); + lua_setfield(L, -2, i->first.c_str()); + } + lua_setfield(L, -2, "damage_groups"); +} + +/******************************************************************************/ +void push_inventory_list(Inventory *inv, const char *name, + lua_State *L) +{ + InventoryList *invlist = inv->getList(name); + if(invlist == NULL){ + lua_pushnil(L); + return; + } + std::vector items; + for(u32 i=0; igetSize(); i++) + items.push_back(invlist->getItem(i)); + push_items(L, items); +} + +/******************************************************************************/ +void read_inventory_list(Inventory *inv, const char *name, + lua_State *L, int tableindex, Server* srv,int forcesize) +{ + if(tableindex < 0) + tableindex = lua_gettop(L) + 1 + tableindex; + // If nil, delete list + if(lua_isnil(L, tableindex)){ + inv->deleteList(name); + return; + } + // Otherwise set list + std::vector items = read_items(L, tableindex,srv); + int listsize = (forcesize != -1) ? forcesize : items.size(); + InventoryList *invlist = inv->addList(name, listsize); + int index = 0; + for(std::vector::const_iterator + i = items.begin(); i != items.end(); i++){ + if(forcesize != -1 && index == forcesize) + break; + invlist->changeItem(index, *i); + index++; + } + while(forcesize != -1 && index < forcesize){ + invlist->deleteItem(index); + index++; + } +} + +/******************************************************************************/ +ToolCapabilities read_tool_capabilities( + lua_State *L, int table) +{ + ToolCapabilities toolcap; + getfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval); + getintfield(L, table, "max_drop_level", toolcap.max_drop_level); + lua_getfield(L, table, "groupcaps"); + if(lua_istable(L, -1)){ + int table_groupcaps = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table_groupcaps) != 0){ + // key at index -2 and value at index -1 + std::string groupname = luaL_checkstring(L, -2); + if(lua_istable(L, -1)){ + int table_groupcap = lua_gettop(L); + // This will be created + ToolGroupCap groupcap; + // Read simple parameters + getintfield(L, table_groupcap, "maxlevel", groupcap.maxlevel); + getintfield(L, table_groupcap, "uses", groupcap.uses); + // DEPRECATED: maxwear + float maxwear = 0; + if(getfloatfield(L, table_groupcap, "maxwear", maxwear)){ + if(maxwear != 0) + groupcap.uses = 1.0/maxwear; + else + groupcap.uses = 0; + infostream< &result) +{ + if (!lua_istable(L,index)) + return; + result.clear(); + lua_pushnil(L); + if(index < 0) + index -= 1; + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + std::string name = luaL_checkstring(L, -2); + int rating = luaL_checkinteger(L, -1); + result[name] = rating; + // removes value, keeps key for next iteration + lua_pop(L, 1); + } +} + +/******************************************************************************/ +void push_items(lua_State *L, const std::vector &items) +{ + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + // Create and fill table + lua_newtable(L); + int table = lua_gettop(L); + for(u32 i=0; i read_items(lua_State *L, int index,Server* srv) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + std::vector items; + luaL_checktype(L, index, LUA_TTABLE); + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + items.push_back(read_item(L, -1, srv)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return items; +} + +/******************************************************************************/ +void luaentity_get(lua_State *L, u16 id) +{ + // Get minetest.luaentities[i] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "luaentities"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnumber(L, id); + lua_gettable(L, -2); + lua_remove(L, -2); // luaentities + lua_remove(L, -2); // minetest +} + +/******************************************************************************/ +NoiseParams *read_noiseparams(lua_State *L, int index) +{ + if (index < 0) + index = lua_gettop(L) + 1 + index; + + if (!lua_istable(L, index)) + return NULL; + + NoiseParams *np = new NoiseParams; + + np->offset = getfloatfield_default(L, index, "offset", 0.0); + np->scale = getfloatfield_default(L, index, "scale", 0.0); + lua_getfield(L, index, "spread"); + np->spread = read_v3f(L, -1); + lua_pop(L, 1); + np->seed = getintfield_default(L, index, "seed", 0); + np->octaves = getintfield_default(L, index, "octaves", 0); + np->persist = getfloatfield_default(L, index, "persist", 0.0); + + return np; +} diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h new file mode 100644 index 00000000..251a72e2 --- /dev/null +++ b/src/script/common/c_content.h @@ -0,0 +1,146 @@ +/* +Minetest +Copyright (C) 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. +*/ + + +/******************************************************************************/ +/******************************************************************************/ +/* WARNING!!!! do NOT add this header in any include file or any code file */ +/* not being a script/modapi file!!!!!!!! */ +/******************************************************************************/ +/******************************************************************************/ + +#ifndef C_CONTENT_H_ +#define C_CONTENT_H_ + +extern "C" { +#include +} + +#include +#include +#include + +#include "irrlichttypes_bloated.h" +#include "util/string.h" + +class MapNode; +class INodeDefManager; +class PointedThing; +class ItemStack; +class ItemDefinition; +class ToolCapabilities; +class ObjectProperties; +class SimpleSoundSpec; +class ServerSoundParams; +class Inventory; +class NodeBox; +class ContentFeatures; +class TileDef; +class Server; +struct DigParams; +struct HitParams; +struct EnumString; +struct NoiseParams; + + +ContentFeatures read_content_features (lua_State *L, int index); +TileDef read_tiledef (lua_State *L, int index); +void read_soundspec (lua_State *L, int index, + SimpleSoundSpec &spec); +NodeBox read_nodebox (lua_State *L, int index); + +void read_server_sound_params (lua_State *L, int index, + ServerSoundParams ¶ms); + +void push_dig_params (lua_State *L,const DigParams ¶ms); +void push_hit_params (lua_State *L,const HitParams ¶ms); + +ItemStack read_item (lua_State *L, int index, Server* srv); + + +ToolCapabilities read_tool_capabilities (lua_State *L, + int table); +void push_tool_capabilities (lua_State *L, + const ToolCapabilities &prop); + +ItemDefinition read_item_definition (lua_State *L, + int index, + ItemDefinition default_def); +void read_object_properties (lua_State *L, + int index, + ObjectProperties *prop); + +//TODO fix parameter oreder! +void push_inventory_list (Inventory *inv, + const char *name, + lua_State *L); +void read_inventory_list (Inventory *inv, + const char *name, + lua_State *L, + int tableindex, + Server* srv, + int forcesize=-1); + +MapNode readnode (lua_State *L, + int index, + INodeDefManager *ndef); +void pushnode (lua_State *L, + const MapNode &n, + INodeDefManager *ndef); + +NodeBox read_nodebox (lua_State *L, int index); + +void read_groups (lua_State *L, + int index, + std::map &result); + +//TODO rename to "read_enum_field" +int getenumfield (lua_State *L, + int table, + const char *fieldname, + const EnumString *spec, + int default_); + +u32 getflagsfield (lua_State *L, int table, + const char *fieldname, + FlagDesc *flagdesc); + +void push_items (lua_State *L, + const std::vector &items); + +std::vector read_items (lua_State *L, + int index, + Server* srv); + +void read_soundspec (lua_State *L, + int index, + SimpleSoundSpec &spec); + + +bool string_to_enum (const EnumString *spec, + int &result, + const std::string &str); + +NoiseParams* read_noiseparams (lua_State *L, int index); + +void luaentity_get (lua_State *L,u16 id); + +extern struct EnumString es_TileAnimationType[]; + +#endif /* C_CONTENT_H_ */ diff --git a/src/scriptapi_types.cpp b/src/script/common/c_converter.cpp similarity index 86% rename from src/scriptapi_types.cpp rename to src/script/common/c_converter.cpp index f2f6b6bd..65c3654a 100644 --- a/src/scriptapi_types.cpp +++ b/src/script/common/c_converter.cpp @@ -17,19 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_types.h" - extern "C" { -#include +#include "lua.h" +#include "lauxlib.h" } #include "util/numeric.h" -#include "nodedef.h" - -/* - C struct <-> Lua table converter functions -*/ +#include "common/c_converter.h" +#include "constants.h" void push_v3f(lua_State *L, v3f p) { @@ -345,44 +340,4 @@ void setboolfield(lua_State *L, int table, lua_setfield(L, table, fieldname); } -u32 getflagsfield(lua_State *L, int table, - const char *fieldname, FlagDesc *flagdesc) { - std::string flagstring; - - flagstring = getstringfield_default(L, table, fieldname, ""); - return readFlagString(flagstring, flagdesc); -} -/* minetest specific types */ -MapNode readnode(lua_State *L, int index, INodeDefManager *ndef) -{ - lua_getfield(L, index, "name"); - const char *name = luaL_checkstring(L, -1); - lua_pop(L, 1); - u8 param1; - lua_getfield(L, index, "param1"); - if(lua_isnil(L, -1)) - param1 = 0; - else - param1 = lua_tonumber(L, -1); - lua_pop(L, 1); - u8 param2; - lua_getfield(L, index, "param2"); - if(lua_isnil(L, -1)) - param2 = 0; - else - param2 = lua_tonumber(L, -1); - lua_pop(L, 1); - return MapNode(ndef, name, param1, param2); -} - -void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef) -{ - lua_newtable(L); - lua_pushstring(L, ndef->get(n).name.c_str()); - lua_setfield(L, -2, "name"); - lua_pushnumber(L, n.getParam1()); - lua_setfield(L, -2, "param1"); - lua_pushnumber(L, n.getParam2()); - lua_setfield(L, -2, "param2"); -} diff --git a/src/scriptapi_types.h b/src/script/common/c_converter.h similarity index 59% rename from src/scriptapi_types.h rename to src/script/common/c_converter.h index dd0b125e..ab008312 100644 --- a/src/scriptapi_types.h +++ b/src/script/common/c_converter.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,16 +17,21 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_TYPES_H_ -#define LUA_TYPES_H_ -#include -#include +/******************************************************************************/ +/******************************************************************************/ +/* WARNING!!!! do NOT add this header in any include file or any code file */ +/* not being a script/modapi file!!!!!!!! */ +/******************************************************************************/ +/******************************************************************************/ +#ifndef C_CONVERTER_H_ +#define C_CONVERTER_H_ + #include +#include #include "irrlichttypes_bloated.h" -#include "porting.h" -#include "map.h" +#include "common/c_types.h" extern "C" { #include @@ -51,8 +56,6 @@ bool getboolfield(lua_State *L, int table, const char *fieldname, bool &result); bool getfloatfield(lua_State *L, int table, const char *fieldname, float &result); -u32 getflagsfield(lua_State *L, int table, - const char *fieldname, FlagDesc *flagdesc); std::string checkstringfield(lua_State *L, int table, const char *fieldname); @@ -65,26 +68,29 @@ void setboolfield(lua_State *L, int table, const char *fieldname, bool value); -v3f checkFloatPos (lua_State *L, int index); -v3f check_v3f (lua_State *L, int index); -v3s16 check_v3s16 (lua_State *L, int index); +v3f checkFloatPos (lua_State *L, int index); +v3f check_v3f (lua_State *L, int index); +v3s16 check_v3s16 (lua_State *L, int index); -v3f read_v3f (lua_State *L, int index); -v2f read_v2f (lua_State *L, int index); -v2s16 read_v2s16 (lua_State *L, int index); -video::SColor readARGB8 (lua_State *L, int index); -aabb3f read_aabb3f (lua_State *L, int index, f32 scale); -v3s16 read_v3s16 (lua_State *L, int index); +v3f read_v3f (lua_State *L, int index); +v2f read_v2f (lua_State *L, int index); +v2s16 read_v2s16 (lua_State *L, int index); +video::SColor readARGB8 (lua_State *L, int index); +aabb3f read_aabb3f (lua_State *L, int index, f32 scale); +v3s16 read_v3s16 (lua_State *L, int index); std::vector - read_aabb3f_vector (lua_State *L, int index, f32 scale); + read_aabb3f_vector (lua_State *L, int index, f32 scale); -void push_v3s16 (lua_State *L, v3s16 p); -void pushFloatPos (lua_State *L, v3f p); -void push_v3f (lua_State *L, v3f p); -void push_v2f (lua_State *L, v2f p); +void push_v3s16 (lua_State *L, v3s16 p); +void pushFloatPos (lua_State *L, v3f p); +void push_v3f (lua_State *L, v3f p); +void push_v2f (lua_State *L, v2f p); -MapNode readnode(lua_State *L, int index, INodeDefManager *ndef); -void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef); -#endif /* LUA_TYPES_H_ */ +void warn_if_field_exists (lua_State *L, + int table, + const char *fieldname, + const std::string &message); + +#endif /* C_CONVERTER_H_ */ diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp new file mode 100644 index 00000000..42e9fb3e --- /dev/null +++ b/src/script/common/c_internal.cpp @@ -0,0 +1,65 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "common/c_internal.h" +#include "server.h" +#include "cpp_api/scriptapi.h" + +ScriptApi* get_scriptapi(lua_State *L) +{ + // Get server from registry + lua_getfield(L, LUA_REGISTRYINDEX, "scriptapi"); + ScriptApi* sapi_ptr = (ScriptApi*) lua_touserdata(L, -1); + lua_pop(L, 1); + return sapi_ptr; +} + +std::string script_get_backtrace(lua_State *L) +{ + std::string s; + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if(lua_istable(L, -1)){ + lua_getfield(L, -1, "traceback"); + if(lua_isfunction(L, -1)){ + lua_call(L, 0, 1); + if(lua_isstring(L, -1)){ + s += lua_tostring(L, -1); + } + lua_pop(L, 1); + } + else{ + lua_pop(L, 1); + } + } + lua_pop(L, 1); + return s; +} + +void script_error(lua_State* L,const char *fmt, ...) +{ + va_list argp; + va_start(argp, fmt); + char buf[10000]; + vsnprintf(buf, 10000, fmt, argp); + va_end(argp); + //errorstream<<"SCRIPT ERROR: "< + +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. +*/ + +/******************************************************************************/ +/******************************************************************************/ +/* WARNING!!!! do NOT add this header in any include file or any code file */ +/* not being a modapi file!!!!!!!! */ +/******************************************************************************/ +/******************************************************************************/ + +#ifndef C_INTERNAL_H_ +#define C_INTERNAL_H_ + +class Server; +class ScriptApi; +#include + +#include "lua_api/l_base.h" + +extern "C" { +#include "lua.h" +} + +#define luamethod(class, name) {#name, class::l_##name} +#define STACK_TO_SERVER(L) get_scriptapi(L)->getServer() +#define API_FCT(name) registerFunction(L,#name,l_##name,top) + +#define REGISTER_LUA_REF(cln) \ +class ModApi_##cln : public ModApiBase { \ + public: \ + ModApi_##cln() : ModApiBase() {}; \ + bool Initialize(lua_State* L, int top) { \ + cln::Register(L); \ + return true; \ + }; \ +}; \ +ModApi_##cln macro_generated_prototype__##cln; + + +ScriptApi* get_scriptapi (lua_State *L); +std::string script_get_backtrace (lua_State *L); +void script_error (lua_State *L, const char *fmt, ...); + +#endif /* C_INTERNAL_H_ */ diff --git a/src/scriptapi_content.h b/src/script/common/c_types.cpp similarity index 60% rename from src/scriptapi_content.h rename to src/script/common/c_types.cpp index 801f3856..ac724c42 100644 --- a/src/scriptapi_content.h +++ b/src/script/common/c_types.cpp @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,21 +17,24 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_CONTENT_H_ -#define LUA_CONTENT_H_ +#include -extern "C" { -#include +#include "common/c_types.h" +#include "common/c_internal.h" +#include "itemdef.h" + +LuaError::LuaError(lua_State *L, const std::string &s) +{ + m_s = "LuaError: "; + m_s += s + "\n"; + m_s += script_get_backtrace(L); } -#include "nodedef.h" - -ContentFeatures read_content_features (lua_State *L, int index); -TileDef read_tiledef (lua_State *L, int index); -void read_soundspec (lua_State *L, int index, - SimpleSoundSpec &spec); -NodeBox read_nodebox (lua_State *L, int index); - -extern struct EnumString es_TileAnimationType[]; - -#endif /* LUA_CONTENT_H_ */ +struct EnumString es_ItemType[] = + { + {ITEM_NONE, "none"}, + {ITEM_NODE, "node"}, + {ITEM_CRAFT, "craft"}, + {ITEM_TOOL, "tool"}, + {0, NULL}, + }; diff --git a/src/script/common/c_types.h b/src/script/common/c_types.h new file mode 100644 index 00000000..7df86943 --- /dev/null +++ b/src/script/common/c_types.h @@ -0,0 +1,90 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef C_TYPES_H_ +#define C_TYPES_H_ + +extern "C" { +#include "lua.h" +} + +#include + +struct EnumString +{ + int num; + const char *str; +}; + +class StackUnroller +{ +private: + lua_State *m_lua; + int m_original_top; +public: + StackUnroller(lua_State *L): + m_lua(L), + m_original_top(-1) + { + m_original_top = lua_gettop(m_lua); // store stack height + } + ~StackUnroller() + { + lua_settop(m_lua, m_original_top); // restore stack height + } +}; + +class ModNameStorer +{ +private: + lua_State *L; +public: + ModNameStorer(lua_State *L_, const std::string modname): + L(L_) + { + // Store current modname in registry + lua_pushstring(L, modname.c_str()); + lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); + } + ~ModNameStorer() + { + // Clear current modname in registry + lua_pushnil(L); + lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); + } +}; + +class LuaError : public std::exception +{ +public: + LuaError(lua_State *L, const std::string &s); + + virtual ~LuaError() throw() + {} + virtual const char * what() const throw() + { + return m_s.c_str(); + } + std::string m_s; +}; + + +extern EnumString es_ItemType[]; + +#endif /* C_TYPES_H_ */ diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt new file mode 100644 index 00000000..6f5b51a4 --- /dev/null +++ b/src/script/cpp_api/CMakeLists.txt @@ -0,0 +1,11 @@ +set(SCRIPT_CPP_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/s_base.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_entity.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_env.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_inventory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_item.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_node.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_nodemeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/s_player.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/scriptapi.cpp + PARENT_SCOPE) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp new file mode 100644 index 00000000..e2e58635 --- /dev/null +++ b/src/script/cpp_api/s_base.cpp @@ -0,0 +1,264 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include +#include + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +} + +#include "cpp_api/s_base.h" +#include "lua_api/l_object.h" +#include "serverobject.h" + +ScriptApiBase::ScriptApiBase() : + m_luastackmutex(), +#ifdef LOCK_DEBUG + m_locked(false), +#endif + m_luastack(0), + m_server(0), + m_environment(0) +{ + +} + + +void ScriptApiBase::realityCheck() +{ + int top = lua_gettop(m_luastack); + if(top >= 30){ + dstream<<"Stack is over 30:"<= nargs + 1); + lua_pushnil(L); + lua_insert(L, -(nargs + 1) - 1); + // Stack now looks like this: + // ... ... + + int rv = lua_gettop(L) - nargs - 1; + int table = rv + 1; + int arg = table + 1; + + luaL_checktype(L, table, LUA_TTABLE); + + // Foreach + lua_pushnil(L); + bool first_loop = true; + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TFUNCTION); + // Call function + for(int i = 0; i < nargs; i++) + lua_pushvalue(L, arg+i); + if(lua_pcall(L, nargs, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + + // Move return value to designated space in stack + // Or pop it + if(first_loop){ + // Result of first callback is always moved + lua_replace(L, rv); + first_loop = false; + } else { + // Otherwise, what happens depends on the mode + if(mode == RUN_CALLBACKS_MODE_FIRST) + lua_pop(L, 1); + else if(mode == RUN_CALLBACKS_MODE_LAST) + lua_replace(L, rv); + else if(mode == RUN_CALLBACKS_MODE_AND || + mode == RUN_CALLBACKS_MODE_AND_SC){ + if((bool)lua_toboolean(L, rv) == true && + (bool)lua_toboolean(L, -1) == false) + lua_replace(L, rv); + else + lua_pop(L, 1); + } + else if(mode == RUN_CALLBACKS_MODE_OR || + mode == RUN_CALLBACKS_MODE_OR_SC){ + if((bool)lua_toboolean(L, rv) == false && + (bool)lua_toboolean(L, -1) == true) + lua_replace(L, rv); + else + lua_pop(L, 1); + } + else + assert(0); + } + + // Handle short circuit modes + if(mode == RUN_CALLBACKS_MODE_AND_SC && + (bool)lua_toboolean(L, rv) == false) + break; + else if(mode == RUN_CALLBACKS_MODE_OR_SC && + (bool)lua_toboolean(L, rv) == true) + break; + + // value removed, keep key for next iteration + } + + // Remove stuff from stack, leaving only the return value + lua_settop(L, rv); + + // Fix return value in case no callbacks were called + if(first_loop){ + if(mode == RUN_CALLBACKS_MODE_AND || + mode == RUN_CALLBACKS_MODE_AND_SC){ + lua_pop(L, 1); + lua_pushboolean(L, true); + } + else if(mode == RUN_CALLBACKS_MODE_OR || + mode == RUN_CALLBACKS_MODE_OR_SC){ + lua_pop(L, 1); + lua_pushboolean(L, false); + } + } +} + +void ScriptApiBase::addObjectReference(ServerActiveObject *cobj) +{ + SCRIPTAPI_PRECHECKHEADER + //infostream<<"scriptapi_add_object_reference: id="<getId()<getId()); // Push id + lua_pushvalue(L, object); // Copy object to top of stack + lua_settable(L, objectstable); +} + +void ScriptApiBase::removeObjectReference(ServerActiveObject *cobj) +{ + SCRIPTAPI_PRECHECKHEADER + //infostream<<"scriptapi_rm_object_reference: id="<getId()<getId()); // Push id + lua_gettable(L, objectstable); + // Set object reference to NULL + ObjectRef::set_null(L); + lua_pop(L, 1); // pop object + + // Set object_refs[id] = nil + lua_pushnumber(L, cobj->getId()); // Push id + lua_pushnil(L); + lua_settable(L, objectstable); +} + +// Creates a new anonymous reference if cobj=NULL or id=0 +void ScriptApiBase::objectrefGetOrCreate( + ServerActiveObject *cobj) +{ + lua_State *L = getStack(); + + if(cobj == NULL || cobj->getId() == 0){ + ObjectRef::create(L, cobj); + } else { + objectrefGet(cobj->getId()); + } +} + +void ScriptApiBase::objectrefGet(u16 id) +{ + lua_State *L = getStack(); + + // Get minetest.object_refs[i] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "object_refs"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnumber(L, id); + lua_gettable(L, -2); + lua_remove(L, -2); // object_refs + lua_remove(L, -2); // minetest +} diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h new file mode 100644 index 00000000..da4e17d8 --- /dev/null +++ b/src/script/cpp_api/s_base.h @@ -0,0 +1,167 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef S_BASE_H_ +#define S_BASE_H_ + +#include + +#include "irrlichttypes.h" +#include "jmutex.h" +#include "jmutexautolock.h" +#include "common/c_types.h" +#include "debug.h" + +#define LOCK_DEBUG + +class Server; +class Environment; +class ServerActiveObject; +class LuaABM; +class InvRef; +class ModApiBase; +class ModApiEnvMod; +class ObjectRef; +class NodeMetaRef; + + +/* definitions */ +// What scriptapi_run_callbacks does with the return values of callbacks. +// Regardless of the mode, if only one callback is defined, +// its return value is the total return value. +// Modes only affect the case where 0 or >= 2 callbacks are defined. +enum RunCallbacksMode +{ + // Returns the return value of the first callback + // Returns nil if list of callbacks is empty + RUN_CALLBACKS_MODE_FIRST, + // Returns the return value of the last callback + // Returns nil if list of callbacks is empty + RUN_CALLBACKS_MODE_LAST, + // If any callback returns a false value, the first such is returned + // Otherwise, the first callback's return value (trueish) is returned + // Returns true if list of callbacks is empty + RUN_CALLBACKS_MODE_AND, + // Like above, but stops calling callbacks (short circuit) + // after seeing the first false value + RUN_CALLBACKS_MODE_AND_SC, + // If any callback returns a true value, the first such is returned + // Otherwise, the first callback's return value (falseish) is returned + // Returns false if list of callbacks is empty + RUN_CALLBACKS_MODE_OR, + // Like above, but stops calling callbacks (short circuit) + // after seeing the first true value + RUN_CALLBACKS_MODE_OR_SC, + // Note: "a true value" and "a false value" refer to values that + // are converted by lua_toboolean to true or false, respectively. +}; + + +class ScriptApiBase { +public: + + /* object */ + void addObjectReference(ServerActiveObject *cobj); + void removeObjectReference(ServerActiveObject *cobj); + + ScriptApiBase(); + +protected: + friend class LuaABM; + friend class InvRef; + friend class ObjectRef; + friend class NodeMetaRef; + friend class ModApiBase; + friend class ModApiEnvMod; + + + inline lua_State* getStack() + { return m_luastack; } + + bool setStack(lua_State* stack) { + if (m_luastack == 0) { + m_luastack = stack; + return true; + } + return false; + } + + void realityCheck(); + void scriptError(const char *fmt, ...); + void stackDump(std::ostream &o); + void runCallbacks(int nargs,RunCallbacksMode mode); + + inline Server* getServer() { return m_server; } + void setServer(Server* server) { m_server = server; } + + Environment* getEnv() { return m_environment; } + void setEnv(Environment* env) { m_environment = env; } + + void objectrefGetOrCreate(ServerActiveObject *cobj); + void objectrefGet(u16 id); + + JMutex m_luastackmutex; +#ifdef LOCK_DEBUG + bool m_locked; +#endif +private: + lua_State* m_luastack; + + Server* m_server; + Environment* m_environment; + + +}; + +#ifdef LOCK_DEBUG +class LockChecker { +public: + LockChecker(bool* variable) { + assert(*variable == false); + + m_variable = variable; + *m_variable = true; + } + ~LockChecker() { + *m_variable = false; + } +private: +bool* m_variable; +}; + +#define LOCK_CHECK LockChecker(&(this->m_locked)) +#else +#define LOCK_CHECK while(0) +#endif + +#define LUA_STACK_AUTOLOCK JMutexAutoLock(this->m_luastackmutex) + +#define SCRIPTAPI_PRECHECKHEADER \ + LUA_STACK_AUTOLOCK; \ + LOCK_CHECK; \ + realityCheck(); \ + lua_State *L = getStack(); \ + assert(lua_checkstack(L, 20)); \ + StackUnroller stack_unroller(L); + +#define PLAYER_TO_SA(p) p->getEnv()->getScriptIface() +#define ENV_TO_SA(env) env->getScriptIface() +#define SERVER_TO_SA(srv) srv->getScriptIface() + +#endif /* S_BASE_H_ */ diff --git a/src/scriptapi_entity.cpp b/src/script/cpp_api/s_entity.cpp similarity index 72% rename from src/scriptapi_entity.cpp rename to src/script/cpp_api/s_entity.cpp index c15f801a..2a5a6066 100644 --- a/src/scriptapi_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -17,43 +17,22 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_entity.h" +#include "cpp_api/s_entity.h" +#include "log.h" +#include "object_properties.h" +#include "common/c_converter.h" +#include "common/c_content.h" extern "C" { -#include +#include "lauxlib.h" } -#include "log.h" -#include "script.h" -#include "scriptapi_types.h" -#include "scriptapi_object.h" -#include "scriptapi_common.h" - - -void luaentity_get(lua_State *L, u16 id) +bool ScriptApiEntity::luaentity_Add(u16 id, const char *name) { - // Get minetest.luaentities[i] - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "luaentities"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); - lua_gettable(L, -2); - lua_remove(L, -2); // luaentities - lua_remove(L, -2); // minetest -} + SCRIPTAPI_PRECHECKHEADER -/* - luaentity -*/ - -bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); verbosestream<<"scriptapi_luaentity_add: id="< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_ENTITY_H_ +#define S_ENTITY_H_ + +#include "cpp_api/s_base.h" +#include "irr_v3d.h" + +class ObjectProperties; +class ToolCapabilities; + +class ScriptApiEntity + : virtual public ScriptApiBase +{ +public: + bool luaentity_Add(u16 id, const char *name); + void luaentity_Activate(u16 id, + const std::string &staticdata, u32 dtime_s); + void luaentity_Remove(u16 id); + std::string luaentity_GetStaticdata(u16 id); + void luaentity_GetProperties(u16 id, + ObjectProperties *prop); + void luaentity_Step(u16 id, float dtime); + void luaentity_Punch(u16 id, + ServerActiveObject *puncher, float time_from_last_punch, + const ToolCapabilities *toolcap, v3f dir); + void luaentity_Rightclick(u16 id, + ServerActiveObject *clicker); +}; + + + +#endif /* S_ENTITY_H_ */ diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp new file mode 100644 index 00000000..334d196b --- /dev/null +++ b/src/script/cpp_api/s_env.cpp @@ -0,0 +1,132 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/s_env.h" +#include "common/c_converter.h" +#include "log.h" +#include "environment.h" +#include "lua_api/l_env.h" + +extern "C" { +#include "lauxlib.h" +} + +void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, + u32 blockseed) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_generateds + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_generateds"); + // Call callbacks + push_v3s16(L, minp); + push_v3s16(L, maxp); + lua_pushnumber(L, blockseed); + runCallbacks(3, RUN_CALLBACKS_MODE_FIRST); +} + +void ScriptApiEnv::environment_Step(float dtime) +{ + SCRIPTAPI_PRECHECKHEADER + //infostream<<"scriptapi_environment_step"< trigger_contents; + lua_getfield(L, current_abm, "nodenames"); + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + trigger_contents.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if(lua_isstring(L, -1)){ + trigger_contents.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + std::set required_neighbors; + lua_getfield(L, current_abm, "neighbors"); + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + required_neighbors.insert(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if(lua_isstring(L, -1)){ + required_neighbors.insert(lua_tostring(L, -1)); + } + lua_pop(L, 1); + + float trigger_interval = 10.0; + getfloatfield(L, current_abm, "interval", trigger_interval); + + int trigger_chance = 50; + getintfield(L, current_abm, "chance", trigger_chance); + + LuaABM *abm = new LuaABM(L, id, trigger_contents, + required_neighbors, trigger_interval, trigger_chance); + + env->addActiveBlockModifier(abm); + + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); +} diff --git a/src/script.h b/src/script/cpp_api/s_env.h similarity index 59% rename from src/script.h rename to src/script/cpp_api/s_env.h index dbacae5a..c5b739eb 100644 --- a/src/script.h +++ b/src/script/cpp_api/s_env.h @@ -17,33 +17,24 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef SCRIPT_HEADER -#define SCRIPT_HEADER +#ifndef S_ENV_H_ +#define S_ENV_H_ -#include -#include +#include "cpp_api/s_base.h" +#include "irr_v3d.h" -typedef struct lua_State lua_State; +class ServerEnvironment; -class LuaError : public std::exception +class ScriptApiEnv + : virtual public ScriptApiBase { public: - LuaError(lua_State *L, const std::string &s); + // On environment step + void environment_Step(float dtime); + // After generating a piece of map + void environment_OnGenerated(v3s16 minp, v3s16 maxp,u32 blockseed); - virtual ~LuaError() throw() - {} - virtual const char * what() const throw() - { - return m_s.c_str(); - } - std::string m_s; + void initializeEnvironment(ServerEnvironment *env); }; -lua_State* script_init(); -void script_deinit(lua_State *L); -std::string script_get_backtrace(lua_State *L); -void script_error(lua_State *L, const char *fmt, ...); -bool script_load(lua_State *L, const char *path); - -#endif - +#endif /* S_ENV_H_ */ diff --git a/src/script/cpp_api/s_inventory.cpp b/src/script/cpp_api/s_inventory.cpp new file mode 100644 index 00000000..2402d198 --- /dev/null +++ b/src/script/cpp_api/s_inventory.cpp @@ -0,0 +1,260 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/s_inventory.h" +#include "inventorymanager.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "log.h" + +// Return number of accepted items to be moved +int ScriptApiDetached::detached_inventory_AllowMove( + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getDetachedInventoryCallback(name, "allow_move")) + return count; + + // function(inv, from_list, from_index, to_list, to_index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 7, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_move should return a number"); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be put +int ScriptApiDetached::detached_inventory_AllowPut( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getDetachedInventoryCallback(name, "allow_put")) + return stack.count; // All will be accepted + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_put should return a number"); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be taken +int ScriptApiDetached::detached_inventory_AllowTake( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getDetachedInventoryCallback(name, "allow_take")) + return stack.count; // All will be accepted + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_take should return a number"); + return luaL_checkinteger(L, -1); +} + +// Report moved items +void ScriptApiDetached::detached_inventory_OnMove( + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getDetachedInventoryCallback(name, "on_move")) + return; + + // function(inv, from_list, from_index, to_list, to_index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 7, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +// Report put items +void ScriptApiDetached::detached_inventory_OnPut( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getDetachedInventoryCallback(name, "on_put")) + return; + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +// Report taken items +void ScriptApiDetached::detached_inventory_OnTake( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getDetachedInventoryCallback(name, "on_take")) + return; + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +// Retrieves minetest.detached_inventories[name][callbackname] +// If that is nil or on error, return false and stack is unchanged +// If that is a function, returns true and pushes the +// function onto the stack +bool ScriptApiDetached::getDetachedInventoryCallback( + const std::string &name, const char *callbackname) +{ + lua_State *L = getStack(); + + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "detached_inventories"); + lua_remove(L, -2); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, name.c_str()); + lua_remove(L, -2); + // Should be a table + if(lua_type(L, -1) != LUA_TTABLE) + { + errorstream<<"Item \""< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_INVENTORY_H_ +#define S_INVENTORY_H_ + +#include "cpp_api/s_base.h" + +class ItemStack; + +class ScriptApiDetached + : virtual public ScriptApiBase +{ +public: + /* Detached inventory callbacks */ + // Return number of accepted items to be moved + int detached_inventory_AllowMove( + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); + // Return number of accepted items to be put + int detached_inventory_AllowPut( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); + // Return number of accepted items to be taken + int detached_inventory_AllowTake( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); + // Report moved items + void detached_inventory_OnMove( + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); + // Report put items + void detached_inventory_OnPut( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); + // Report taken items + void detached_inventory_OnTake( + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); +private: + bool getDetachedInventoryCallback( + const std::string &name, const char *callbackname); +}; + + + +#endif /* S_INVENTORY_H_ */ diff --git a/src/script/cpp_api/s_item.cpp b/src/script/cpp_api/s_item.cpp new file mode 100644 index 00000000..6937ebbe --- /dev/null +++ b/src/script/cpp_api/s_item.cpp @@ -0,0 +1,164 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/s_item.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "lua_api/l_item.h" +#include "server.h" + +bool ScriptApiItem::item_OnDrop(ItemStack &item, + ServerActiveObject *dropper, v3f pos) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getItemCallback(item.name.c_str(), "on_drop")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectrefGetOrCreate(dropper); + pushFloatPos(L, pos); + if(lua_pcall(L, 3, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L,-1, getServer()); + return true; +} + +bool ScriptApiItem::item_OnPlace(ItemStack &item, + ServerActiveObject *placer, const PointedThing &pointed) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getItemCallback(item.name.c_str(), "on_place")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectrefGetOrCreate(placer); + pushPointedThing(pointed); + if(lua_pcall(L, 3, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L,-1, getServer()); + return true; +} + +bool ScriptApiItem::item_OnUse(ItemStack &item, + ServerActiveObject *user, const PointedThing &pointed) +{ + SCRIPTAPI_PRECHECKHEADER + + // Push callback function on stack + if(!getItemCallback(item.name.c_str(), "on_use")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectrefGetOrCreate(user); + pushPointedThing(pointed); + if(lua_pcall(L, 3, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L,-1, getServer()); + return true; +} + +// Retrieves minetest.registered_items[name][callbackname] +// If that is nil or on error, return false and stack is unchanged +// If that is a function, returns true and pushes the +// function onto the stack +// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default +// is tried instead so unknown items can still be manipulated to some degree +bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname) +{ + lua_State* L = getStack(); + + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_items"); + lua_remove(L, -2); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, name); + lua_remove(L, -2); + // Should be a table + if(lua_type(L, -1) != LUA_TTABLE) + { + // Report error and clean up + errorstream<<"Item \""< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_ITEM_H_ +#define S_ITEM_H_ + +#include "cpp_api/s_base.h" +#include "irr_v3d.h" + +class PointedThing; +class ItemStack; +class ServerActiveObject; +class ItemDefinition; +class LuaItemStack; +class ModApiItemMod; + +class ScriptApiItem +: virtual public ScriptApiBase +{ +public: + bool item_OnDrop(ItemStack &item, + ServerActiveObject *dropper, v3f pos); + bool item_OnPlace(ItemStack &item, + ServerActiveObject *placer, const PointedThing &pointed); + bool item_OnUse(ItemStack &item, + ServerActiveObject *user, const PointedThing &pointed); + +protected: + friend class LuaItemStack; + friend class ModApiItemMod; + + bool getItemCallback(const char *name, const char *callbackname); +private: + void pushPointedThing(const PointedThing& pointed); + +}; + + +#endif /* S_ITEM_H_ */ diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp new file mode 100644 index 00000000..969e2f4d --- /dev/null +++ b/src/script/cpp_api/s_node.cpp @@ -0,0 +1,233 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/s_node.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "nodedef.h" +#include "server.h" + + +struct EnumString ScriptApiNode::es_DrawType[] = + { + {NDT_NORMAL, "normal"}, + {NDT_AIRLIKE, "airlike"}, + {NDT_LIQUID, "liquid"}, + {NDT_FLOWINGLIQUID, "flowingliquid"}, + {NDT_GLASSLIKE, "glasslike"}, + {NDT_GLASSLIKE_FRAMED, "glasslike_framed"}, + {NDT_ALLFACES, "allfaces"}, + {NDT_ALLFACES_OPTIONAL, "allfaces_optional"}, + {NDT_TORCHLIKE, "torchlike"}, + {NDT_SIGNLIKE, "signlike"}, + {NDT_PLANTLIKE, "plantlike"}, + {NDT_FENCELIKE, "fencelike"}, + {NDT_RAILLIKE, "raillike"}, + {NDT_NODEBOX, "nodebox"}, + {0, NULL}, + }; + +struct EnumString ScriptApiNode::es_ContentParamType2[] = + { + {CPT2_NONE, "none"}, + {CPT2_FULL, "full"}, + {CPT2_FLOWINGLIQUID, "flowingliquid"}, + {CPT2_FACEDIR, "facedir"}, + {CPT2_WALLMOUNTED, "wallmounted"}, + {0, NULL}, + }; + +struct EnumString ScriptApiNode::es_LiquidType[] = + { + {LIQUID_NONE, "none"}, + {LIQUID_FLOWING, "flowing"}, + {LIQUID_SOURCE, "source"}, + {0, NULL}, + }; + +struct EnumString ScriptApiNode::es_ContentParamType[] = + { + {CPT_NONE, "none"}, + {CPT_LIGHT, "light"}, + {0, NULL}, + }; + +struct EnumString ScriptApiNode::es_NodeBoxType[] = + { + {NODEBOX_REGULAR, "regular"}, + {NODEBOX_FIXED, "fixed"}, + {NODEBOX_WALLMOUNTED, "wallmounted"}, + {0, NULL}, + }; + +ScriptApiNode::ScriptApiNode() { +} + +ScriptApiNode::~ScriptApiNode() { +} + +bool ScriptApiNode::node_on_punch(v3s16 p, MapNode node, + ServerActiveObject *puncher) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "on_punch")) + return false; + + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + objectrefGetOrCreate(puncher); + if(lua_pcall(L, 3, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + return true; +} + +bool ScriptApiNode::node_on_dig(v3s16 p, MapNode node, + ServerActiveObject *digger) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "on_dig")) + return false; + + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + objectrefGetOrCreate(digger); + if(lua_pcall(L, 3, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + return true; +} + +void ScriptApiNode::node_on_construct(v3s16 p, MapNode node) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "on_construct")) + return; + + // Call function + push_v3s16(L, p); + if(lua_pcall(L, 1, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +void ScriptApiNode::node_on_destruct(v3s16 p, MapNode node) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "on_destruct")) + return; + + // Call function + push_v3s16(L, p); + if(lua_pcall(L, 1, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +void ScriptApiNode::node_after_destruct(v3s16 p, MapNode node) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "after_destruct")) + return; + + // Call function + push_v3s16(L, p); + pushnode(L, node, ndef); + if(lua_pcall(L, 2, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +bool ScriptApiNode::node_on_timer(v3s16 p, MapNode node, f32 dtime) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "on_timer")) + return false; + + // Call function + push_v3s16(L, p); + lua_pushnumber(L,dtime); + if(lua_pcall(L, 2, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true) + return true; + + return false; +} + +void ScriptApiNode::node_on_receive_fields(v3s16 p, + const std::string &formname, + const std::map &fields, + ServerActiveObject *sender) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), "on_receive_fields")) + return; + + // Call function + // param 1 + push_v3s16(L, p); + // param 2 + lua_pushstring(L, formname.c_str()); + // param 3 + lua_newtable(L); + for(std::map::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + lua_pushstring(L, name.c_str()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + // param 4 + objectrefGetOrCreate(sender); + if(lua_pcall(L, 4, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h new file mode 100644 index 00000000..bff6072b --- /dev/null +++ b/src/script/cpp_api/s_node.h @@ -0,0 +1,62 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef S_NODE_H_ +#define S_NODE_H_ + +#include + +#include "irr_v3d.h" +#include "cpp_api/s_base.h" +#include "cpp_api/s_nodemeta.h" + +class MapNode; +class ServerActiveObject; + +class ScriptApiNode + : virtual public ScriptApiBase, + public ScriptApiNodemeta +{ +public: + ScriptApiNode(); + virtual ~ScriptApiNode(); + + bool node_on_punch(v3s16 p, MapNode node, + ServerActiveObject *puncher); + bool node_on_dig(v3s16 p, MapNode node, + ServerActiveObject *digger); + void node_on_construct(v3s16 p, MapNode node); + void node_on_destruct(v3s16 p, MapNode node); + void node_after_destruct(v3s16 p, MapNode node); + bool node_on_timer(v3s16 p, MapNode node, f32 dtime); + void node_on_receive_fields(v3s16 p, + const std::string &formname, + const std::map &fields, + ServerActiveObject *sender); +public: + static struct EnumString es_DrawType[]; + static struct EnumString es_ContentParamType[]; + static struct EnumString es_ContentParamType2[]; + static struct EnumString es_LiquidType[]; + static struct EnumString es_NodeBoxType[]; +}; + + + +#endif /* S_NODE_H_ */ diff --git a/src/script/cpp_api/s_nodemeta.cpp b/src/script/cpp_api/s_nodemeta.cpp new file mode 100644 index 00000000..56cea8e5 --- /dev/null +++ b/src/script/cpp_api/s_nodemeta.cpp @@ -0,0 +1,261 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/s_nodemeta.h" +#include "common/c_converter.h" +#include "nodedef.h" +#include "mapnode.h" +#include "server.h" +#include "lua_api/l_item.h" + +extern "C" { +#include "lauxlib.h" +} + +// Return number of accepted items to be moved +int ScriptApiNodemeta::nodemeta_inventory_AllowMove(v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), + "allow_metadata_inventory_move")) + return count; + + // function(pos, from_list, from_index, to_list, to_index, count, player) + // pos + push_v3s16(L, p); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 7, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_metadata_inventory_move should return a number"); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be put +int ScriptApiNodemeta::nodemeta_inventory_AllowPut(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), + "allow_metadata_inventory_put")) + return stack.count; + + // Call function(pos, listname, index, stack, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_metadata_inventory_put should return a number"); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be taken +int ScriptApiNodemeta::nodemeta_inventory_AllowTake(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), + "allow_metadata_inventory_take")) + return stack.count; + + // Call function(pos, listname, index, count, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + if(!lua_isnumber(L, -1)) + throw LuaError(L, "allow_metadata_inventory_take should return a number"); + return luaL_checkinteger(L, -1); +} + +// Report moved items +void ScriptApiNodemeta::nodemeta_inventory_OnMove(v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), + "on_metadata_inventory_move")) + return; + + // function(pos, from_list, from_index, to_list, to_index, count, player) + // pos + push_v3s16(L, p); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 7, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +// Report put items +void ScriptApiNodemeta::nodemeta_inventory_OnPut(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), + "on_metadata_inventory_put")) + return; + + // Call function(pos, listname, index, stack, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +// Report taken items +void ScriptApiNodemeta::nodemeta_inventory_OnTake(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + INodeDefManager *ndef = getServer()->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = getEnv()->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return; + + // Push callback function on stack + if(!getItemCallback(ndef->get(node).name.c_str(), + "on_metadata_inventory_take")) + return; + + // Call function(pos, listname, index, stack, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectrefGetOrCreate(player); + if(lua_pcall(L, 5, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +ScriptApiNodemeta::ScriptApiNodemeta() { +} + +ScriptApiNodemeta::~ScriptApiNodemeta() { +} + + + diff --git a/src/script/cpp_api/s_nodemeta.h b/src/script/cpp_api/s_nodemeta.h new file mode 100644 index 00000000..9be126c6 --- /dev/null +++ b/src/script/cpp_api/s_nodemeta.h @@ -0,0 +1,67 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef S_NODEMETA_H_ +#define S_NODEMETA_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_item.h" +#include "irr_v3d.h" + +class ItemStack; + +class ScriptApiNodemeta + : virtual public ScriptApiBase, + public ScriptApiItem +{ +public: + ScriptApiNodemeta(); + virtual ~ScriptApiNodemeta(); + + // Return number of accepted items to be moved + int nodemeta_inventory_AllowMove(v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); + // Return number of accepted items to be put + int nodemeta_inventory_AllowPut(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); + // Return number of accepted items to be taken + int nodemeta_inventory_AllowTake(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); + // Report moved items + void nodemeta_inventory_OnMove(v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); + // Report put items + void nodemeta_inventory_OnPut(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); + // Report taken items + void nodemeta_inventory_OnTake(v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); +private: + +}; + +#endif /* S_NODEMETA_H_ */ diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp new file mode 100644 index 00000000..e736d745 --- /dev/null +++ b/src/script/cpp_api/s_player.cpp @@ -0,0 +1,113 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/s_player.h" + +void ScriptApiPlayer::on_newplayer(ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_newplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_newplayers"); + // Call callbacks + objectrefGetOrCreate(player); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +void ScriptApiPlayer::on_dieplayer(ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_dieplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_dieplayers"); + // Call callbacks + objectrefGetOrCreate(player); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApiPlayer::on_respawnplayer(ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_respawnplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_respawnplayers"); + // Call callbacks + objectrefGetOrCreate(player); + runCallbacks(1, RUN_CALLBACKS_MODE_OR); + bool positioning_handled_by_some = lua_toboolean(L, -1); + return positioning_handled_by_some; +} + +void ScriptApiPlayer::on_joinplayer(ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_joinplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_joinplayers"); + // Call callbacks + objectrefGetOrCreate(player); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +void ScriptApiPlayer::on_leaveplayer(ServerActiveObject *player) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_leaveplayers + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_leaveplayers"); + // Call callbacks + objectrefGetOrCreate(player); + runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); +} + +void ScriptApiPlayer::on_playerReceiveFields(ServerActiveObject *player, + const std::string &formname, + const std::map &fields) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_chat_messages + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_player_receive_fields"); + // Call callbacks + // param 1 + objectrefGetOrCreate(player); + // param 2 + lua_pushstring(L, formname.c_str()); + // param 3 + lua_newtable(L); + for(std::map::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + lua_pushstring(L, name.c_str()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + runCallbacks(3, RUN_CALLBACKS_MODE_OR_SC); +} +ScriptApiPlayer::~ScriptApiPlayer() { +} + + diff --git a/src/script/cpp_api/s_player.h b/src/script/cpp_api/s_player.h new file mode 100644 index 00000000..663e3c2a --- /dev/null +++ b/src/script/cpp_api/s_player.h @@ -0,0 +1,44 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef S_PLAYER_H_ +#define S_PLAYER_H_ + +#include "cpp_api/s_base.h" + + +class ScriptApiPlayer + : virtual public ScriptApiBase +{ +public: + virtual ~ScriptApiPlayer(); + + void on_newplayer(ServerActiveObject *player); + void on_dieplayer(ServerActiveObject *player); + bool on_respawnplayer(ServerActiveObject *player); + void on_joinplayer(ServerActiveObject *player); + void on_leaveplayer(ServerActiveObject *player); + + void on_playerReceiveFields(ServerActiveObject *player, + const std::string &formname, + const std::map &fields); +}; + + +#endif /* S_PLAYER_H_ */ diff --git a/src/script/cpp_api/scriptapi.cpp b/src/script/cpp_api/scriptapi.cpp new file mode 100644 index 00000000..b6d376b1 --- /dev/null +++ b/src/script/cpp_api/scriptapi.cpp @@ -0,0 +1,291 @@ +/* +Minetest +Copyright (C) 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. +*/ + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" +} + + +#include "scriptapi.h" +#include "common/c_converter.h" +#include "lua_api/l_base.h" +#include "log.h" +#include "mods.h" + +int script_ErrorHandler(lua_State *L) { + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); + lua_pushinteger(L, 2); + lua_call(L, 2, 1); + return 1; +} + + +bool ScriptApi::getAuth(const std::string &playername, + std::string *dst_password, std::set *dst_privs) +{ + SCRIPTAPI_PRECHECKHEADER + + getAuthHandler(); + lua_getfield(L, -1, "get_auth"); + if(lua_type(L, -1) != LUA_TFUNCTION) + throw LuaError(L, "Authentication handler missing get_auth"); + lua_pushstring(L, playername.c_str()); + if(lua_pcall(L, 1, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + + // nil = login not allowed + if(lua_isnil(L, -1)) + return false; + luaL_checktype(L, -1, LUA_TTABLE); + + std::string password; + bool found = getstringfield(L, -1, "password", password); + if(!found) + throw LuaError(L, "Authentication handler didn't return password"); + if(dst_password) + *dst_password = password; + + lua_getfield(L, -1, "privileges"); + if(!lua_istable(L, -1)) + throw LuaError(L, + "Authentication handler didn't return privilege table"); + if(dst_privs) + readPrivileges(-1, *dst_privs); + lua_pop(L, 1); + + return true; +} + +void ScriptApi::getAuthHandler() +{ + lua_State *L = getStack(); + + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_auth_handler"); + if(lua_isnil(L, -1)){ + lua_pop(L, 1); + lua_getfield(L, -1, "builtin_auth_handler"); + } + if(lua_type(L, -1) != LUA_TTABLE) + throw LuaError(L, "Authentication handler table not valid"); +} + +void ScriptApi::readPrivileges(int index,std::set &result) +{ + lua_State *L = getStack(); + + result.clear(); + lua_pushnil(L); + if(index < 0) + index -= 1; + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + std::string key = luaL_checkstring(L, -2); + bool value = lua_toboolean(L, -1); + if(value) + result.insert(key); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } +} + +void ScriptApi::createAuth(const std::string &playername, + const std::string &password) +{ + SCRIPTAPI_PRECHECKHEADER + + getAuthHandler(); + lua_getfield(L, -1, "create_auth"); + if(lua_type(L, -1) != LUA_TFUNCTION) + throw LuaError(L, "Authentication handler missing create_auth"); + lua_pushstring(L, playername.c_str()); + lua_pushstring(L, password.c_str()); + if(lua_pcall(L, 2, 0, 0)) + scriptError("error: %s", lua_tostring(L, -1)); +} + +bool ScriptApi::setPassword(const std::string &playername, + const std::string &password) +{ + SCRIPTAPI_PRECHECKHEADER + + getAuthHandler(); + lua_getfield(L, -1, "set_password"); + if(lua_type(L, -1) != LUA_TFUNCTION) + throw LuaError(L, "Authentication handler missing set_password"); + lua_pushstring(L, playername.c_str()); + lua_pushstring(L, password.c_str()); + if(lua_pcall(L, 2, 1, 0)) + scriptError("error: %s", lua_tostring(L, -1)); + return lua_toboolean(L, -1); +} + +bool ScriptApi::on_chat_message(const std::string &name, + const std::string &message) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get minetest.registered_on_chat_messages + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_chat_messages"); + // Call callbacks + lua_pushstring(L, name.c_str()); + lua_pushstring(L, message.c_str()); + runCallbacks(2, RUN_CALLBACKS_MODE_OR_SC); + bool ate = lua_toboolean(L, -1); + return ate; +} + +void ScriptApi::on_shutdown() +{ + SCRIPTAPI_PRECHECKHEADER + + // Get registered shutdown hooks + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_on_shutdown"); + // Call callbacks + runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApi::loadMod(const std::string &scriptpath,const std::string &modname) +{ + ModNameStorer modnamestorer(getStack(), modname); + + if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){ + errorstream<<"Error loading mod \""<::iterator i = m_mod_api_modules->begin(); + i != m_mod_api_modules->end(); i++) { + //initializers are called within minetest global table! + lua_getglobal(L, "minetest"); + int top = lua_gettop(L); + bool ModInitializedSuccessfull = (*i)->Initialize(L,top); + assert(ModInitializedSuccessfull); + } + + infostream << "SCRIPTAPI: initialized " << m_mod_api_modules->size() + << " modules" << std::endl; + + // Get the main minetest table + lua_getglobal(L, "minetest"); + + // Add tables to minetest + lua_newtable(L); + lua_setfield(L, -2, "object_refs"); + + lua_newtable(L); + lua_setfield(L, -2, "luaentities"); +} + +ScriptApi::~ScriptApi() { + lua_close(getStack()); +} + +bool ScriptApi::scriptLoad(const char *path) +{ + lua_State* L = getStack(); + setStack(0); + + verbosestream<<"Loading and running script from "<(); + + assert(ScriptApi::m_mod_api_modules != 0); + + ScriptApi::m_mod_api_modules->push_back(ptr); + + return true; +} + +std::vector* ScriptApi::m_mod_api_modules = 0; diff --git a/src/script/cpp_api/scriptapi.h b/src/script/cpp_api/scriptapi.h new file mode 100644 index 00000000..bbd0bdda --- /dev/null +++ b/src/script/cpp_api/scriptapi.h @@ -0,0 +1,82 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef SCRIPTAPI_H_ +#define SCRIPTAPI_H_ + +#include +#include +#include + +#include "cpp_api/s_base.h" +#include "cpp_api/s_player.h" +#include "cpp_api/s_env.h" +#include "cpp_api/s_node.h" +#include "cpp_api/s_inventory.h" +#include "cpp_api/s_entity.h" + +class ModApiBase; + +/*****************************************************************************/ +/* Scriptapi <-> Core Interface */ +/*****************************************************************************/ + +class ScriptApi + : virtual public ScriptApiBase, + public ScriptApiPlayer, + public ScriptApiEnv, + public ScriptApiNode, + public ScriptApiDetached, + public ScriptApiEntity +{ +public: + ScriptApi(); + ScriptApi(Server* server); + ~ScriptApi(); + + // Returns true if script handled message + bool on_chat_message(const std::string &name, const std::string &message); + + /* server */ + void on_shutdown(); + + /* auth */ + bool getAuth(const std::string &playername, + std::string *dst_password, std::set *dst_privs); + void createAuth(const std::string &playername, + const std::string &password); + bool setPassword(const std::string &playername, + const std::string &password); + + /** register a lua api module to scriptapi */ + static bool registerModApiModule(ModApiBase* prototype); + /** load a mod **/ + bool loadMod(const std::string &scriptpath,const std::string &modname); + +private: + void getAuthHandler(); + void readPrivileges(int index,std::set &result); + + bool scriptLoad(const char *path); + + static std::vector* m_mod_api_modules; + +}; + +#endif /* SCRIPTAPI_H_ */ diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt new file mode 100644 index 00000000..7610ce8f --- /dev/null +++ b/src/script/lua_api/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SCRIPT_LUA_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/l_base.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/luaapi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_nodetimer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_noise.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_object.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_particles.cpp + PARENT_SCOPE) diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp new file mode 100644 index 00000000..b1766e6d --- /dev/null +++ b/src/script/lua_api/l_base.cpp @@ -0,0 +1,63 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/scriptapi.h" +#include "lua_api/l_base.h" +#include "common/c_internal.h" +#include "log.h" + +extern "C" { +#include "lua.h" +} + +ModApiBase::ModApiBase() { + ScriptApi::registerModApiModule(this); +} + +Server* ModApiBase::getServer(lua_State* L) { + return get_scriptapi(L)->getServer(); +} + +Environment* ModApiBase::getEnv(lua_State* L) { + return get_scriptapi(L)->getEnv(); +} + +bool ModApiBase::registerFunction( lua_State* L, + const char* name, + lua_CFunction fct, + int top + ) { + //TODO check presence first! + + lua_pushstring(L,name); + lua_pushcfunction(L,fct); + lua_settable(L, top); + + return true; +} + +struct EnumString es_BiomeTerrainType[] = +{ + {BIOME_TERRAIN_NORMAL, "normal"}, + {BIOME_TERRAIN_LIQUID, "liquid"}, + {BIOME_TERRAIN_NETHER, "nether"}, + {BIOME_TERRAIN_AETHER, "aether"}, + {BIOME_TERRAIN_FLAT, "flat"}, + {0, NULL}, +}; diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h new file mode 100644 index 00000000..f2e0d59a --- /dev/null +++ b/src/script/lua_api/l_base.h @@ -0,0 +1,63 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef L_BASE_H_ +#define L_BASE_H_ + +#include "biome.h" +#include "common/c_types.h" + +extern "C" { +#include "lua.h" +} + +extern struct EnumString es_BiomeTerrainType[]; + +class ScriptApi; +class Server; +class Environment; + +typedef class ModApiBase { + +public: + ModApiBase(); + + virtual bool Initialize(lua_State* L, int top) = 0; + virtual ~ModApiBase() {}; + +protected: + static Server* getServer( lua_State* L); + static Environment* getEnv( lua_State* L); + static bool registerFunction( lua_State* L, + const char* name, + lua_CFunction fct, + int top + ); +} ModApiBase; + +#if (defined(WIN32) || defined(_WIN32_WCE)) +#define NO_MAP_LOCK_REQUIRED +#else +#include "main.h" +#include "profiler.h" +#define NO_MAP_LOCK_REQUIRED ScopeProfiler nolocktime(g_profiler,"Scriptapi: unlockable time",SPT_ADD) +//#define NO_ENVLOCK_REQUIRED assert(getServer(L).m_env_mutex.IsLocked() == false) +#endif + +#endif /* L_BASE_H_ */ diff --git a/src/scriptapi_craft.cpp b/src/script/lua_api/l_craft.cpp similarity index 86% rename from src/scriptapi_craft.cpp rename to src/script/lua_api/l_craft.cpp index 45990823..a32fb9df 100644 --- a/src/scriptapi_craft.cpp +++ b/src/script/lua_api/l_craft.cpp @@ -17,20 +17,24 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_craft.h" + +#include "lua_api/l_craft.h" +#include "common/c_internal.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "server.h" +#include "lua_api/l_item.h" extern "C" { -#include +#include "lauxlib.h" } -#include "script.h" -#include "scriptapi_types.h" -#include "scriptapi_common.h" -#include "scriptapi_item.h" +ModApiCraft::ModApiCraft() + : ModApiBase() { +} -struct EnumString es_CraftMethod[] = +struct EnumString ModApiCraft::es_CraftMethod[] = { {CRAFT_METHOD_NORMAL, "normal"}, {CRAFT_METHOD_COOKING, "cooking"}, @@ -40,7 +44,7 @@ struct EnumString es_CraftMethod[] = // helper for register_craft -bool read_craft_recipe_shaped(lua_State *L, int index, +bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index, int &width, std::vector &recipe) { if(index < 0) @@ -81,7 +85,7 @@ bool read_craft_recipe_shaped(lua_State *L, int index, } // helper for register_craft -bool read_craft_recipe_shapeless(lua_State *L, int index, +bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index, std::vector &recipe) { if(index < 0) @@ -103,7 +107,7 @@ bool read_craft_recipe_shapeless(lua_State *L, int index, } // helper for register_craft -bool read_craft_replacements(lua_State *L, int index, +bool ModApiCraft::readCraftReplacements(lua_State *L, int index, CraftReplacements &replacements) { if(index < 0) @@ -135,15 +139,16 @@ bool read_craft_replacements(lua_State *L, int index, return true; } // register_craft({output=item, recipe={{item00,item10},{item01,item11}}) -int l_register_craft(lua_State *L) +int ModApiCraft::l_register_craft(lua_State *L) { + NO_MAP_LOCK_REQUIRED; //infostream<<"register_craft"<getWritableCraftDefManager(); + getServer(L)->getWritableCraftDefManager(); std::string type = getstringfield_default(L, table, "type", "shaped"); @@ -161,7 +166,7 @@ int l_register_craft(lua_State *L) if(lua_isnil(L, -1)) throw LuaError(L, "Crafting definition is missing a recipe" " (output=\"" + output + "\")"); - if(!read_craft_recipe_shaped(L, -1, width, recipe)) + if(!readCraftRecipeShaped(L, -1, width, recipe)) throw LuaError(L, "Invalid crafting recipe" " (output=\"" + output + "\")"); @@ -169,7 +174,7 @@ int l_register_craft(lua_State *L) lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { - if(!read_craft_replacements(L, -1, replacements)) + if(!readCraftReplacements(L, -1, replacements)) throw LuaError(L, "Invalid replacements" " (output=\"" + output + "\")"); } @@ -193,7 +198,7 @@ int l_register_craft(lua_State *L) throw LuaError(L, "Crafting definition (shapeless)" " is missing a recipe" " (output=\"" + output + "\")"); - if(!read_craft_recipe_shapeless(L, -1, recipe)) + if(!readCraftRecipeShapeless(L, -1, recipe)) throw LuaError(L, "Invalid crafting recipe" " (output=\"" + output + "\")"); @@ -201,7 +206,7 @@ int l_register_craft(lua_State *L) lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { - if(!read_craft_replacements(L, -1, replacements)) + if(!readCraftReplacements(L, -1, replacements)) throw LuaError(L, "Invalid replacements" " (output=\"" + output + "\")"); } @@ -242,7 +247,7 @@ int l_register_craft(lua_State *L) lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { - if(!read_craft_replacements(L, -1, replacements)) + if(!readCraftReplacements(L, -1, replacements)) throw LuaError(L, "Invalid replacements" " (cooking output=\"" + output + "\")"); } @@ -266,7 +271,7 @@ int l_register_craft(lua_State *L) lua_getfield(L, table, "replacements"); if(!lua_isnil(L, -1)) { - if(!read_craft_replacements(L, -1, replacements)) + if(!readCraftReplacements(L, -1, replacements)) throw LuaError(L, "Invalid replacements" " (fuel recipe=\"" + recipe + "\")"); } @@ -285,8 +290,10 @@ int l_register_craft(lua_State *L) } // get_craft_result(input) -int l_get_craft_result(lua_State *L) +int ModApiCraft::l_get_craft_result(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + int input_i = 1; std::string method_s = getstringfield_default(L, input_i, "method", "normal"); enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method", @@ -297,10 +304,10 @@ int l_get_craft_result(lua_State *L) width = luaL_checkinteger(L, -1); lua_pop(L, 1); lua_getfield(L, input_i, "items"); - std::vector items = read_items(L, -1); + std::vector items = read_items(L, -1,getServer(L)); lua_pop(L, 1); // items - IGameDef *gdef = get_server(L); + IGameDef *gdef = getServer(L); ICraftDefManager *cdef = gdef->cdef(); CraftInput input(method, width, items); CraftOutput output; @@ -328,13 +335,15 @@ int l_get_craft_result(lua_State *L) } // get_craft_recipe(result item) -int l_get_craft_recipe(lua_State *L) +int ModApiCraft::l_get_craft_recipe(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + int k = 1; int input_i = 1; std::string o_item = luaL_checkstring(L,input_i); - IGameDef *gdef = get_server(L); + IGameDef *gdef = getServer(L); ICraftDefManager *cdef = gdef->cdef(); CraftInput input; CraftOutput output(o_item,0); @@ -379,10 +388,12 @@ int l_get_craft_recipe(lua_State *L) } // get_all_craft_recipes(result item) -int l_get_all_craft_recipes(lua_State *L) +int ModApiCraft::l_get_all_craft_recipes(lua_State *L) { + NO_MAP_LOCK_REQUIRED; + std::string o_item = luaL_checkstring(L,1); - IGameDef *gdef = get_server(L); + IGameDef *gdef = getServer(L); ICraftDefManager *cdef = gdef->cdef(); CraftInput input; CraftOutput output(o_item,0); @@ -423,8 +434,8 @@ int l_get_all_craft_recipes(lua_State *L) { if (i->empty()) continue; - lua_pushinteger(L, k); - lua_pushstring(L, i->name.c_str()); + lua_pushinteger(L,k); + lua_pushstring(L,i->name.c_str()); lua_settable(L, -3); } lua_setfield(L, -2, "items"); @@ -451,3 +462,16 @@ int l_get_all_craft_recipes(lua_State *L) } return 1; } + +bool ModApiCraft::Initialize(lua_State* L, int top) { + bool retval = true; + + retval &= API_FCT(get_all_craft_recipes); + retval &= API_FCT(get_craft_recipe); + retval &= API_FCT(get_craft_result); + retval &= API_FCT(register_craft); + + return retval; +} + +ModApiCraft modapicraft_prototype; diff --git a/src/script/lua_api/l_craft.h b/src/script/lua_api/l_craft.h new file mode 100644 index 00000000..d8319199 --- /dev/null +++ b/src/script/lua_api/l_craft.h @@ -0,0 +1,52 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef L_CRAFT_H_ +#define L_CRAFT_H_ + +#include + +extern "C" { +#include +} + +#include "lua_api/l_base.h" +#include "craftdef.h" + +class ModApiCraft : public ModApiBase { +public: + ModApiCraft(); + bool Initialize(lua_State* L, int top); +private: + static int l_register_craft(lua_State *L); + static int l_get_craft_recipe(lua_State *L); + static int l_get_all_craft_recipes(lua_State *L); + static int l_get_craft_result(lua_State *L); + + static bool readCraftReplacements(lua_State *L, int index, + CraftReplacements &replacements); + static bool readCraftRecipeShapeless(lua_State *L, int index, + std::vector &recipe); + static bool readCraftRecipeShaped(lua_State *L, int index, + int &width, std::vector &recipe); + + static struct EnumString es_CraftMethod[]; +}; + +#endif /* L_CRAFT_H_ */ diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp new file mode 100644 index 00000000..58cf337e --- /dev/null +++ b/src/script/lua_api/l_env.cpp @@ -0,0 +1,687 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#include "cpp_api/scriptapi.h" +#include "lua_api/l_base.h" +#include "lua_api/l_env.h" +#include "environment.h" +#include "server.h" +#include "daynightratio.h" +#include "util/pointedthing.h" +#include "content_sao.h" + +#include "common/c_converter.h" +#include "common/c_content.h" +#include "common/c_internal.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_noise.h" +#include "treegen.h" +#include "pathfinder.h" + + +#define GET_ENV_PTR ServerEnvironment* env = \ + dynamic_cast(getEnv(L)); \ + if( env == NULL) return 0 + +void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider) +{ + ScriptApi* scriptIface = SERVER_TO_SA(env); + scriptIface->realityCheck(); + + lua_State* L = scriptIface->getStack(); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Get minetest.registered_abms + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_abms"); + luaL_checktype(L, -1, LUA_TTABLE); + int registered_abms = lua_gettop(L); + + // Get minetest.registered_abms[m_id] + lua_pushnumber(L, m_id); + lua_gettable(L, registered_abms); + if(lua_isnil(L, -1)) + assert(0); + + // Call action + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "action"); + luaL_checktype(L, -1, LUA_TFUNCTION); + push_v3s16(L, p); + pushnode(L, n, env->getGameDef()->ndef()); + lua_pushnumber(L, active_object_count); + lua_pushnumber(L, active_object_count_wider); + if(lua_pcall(L, 4, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +// Exported functions + +// minetest.set_node(pos, node) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_set_node(lua_State *L) +{ + GET_ENV_PTR; + + INodeDefManager *ndef = env->getGameDef()->ndef(); + // parameters + v3s16 pos = read_v3s16(L, 1); + MapNode n = readnode(L, 2, ndef); + // Do it + bool succeeded = env->setNode(pos, n); + lua_pushboolean(L, succeeded); + return 1; +} + +int ModApiEnvMod::l_add_node(lua_State *L) +{ + return l_set_node(L); +} + +// minetest.remove_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_remove_node(lua_State *L) +{ + GET_ENV_PTR; + + // parameters + v3s16 pos = read_v3s16(L, 1); + // Do it + bool succeeded = env->removeNode(pos); + lua_pushboolean(L, succeeded); + return 1; +} + +// minetest.get_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_get_node(lua_State *L) +{ + GET_ENV_PTR; + + // pos + v3s16 pos = read_v3s16(L, 1); + // Do it + MapNode n = env->getMap().getNodeNoEx(pos); + // Return node + pushnode(L, n, env->getGameDef()->ndef()); + return 1; +} + +// minetest.get_node_or_nil(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_get_node_or_nil(lua_State *L) +{ + GET_ENV_PTR; + + // pos + v3s16 pos = read_v3s16(L, 1); + // Do it + try{ + MapNode n = env->getMap().getNode(pos); + // Return node + pushnode(L, n, env->getGameDef()->ndef()); + return 1; + } catch(InvalidPositionException &e) + { + lua_pushnil(L); + return 1; + } +} + +// minetest.get_node_light(pos, timeofday) +// pos = {x=num, y=num, z=num} +// timeofday: nil = current time, 0 = night, 0.5 = day +int ModApiEnvMod::l_get_node_light(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + v3s16 pos = read_v3s16(L, 1); + u32 time_of_day = env->getTimeOfDay(); + if(lua_isnumber(L, 2)) + time_of_day = 24000.0 * lua_tonumber(L, 2); + time_of_day %= 24000; + u32 dnr = time_to_daynight_ratio(time_of_day, true); + try{ + MapNode n = env->getMap().getNode(pos); + INodeDefManager *ndef = env->getGameDef()->ndef(); + lua_pushinteger(L, n.getLightBlend(dnr, ndef)); + return 1; + } catch(InvalidPositionException &e) + { + lua_pushnil(L); + return 1; + } +} + +// minetest.place_node(pos, node) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_place_node(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + MapNode n = readnode(L, 2, env->getGameDef()->ndef()); + + // Don't attempt to load non-loaded area as of now + MapNode n_old = env->getMap().getNodeNoEx(pos); + if(n_old.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Create item to place + INodeDefManager *ndef = getServer(L)->ndef(); + IItemDefManager *idef = getServer(L)->idef(); + ItemStack item(ndef->get(n).name, 1, 0, "", idef); + // Make pointed position + PointedThing pointed; + pointed.type = POINTEDTHING_NODE; + pointed.node_abovesurface = pos; + pointed.node_undersurface = pos + v3s16(0,-1,0); + // Place it with a NULL placer (appears in Lua as a non-functional + // ObjectRef) + bool success = get_scriptapi(L)->item_OnPlace(item, NULL, pointed); + lua_pushboolean(L, success); + return 1; +} + +// minetest.dig_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_dig_node(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + + // Don't attempt to load non-loaded area as of now + MapNode n = env->getMap().getNodeNoEx(pos); + if(n.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Dig it out with a NULL digger (appears in Lua as a + // non-functional ObjectRef) + bool success = get_scriptapi(L)->node_on_dig(pos, n, NULL); + lua_pushboolean(L, success); + return 1; +} + +// minetest.punch_node(pos) +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_punch_node(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos = read_v3s16(L, 1); + + // Don't attempt to load non-loaded area as of now + MapNode n = env->getMap().getNodeNoEx(pos); + if(n.getContent() == CONTENT_IGNORE){ + lua_pushboolean(L, false); + return 1; + } + // Punch it with a NULL puncher (appears in Lua as a non-functional + // ObjectRef) + bool success = get_scriptapi(L)->node_on_punch(pos, n, NULL); + lua_pushboolean(L, success); + return 1; +} + +// minetest.get_meta(pos) +int ModApiEnvMod::l_get_meta(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + v3s16 p = read_v3s16(L, 1); + NodeMetaRef::create(L, p, env); + return 1; +} + +// minetest.get_node_timer(pos) +int ModApiEnvMod::l_get_node_timer(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + v3s16 p = read_v3s16(L, 1); + NodeTimerRef::create(L, p, env); + return 1; +} + +// minetest.add_entity(pos, entityname) -> ObjectRef or nil +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_add_entity(lua_State *L) +{ + GET_ENV_PTR; + + // pos + v3f pos = checkFloatPos(L, 1); + // content + const char *name = luaL_checkstring(L, 2); + // Do it + ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, ""); + int objectid = env->addActiveObject(obj); + // If failed to add, return nothing (reads as nil) + if(objectid == 0) + return 0; + // Return ObjectRef + get_scriptapi(L)->objectrefGetOrCreate(obj); + return 1; +} + +// minetest.add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil +// pos = {x=num, y=num, z=num} +int ModApiEnvMod::l_add_item(lua_State *L) +{ + GET_ENV_PTR; + + // pos + //v3f pos = checkFloatPos(L, 1); + // item + ItemStack item = read_item(L, 2,getServer(L)); + if(item.empty() || !item.isKnown(getServer(L)->idef())) + return 0; + // Use minetest.spawn_item to spawn a __builtin:item + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "spawn_item"); + if(lua_isnil(L, -1)) + return 0; + lua_pushvalue(L, 1); + lua_pushstring(L, item.getItemString().c_str()); + if(lua_pcall(L, 2, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return 1; + /*lua_pushvalue(L, 1); + lua_pushstring(L, "__builtin:item"); + lua_pushstring(L, item.getItemString().c_str()); + return l_add_entity(L);*/ + /*// Do it + ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString()); + int objectid = env->addActiveObject(obj); + // If failed to add, return nothing (reads as nil) + if(objectid == 0) + return 0; + // Return ObjectRef + objectrefGetOrCreate(L, obj); + return 1;*/ +} + +// minetest.get_player_by_name(name) +int ModApiEnvMod::l_get_player_by_name(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + const char *name = luaL_checkstring(L, 1); + Player *player = env->getPlayer(name); + if(player == NULL){ + lua_pushnil(L); + return 1; + } + PlayerSAO *sao = player->getPlayerSAO(); + if(sao == NULL){ + lua_pushnil(L); + return 1; + } + // Put player on stack + get_scriptapi(L)->objectrefGetOrCreate(sao); + return 1; +} + +// minetest.get_objects_inside_radius(pos, radius) +int ModApiEnvMod::l_get_objects_inside_radius(lua_State *L) +{ + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + + GET_ENV_PTR; + + // Do it + v3f pos = checkFloatPos(L, 1); + float radius = luaL_checknumber(L, 2) * BS; + std::set ids = env->getObjectsInsideRadius(pos, radius); + lua_newtable(L); + int table = lua_gettop(L); + for(std::set::const_iterator + i = ids.begin(); i != ids.end(); i++){ + ServerActiveObject *obj = env->getActiveObject(*i); + // Insert object reference into table + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + get_scriptapi(L)->objectrefGetOrCreate(obj); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + return 1; +} + +// minetest.set_timeofday(val) +// val = 0...1 +int ModApiEnvMod::l_set_timeofday(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + float timeofday_f = luaL_checknumber(L, 1); + assert(timeofday_f >= 0.0 && timeofday_f <= 1.0); + int timeofday_mh = (int)(timeofday_f * 24000.0); + // This should be set directly in the environment but currently + // such changes aren't immediately sent to the clients, so call + // the server instead. + //env->setTimeOfDay(timeofday_mh); + getServer(L)->setTimeOfDay(timeofday_mh); + return 0; +} + +// minetest.get_timeofday() -> 0...1 +int ModApiEnvMod::l_get_timeofday(lua_State *L) +{ + GET_ENV_PTR; + + // Do it + int timeofday_mh = env->getTimeOfDay(); + float timeofday_f = (float)timeofday_mh / 24000.0; + lua_pushnumber(L, timeofday_f); + return 1; +} + + +// minetest.find_node_near(pos, radius, nodenames) -> pos or nil +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_node_near(lua_State *L) +{ + GET_ENV_PTR; + + INodeDefManager *ndef = getServer(L)->ndef(); + v3s16 pos = read_v3s16(L, 1); + int radius = luaL_checkinteger(L, 2); + std::set filter; + if(lua_istable(L, 3)){ + int table = 3; + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + ndef->getIds(lua_tostring(L, -1), filter); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if(lua_isstring(L, 3)){ + ndef->getIds(lua_tostring(L, 3), filter); + } + + for(int d=1; d<=radius; d++){ + std::list list; + getFacePositions(list, d); + for(std::list::iterator i = list.begin(); + i != list.end(); ++i){ + v3s16 p = pos + (*i); + content_t c = env->getMap().getNodeNoEx(p).getContent(); + if(filter.count(c) != 0){ + push_v3s16(L, p); + return 1; + } + } + } + return 0; +} + +// minetest.find_nodes_in_area(minp, maxp, nodenames) -> list of positions +// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" +int ModApiEnvMod::l_find_nodes_in_area(lua_State *L) +{ + GET_ENV_PTR; + + INodeDefManager *ndef = getServer(L)->ndef(); + v3s16 minp = read_v3s16(L, 1); + v3s16 maxp = read_v3s16(L, 2); + std::set filter; + if(lua_istable(L, 3)){ + int table = 3; + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + ndef->getIds(lua_tostring(L, -1), filter); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if(lua_isstring(L, 3)){ + ndef->getIds(lua_tostring(L, 3), filter); + } + + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + + lua_newtable(L); + int table = lua_gettop(L); + for(s16 x=minp.X; x<=maxp.X; x++) + for(s16 y=minp.Y; y<=maxp.Y; y++) + for(s16 z=minp.Z; z<=maxp.Z; z++) + { + v3s16 p(x,y,z); + content_t c = env->getMap().getNodeNoEx(p).getContent(); + if(filter.count(c) != 0){ + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + push_v3s16(L, p); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + } + return 1; +} + +// minetest.get_perlin(seeddiff, octaves, persistence, scale) +// returns world-specific PerlinNoise +int ModApiEnvMod::l_get_perlin(lua_State *L) +{ + GET_ENV_PTR; + + int seeddiff = luaL_checkint(L, 1); + int octaves = luaL_checkint(L, 2); + float persistence = luaL_checknumber(L, 3); + float scale = luaL_checknumber(L, 4); + + LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale); + *(void **)(lua_newuserdata(L, sizeof(void *))) = n; + luaL_getmetatable(L, "PerlinNoise"); + lua_setmetatable(L, -2); + return 1; +} + +// minetest.get_perlin_map(noiseparams, size) +// returns world-specific PerlinNoiseMap +int ModApiEnvMod::l_get_perlin_map(lua_State *L) +{ + GET_ENV_PTR; + + NoiseParams *np = read_noiseparams(L, 1); + if (!np) + return 0; + v3s16 size = read_v3s16(L, 2); + + int seed = (int)(env->getServerMap().getSeed()); + LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(np, seed, size); + *(void **)(lua_newuserdata(L, sizeof(void *))) = n; + luaL_getmetatable(L, "PerlinNoiseMap"); + lua_setmetatable(L, -2); + return 1; +} + +// minetest.clear_objects() +// clear all objects in the environment +int ModApiEnvMod::l_clear_objects(lua_State *L) +{ + GET_ENV_PTR; + + env->clearAllObjects(); + return 0; +} + +// minetest.line_of_sight(pos1, pos2, stepsize) -> true/false +int ModApiEnvMod::l_line_of_sight(lua_State *L) { + float stepsize = 1.0; + + GET_ENV_PTR; + + // read position 1 from lua + v3f pos1 = checkFloatPos(L, 2); + // read position 2 from lua + v3f pos2 = checkFloatPos(L, 2); + //read step size from lua + if(lua_isnumber(L, 3)) + stepsize = lua_tonumber(L, 3); + + return (env->line_of_sight(pos1,pos2,stepsize)); +} + +// minetest.find_path(pos1, pos2, searchdistance, +// max_jump, max_drop, algorithm) -> table containing path +int ModApiEnvMod::l_find_path(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 pos1 = read_v3s16(L, 2); + v3s16 pos2 = read_v3s16(L, 3); + unsigned int searchdistance = luaL_checkint(L, 4); + unsigned int max_jump = luaL_checkint(L, 5); + unsigned int max_drop = luaL_checkint(L, 6); + algorithm algo = A_PLAIN_NP; + if(! lua_isnil(L, 7)) { + std::string algorithm = luaL_checkstring(L,7); + + if (algorithm == "A*") + algo = A_PLAIN; + + if (algorithm == "Dijkstra") + algo = DIJKSTRA; + } + + std::vector path = + get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo); + + if (path.size() > 0) + { + lua_newtable(L); + int top = lua_gettop(L); + unsigned int index = 1; + for (std::vector::iterator i = path.begin(); i != path.end();i++) + { + lua_pushnumber(L,index); + push_v3s16(L, *i); + lua_settable(L, top); + index++; + } + return 1; + } + + return 0; +} + +// minetest.spawn_tree(pos, treedef) +int ModApiEnvMod::l_spawn_tree(lua_State *L) +{ + GET_ENV_PTR; + + v3s16 p0 = read_v3s16(L, 1); + + treegen::TreeDef tree_def; + std::string trunk,leaves,fruit; + INodeDefManager *ndef = env->getGameDef()->ndef(); + + if(lua_istable(L, 2)) + { + getstringfield(L, 2, "axiom", tree_def.initial_axiom); + getstringfield(L, 2, "rules_a", tree_def.rules_a); + getstringfield(L, 2, "rules_b", tree_def.rules_b); + getstringfield(L, 2, "rules_c", tree_def.rules_c); + getstringfield(L, 2, "rules_d", tree_def.rules_d); + getstringfield(L, 2, "trunk", trunk); + tree_def.trunknode=ndef->getId(trunk); + getstringfield(L, 2, "leaves", leaves); + tree_def.leavesnode=ndef->getId(leaves); + tree_def.leaves2_chance=0; + getstringfield(L, 2, "leaves2", leaves); + if (leaves !="") + { + tree_def.leaves2node=ndef->getId(leaves); + getintfield(L, 2, "leaves2_chance", tree_def.leaves2_chance); + } + getintfield(L, 2, "angle", tree_def.angle); + getintfield(L, 2, "iterations", tree_def.iterations); + getintfield(L, 2, "random_level", tree_def.iterations_random_level); + getstringfield(L, 2, "trunk_type", tree_def.trunk_type); + getboolfield(L, 2, "thin_branches", tree_def.thin_branches); + tree_def.fruit_chance=0; + getstringfield(L, 2, "fruit", fruit); + if (fruit != "") + { + tree_def.fruitnode=ndef->getId(fruit); + getintfield(L, 2, "fruit_chance",tree_def.fruit_chance); + } + getintfield(L, 2, "seed", tree_def.seed); + } + else + return 0; + treegen::spawn_ltree (env, p0, ndef, tree_def); + return 1; +} + +bool ModApiEnvMod::Initialize(lua_State *L,int top) +{ + + bool retval = true; + + retval &= API_FCT(set_node); + retval &= API_FCT(add_node); + retval &= API_FCT(add_item); + retval &= API_FCT(remove_node); + retval &= API_FCT(get_node); + retval &= API_FCT(get_node_or_nil); + retval &= API_FCT(get_node_light); + retval &= API_FCT(place_node); + retval &= API_FCT(dig_node); + retval &= API_FCT(punch_node); + retval &= API_FCT(add_entity); + retval &= API_FCT(get_meta); + retval &= API_FCT(get_node_timer); + retval &= API_FCT(get_player_by_name); + retval &= API_FCT(get_objects_inside_radius); + retval &= API_FCT(set_timeofday); + retval &= API_FCT(get_timeofday); + retval &= API_FCT(find_node_near); + retval &= API_FCT(find_nodes_in_area); + retval &= API_FCT(get_perlin); + retval &= API_FCT(get_perlin_map); + retval &= API_FCT(clear_objects); + retval &= API_FCT(spawn_tree); + + return retval; +} + +ModApiEnvMod modapienv_prototype; diff --git a/src/scriptapi_env.h b/src/script/lua_api/l_env.h similarity index 51% rename from src/scriptapi_env.h rename to src/script/lua_api/l_env.h index 2b7ea957..61ecaecc 100644 --- a/src/scriptapi_env.h +++ b/src/script/lua_api/l_env.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_ENVIRONMENT_H_ -#define LUA_ENVIRONMENT_H_ +#ifndef L_ENV_H_ +#define L_ENV_H_ extern "C" { #include @@ -26,145 +26,147 @@ extern "C" { } #include "environment.h" +#include "lua_api/l_base.h" -/* - EnvRef -*/ - -class EnvRef +class ModApiEnvMod + :public ModApiBase { private: - ServerEnvironment *m_env; - - static const char className[]; - static const luaL_reg methods[]; - - static int gc_object(lua_State *L) ; - - static EnvRef *checkobject(lua_State *L, int narg); - - // Exported functions - - // EnvRef:set_node(pos, node) + // minetest.set_node(pos, node) // pos = {x=num, y=num, z=num} static int l_set_node(lua_State *L); static int l_add_node(lua_State *L); - // EnvRef:remove_node(pos) + // minetest.remove_node(pos) // pos = {x=num, y=num, z=num} static int l_remove_node(lua_State *L); - // EnvRef:get_node(pos) + // minetest.get_node(pos) // pos = {x=num, y=num, z=num} static int l_get_node(lua_State *L); - // EnvRef:get_node_or_nil(pos) + // minetest.get_node_or_nil(pos) // pos = {x=num, y=num, z=num} static int l_get_node_or_nil(lua_State *L); - // EnvRef:get_node_light(pos, timeofday) + // minetest.get_node_light(pos, timeofday) // pos = {x=num, y=num, z=num} // timeofday: nil = current time, 0 = night, 0.5 = day static int l_get_node_light(lua_State *L); - // EnvRef:place_node(pos, node) + // minetest.place_node(pos, node) // pos = {x=num, y=num, z=num} static int l_place_node(lua_State *L); - // EnvRef:dig_node(pos) + // minetest.dig_node(pos) // pos = {x=num, y=num, z=num} static int l_dig_node(lua_State *L); - // EnvRef:punch_node(pos) + // minetest.punch_node(pos) // pos = {x=num, y=num, z=num} static int l_punch_node(lua_State *L); - // EnvRef:get_meta(pos) + // minetest.get_meta(pos) static int l_get_meta(lua_State *L); - // EnvRef:get_node_timer(pos) + // minetest.get_node_timer(pos) static int l_get_node_timer(lua_State *L); - // EnvRef:add_entity(pos, entityname) -> ObjectRef or nil + // minetest.add_entity(pos, entityname) -> ObjectRef or nil // pos = {x=num, y=num, z=num} static int l_add_entity(lua_State *L); - // EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil + // minetest.add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil // pos = {x=num, y=num, z=num} static int l_add_item(lua_State *L); - // EnvRef:add_rat(pos) - // pos = {x=num, y=num, z=num} - static int l_add_rat(lua_State *L); - - // EnvRef:add_firefly(pos) - // pos = {x=num, y=num, z=num} - static int l_add_firefly(lua_State *L); - - // EnvRef:get_player_by_name(name) + // minetest.get_player_by_name(name) static int l_get_player_by_name(lua_State *L); - // EnvRef:get_objects_inside_radius(pos, radius) + // minetest.get_objects_inside_radius(pos, radius) static int l_get_objects_inside_radius(lua_State *L); - // EnvRef:set_timeofday(val) + // minetest.set_timeofday(val) // val = 0...1 static int l_set_timeofday(lua_State *L); - // EnvRef:get_timeofday() -> 0...1 + // minetest.get_timeofday() -> 0...1 static int l_get_timeofday(lua_State *L); - // EnvRef:find_node_near(pos, radius, nodenames) -> pos or nil + // minetest.find_node_near(pos, radius, nodenames) -> pos or nil // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_node_near(lua_State *L); - // EnvRef:find_nodes_in_area(minp, maxp, nodenames) -> list of positions + // minetest.find_nodes_in_area(minp, maxp, nodenames) -> list of positions // nodenames: eg. {"ignore", "group:tree"} or "default:dirt" static int l_find_nodes_in_area(lua_State *L); - // EnvRef:get_perlin(seeddiff, octaves, persistence, scale) - // returns world-specific PerlinNoise + // minetest.get_perlin(seeddiff, octaves, persistence, scale) + // returns world-specific PerlinNoise static int l_get_perlin(lua_State *L); - // EnvRef:get_perlin_map(noiseparams, size) - // returns world-specific PerlinNoiseMap + // minetest.get_perlin_map(noiseparams, size) + // returns world-specific PerlinNoiseMap static int l_get_perlin_map(lua_State *L); - // EnvRef:clear_objects() + // minetest.clear_objects() // clear all objects in the environment static int l_clear_objects(lua_State *L); + // minetest.spawn_tree(pos, treedef) static int l_spawn_tree(lua_State *L); - + // minetest.line_of_sight(pos1, pos2, stepsize) -> true/false static int l_line_of_sight(lua_State *L); - //find a path between two positions + // minetest.find_path(pos1, pos2, searchdistance, + // max_jump, max_drop, algorithm) -> table containing path static int l_find_path(lua_State *L); public: - EnvRef(ServerEnvironment *env); - - ~EnvRef(); - - // Creates an EnvRef and leaves it on top of stack - // Not callable from Lua; all references are created on the C side. - static void create(lua_State *L, ServerEnvironment *env); - - static void set_null(lua_State *L); - - static void Register(lua_State *L); + bool Initialize(lua_State *L, int top); }; -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -// On environment step -void scriptapi_environment_step(lua_State *L, float dtime); -// After generating a piece of map -void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp, - u32 blockseed); -void scriptapi_add_environment(lua_State *L, ServerEnvironment *env); +class LuaABM : public ActiveBlockModifier +{ +private: + int m_id; -#endif /* LUA_ENVIRONMENT_H_ */ + std::set m_trigger_contents; + std::set m_required_neighbors; + float m_trigger_interval; + u32 m_trigger_chance; +public: + LuaABM(lua_State *L, int id, + const std::set &trigger_contents, + const std::set &required_neighbors, + float trigger_interval, u32 trigger_chance): + m_id(id), + m_trigger_contents(trigger_contents), + m_required_neighbors(required_neighbors), + m_trigger_interval(trigger_interval), + m_trigger_chance(trigger_chance) + { + } + virtual std::set getTriggerContents() + { + return m_trigger_contents; + } + virtual std::set getRequiredNeighbors() + { + return m_required_neighbors; + } + virtual float getTriggerInterval() + { + return m_trigger_interval; + } + virtual u32 getTriggerChance() + { + return m_trigger_chance; + } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider); +}; + +#endif /* L_ENV_H_ */ diff --git a/src/scriptapi_inventory.cpp b/src/script/lua_api/l_inventory.cpp similarity index 52% rename from src/scriptapi_inventory.cpp rename to src/script/lua_api/l_inventory.cpp index bb40748d..884b33e6 100644 --- a/src/scriptapi_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -17,17 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_inventory.h" +#include "cpp_api/scriptapi.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "common/c_internal.h" #include "server.h" -#include "script.h" #include "log.h" -#include "scriptapi_types.h" -#include "scriptapi_common.h" -#include "scriptapi_inventory.h" -#include "scriptapi_item.h" -#include "scriptapi_object.h" - +#include "inventorymanager.h" /* InvRef @@ -42,12 +40,13 @@ InvRef* InvRef::checkobject(lua_State *L, int narg) Inventory* InvRef::getinv(lua_State *L, InvRef *ref) { - return get_server(L)->getInventory(ref->m_loc); + return STACK_TO_SERVER(L)->getInventory(ref->m_loc); } InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, const char *listname) { + NO_MAP_LOCK_REQUIRED; Inventory *inv = getinv(L, ref); if(!inv) return NULL; @@ -57,7 +56,7 @@ InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) { // Inform other things that the inventory has changed - get_server(L)->setInventoryModified(ref->m_loc); + STACK_TO_SERVER(L)->setInventoryModified(ref->m_loc); } // Exported functions @@ -72,6 +71,7 @@ int InvRef::gc_object(lua_State *L) { // is_empty(self, listname) -> true/false int InvRef::l_is_empty(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); InventoryList *list = getlist(L, ref, listname); @@ -86,6 +86,7 @@ int InvRef::l_is_empty(lua_State *L) // get_size(self, listname) int InvRef::l_get_size(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); InventoryList *list = getlist(L, ref, listname); @@ -100,6 +101,7 @@ int InvRef::l_get_size(lua_State *L) // get_width(self, listname) int InvRef::l_get_width(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); InventoryList *list = getlist(L, ref, listname); @@ -114,6 +116,7 @@ int InvRef::l_get_width(lua_State *L) // set_size(self, listname, size) int InvRef::l_set_size(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int newsize = luaL_checknumber(L, 3); @@ -136,6 +139,7 @@ int InvRef::l_set_size(lua_State *L) // set_width(self, listname, size) int InvRef::l_set_width(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int newwidth = luaL_checknumber(L, 3); @@ -153,6 +157,7 @@ int InvRef::l_set_width(lua_State *L) // get_stack(self, listname, i) -> itemstack int InvRef::l_get_stack(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int i = luaL_checknumber(L, 3) - 1; @@ -167,10 +172,11 @@ int InvRef::l_get_stack(lua_State *L) // set_stack(self, listname, i, stack) -> true/false int InvRef::l_set_stack(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int i = luaL_checknumber(L, 3) - 1; - ItemStack newitem = read_item(L, 4); + ItemStack newitem = read_item(L, 4,STACK_TO_SERVER(L)); InventoryList *list = getlist(L, ref, listname); if(list != NULL && i >= 0 && i < (int) list->getSize()){ list->changeItem(i, newitem); @@ -185,25 +191,27 @@ int InvRef::l_set_stack(lua_State *L) // get_list(self, listname) -> list or nil int InvRef::l_get_list(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); Inventory *inv = getinv(L, ref); - inventory_get_list_to_lua(inv, listname, L); + push_inventory_list(inv, listname, L); return 1; } // set_list(self, listname, list) int InvRef::l_set_list(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); Inventory *inv = getinv(L, ref); InventoryList *list = inv->getList(listname); if(list) - inventory_set_list_from_lua(inv, listname, L, 3, - list->getSize()); + read_inventory_list(inv, listname, L, 3, + STACK_TO_SERVER(L),list->getSize()); else - inventory_set_list_from_lua(inv, listname, L, 3); + read_inventory_list(inv, listname, L, 3,STACK_TO_SERVER(L)); reportInventoryChange(L, ref); return 0; } @@ -212,9 +220,10 @@ int InvRef::l_set_list(lua_State *L) // Returns the leftover stack int InvRef::l_add_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); + ItemStack item = read_item(L, 3,STACK_TO_SERVER(L)); InventoryList *list = getlist(L, ref, listname); if(list){ ItemStack leftover = list->addItem(item); @@ -231,9 +240,10 @@ int InvRef::l_add_item(lua_State *L) // Returns true if the item completely fits into the list int InvRef::l_room_for_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); + ItemStack item = read_item(L, 3,STACK_TO_SERVER(L)); InventoryList *list = getlist(L, ref, listname); if(list){ lua_pushboolean(L, list->roomForItem(item)); @@ -247,9 +257,10 @@ int InvRef::l_room_for_item(lua_State *L) // Returns true if the list contains the given count of the given item name int InvRef::l_contains_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); + ItemStack item = read_item(L, 3, STACK_TO_SERVER(L)); InventoryList *list = getlist(L, ref, listname); if(list){ lua_pushboolean(L, list->containsItem(item)); @@ -263,9 +274,10 @@ int InvRef::l_contains_item(lua_State *L) // Returns the items that were actually removed int InvRef::l_remove_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); + ItemStack item = read_item(L, 3,STACK_TO_SERVER(L)); InventoryList *list = getlist(L, ref, listname); if(list){ ItemStack removed = list->removeItem(item); @@ -281,6 +293,7 @@ int InvRef::l_remove_item(lua_State *L) // get_location() -> location (like minetest.get_inventory(location)) int InvRef::l_get_location(lua_State *L) { + NO_MAP_LOCK_REQUIRED; InvRef *ref = checkobject(L, 1); const InventoryLocation &loc = ref->m_loc; switch(loc.type){ @@ -329,6 +342,7 @@ InvRef::~InvRef() // Not callable from Lua; all references are created on the C side. void InvRef::create(lua_State *L, const InventoryLocation &loc) { + NO_MAP_LOCK_REQUIRED; InvRef *o = new InvRef(loc); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); @@ -336,6 +350,7 @@ void InvRef::create(lua_State *L, const InventoryLocation &loc) } void InvRef::createPlayer(lua_State *L, Player *player) { + NO_MAP_LOCK_REQUIRED; InventoryLocation loc; loc.setPlayer(player->getName()); create(L, loc); @@ -394,328 +409,52 @@ const luaL_reg InvRef::methods[] = { {0,0} }; -void inventory_get_list_to_lua(Inventory *inv, const char *name, - lua_State *L) -{ - InventoryList *invlist = inv->getList(name); - if(invlist == NULL){ - lua_pushnil(L); - return; - } - std::vector items; - for(u32 i=0; igetSize(); i++) - items.push_back(invlist->getItem(i)); - push_items(L, items); -} - -void inventory_set_list_from_lua(Inventory *inv, const char *name, - lua_State *L, int tableindex, int forcesize) -{ - if(tableindex < 0) - tableindex = lua_gettop(L) + 1 + tableindex; - // If nil, delete list - if(lua_isnil(L, tableindex)){ - inv->deleteList(name); - return; - } - // Otherwise set list - std::vector items = read_items(L, tableindex); - int listsize = (forcesize != -1) ? forcesize : items.size(); - InventoryList *invlist = inv->addList(name, listsize); - int index = 0; - for(std::vector::const_iterator - i = items.begin(); i != items.end(); i++){ - if(forcesize != -1 && index == forcesize) - break; - invlist->changeItem(index, *i); - index++; - } - while(forcesize != -1 && index < forcesize){ - invlist->deleteItem(index); - index++; - } -} - // get_inventory(location) -int l_get_inventory(lua_State *L) +int ModApiInventory::l_get_inventory(lua_State *L) { InventoryLocation loc; std::string type = checkstringfield(L, 1, "type"); - if(type == "player"){ - std::string name = checkstringfield(L, 1, "name"); - loc.setPlayer(name); - } else if(type == "node"){ - lua_getfield(L, 1, "pos"); - v3s16 pos = check_v3s16(L, -1); - loc.setNodeMeta(pos); - } else if(type == "detached"){ - std::string name = checkstringfield(L, 1, "name"); - loc.setDetached(name); + + if(type != "pos"){ + NO_MAP_LOCK_REQUIRED; + + if(type == "player"){ + std::string name = checkstringfield(L, 1, "name"); + loc.setPlayer(name); + } else if(type == "detached"){ + std::string name = checkstringfield(L, 1, "name"); + loc.setDetached(name); + } + + if(getServer(L)->getInventory(loc) != NULL) + InvRef::create(L, loc); + else + lua_pushnil(L); + + return 1; + } + else { + if(type == "node"){ + lua_getfield(L, 1, "pos"); + v3s16 pos = check_v3s16(L, -1); + loc.setNodeMeta(pos); + } + if(getServer(L)->getInventory(loc) != NULL) + InvRef::create(L, loc); + else + lua_pushnil(L); + return 1; } - if(get_server(L)->getInventory(loc) != NULL) - InvRef::create(L, loc); - else - lua_pushnil(L); - return 1; -} - -/* - Detached inventory callbacks -*/ - -// Retrieves minetest.detached_inventories[name][callbackname] -// If that is nil or on error, return false and stack is unchanged -// If that is a function, returns true and pushes the -// function onto the stack -static bool get_detached_inventory_callback(lua_State *L, - const std::string &name, const char *callbackname) -{ - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "detached_inventories"); - lua_remove(L, -2); - luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, name.c_str()); - lua_remove(L, -2); - // Should be a table - if(lua_type(L, -1) != LUA_TTABLE) - { - errorstream<<"Item \""<createDetachedInventory(name) != NULL){ + if(getServer(L)->createDetachedInventory(name) != NULL){ InventoryLocation loc; loc.setDetached(name); InvRef::create(L, loc); @@ -724,3 +463,21 @@ int l_create_detached_inventory_raw(lua_State *L) } return 1; } + +bool ModApiInventory::Initialize(lua_State *L, int top) { + bool retval = true; + + retval &= API_FCT(create_detached_inventory_raw); + retval &= API_FCT(get_inventory); + + InvRef::Register(L); + + return retval; +} + +ModApiInventory::ModApiInventory() + : ModApiBase() { + +} + +ModApiInventory modapiinventory_prototype; diff --git a/src/scriptapi_inventory.h b/src/script/lua_api/l_inventory.h similarity index 60% rename from src/scriptapi_inventory.h rename to src/script/lua_api/l_inventory.h index 14f4fe02..83e8039b 100644 --- a/src/scriptapi_inventory.h +++ b/src/script/lua_api/l_inventory.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_INVENTORY_H_ -#define LUA_INVENTORY_H_ +#ifndef L_INVENTORY_H_ +#define L_INVENTORY_H_ extern "C" { #include @@ -30,6 +30,7 @@ extern "C" { #include "serverobject.h" #include "inventory.h" +#include "lua_api/l_base.h" /* InvRef */ @@ -115,51 +116,22 @@ public: static void Register(lua_State *L); }; -void inventory_get_list_to_lua(Inventory *inv, const char *name,lua_State *L); -void inventory_set_list_from_lua(Inventory *inv, const char *name, - lua_State *L, int tableindex, int forcesize=-1); +class ModApiInventory + : public ModApiBase +{ +public: + ModApiInventory(); -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -/* Detached inventory callbacks */ -// Return number of accepted items to be moved -int scriptapi_detached_inventory_allow_move(lua_State *L, - const std::string &name, - const std::string &from_list, int from_index, - const std::string &to_list, int to_index, - int count, ServerActiveObject *player); -// Return number of accepted items to be put -int scriptapi_detached_inventory_allow_put(lua_State *L, - const std::string &name, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); -// Return number of accepted items to be taken -int scriptapi_detached_inventory_allow_take(lua_State *L, - const std::string &name, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); -// Report moved items -void scriptapi_detached_inventory_on_move(lua_State *L, - const std::string &name, - const std::string &from_list, int from_index, - const std::string &to_list, int to_index, - int count, ServerActiveObject *player); -// Report put items -void scriptapi_detached_inventory_on_put(lua_State *L, - const std::string &name, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); -// Report taken items -void scriptapi_detached_inventory_on_take(lua_State *L, - const std::string &name, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); + bool Initialize(lua_State *L, int top); -/*****************************************************************************/ -/* Mod API */ -/*****************************************************************************/ -int l_create_detached_inventory_raw(lua_State *L); -int l_get_inventory(lua_State *L); + static int l_create_detached_inventory_raw(lua_State *L); + static int l_get_inventory(lua_State *L); +private: + static void inventory_set_list_from_lua(Inventory *inv, const char *name, + lua_State *L, int tableindex, int forcesize); + static void inventory_get_list_to_lua(Inventory *inv, const char *name, + lua_State *L); -#endif /* LUA_INVENTORY_H_ */ +}; + +#endif /* L_INVENTORY_H_ */ diff --git a/src/scriptapi_item.cpp b/src/script/lua_api/l_item.cpp similarity index 55% rename from src/scriptapi_item.cpp rename to src/script/lua_api/l_item.cpp index b266d856..e9997d48 100644 --- a/src/scriptapi_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -17,284 +17,12 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" +#include "lua_api/l_item.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "cpp_api/scriptapi.h" #include "server.h" -#include "script.h" -#include "tool.h" -#include "nodedef.h" -#include "util/pointedthing.h" -#include "scriptapi_item.h" -#include "scriptapi_types.h" -#include "scriptapi_common.h" -#include "scriptapi_object.h" -#include "scriptapi_content.h" - - -struct EnumString es_ItemType[] = -{ - {ITEM_NONE, "none"}, - {ITEM_NODE, "node"}, - {ITEM_CRAFT, "craft"}, - {ITEM_TOOL, "tool"}, - {0, NULL}, -}; - - -/* - ItemDefinition -*/ - -ItemDefinition read_item_definition(lua_State *L, int index, - ItemDefinition default_def) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - - // Read the item definition - ItemDefinition def = default_def; - - def.type = (ItemType)getenumfield(L, index, "type", - es_ItemType, ITEM_NONE); - getstringfield(L, index, "name", def.name); - getstringfield(L, index, "description", def.description); - getstringfield(L, index, "inventory_image", def.inventory_image); - getstringfield(L, index, "wield_image", def.wield_image); - - lua_getfield(L, index, "wield_scale"); - if(lua_istable(L, -1)){ - def.wield_scale = check_v3f(L, -1); - } - lua_pop(L, 1); - - def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max); - if(def.stack_max == 0) - def.stack_max = 1; - - lua_getfield(L, index, "on_use"); - def.usable = lua_isfunction(L, -1); - lua_pop(L, 1); - - getboolfield(L, index, "liquids_pointable", def.liquids_pointable); - - warn_if_field_exists(L, index, "tool_digging_properties", - "deprecated: use tool_capabilities"); - - lua_getfield(L, index, "tool_capabilities"); - if(lua_istable(L, -1)){ - def.tool_capabilities = new ToolCapabilities( - read_tool_capabilities(L, -1)); - } - - // If name is "" (hand), ensure there are ToolCapabilities - // because it will be looked up there whenever any other item has - // no ToolCapabilities - if(def.name == "" && def.tool_capabilities == NULL){ - def.tool_capabilities = new ToolCapabilities(); - } - - lua_getfield(L, index, "groups"); - read_groups(L, -1, def.groups); - lua_pop(L, 1); - - lua_getfield(L, index, "sounds"); - if(lua_istable(L, -1)){ - lua_getfield(L, -1, "place"); - read_soundspec(L, -1, def.sound_place); - lua_pop(L, 1); - } - lua_pop(L, 1); - - // Client shall immediately place this node when player places the item. - // Server will update the precise end result a moment later. - // "" = no prediction - getstringfield(L, index, "node_placement_prediction", - def.node_placement_prediction); - - return def; -} - -// register_item_raw({lots of stuff}) -int l_register_item_raw(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TTABLE); - int table = 1; - - // Get the writable item and node definition managers from the server - IWritableItemDefManager *idef = - get_server(L)->getWritableItemDefManager(); - IWritableNodeDefManager *ndef = - get_server(L)->getWritableNodeDefManager(); - - // Check if name is defined - std::string name; - lua_getfield(L, table, "name"); - if(lua_isstring(L, -1)){ - name = lua_tostring(L, -1); - verbosestream<<"register_item_raw: "<registerItem(def); - - // Read the node definition (content features) and register it - if(def.type == ITEM_NODE) - { - ContentFeatures f = read_content_features(L, table); - ndef->set(f.name, f); - } - - return 0; /* number of results */ -} - -// register_alias_raw(name, convert_to_name) -int l_register_alias_raw(lua_State *L) -{ - std::string name = luaL_checkstring(L, 1); - std::string convert_to = luaL_checkstring(L, 2); - - // Get the writable item definition manager from the server - IWritableItemDefManager *idef = - get_server(L)->getWritableItemDefManager(); - - idef->registerAlias(name, convert_to); - - return 0; /* number of results */ -} - -// Retrieves minetest.registered_items[name][callbackname] -// If that is nil or on error, return false and stack is unchanged -// If that is a function, returns true and pushes the -// function onto the stack -// If minetest.registered_items[name] doesn't exist, minetest.nodedef_default -// is tried instead so unknown items can still be manipulated to some degree -bool get_item_callback(lua_State *L, - const char *name, const char *callbackname) -{ - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_items"); - lua_remove(L, -2); - luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, name); - lua_remove(L, -2); - // Should be a table - if(lua_type(L, -1) != LUA_TTABLE) - { - // Report error and clean up - errorstream<<"Item \""< true/false int LuaItemStack::l_is_empty(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; lua_pushboolean(L, item.empty()); @@ -316,6 +45,7 @@ int LuaItemStack::l_is_empty(lua_State *L) // get_name(self) -> string int LuaItemStack::l_get_name(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; lua_pushstring(L, item.name.c_str()); @@ -325,6 +55,7 @@ int LuaItemStack::l_get_name(lua_State *L) // get_count(self) -> number int LuaItemStack::l_get_count(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; lua_pushinteger(L, item.count); @@ -334,6 +65,7 @@ int LuaItemStack::l_get_count(lua_State *L) // get_wear(self) -> number int LuaItemStack::l_get_wear(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; lua_pushinteger(L, item.wear); @@ -343,6 +75,7 @@ int LuaItemStack::l_get_wear(lua_State *L) // get_metadata(self) -> string int LuaItemStack::l_get_metadata(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); @@ -352,6 +85,7 @@ int LuaItemStack::l_get_metadata(lua_State *L) // clear(self) -> true int LuaItemStack::l_clear(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); o->m_stack.clear(); lua_pushboolean(L, true); @@ -361,8 +95,9 @@ int LuaItemStack::l_clear(lua_State *L) // replace(self, itemstack or itemstring or table or nil) -> true int LuaItemStack::l_replace(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); - o->m_stack = read_item(L, 2); + o->m_stack = read_item(L,2,STACK_TO_SERVER(L)); lua_pushboolean(L, true); return 1; } @@ -370,6 +105,7 @@ int LuaItemStack::l_replace(lua_State *L) // to_string(self) -> string int LuaItemStack::l_to_string(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); std::string itemstring = o->m_stack.getItemString(); lua_pushstring(L, itemstring.c_str()); @@ -379,6 +115,7 @@ int LuaItemStack::l_to_string(lua_State *L) // to_table(self) -> table or nil int LuaItemStack::l_to_table(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); const ItemStack &item = o->m_stack; if(item.empty()) @@ -403,18 +140,20 @@ int LuaItemStack::l_to_table(lua_State *L) // get_stack_max(self) -> number int LuaItemStack::l_get_stack_max(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - lua_pushinteger(L, item.getStackMax(get_server(L)->idef())); + lua_pushinteger(L, item.getStackMax(STACK_TO_SERVER(L)->idef())); return 1; } // get_free_space(self) -> number int LuaItemStack::l_get_free_space(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - lua_pushinteger(L, item.freeSpace(get_server(L)->idef())); + lua_pushinteger(L, item.freeSpace(STACK_TO_SERVER(L)->idef())); return 1; } @@ -422,9 +161,10 @@ int LuaItemStack::l_get_free_space(lua_State *L) // Checks if the item is defined. int LuaItemStack::l_is_known(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - bool is_known = item.isKnown(get_server(L)->idef()); + bool is_known = item.isKnown(STACK_TO_SERVER(L)->idef()); lua_pushboolean(L, is_known); return 1; } @@ -434,6 +174,7 @@ int LuaItemStack::l_is_known(lua_State *L) // or a fallback one (name="unknown") int LuaItemStack::l_get_definition(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; @@ -455,10 +196,11 @@ int LuaItemStack::l_get_definition(lua_State *L) // Returns those of the hand ("") if this item has none associated. int LuaItemStack::l_get_tool_capabilities(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; const ToolCapabilities &prop = - item.getToolCapabilities(get_server(L)->idef()); + item.getToolCapabilities(STACK_TO_SERVER(L)->idef()); push_tool_capabilities(L, prop); return 1; } @@ -469,10 +211,11 @@ int LuaItemStack::l_get_tool_capabilities(lua_State *L) // Returns true if the item is (or was) a tool. int LuaItemStack::l_add_wear(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; int amount = lua_tointeger(L, 2); - bool result = item.addWear(amount, get_server(L)->idef()); + bool result = item.addWear(amount, STACK_TO_SERVER(L)->idef()); lua_pushboolean(L, result); return 1; } @@ -481,10 +224,11 @@ int LuaItemStack::l_add_wear(lua_State *L) // Returns leftover item stack int LuaItemStack::l_add_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - ItemStack newitem = read_item(L, 2); - ItemStack leftover = item.addItem(newitem, get_server(L)->idef()); + ItemStack newitem = read_item(L,-2, STACK_TO_SERVER(L)); + ItemStack leftover = item.addItem(newitem, STACK_TO_SERVER(L)->idef()); create(L, leftover); return 1; } @@ -494,11 +238,12 @@ int LuaItemStack::l_add_item(lua_State *L) // Second return value is the would-be-left-over item stack int LuaItemStack::l_item_fits(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - ItemStack newitem = read_item(L, 2); + ItemStack newitem = read_item(L, 2 ,STACK_TO_SERVER(L)); ItemStack restitem; - bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef()); + bool fits = item.itemFits(newitem, &restitem, STACK_TO_SERVER(L)->idef()); lua_pushboolean(L, fits); // first return value create(L, restitem); // second return value return 2; @@ -507,6 +252,7 @@ int LuaItemStack::l_item_fits(lua_State *L) // take_item(self, takecount=1) -> itemstack int LuaItemStack::l_take_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; u32 takecount = 1; @@ -520,6 +266,7 @@ int LuaItemStack::l_take_item(lua_State *L) // peek_item(self, peekcount=1) -> itemstack int LuaItemStack::l_peek_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; u32 peekcount = 1; @@ -552,7 +299,8 @@ ItemStack& LuaItemStack::getItem() // Creates an LuaItemStack and leaves it on top of stack int LuaItemStack::create_object(lua_State *L) { - ItemStack item = read_item(L, 1); + NO_MAP_LOCK_REQUIRED; + ItemStack item = read_item(L,1,STACK_TO_SERVER(L)); LuaItemStack *o = new LuaItemStack(item); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); @@ -562,6 +310,7 @@ int LuaItemStack::create_object(lua_State *L) // Not callable from Lua int LuaItemStack::create(lua_State *L, const ItemStack &item) { + NO_MAP_LOCK_REQUIRED; LuaItemStack *o = new LuaItemStack(item); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); @@ -629,90 +378,95 @@ const luaL_reg LuaItemStack::methods[] = { {0,0} }; -ItemStack read_item(lua_State *L, int index) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - - if(lua_isnil(L, index)) - { - return ItemStack(); - } - else if(lua_isuserdata(L, index)) - { - // Convert from LuaItemStack - LuaItemStack *o = LuaItemStack::checkobject(L, index); - return o->getItem(); - } - else if(lua_isstring(L, index)) - { - // Convert from itemstring - std::string itemstring = lua_tostring(L, index); - IItemDefManager *idef = get_server(L)->idef(); - try - { - ItemStack item; - item.deSerialize(itemstring, idef); - return item; - } - catch(SerializationError &e) - { - infostream<<"WARNING: unable to create item from itemstring" - <<": "<idef(); - std::string name = getstringfield_default(L, index, "name", ""); - int count = getintfield_default(L, index, "count", 1); - int wear = getintfield_default(L, index, "wear", 0); - std::string metadata = getstringfield_default(L, index, "metadata", ""); - return ItemStack(name, count, wear, metadata, idef); - } - else - { - throw LuaError(L, "Expecting itemstack, itemstring, table or nil"); - } +ModApiItemMod::ModApiItemMod() { } -std::vector read_items(lua_State *L, int index) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; +/* + ItemDefinition +*/ - std::vector items; - luaL_checktype(L, index, LUA_TTABLE); - lua_pushnil(L); - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - items.push_back(read_item(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); +// register_item_raw({lots of stuff}) +int ModApiItemMod::l_register_item_raw(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + + ScriptApi* scriptIface = get_scriptapi(L); + + // Get the writable item and node definition managers from the server + IWritableItemDefManager *idef = + scriptIface->getServer()->getWritableItemDefManager(); + IWritableNodeDefManager *ndef = + scriptIface->getServer()->getWritableNodeDefManager(); + + // Check if name is defined + std::string name; + lua_getfield(L, table, "name"); + if(lua_isstring(L, -1)){ + name = lua_tostring(L, -1); + verbosestream<<"register_item_raw: "<registerItem(def); + + // Read the node definition (content features) and register it + if(def.type == ITEM_NODE) + { + ContentFeatures f = read_content_features(L, table); + ndef->set(f.name, f); + } + + return 0; /* number of results */ } -// creates a table of ItemStacks -void push_items(lua_State *L, const std::vector &items) +// register_alias_raw(name, convert_to_name) +int ModApiItemMod::l_register_alias_raw(lua_State *L) { - // Get the table insert function - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - // Create and fill table - lua_newtable(L); - int table = lua_gettop(L); - for(u32 i=0; igetWritableItemDefManager(); + + idef->registerAlias(name, convert_to); + + return 0; /* number of results */ } + +bool ModApiItemMod::Initialize(lua_State *L,int top) { + + bool retval = true; + + retval &= API_FCT(register_item_raw); + retval &= API_FCT(register_alias_raw); + + LuaItemStack::Register(L); + + return retval; +} + +ModApiItemMod modapi_item_prototyp; diff --git a/src/scriptapi_item.h b/src/script/lua_api/l_item.h similarity index 69% rename from src/scriptapi_item.h rename to src/script/lua_api/l_item.h index e0f21399..cbed4ae0 100644 --- a/src/scriptapi_item.h +++ b/src/script/lua_api/l_item.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_ITEM_H_ -#define LUA_ITEM_H_ +#ifndef L_ITEM_H_ +#define L_ITEM_H_ extern "C" { #include @@ -32,11 +32,9 @@ extern "C" { #include "util/pointedthing.h" #include "inventory.h" -#include "inventory.h" +#include "lua_api/l_base.h" -ItemStack read_item(lua_State *L, int index); -std::vector read_items(lua_State *L, int index); -void push_items(lua_State *L, const std::vector &items); +class ModApiInventory; class LuaItemStack { @@ -136,31 +134,18 @@ public: }; -/*****************************************************************************/ -/* Mod API */ -/*****************************************************************************/ -int l_register_item_raw(lua_State *L); -int l_register_alias_raw(lua_State *L); +class ModApiItemMod + :virtual public ModApiBase +{ +public: + ModApiItemMod(); -/*****************************************************************************/ -/* scriptapi internal */ -/*****************************************************************************/ -bool get_item_callback(lua_State *L, - const char *name, const char *callbackname); -ItemDefinition read_item_definition(lua_State *L, int index, - ItemDefinition default_def = ItemDefinition()); + bool Initialize(lua_State *L,int top); -extern struct EnumString es_ItemType[]; - -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -bool scriptapi_item_on_drop(lua_State *L, ItemStack &item, - ServerActiveObject *dropper, v3f pos); -bool scriptapi_item_on_place(lua_State *L, ItemStack &item, - ServerActiveObject *placer, const PointedThing &pointed); -bool scriptapi_item_on_use(lua_State *L, ItemStack &item, - ServerActiveObject *user, const PointedThing &pointed); + static int l_register_item_raw(lua_State *L); + static int l_register_alias_raw(lua_State *L); +}; -#endif /* LUA_ITEM_H_ */ + +#endif /* L_ITEM_H_ */ diff --git a/src/scriptapi_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp similarity index 54% rename from src/scriptapi_nodemeta.cpp rename to src/script/lua_api/l_nodemeta.cpp index b66c9a02..511fb38c 100644 --- a/src/scriptapi_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -17,15 +17,13 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_nodemeta.h" +#include "cpp_api/scriptapi.h" +#include "common/c_converter.h" +#include "common/c_content.h" #include "map.h" -#include "script.h" -#include "scriptapi_types.h" -#include "scriptapi_inventory.h" -#include "scriptapi_common.h" -#include "scriptapi_item.h" -#include "scriptapi_object.h" +#include "lua_api/l_nodemeta.h" +#include "common/c_internal.h" +#include "lua_api/l_inventory.h" /* @@ -213,7 +211,7 @@ int NodeMetaRef::l_to_table(lua_State *L) std::vector lists = inv->getLists(); for(std::vector::const_iterator i = lists.begin(); i != lists.end(); i++){ - inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L); + push_inventory_list(inv, (*i)->getName().c_str(), L); lua_setfield(L, -2, (*i)->getName().c_str()); } } @@ -259,7 +257,7 @@ int NodeMetaRef::l_from_table(lua_State *L) while(lua_next(L, inventorytable) != 0){ // key at index -2 and value at index -1 std::string name = lua_tostring(L, -2); - inventory_set_list_from_lua(inv, name.c_str(), L, -1); + read_inventory_list(inv, name.c_str(), L, -1,STACK_TO_SERVER(L)); lua_pop(L, 1); // removes value, keeps key for next iteration } reportMetadataChange(ref); @@ -331,241 +329,4 @@ const luaL_reg NodeMetaRef::methods[] = { {0,0} }; -/* - Node metadata inventory callbacks -*/ - -// Return number of accepted items to be moved -int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p, - const std::string &from_list, int from_index, - const std::string &to_list, int to_index, - int count, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return 0; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), - "allow_metadata_inventory_move")) - return count; - - // function(pos, from_list, from_index, to_list, to_index, count, player) - // pos - push_v3s16(L, p); - // from_list - lua_pushstring(L, from_list.c_str()); - // from_index - lua_pushinteger(L, from_index + 1); - // to_list - lua_pushstring(L, to_list.c_str()); - // to_index - lua_pushinteger(L, to_index + 1); - // count - lua_pushinteger(L, count); - // player - objectref_get_or_create(L, player); - if(lua_pcall(L, 7, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if(!lua_isnumber(L, -1)) - throw LuaError(L, "allow_metadata_inventory_move should return a number"); - return luaL_checkinteger(L, -1); -} - -// Return number of accepted items to be put -int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return 0; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), - "allow_metadata_inventory_put")) - return stack.count; - - // Call function(pos, listname, index, stack, player) - // pos - push_v3s16(L, p); - // listname - lua_pushstring(L, listname.c_str()); - // index - lua_pushinteger(L, index + 1); - // stack - LuaItemStack::create(L, stack); - // player - objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if(!lua_isnumber(L, -1)) - throw LuaError(L, "allow_metadata_inventory_put should return a number"); - return luaL_checkinteger(L, -1); -} - -// Return number of accepted items to be taken -int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return 0; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), - "allow_metadata_inventory_take")) - return stack.count; - - // Call function(pos, listname, index, count, player) - // pos - push_v3s16(L, p); - // listname - lua_pushstring(L, listname.c_str()); - // index - lua_pushinteger(L, index + 1); - // stack - LuaItemStack::create(L, stack); - // player - objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if(!lua_isnumber(L, -1)) - throw LuaError(L, "allow_metadata_inventory_take should return a number"); - return luaL_checkinteger(L, -1); -} - -// Report moved items -void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p, - const std::string &from_list, int from_index, - const std::string &to_list, int to_index, - int count, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), - "on_metadata_inventory_move")) - return; - - // function(pos, from_list, from_index, to_list, to_index, count, player) - // pos - push_v3s16(L, p); - // from_list - lua_pushstring(L, from_list.c_str()); - // from_index - lua_pushinteger(L, from_index + 1); - // to_list - lua_pushstring(L, to_list.c_str()); - // to_index - lua_pushinteger(L, to_index + 1); - // count - lua_pushinteger(L, count); - // player - objectref_get_or_create(L, player); - if(lua_pcall(L, 7, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -// Report put items -void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), - "on_metadata_inventory_put")) - return; - - // Call function(pos, listname, index, stack, player) - // pos - push_v3s16(L, p); - // listname - lua_pushstring(L, listname.c_str()); - // index - lua_pushinteger(L, index + 1); - // stack - LuaItemStack::create(L, stack); - // player - objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -// Report taken items -void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), - "on_metadata_inventory_take")) - return; - - // Call function(pos, listname, index, stack, player) - // pos - push_v3s16(L, p); - // listname - lua_pushstring(L, listname.c_str()); - // index - lua_pushinteger(L, index + 1); - // stack - LuaItemStack::create(L, stack); - // player - objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} +REGISTER_LUA_REF(NodeMetaRef); diff --git a/src/scriptapi_nodemeta.h b/src/script/lua_api/l_nodemeta.h similarity index 58% rename from src/scriptapi_nodemeta.h rename to src/script/lua_api/l_nodemeta.h index 017abe18..23404a08 100644 --- a/src/scriptapi_nodemeta.h +++ b/src/script/lua_api/l_nodemeta.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -16,8 +16,8 @@ You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_NODEMETA_H_ -#define LUA_NODEMETA_H_ +#ifndef L_NODEMETA_H_ +#define L_NODEMETA_H_ extern "C" { #include @@ -90,34 +90,4 @@ public: static void Register(lua_State *L); }; -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -// Return number of accepted items to be moved -int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p, - const std::string &from_list, int from_index, - const std::string &to_list, int to_index, - int count, ServerActiveObject *player); -// Return number of accepted items to be put -int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); -// Return number of accepted items to be taken -int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); -// Report moved items -void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p, - const std::string &from_list, int from_index, - const std::string &to_list, int to_index, - int count, ServerActiveObject *player); -// Report put items -void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); -// Report taken items -void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player); - -#endif //LUA_NODEMETA_H_ +#endif //L_NODEMETA_H_ diff --git a/src/scriptapi_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp similarity index 97% rename from src/scriptapi_nodetimer.cpp rename to src/script/lua_api/l_nodetimer.cpp index 5e3289ae..60e4ec06 100644 --- a/src/scriptapi_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -17,8 +17,9 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_nodetimer.h" +#include "cpp_api/scriptapi.h" +#include "lua_api/l_nodetimer.h" +#include "common/c_internal.h" #include "map.h" @@ -164,3 +165,5 @@ const luaL_reg NodeTimerRef::methods[] = { luamethod(NodeTimerRef, get_elapsed), {0,0} }; + +REGISTER_LUA_REF(NodeTimerRef); diff --git a/src/scriptapi_nodetimer.h b/src/script/lua_api/l_nodetimer.h similarity index 94% rename from src/scriptapi_nodetimer.h rename to src/script/lua_api/l_nodetimer.h index a4536d94..f652b490 100644 --- a/src/scriptapi_nodetimer.h +++ b/src/script/lua_api/l_nodetimer.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,8 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_NODETIMER_H_ -#define LUA_NODETIMER_H_ +#ifndef L_NODETIMER_H_ +#define L_NODETIMER_H_ extern "C" { #include @@ -67,4 +67,4 @@ public: -#endif /* LUA_NODETIMER_H_ */ +#endif /* L_NODETIMER_H_ */ diff --git a/src/scriptapi_noise.cpp b/src/script/lua_api/l_noise.cpp similarity index 92% rename from src/scriptapi_noise.cpp rename to src/script/lua_api/l_noise.cpp index 2c1a83c4..e0abeae9 100644 --- a/src/scriptapi_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -17,10 +17,10 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_noise.h" -#include "scriptapi_types.h" -#include "script.h" +#include "lua_api/l_noise.h" +#include "common/c_internal.h" +#include "common/c_converter.h" +#include "log.h" // garbage collector int LuaPerlinNoise::gc_object(lua_State *L) @@ -32,6 +32,7 @@ int LuaPerlinNoise::gc_object(lua_State *L) int LuaPerlinNoise::l_get2d(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaPerlinNoise *o = checkobject(L, 1); v2f pos2d = read_v2f(L,2); lua_Number val = noise2d_perlin(pos2d.X/o->scale, pos2d.Y/o->scale, o->seed, o->octaves, o->persistence); @@ -40,6 +41,7 @@ int LuaPerlinNoise::l_get2d(lua_State *L) } int LuaPerlinNoise::l_get3d(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaPerlinNoise *o = checkobject(L, 1); v3f pos3d = read_v3f(L,2); lua_Number val = noise3d_perlin(pos3d.X/o->scale, pos3d.Y/o->scale, pos3d.Z/o->scale, o->seed, o->octaves, o->persistence); @@ -65,6 +67,7 @@ LuaPerlinNoise::~LuaPerlinNoise() // Creates an LuaPerlinNoise and leaves it on top of stack int LuaPerlinNoise::create_object(lua_State *L) { + NO_MAP_LOCK_REQUIRED; int seed = luaL_checkint(L, 1); int octaves = luaL_checkint(L, 2); float persistence = luaL_checknumber(L, 3); @@ -78,6 +81,7 @@ int LuaPerlinNoise::create_object(lua_State *L) LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg) { + NO_MAP_LOCK_REQUIRED; luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); if(!ud) luaL_typerror(L, narg, className); @@ -133,6 +137,7 @@ int LuaPerlinNoiseMap::gc_object(lua_State *L) int LuaPerlinNoiseMap::l_get2dMap(lua_State *L) { + NO_MAP_LOCK_REQUIRED; int i = 0; LuaPerlinNoiseMap *o = checkobject(L, 1); @@ -156,6 +161,7 @@ int LuaPerlinNoiseMap::l_get2dMap(lua_State *L) int LuaPerlinNoiseMap::l_get3dMap(lua_State *L) { + NO_MAP_LOCK_REQUIRED; int i = 0; LuaPerlinNoiseMap *o = checkobject(L, 1); @@ -252,32 +258,6 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = { {0,0} }; -/* - NoiseParams -*/ -NoiseParams *read_noiseparams(lua_State *L, int index) -{ - if (index < 0) - index = lua_gettop(L) + 1 + index; - - if (!lua_istable(L, index)) - return NULL; - - NoiseParams *np = new NoiseParams; - - np->offset = getfloatfield_default(L, index, "offset", 0.0); - np->scale = getfloatfield_default(L, index, "scale", 0.0); - lua_getfield(L, index, "spread"); - np->spread = read_v3f(L, -1); - lua_pop(L, 1); - np->seed = getintfield_default(L, index, "seed", 0); - np->octaves = getintfield_default(L, index, "octaves", 0); - np->persist = getfloatfield_default(L, index, "persist", 0.0); - - return np; -} - - /* LuaPseudoRandom */ @@ -293,6 +273,7 @@ int LuaPseudoRandom::gc_object(lua_State *L) // next(self, min=0, max=32767) -> get next value int LuaPseudoRandom::l_next(lua_State *L) { + NO_MAP_LOCK_REQUIRED; LuaPseudoRandom *o = checkobject(L, 1); int min = 0; int max = 32767; @@ -386,3 +367,7 @@ const luaL_reg LuaPseudoRandom::methods[] = { luamethod(LuaPseudoRandom, next), {0,0} }; + +REGISTER_LUA_REF(LuaPseudoRandom); +REGISTER_LUA_REF(LuaPerlinNoiseMap); +REGISTER_LUA_REF(LuaPerlinNoise); diff --git a/src/scriptapi_noise.h b/src/script/lua_api/l_noise.h similarity index 96% rename from src/scriptapi_noise.h rename to src/script/lua_api/l_noise.h index a02383fd..d0b51d75 100644 --- a/src/scriptapi_noise.h +++ b/src/script/lua_api/l_noise.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,14 +17,15 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_PERLIN_H_ -#define LUA_PERLIN_H_ +#ifndef L_NOISE_H_ +#define L_NOISE_H_ extern "C" { #include #include } +#include "irr_v3d.h" #include "noise.h" class LuaPerlinNoise @@ -130,4 +131,4 @@ public: NoiseParams *read_noiseparams(lua_State *L, int index); -#endif /* LUA_PERLIN_H_ */ +#endif /* L_NOISE_H_ */ diff --git a/src/scriptapi_object.cpp b/src/script/lua_api/l_object.cpp similarity index 82% rename from src/scriptapi_object.cpp rename to src/script/lua_api/l_object.cpp index 851f1761..6dec3db8 100644 --- a/src/scriptapi_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -17,15 +17,19 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_object.h" +#include "cpp_api/scriptapi.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "lua_api/l_object.h" +#include "common/c_internal.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" #include "log.h" #include "tool.h" -#include "scriptapi_types.h" -#include "scriptapi_inventory.h" -#include "scriptapi_item.h" -#include "scriptapi_entity.h" -#include "scriptapi_common.h" +#include "serverobject.h" +#include "content_object.h" +#include "content_sao.h" +#include "server.h" #include "hud.h" @@ -35,7 +39,7 @@ struct EnumString es_HudElementType[] = {HUD_ELEM_TEXT, "text"}, {HUD_ELEM_STATBAR, "statbar"}, {HUD_ELEM_INVENTORY, "inventory"}, - {0, NULL}, +{0, NULL}, }; struct EnumString es_HudElementStat[] = @@ -61,7 +65,6 @@ struct EnumString es_HudBuiltinElement[] = {0, NULL}, }; - /* ObjectRef */ @@ -122,6 +125,7 @@ int ObjectRef::gc_object(lua_State *L) { // remove(self) int ObjectRef::l_remove(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -134,6 +138,7 @@ int ObjectRef::l_remove(lua_State *L) // returns: {x=num, y=num, z=num} int ObjectRef::l_getpos(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -151,6 +156,7 @@ int ObjectRef::l_getpos(lua_State *L) // setpos(self, pos) int ObjectRef::l_setpos(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); @@ -165,6 +171,7 @@ int ObjectRef::l_setpos(lua_State *L) // moveto(self, pos, continuous=false) int ObjectRef::l_moveto(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); //LuaEntitySAO *co = getluaobject(ref); ServerActiveObject *co = getobject(ref); @@ -181,31 +188,23 @@ int ObjectRef::l_moveto(lua_State *L) // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) int ObjectRef::l_punch(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); + ObjectRef *puncher_ref = checkobject(L, 2); ServerActiveObject *co = getobject(ref); + ServerActiveObject *puncher = getobject(puncher_ref); if(co == NULL) return 0; - - ServerActiveObject *puncher = 0; - v3f dir(0,0,0); - - if (!lua_isnil(L,2)) { - ObjectRef *puncher_ref = checkobject(L, 2); - puncher = getobject(puncher_ref); - if(puncher == NULL) return 0; - + if(puncher == NULL) return 0; + v3f dir; + if(lua_type(L, 5) != LUA_TTABLE) dir = co->getBasePosition() - puncher->getBasePosition(); - } - + else + dir = read_v3f(L, 5); float time_from_last_punch = 1000000; if(lua_isnumber(L, 3)) time_from_last_punch = lua_tonumber(L, 3); - ToolCapabilities toolcap = read_tool_capabilities(L, 4); - - if(lua_type(L, 5) == LUA_TTABLE) - dir = read_v3f(L, 5); dir.normalize(); - // Do it co->punch(dir, &toolcap, puncher, time_from_last_punch); return 0; @@ -214,6 +213,7 @@ int ObjectRef::l_punch(lua_State *L) // right_click(self, clicker); clicker = an another ObjectRef int ObjectRef::l_right_click(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ObjectRef *ref2 = checkobject(L, 2); ServerActiveObject *co = getobject(ref); @@ -230,6 +230,7 @@ int ObjectRef::l_right_click(lua_State *L) // returns: nil int ObjectRef::l_set_hp(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); luaL_checknumber(L, 2); ServerActiveObject *co = getobject(ref); @@ -248,6 +249,7 @@ int ObjectRef::l_set_hp(lua_State *L) // 0 if not applicable to this type of object int ObjectRef::l_get_hp(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL){ @@ -266,12 +268,13 @@ int ObjectRef::l_get_hp(lua_State *L) // get_inventory(self) int ObjectRef::l_get_inventory(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; // Do it InventoryLocation loc = co->getInventoryLocation(); - if(get_server(L)->getInventory(loc) != NULL) + if(STACK_TO_SERVER(L)->getInventory(loc) != NULL) InvRef::create(L, loc); else lua_pushnil(L); // An object may have no inventory (nil) @@ -281,6 +284,7 @@ int ObjectRef::l_get_inventory(lua_State *L) // get_wield_list(self) int ObjectRef::l_get_wield_list(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -292,6 +296,7 @@ int ObjectRef::l_get_wield_list(lua_State *L) // get_wield_index(self) int ObjectRef::l_get_wield_index(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -303,6 +308,7 @@ int ObjectRef::l_get_wield_index(lua_State *L) // get_wielded_item(self) int ObjectRef::l_get_wielded_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL){ @@ -318,11 +324,12 @@ int ObjectRef::l_get_wielded_item(lua_State *L) // set_wielded_item(self, itemstack or itemstring or table or nil) int ObjectRef::l_set_wielded_item(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; // Do it - ItemStack item = read_item(L, 2); + ItemStack item = read_item(L, 2,STACK_TO_SERVER(L)); bool success = co->setWieldedItem(item); lua_pushboolean(L, success); return 1; @@ -331,6 +338,7 @@ int ObjectRef::l_set_wielded_item(lua_State *L) // set_armor_groups(self, groups) int ObjectRef::l_set_armor_groups(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -366,6 +374,7 @@ int ObjectRef::l_set_physics_override(lua_State *L) // set_animation(self, frame_range, frame_speed, frame_blend) int ObjectRef::l_set_animation(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -386,6 +395,7 @@ int ObjectRef::l_set_animation(lua_State *L) // set_bone_position(self, std::string bone, v3f position, v3f rotation) int ObjectRef::l_set_bone_position(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -406,6 +416,7 @@ int ObjectRef::l_set_bone_position(lua_State *L) // set_attach(self, parent, bone, position, rotation) int ObjectRef::l_set_attach(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ObjectRef *parent_ref = checkobject(L, 2); ServerActiveObject *co = getobject(ref); @@ -429,6 +440,7 @@ int ObjectRef::l_set_attach(lua_State *L) // set_detach(self) int ObjectRef::l_set_detach(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -440,6 +452,7 @@ int ObjectRef::l_set_detach(lua_State *L) // set_properties(self, properties) int ObjectRef::l_set_properties(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; @@ -456,6 +469,7 @@ int ObjectRef::l_set_properties(lua_State *L) // setvelocity(self, {x=num, y=num, z=num}) int ObjectRef::l_setvelocity(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -468,6 +482,7 @@ int ObjectRef::l_setvelocity(lua_State *L) // getvelocity(self) int ObjectRef::l_getvelocity(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -480,6 +495,7 @@ int ObjectRef::l_getvelocity(lua_State *L) // setacceleration(self, {x=num, y=num, z=num}) int ObjectRef::l_setacceleration(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -493,6 +509,7 @@ int ObjectRef::l_setacceleration(lua_State *L) // getacceleration(self) int ObjectRef::l_getacceleration(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -505,6 +522,7 @@ int ObjectRef::l_getacceleration(lua_State *L) // setyaw(self, radians) int ObjectRef::l_setyaw(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -517,6 +535,7 @@ int ObjectRef::l_setyaw(lua_State *L) // getyaw(self) int ObjectRef::l_getyaw(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -529,6 +548,7 @@ int ObjectRef::l_getyaw(lua_State *L) // settexturemod(self, mod) int ObjectRef::l_settexturemod(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -542,6 +562,7 @@ int ObjectRef::l_settexturemod(lua_State *L) // select_horiz_by_yawpitch=false) int ObjectRef::l_setsprite(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -566,6 +587,7 @@ int ObjectRef::l_setsprite(lua_State *L) // get_entity_name(self) int ObjectRef::l_get_entity_name(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -578,6 +600,7 @@ int ObjectRef::l_get_entity_name(lua_State *L) // get_luaentity(self) int ObjectRef::l_get_luaentity(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); LuaEntitySAO *co = getluaobject(ref); if(co == NULL) return 0; @@ -591,6 +614,7 @@ int ObjectRef::l_get_luaentity(lua_State *L) // is_player(self) int ObjectRef::l_is_player(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); lua_pushboolean(L, (player != NULL)); @@ -600,6 +624,7 @@ int ObjectRef::l_is_player(lua_State *L) // get_player_name(self) int ObjectRef::l_get_player_name(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL){ @@ -614,6 +639,7 @@ int ObjectRef::l_get_player_name(lua_State *L) // get_look_dir(self) int ObjectRef::l_get_look_dir(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL) return 0; @@ -628,6 +654,7 @@ int ObjectRef::l_get_look_dir(lua_State *L) // get_look_pitch(self) int ObjectRef::l_get_look_pitch(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL) return 0; @@ -639,6 +666,7 @@ int ObjectRef::l_get_look_pitch(lua_State *L) // get_look_yaw(self) int ObjectRef::l_get_look_yaw(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL) return 0; @@ -650,6 +678,7 @@ int ObjectRef::l_get_look_yaw(lua_State *L) // set_look_pitch(self, radians) int ObjectRef::l_set_look_pitch(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); if(co == NULL) return 0; @@ -662,6 +691,7 @@ int ObjectRef::l_set_look_pitch(lua_State *L) // set_look_yaw(self, radians) int ObjectRef::l_set_look_yaw(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); if(co == NULL) return 0; @@ -674,13 +704,14 @@ int ObjectRef::l_set_look_yaw(lua_State *L) // set_inventory_formspec(self, formspec) int ObjectRef::l_set_inventory_formspec(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL) return 0; std::string formspec = luaL_checkstring(L, 2); player->inventory_formspec = formspec; - get_server(L)->reportInventoryFormspecModified(player->getName()); + STACK_TO_SERVER(L)->reportInventoryFormspecModified(player->getName()); lua_pushboolean(L, true); return 1; } @@ -688,6 +719,7 @@ int ObjectRef::l_set_inventory_formspec(lua_State *L) // get_inventory_formspec(self) -> formspec int ObjectRef::l_get_inventory_formspec(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL) return 0; @@ -700,6 +732,7 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L) // get_player_control(self) int ObjectRef::l_get_player_control(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL){ @@ -733,6 +766,7 @@ int ObjectRef::l_get_player_control(lua_State *L) // get_player_control_bits(self) int ObjectRef::l_get_player_control_bits(lua_State *L) { + NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); Player *player = getplayer(ref); if(player == NULL){ @@ -753,23 +787,23 @@ int ObjectRef::l_hud_add(lua_State *L) return 0; HudElement *elem = new HudElement; - + elem->type = (HudElementType)getenumfield(L, 2, "hud_elem_type", es_HudElementType, HUD_ELEM_TEXT); - + lua_getfield(L, 2, "position"); elem->pos = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); lua_pop(L, 1); - + lua_getfield(L, 2, "scale"); elem->scale = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); lua_pop(L, 1); - + elem->name = getstringfield_default(L, 2, "name", ""); elem->text = getstringfield_default(L, 2, "text", ""); elem->number = getintfield_default(L, 2, "number", 0); elem->item = getintfield_default(L, 2, "item", 0); - elem->dir = getintfield_default(L, 2, "direction", 0); + elem->dir = getintfield_default(L, 2, "dir", 0); lua_getfield(L, 2, "alignment"); elem->align = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); @@ -779,7 +813,7 @@ int ObjectRef::l_hud_add(lua_State *L) elem->offset = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); lua_pop(L, 1); - u32 id = get_server(L)->hudAdd(player, elem); + u32 id = STACK_TO_SERVER(L)->hudAdd(player, elem); if (id == (u32)-1) { delete elem; return 0; @@ -800,8 +834,8 @@ int ObjectRef::l_hud_remove(lua_State *L) u32 id = -1; if (!lua_isnil(L, 2)) id = lua_tonumber(L, 2); - - if (!get_server(L)->hudRemove(player, id)) + + if (!STACK_TO_SERVER(L)->hudRemove(player, id)) return 0; lua_pushboolean(L, true); @@ -816,23 +850,21 @@ int ObjectRef::l_hud_change(lua_State *L) if (player == NULL) return 0; - u32 id = !lua_isnil(L, 2) ? lua_tonumber(L, 2) : -1; + u32 id = -1; + if (!lua_isnil(L, 2)) + id = lua_tonumber(L, 2); + + HudElementStat stat = (HudElementStat)getenumfield(L, 3, "stat", + es_HudElementStat, HUD_STAT_NUMBER); + if (id >= player->hud.size()) return 0; - - HudElementStat stat = HUD_STAT_NUMBER; - if (!lua_isnil(L, 3)) { - int statint; - std::string statstr = lua_tostring(L, 3); - stat = string_to_enum(es_HudElementStat, statint, statstr) ? - (HudElementStat)statint : HUD_STAT_NUMBER; - } - + void *value = NULL; HudElement *e = player->hud[id]; if (!e) return 0; - + switch (stat) { case HUD_STAT_POS: e->pos = read_v2f(L, 4); @@ -869,7 +901,7 @@ int ObjectRef::l_hud_change(lua_State *L) value = &e->offset; } - get_server(L)->hudChange(player, id, stat, value); + STACK_TO_SERVER(L)->hudChange(player, id, stat, value); lua_pushboolean(L, true); return 1; @@ -886,34 +918,34 @@ int ObjectRef::l_hud_get(lua_State *L) u32 id = lua_tonumber(L, -1); if (id >= player->hud.size()) return 0; - + HudElement *e = player->hud[id]; if (!e) return 0; - + lua_newtable(L); - + lua_pushstring(L, es_HudElementType[(u8)e->type].str); lua_setfield(L, -2, "type"); - + push_v2f(L, e->pos); lua_setfield(L, -2, "position"); - + lua_pushstring(L, e->name.c_str()); lua_setfield(L, -2, "name"); - + push_v2f(L, e->scale); lua_setfield(L, -2, "scale"); - + lua_pushstring(L, e->text.c_str()); lua_setfield(L, -2, "text"); - + lua_pushnumber(L, e->number); lua_setfield(L, -2, "number"); - + lua_pushnumber(L, e->item); lua_setfield(L, -2, "item"); - + lua_pushnumber(L, e->dir); lua_setfield(L, -2, "dir"); @@ -939,7 +971,7 @@ int ObjectRef::l_hud_set_flags(lua_State *L) mask |= esp[i].num; } } - if (!get_server(L)->hudSetFlags(player, flags, mask)) + if (!STACK_TO_SERVER(L)->hudSetFlags(player, flags, mask)) return 0; lua_pushboolean(L, true); @@ -1060,159 +1092,4 @@ const luaL_reg ObjectRef::methods[] = { {0,0} }; -// Creates a new anonymous reference if cobj=NULL or id=0 -void objectref_get_or_create(lua_State *L, - ServerActiveObject *cobj) -{ - if(cobj == NULL || cobj->getId() == 0){ - ObjectRef::create(L, cobj); - } else { - objectref_get(L, cobj->getId()); - } -} - -void objectref_get(lua_State *L, u16 id) -{ - // Get minetest.object_refs[i] - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "object_refs"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); - lua_gettable(L, -2); - lua_remove(L, -2); // object_refs - lua_remove(L, -2); // minetest -} - -/* - ObjectProperties -*/ - -void read_object_properties(lua_State *L, int index, - ObjectProperties *prop) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - if(!lua_istable(L, index)) - return; - - prop->hp_max = getintfield_default(L, -1, "hp_max", 10); - - getboolfield(L, -1, "physical", prop->physical); - - getfloatfield(L, -1, "weight", prop->weight); - - lua_getfield(L, -1, "collisionbox"); - if(lua_istable(L, -1)) - prop->collisionbox = read_aabb3f(L, -1, 1.0); - lua_pop(L, 1); - - getstringfield(L, -1, "visual", prop->visual); - - getstringfield(L, -1, "mesh", prop->mesh); - - lua_getfield(L, -1, "visual_size"); - if(lua_istable(L, -1)) - prop->visual_size = read_v2f(L, -1); - lua_pop(L, 1); - - lua_getfield(L, -1, "textures"); - if(lua_istable(L, -1)){ - prop->textures.clear(); - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - if(lua_isstring(L, -1)) - prop->textures.push_back(lua_tostring(L, -1)); - else - prop->textures.push_back(""); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } - lua_pop(L, 1); - - lua_getfield(L, -1, "colors"); - if(lua_istable(L, -1)){ - prop->colors.clear(); - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - if(lua_isstring(L, -1)) - prop->colors.push_back(readARGB8(L, -1)); - else - prop->colors.push_back(video::SColor(255, 255, 255, 255)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } - lua_pop(L, 1); - - lua_getfield(L, -1, "spritediv"); - if(lua_istable(L, -1)) - prop->spritediv = read_v2s16(L, -1); - lua_pop(L, 1); - - lua_getfield(L, -1, "initial_sprite_basepos"); - if(lua_istable(L, -1)) - prop->initial_sprite_basepos = read_v2s16(L, -1); - lua_pop(L, 1); - - getboolfield(L, -1, "is_visible", prop->is_visible); - getboolfield(L, -1, "makes_footstep_sound", prop->makes_footstep_sound); - getfloatfield(L, -1, "automatic_rotate", prop->automatic_rotate); -} - -/* - object_reference -*/ - -void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_add_object_reference: id="<getId()<getId()); // Push id - lua_pushvalue(L, object); // Copy object to top of stack - lua_settable(L, objectstable); -} - -void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_rm_object_reference: id="<getId()<getId()); // Push id - lua_gettable(L, objectstable); - // Set object reference to NULL - ObjectRef::set_null(L); - lua_pop(L, 1); // pop object - - // Set object_refs[id] = nil - lua_pushnumber(L, cobj->getId()); // Push id - lua_pushnil(L); - lua_settable(L, objectstable); -} +REGISTER_LUA_REF(ObjectRef) diff --git a/src/scriptapi_object.h b/src/script/lua_api/l_object.h similarity index 84% rename from src/scriptapi_object.h rename to src/script/lua_api/l_object.h index 82535ee9..88f980a2 100644 --- a/src/scriptapi_object.h +++ b/src/script/lua_api/l_object.h @@ -17,17 +17,18 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_OBJECT_H_ -#define LUA_OBJECT_H_ +#ifndef L_OBJECT_H_ +#define L_OBJECT_H_ extern "C" { #include #include } -#include "serverobject.h" -#include "content_sao.h" -#include "player.h" +class ServerActiveObject; +class LuaEntitySAO; +class PlayerSAO; +class Player; /* ObjectRef @@ -199,6 +200,9 @@ private: // hud_change(self, id, stat, data) static int l_hud_change(lua_State *L); + // hud_get_next_id(self) + static u32 hud_get_next_id(lua_State *L); + // hud_get(self, id) static int l_hud_get(lua_State *L); @@ -219,20 +223,4 @@ public: static void Register(lua_State *L); }; -/*****************************************************************************/ -/* scriptapi internal */ -/*****************************************************************************/ -// Creates a new anonymous reference if cobj=NULL or id=0 -void objectref_get_or_create(lua_State *L, - ServerActiveObject *cobj); -void objectref_get(lua_State *L, u16 id); -void read_object_properties(lua_State *L, int index, - ObjectProperties *prop); - -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj); -void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj); - -#endif /* LUA_OBJECT_H_ */ +#endif /* L_OBJECT_H_ */ diff --git a/src/scriptapi_particles.cpp b/src/script/lua_api/l_particles.cpp similarity index 79% rename from src/scriptapi_particles.cpp rename to src/script/lua_api/l_particles.cpp index dc9b3776..c291cc21 100644 --- a/src/scriptapi_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -17,12 +17,22 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "scriptapi.h" -#include "scriptapi_particles.h" +#include "cpp_api/scriptapi.h" +#include "common/c_converter.h" +#include "lua_api/l_base.h" +#include "lua_api/l_particles.h" #include "server.h" -#include "script.h" -#include "scriptapi_types.h" -#include "scriptapi_common.h" +#include "common/c_internal.h" + +bool ModApiParticles::Initialize(lua_State *L, int top) { + bool retval = true; + + retval &= API_FCT(add_particle); + retval &= API_FCT(add_particlespawner); + retval &= API_FCT(delete_particlespawner); + + return retval; +} // add_particle(pos, velocity, acceleration, expirationtime, // size, collisiondetection, texture, player) @@ -30,10 +40,8 @@ with this program; if not, write to the Free Software Foundation, Inc., // expirationtime = num (seconds) // size = num // texture = e.g."default_wood.png" -int l_add_particle(lua_State *L) +int ModApiParticles::l_add_particle(lua_State *L) { - // Get server from registry - Server *server = get_server(L); // Get parameters v3f pos = check_v3f(L, 1); v3f vel = check_v3f(L, 2); @@ -46,13 +54,13 @@ int l_add_particle(lua_State *L) if (lua_gettop(L) == 8) // only spawn for a single player { const char *playername = luaL_checkstring(L, 8); - server->spawnParticle(playername, + getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, collisiondetection, texture); } else // spawn for all players { - server->spawnParticleAll(pos, vel, acc, + getServer(L)->spawnParticleAll(pos, vel, acc, expirationtime, size, collisiondetection, texture); } return 1; @@ -72,10 +80,8 @@ int l_add_particle(lua_State *L) // minsize/maxsize = num // collisiondetection = bool // texture = e.g."default_wood.png" -int l_add_particlespawner(lua_State *L) +int ModApiParticles::l_add_particlespawner(lua_State *L) { - // Get server from registry - Server *server = get_server(L); // Get parameters u16 amount = luaL_checknumber(L, 1); float time = luaL_checknumber(L, 2); @@ -95,7 +101,7 @@ int l_add_particlespawner(lua_State *L) if (lua_gettop(L) == 15) // only spawn for a single player { const char *playername = luaL_checkstring(L, 15); - u32 id = server->addParticleSpawner(playername, + u32 id = getServer(L)->addParticleSpawner(playername, amount, time, minpos, maxpos, minvel, maxvel, @@ -108,7 +114,7 @@ int l_add_particlespawner(lua_State *L) } else // spawn for all players { - u32 id = server->addParticleSpawnerAll( amount, time, + u32 id = getServer(L)->addParticleSpawnerAll( amount, time, minpos, maxpos, minvel, maxvel, minacc, maxacc, @@ -123,21 +129,21 @@ int l_add_particlespawner(lua_State *L) // delete_particlespawner(id, player) // player (string) is optional -int l_delete_particlespawner(lua_State *L) +int ModApiParticles::l_delete_particlespawner(lua_State *L) { - // Get server from registry - Server *server = get_server(L); // Get parameters u32 id = luaL_checknumber(L, 1); if (lua_gettop(L) == 2) // only delete for one player { const char *playername = luaL_checkstring(L, 2); - server->deleteParticleSpawner(playername, id); + getServer(L)->deleteParticleSpawner(playername, id); } else // delete for all players { - server->deleteParticleSpawnerAll(id); + getServer(L)->deleteParticleSpawnerAll(id); } return 1; } + +ModApiParticles modapiparticles_prototyp; diff --git a/src/scriptapi_particles.h b/src/script/lua_api/l_particles.h similarity index 71% rename from src/scriptapi_particles.h rename to src/script/lua_api/l_particles.h index 4b37d7ce..3729f876 100644 --- a/src/scriptapi_particles.h +++ b/src/script/lua_api/l_particles.h @@ -1,5 +1,5 @@ /* -Minetest-c55 +Minetest Copyright (C) 2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify @@ -17,16 +17,23 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef LUA_PARTICLES_H_ -#define LUA_PARTICLES_H_ +#ifndef L_PARTICLES_H_ +#define L_PARTICLES_H_ extern "C" { #include #include } -int l_add_particle(lua_State *L); -int l_add_particlespawner(lua_State *L); -int l_delete_particlespawner(lua_State *L); +class ModApiParticles : public ModApiBase { +public: + bool Initialize(lua_State *L, int top); +private: + static int l_add_particle(lua_State *L); + static int l_add_particlespawner(lua_State *L); + static int l_delete_particlespawner(lua_State *L); +}; -#endif + + +#endif // L_PARTICLES_H_ diff --git a/src/script/lua_api/luaapi.cpp b/src/script/lua_api/luaapi.cpp new file mode 100644 index 00000000..b75304b3 --- /dev/null +++ b/src/script/lua_api/luaapi.cpp @@ -0,0 +1,653 @@ +/* +Minetest +Copyright (C) 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. +*/ + +extern "C" { +#include "lua.h" +#include "lauxlib.h" +} + +#include "lua_api/l_base.h" +#include "common/c_internal.h" +#include "server.h" +#include "common/c_converter.h" +#include "common/c_content.h" +#include "lua_api/luaapi.h" +#include "settings.h" +#include "tool.h" +#include "rollback.h" +#include "log.h" +#include "emerge.h" +#include "main.h" //required for g_settings + +struct EnumString ModApiBasic::es_OreType[] = +{ + {ORE_SCATTER, "scatter"}, + {ORE_SHEET, "sheet"}, + {ORE_CLAYLIKE, "claylike"}, + {0, NULL}, +}; + + +ModApiBasic::ModApiBasic() : ModApiBase() { +} + +bool ModApiBasic::Initialize(lua_State* L,int top) { + + bool retval = true; + + retval &= API_FCT(debug); + retval &= API_FCT(log); + retval &= API_FCT(request_shutdown); + retval &= API_FCT(get_server_status); + + retval &= API_FCT(register_biome); + + retval &= API_FCT(setting_set); + retval &= API_FCT(setting_get); + retval &= API_FCT(setting_getbool); + retval &= API_FCT(setting_save); + + retval &= API_FCT(chat_send_all); + retval &= API_FCT(chat_send_player); + retval &= API_FCT(show_formspec); + + retval &= API_FCT(get_player_privs); + retval &= API_FCT(get_player_ip); + retval &= API_FCT(get_ban_list); + retval &= API_FCT(get_ban_description); + retval &= API_FCT(ban_player); + retval &= API_FCT(unban_player_of_ip); + retval &= API_FCT(get_password_hash); + retval &= API_FCT(notify_authentication_modified); + + retval &= API_FCT(get_dig_params); + retval &= API_FCT(get_hit_params); + + retval &= API_FCT(get_current_modname); + retval &= API_FCT(get_modpath); + retval &= API_FCT(get_modnames); + + retval &= API_FCT(get_worldpath); + retval &= API_FCT(is_singleplayer); + retval &= API_FCT(sound_play); + retval &= API_FCT(sound_stop); + + retval &= API_FCT(rollback_get_last_node_actor); + retval &= API_FCT(rollback_revert_actions_by); + + retval &= API_FCT(register_ore); + + return retval; +} + +// debug(text) +// Writes a line to dstream +int ModApiBasic::l_debug(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string text = lua_tostring(L, 1); + dstream << text << std::endl; + return 0; +} + +// log([level,] text) +// Writes a line to the logger. +// The one-argument version logs to infostream. +// The two-argument version accept a log level: error, action, info, or verbose. +int ModApiBasic::l_log(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string text; + LogMessageLevel level = LMT_INFO; + if(lua_isnone(L, 2)) + { + text = lua_tostring(L, 1); + } + else + { + std::string levelname = luaL_checkstring(L, 1); + text = luaL_checkstring(L, 2); + if(levelname == "error") + level = LMT_ERROR; + else if(levelname == "action") + level = LMT_ACTION; + else if(levelname == "verbose") + level = LMT_VERBOSE; + } + log_printline(level, text); + return 0; +} + +// request_shutdown() +int ModApiBasic::l_request_shutdown(lua_State *L) +{ + getServer(L)->requestShutdown(); + return 0; +} + +// get_server_status() +int ModApiBasic::l_get_server_status(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushstring(L, wide_to_narrow(getServer(L)->getStatusString()).c_str()); + return 1; +} + +// register_biome({lots of stuff}) +int ModApiBasic::l_register_biome(lua_State *L) +{ + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + BiomeDefManager *bmgr = getServer(L)->getEmergeManager()->biomedef; + if (!bmgr) { + verbosestream << "register_biome: BiomeDefManager not active" << std::endl; + return 0; + } + + enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index, + "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL); + Biome *b = bmgr->createBiome(terrain); + + b->name = getstringfield_default(L, index, "name", ""); + b->top_nodename = getstringfield_default(L, index, "top_node", ""); + b->top_depth = getintfield_default(L, index, "top_depth", 0); + b->filler_nodename = getstringfield_default(L, index, "filler_node", ""); + b->filler_height = getintfield_default(L, index, "filler_height", 0); + b->height_min = getintfield_default(L, index, "height_min", 0); + b->height_max = getintfield_default(L, index, "height_max", 0); + b->heat_point = getfloatfield_default(L, index, "heat_point", 0.); + b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.); + + b->flags = 0; //reserved + b->c_top = CONTENT_IGNORE; + b->c_filler = CONTENT_IGNORE; + bmgr->addBiome(b); + + verbosestream << "register_biome: " << b->name << std::endl; + return 0; +} + + + +// setting_set(name, value) +int ModApiBasic::l_setting_set(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + const char *value = luaL_checkstring(L, 2); + g_settings->set(name, value); + return 0; +} + +// setting_get(name) +int ModApiBasic::l_setting_get(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + try{ + std::string value = g_settings->get(name); + lua_pushstring(L, value.c_str()); + } catch(SettingNotFoundException &e){ + lua_pushnil(L); + } + return 1; +} + +// setting_getbool(name) +int ModApiBasic::l_setting_getbool(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + try{ + bool value = g_settings->getBool(name); + lua_pushboolean(L, value); + } catch(SettingNotFoundException &e){ + lua_pushnil(L); + } + return 1; +} + +// setting_save() +int ModApiBasic::l_setting_save(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + getServer(L)->saveConfig(); + return 0; +} + +// chat_send_all(text) +int ModApiBasic::l_chat_send_all(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *text = luaL_checkstring(L, 1); + // Get server from registry + Server *server = getServer(L); + // Send + server->notifyPlayers(narrow_to_wide(text)); + return 0; +} + +// chat_send_player(name, text, prepend) +int ModApiBasic::l_chat_send_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + const char *text = luaL_checkstring(L, 2); + bool prepend = true; + + if (lua_isboolean(L, 3)) + prepend = lua_toboolean(L, 3); + + // Get server from registry + Server *server = getServer(L); + // Send + server->notifyPlayer(name, narrow_to_wide(text), prepend); + return 0; +} + +// get_player_privs(name, text) +int ModApiBasic::l_get_player_privs(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *name = luaL_checkstring(L, 1); + // Get server from registry + Server *server = getServer(L); + // Do it + lua_newtable(L); + int table = lua_gettop(L); + std::set privs_s = server->getPlayerEffectivePrivs(name); + for(std::set::const_iterator + i = privs_s.begin(); i != privs_s.end(); i++){ + lua_pushboolean(L, true); + lua_setfield(L, table, i->c_str()); + } + lua_pushvalue(L, table); + return 1; +} + +// get_player_ip() +int ModApiBasic::l_get_player_ip(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * name = luaL_checkstring(L, 1); + Player *player = getEnv(L)->getPlayer(name); + if(player == NULL) + { + lua_pushnil(L); // no such player + return 1; + } + try + { + Address addr = getServer(L)->getPeerAddress(getEnv(L)->getPlayer(name)->peer_id); + std::string ip_str = addr.serializeString(); + lua_pushstring(L, ip_str.c_str()); + return 1; + } + catch(con::PeerNotFoundException) // unlikely + { + dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushnil(L); // error + return 1; + } +} + +// get_ban_list() +int ModApiBasic::l_get_ban_list(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushstring(L, getServer(L)->getBanDescription("").c_str()); + return 1; +} + +// get_ban_description() +int ModApiBasic::l_get_ban_description(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * ip_or_name = luaL_checkstring(L, 1); + lua_pushstring(L, getServer(L)->getBanDescription(std::string(ip_or_name)).c_str()); + return 1; +} + +// ban_player() +int ModApiBasic::l_ban_player(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * name = luaL_checkstring(L, 1); + Player *player = getEnv(L)->getPlayer(name); + if(player == NULL) + { + lua_pushboolean(L, false); // no such player + return 1; + } + try + { + Address addr = getServer(L)->getPeerAddress(getEnv(L)->getPlayer(name)->peer_id); + std::string ip_str = addr.serializeString(); + getServer(L)->setIpBanned(ip_str, name); + } + catch(con::PeerNotFoundException) // unlikely + { + dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; + lua_pushboolean(L, false); // error + return 1; + } + lua_pushboolean(L, true); + return 1; +} + +// unban_player_or_ip() +int ModApiBasic::l_unban_player_of_ip(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char * ip_or_name = luaL_checkstring(L, 1); + getServer(L)->unsetIpBanned(ip_or_name); + lua_pushboolean(L, true); + return 1; +} + +// show_formspec(playername,formname,formspec) +int ModApiBasic::l_show_formspec(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + const char *playername = luaL_checkstring(L, 1); + const char *formname = luaL_checkstring(L, 2); + const char *formspec = luaL_checkstring(L, 3); + + if(getServer(L)->showFormspec(playername,formspec,formname)) + { + lua_pushboolean(L, true); + }else{ + lua_pushboolean(L, false); + } + return 1; +} + +// get_dig_params(groups, tool_capabilities[, time_from_last_punch]) +int ModApiBasic::l_get_dig_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::map groups; + read_groups(L, 1, groups); + ToolCapabilities tp = read_tool_capabilities(L, 2); + if(lua_isnoneornil(L, 3)) + push_dig_params(L, getDigParams(groups, &tp)); + else + push_dig_params(L, getDigParams(groups, &tp, + luaL_checknumber(L, 3))); + return 1; +} + +// get_hit_params(groups, tool_capabilities[, time_from_last_punch]) +int ModApiBasic::l_get_hit_params(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::map groups; + read_groups(L, 1, groups); + ToolCapabilities tp = read_tool_capabilities(L, 2); + if(lua_isnoneornil(L, 3)) + push_hit_params(L, getHitParams(groups, &tp)); + else + push_hit_params(L, getHitParams(groups, &tp, + luaL_checknumber(L, 3))); + return 1; +} + +// get_current_modname() +int ModApiBasic::l_get_current_modname(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); + return 1; +} + +// get_modpath(modname) +int ModApiBasic::l_get_modpath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string modname = luaL_checkstring(L, 1); + // Do it + if(modname == "__builtin"){ + std::string path = getServer(L)->getBuiltinLuaPath(); + lua_pushstring(L, path.c_str()); + return 1; + } + const ModSpec *mod = getServer(L)->getModSpec(modname); + if(!mod){ + lua_pushnil(L); + return 1; + } + lua_pushstring(L, mod->path.c_str()); + return 1; +} + +// get_modnames() +// the returned list is sorted alphabetically for you +int ModApiBasic::l_get_modnames(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + // Get a list of mods + std::list mods_unsorted, mods_sorted; + getServer(L)->getModNames(mods_unsorted); + + // Take unsorted items from mods_unsorted and sort them into + // mods_sorted; not great performance but the number of mods on a + // server will likely be small. + for(std::list::iterator i = mods_unsorted.begin(); + i != mods_unsorted.end(); ++i) + { + bool added = false; + for(std::list::iterator x = mods_sorted.begin(); + x != mods_sorted.end(); ++x) + { + // I doubt anybody using Minetest will be using + // anything not ASCII based :) + if((*i).compare(*x) <= 0) + { + mods_sorted.insert(x, *i); + added = true; + break; + } + } + if(!added) + mods_sorted.push_back(*i); + } + + // Get the table insertion function from Lua. + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int insertion_func = lua_gettop(L); + + // Package them up for Lua + lua_newtable(L); + int new_table = lua_gettop(L); + std::list::iterator i = mods_sorted.begin(); + while(i != mods_sorted.end()) + { + lua_pushvalue(L, insertion_func); + lua_pushvalue(L, new_table); + lua_pushstring(L, (*i).c_str()); + if(lua_pcall(L, 2, 0, 0) != 0) + { + script_error(L, "error: %s", lua_tostring(L, -1)); + } + ++i; + } + return 1; +} + +// get_worldpath() +int ModApiBasic::l_get_worldpath(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string worldpath = getServer(L)->getWorldPath(); + lua_pushstring(L, worldpath.c_str()); + return 1; +} + +// sound_play(spec, parameters) +int ModApiBasic::l_sound_play(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + SimpleSoundSpec spec; + read_soundspec(L, 1, spec); + ServerSoundParams params; + read_server_sound_params(L, 2, params); + s32 handle = getServer(L)->playSound(spec, params); + lua_pushinteger(L, handle); + return 1; +} + +// sound_stop(handle) +int ModApiBasic::l_sound_stop(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + int handle = luaL_checkinteger(L, 1); + getServer(L)->stopSound(handle); + return 0; +} + +// is_singleplayer() +int ModApiBasic::l_is_singleplayer(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + lua_pushboolean(L, getServer(L)->isSingleplayer()); + return 1; +} + +// get_password_hash(name, raw_password) +int ModApiBasic::l_get_password_hash(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = luaL_checkstring(L, 1); + std::string raw_password = luaL_checkstring(L, 2); + std::string hash = translatePassword(name, + narrow_to_wide(raw_password)); + lua_pushstring(L, hash.c_str()); + return 1; +} + +// notify_authentication_modified(name) +int ModApiBasic::l_notify_authentication_modified(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + std::string name = ""; + if(lua_isstring(L, 1)) + name = lua_tostring(L, 1); + getServer(L)->reportPrivsModified(name); + return 0; +} + +// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds +int ModApiBasic::l_rollback_get_last_node_actor(lua_State *L) +{ + v3s16 p = read_v3s16(L, 1); + int range = luaL_checknumber(L, 2); + int seconds = luaL_checknumber(L, 3); + Server *server = getServer(L); + IRollbackManager *rollback = server->getRollbackManager(); + v3s16 act_p; + int act_seconds = 0; + std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds); + lua_pushstring(L, actor.c_str()); + push_v3s16(L, act_p); + lua_pushnumber(L, act_seconds); + return 3; +} + +// rollback_revert_actions_by(actor, seconds) -> bool, log messages +int ModApiBasic::l_rollback_revert_actions_by(lua_State *L) +{ + std::string actor = luaL_checkstring(L, 1); + int seconds = luaL_checknumber(L, 2); + Server *server = getServer(L); + IRollbackManager *rollback = server->getRollbackManager(); + std::list actions = rollback->getRevertActions(actor, seconds); + std::list log; + bool success = server->rollbackRevertActions(actions, &log); + // Push boolean result + lua_pushboolean(L, success); + // Get the table insert function and push the log table + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + lua_newtable(L); + int table = lua_gettop(L); + for(std::list::const_iterator i = log.begin(); + i != log.end(); i++) + { + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + lua_pushstring(L, i->c_str()); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + lua_remove(L, -2); // Remove table + lua_remove(L, -2); // Remove insert + return 2; +} + +int ModApiBasic::l_register_ore(lua_State *L) +{ + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + enum OreType oretype = (OreType)getenumfield(L, index, + "ore_type", es_OreType, ORE_SCATTER); + Ore *ore = createOre(oretype); + if (!ore) { + errorstream << "register_ore: ore_type " + << oretype << " not implemented"; + return 0; + } + + ore->ore_name = getstringfield_default(L, index, "ore", ""); + ore->ore_param2 = (u8)getintfield_default(L, index, "ore_param2", 0); + ore->wherein_name = getstringfield_default(L, index, "wherein", ""); + ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1); + ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1); + ore->clust_size = getintfield_default(L, index, "clust_size", 0); + ore->height_min = getintfield_default(L, index, "height_min", 0); + ore->height_max = getintfield_default(L, index, "height_max", 0); + ore->flags = getflagsfield(L, index, "flags", flagdesc_ore); + ore->nthresh = getfloatfield_default(L, index, "noise_threshhold", 0.); + + lua_getfield(L, index, "noise_params"); + ore->np = read_noiseparams(L, -1); + lua_pop(L, 1); + + ore->noise = NULL; + + if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) { + errorstream << "register_ore: clust_scarcity and clust_num_ores" + "must be greater than 0" << std::endl; + delete ore; + return 0; + } + + emerge->ores.push_back(ore); + + verbosestream << "register_ore: ore '" << ore->ore_name + << "' registered" << std::endl; + return 0; +} + +ModApiBasic modapibasic_prototype; diff --git a/src/script/lua_api/luaapi.h b/src/script/lua_api/luaapi.h new file mode 100644 index 00000000..59204696 --- /dev/null +++ b/src/script/lua_api/luaapi.h @@ -0,0 +1,140 @@ +/* +Minetest +Copyright (C) 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. +*/ + +#ifndef LUAAPI_H_ +#define LUAAPI_H_ + + +class ModApiBasic : public ModApiBase { + +public: + ModApiBasic(); + + bool Initialize(lua_State* L,int top); + +private: + // debug(text) + // Writes a line to dstream + static int l_debug(lua_State *L); + + // log([level,] text) + // Writes a line to the logger. + // The one-argument version logs to infostream. + // The two-argument version accept a log level: error, action, info, or verbose. + static int l_log(lua_State *L); + + // request_shutdown() + static int l_request_shutdown(lua_State *L); + + // get_server_status() + static int l_get_server_status(lua_State *L); + + // register_biome_groups({frequencies}) + static int l_register_biome_groups(lua_State *L); + + // register_biome({lots of stuff}) + static int l_register_biome(lua_State *L); + + // setting_set(name, value) + static int l_setting_set(lua_State *L); + + // setting_get(name) + static int l_setting_get(lua_State *L); + + // setting_getbool(name) + static int l_setting_getbool(lua_State *L); + + // setting_save() + static int l_setting_save(lua_State *L); + + // chat_send_all(text) + static int l_chat_send_all(lua_State *L); + + // chat_send_player(name, text) + static int l_chat_send_player(lua_State *L); + + // get_player_privs(name, text) + static int l_get_player_privs(lua_State *L); + + // get_player_ip() + static int l_get_player_ip(lua_State *L); + + // get_ban_list() + static int l_get_ban_list(lua_State *L); + + // get_ban_description() + static int l_get_ban_description(lua_State *L); + + // ban_player() + static int l_ban_player(lua_State *L); + + // unban_player_or_ip() + static int l_unban_player_of_ip(lua_State *L); + + // show_formspec(playername,formname,formspec) + static int l_show_formspec(lua_State *L); + + // get_dig_params(groups, tool_capabilities[, time_from_last_punch]) + static int l_get_dig_params(lua_State *L); + + // get_hit_params(groups, tool_capabilities[, time_from_last_punch]) + static int l_get_hit_params(lua_State *L); + + // get_current_modname() + static int l_get_current_modname(lua_State *L); + + // get_modpath(modname) + static int l_get_modpath(lua_State *L); + + // get_modnames() + // the returned list is sorted alphabetically for you + static int l_get_modnames(lua_State *L); + + // get_worldpath() + static int l_get_worldpath(lua_State *L); + + // sound_play(spec, parameters) + static int l_sound_play(lua_State *L); + + // sound_stop(handle) + static int l_sound_stop(lua_State *L); + + // is_singleplayer() + static int l_is_singleplayer(lua_State *L); + + // get_password_hash(name, raw_password) + static int l_get_password_hash(lua_State *L); + + // notify_authentication_modified(name) + static int l_notify_authentication_modified(lua_State *L); + + // rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds + static int l_rollback_get_last_node_actor(lua_State *L); + + // rollback_revert_actions_by(actor, seconds) -> bool, log messages + static int l_rollback_revert_actions_by(lua_State *L); + + static int l_register_ore(lua_State *L); + + static struct EnumString es_OreType[]; +}; + + + +#endif /* LUAAPI_H_ */ diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp deleted file mode 100644 index 26759a05..00000000 --- a/src/scriptapi.cpp +++ /dev/null @@ -1,1181 +0,0 @@ -/* -Minetest -Copyright (C) 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. -*/ - -#include "scriptapi.h" - -#include -#include -extern "C" { -#include -#include -#include -} - -#include "settings.h" // For accessing g_settings -#include "main.h" // For g_settings -#include "biome.h" -#include "emerge.h" -#include "script.h" -#include "rollback.h" - -#include "scriptapi_types.h" -#include "scriptapi_env.h" -#include "scriptapi_nodetimer.h" -#include "scriptapi_inventory.h" -#include "scriptapi_nodemeta.h" -#include "scriptapi_object.h" -#include "scriptapi_noise.h" -#include "scriptapi_common.h" -#include "scriptapi_item.h" -#include "scriptapi_content.h" -#include "scriptapi_craft.h" -#include "scriptapi_particles.h" - -/*****************************************************************************/ -/* Mod related */ -/*****************************************************************************/ - -class ModNameStorer -{ -private: - lua_State *L; -public: - ModNameStorer(lua_State *L_, const std::string modname): - L(L_) - { - // Store current modname in registry - lua_pushstring(L, modname.c_str()); - lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); - } - ~ModNameStorer() - { - // Clear current modname in registry - lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); - } -}; - -bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, - const std::string &modname) -{ - ModNameStorer modnamestorer(L, modname); - - if(!string_allowed(modname, MODNAME_ALLOWED_CHARS)){ - errorstream<<"Error loading mod \""< &result) -{ - result.clear(); - lua_pushnil(L); - if(index < 0) - index -= 1; - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - std::string key = luaL_checkstring(L, -2); - bool value = lua_toboolean(L, -1); - if(value) - result.insert(key); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } -} - -static void get_auth_handler(lua_State *L) -{ - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_auth_handler"); - if(lua_isnil(L, -1)){ - lua_pop(L, 1); - lua_getfield(L, -1, "builtin_auth_handler"); - } - if(lua_type(L, -1) != LUA_TTABLE) - throw LuaError(L, "Authentication handler table not valid"); -} - -bool scriptapi_get_auth(lua_State *L, const std::string &playername, - std::string *dst_password, std::set *dst_privs) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - get_auth_handler(L); - lua_getfield(L, -1, "get_auth"); - if(lua_type(L, -1) != LUA_TFUNCTION) - throw LuaError(L, "Authentication handler missing get_auth"); - lua_pushstring(L, playername.c_str()); - if(lua_pcall(L, 1, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - - // nil = login not allowed - if(lua_isnil(L, -1)) - return false; - luaL_checktype(L, -1, LUA_TTABLE); - - std::string password; - bool found = getstringfield(L, -1, "password", password); - if(!found) - throw LuaError(L, "Authentication handler didn't return password"); - if(dst_password) - *dst_password = password; - - lua_getfield(L, -1, "privileges"); - if(!lua_istable(L, -1)) - throw LuaError(L, - "Authentication handler didn't return privilege table"); - if(dst_privs) - read_privileges(L, -1, *dst_privs); - lua_pop(L, 1); - - return true; -} - -void scriptapi_create_auth(lua_State *L, const std::string &playername, - const std::string &password) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - get_auth_handler(L); - lua_getfield(L, -1, "create_auth"); - if(lua_type(L, -1) != LUA_TFUNCTION) - throw LuaError(L, "Authentication handler missing create_auth"); - lua_pushstring(L, playername.c_str()); - lua_pushstring(L, password.c_str()); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -bool scriptapi_set_password(lua_State *L, const std::string &playername, - const std::string &password) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - get_auth_handler(L); - lua_getfield(L, -1, "set_password"); - if(lua_type(L, -1) != LUA_TFUNCTION) - throw LuaError(L, "Authentication handler missing set_password"); - lua_pushstring(L, playername.c_str()); - lua_pushstring(L, password.c_str()); - if(lua_pcall(L, 2, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - return lua_toboolean(L, -1); -} - -/*****************************************************************************/ -/* Misc */ -/*****************************************************************************/ -/* - Groups -*/ -void read_groups(lua_State *L, int index, - std::map &result) -{ - if (!lua_istable(L,index)) - return; - result.clear(); - lua_pushnil(L); - if(index < 0) - index -= 1; - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - std::string name = luaL_checkstring(L, -2); - int rating = luaL_checkinteger(L, -1); - result[name] = rating; - // removes value, keeps key for next iteration - lua_pop(L, 1); - } -} - -struct EnumString es_BiomeTerrainType[] = -{ - {BIOME_TERRAIN_NORMAL, "normal"}, - {BIOME_TERRAIN_LIQUID, "liquid"}, - {BIOME_TERRAIN_NETHER, "nether"}, - {BIOME_TERRAIN_AETHER, "aether"}, - {BIOME_TERRAIN_FLAT, "flat"}, - {0, NULL}, -}; - -struct EnumString es_OreType[] = -{ - {ORE_SCATTER, "scatter"}, - {ORE_SHEET, "sheet"}, - {ORE_CLAYLIKE, "claylike"}, - {0, NULL}, -}; - -/*****************************************************************************/ -/* Parameters */ -/*****************************************************************************/ -/* - DigParams -*/ - -static void set_dig_params(lua_State *L, int table, - const DigParams ¶ms) -{ - setboolfield(L, table, "diggable", params.diggable); - setfloatfield(L, table, "time", params.time); - setintfield(L, table, "wear", params.wear); -} - -static void push_dig_params(lua_State *L, - const DigParams ¶ms) -{ - lua_newtable(L); - set_dig_params(L, -1, params); -} - -/* - HitParams -*/ - -static void set_hit_params(lua_State *L, int table, - const HitParams ¶ms) -{ - setintfield(L, table, "hp", params.hp); - setintfield(L, table, "wear", params.wear); -} - -static void push_hit_params(lua_State *L, - const HitParams ¶ms) -{ - lua_newtable(L); - set_hit_params(L, -1, params); -} - -/* - ServerSoundParams -*/ - -static void read_server_sound_params(lua_State *L, int index, - ServerSoundParams ¶ms) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - // Clear - params = ServerSoundParams(); - if(lua_istable(L, index)){ - getfloatfield(L, index, "gain", params.gain); - getstringfield(L, index, "to_player", params.to_player); - lua_getfield(L, index, "pos"); - if(!lua_isnil(L, -1)){ - v3f p = read_v3f(L, -1)*BS; - params.pos = p; - params.type = ServerSoundParams::SSP_POSITIONAL; - } - lua_pop(L, 1); - lua_getfield(L, index, "object"); - if(!lua_isnil(L, -1)){ - ObjectRef *ref = ObjectRef::checkobject(L, -1); - ServerActiveObject *sao = ObjectRef::getobject(ref); - if(sao){ - params.object = sao->getId(); - params.type = ServerSoundParams::SSP_OBJECT; - } - } - lua_pop(L, 1); - params.max_hear_distance = BS*getfloatfield_default(L, index, - "max_hear_distance", params.max_hear_distance/BS); - getboolfield(L, index, "loop", params.loop); - } -} - -/*****************************************************************************/ -/* callbacks */ -/*****************************************************************************/ - -// Push the list of callbacks (a lua table). -// Then push nargs arguments. -// Then call this function, which -// - runs the callbacks -// - removes the table and arguments from the lua stack -// - pushes the return value, computed depending on mode -void scriptapi_run_callbacks(lua_State *L, int nargs, - RunCallbacksMode mode) -{ - // Insert the return value into the lua stack, below the table - assert(lua_gettop(L) >= nargs + 1); - lua_pushnil(L); - lua_insert(L, -(nargs + 1) - 1); - // Stack now looks like this: - // ...
... - - int rv = lua_gettop(L) - nargs - 1; - int table = rv + 1; - int arg = table + 1; - - luaL_checktype(L, table, LUA_TTABLE); - - // Foreach - lua_pushnil(L); - bool first_loop = true; - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TFUNCTION); - // Call function - for(int i = 0; i < nargs; i++) - lua_pushvalue(L, arg+i); - if(lua_pcall(L, nargs, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - - // Move return value to designated space in stack - // Or pop it - if(first_loop){ - // Result of first callback is always moved - lua_replace(L, rv); - first_loop = false; - } else { - // Otherwise, what happens depends on the mode - if(mode == RUN_CALLBACKS_MODE_FIRST) - lua_pop(L, 1); - else if(mode == RUN_CALLBACKS_MODE_LAST) - lua_replace(L, rv); - else if(mode == RUN_CALLBACKS_MODE_AND || - mode == RUN_CALLBACKS_MODE_AND_SC){ - if((bool)lua_toboolean(L, rv) == true && - (bool)lua_toboolean(L, -1) == false) - lua_replace(L, rv); - else - lua_pop(L, 1); - } - else if(mode == RUN_CALLBACKS_MODE_OR || - mode == RUN_CALLBACKS_MODE_OR_SC){ - if((bool)lua_toboolean(L, rv) == false && - (bool)lua_toboolean(L, -1) == true) - lua_replace(L, rv); - else - lua_pop(L, 1); - } - else - assert(0); - } - - // Handle short circuit modes - if(mode == RUN_CALLBACKS_MODE_AND_SC && - (bool)lua_toboolean(L, rv) == false) - break; - else if(mode == RUN_CALLBACKS_MODE_OR_SC && - (bool)lua_toboolean(L, rv) == true) - break; - - // value removed, keep key for next iteration - } - - // Remove stuff from stack, leaving only the return value - lua_settop(L, rv); - - // Fix return value in case no callbacks were called - if(first_loop){ - if(mode == RUN_CALLBACKS_MODE_AND || - mode == RUN_CALLBACKS_MODE_AND_SC){ - lua_pop(L, 1); - lua_pushboolean(L, true); - } - else if(mode == RUN_CALLBACKS_MODE_OR || - mode == RUN_CALLBACKS_MODE_OR_SC){ - lua_pop(L, 1); - lua_pushboolean(L, false); - } - } -} - -bool scriptapi_on_chat_message(lua_State *L, const std::string &name, - const std::string &message) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_chat_messages - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_chat_messages"); - // Call callbacks - lua_pushstring(L, name.c_str()); - lua_pushstring(L, message.c_str()); - scriptapi_run_callbacks(L, 2, RUN_CALLBACKS_MODE_OR_SC); - bool ate = lua_toboolean(L, -1); - return ate; -} - -void scriptapi_on_shutdown(lua_State *L) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get registered shutdown hooks - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_shutdown"); - // Call callbacks - scriptapi_run_callbacks(L, 0, RUN_CALLBACKS_MODE_FIRST); -} - -void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_newplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_newplayers"); - // Call callbacks - objectref_get_or_create(L, player); - scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); -} - -void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_dieplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_dieplayers"); - // Call callbacks - objectref_get_or_create(L, player); - scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); -} - -bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_respawnplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_respawnplayers"); - // Call callbacks - objectref_get_or_create(L, player); - scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_OR); - bool positioning_handled_by_some = lua_toboolean(L, -1); - return positioning_handled_by_some; -} - -void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_joinplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_joinplayers"); - // Call callbacks - objectref_get_or_create(L, player); - scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); -} - -void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_leaveplayers - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_leaveplayers"); - // Call callbacks - objectref_get_or_create(L, player); - scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); -} - -/* - player -*/ -void scriptapi_on_player_receive_fields(lua_State *L, - ServerActiveObject *player, - const std::string &formname, - const std::map &fields) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_on_chat_messages - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_on_player_receive_fields"); - // Call callbacks - // param 1 - objectref_get_or_create(L, player); - // param 2 - lua_pushstring(L, formname.c_str()); - // param 3 - lua_newtable(L); - for(std::map::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; - lua_pushstring(L, name.c_str()); - lua_pushlstring(L, value.c_str(), value.size()); - lua_settable(L, -3); - } - scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_OR_SC); -} -/*****************************************************************************/ -/* Api functions */ -/*****************************************************************************/ - -// debug(text) -// Writes a line to dstream -static int l_debug(lua_State *L) -{ - std::string text = lua_tostring(L, 1); - dstream << text << std::endl; - return 0; -} - -// log([level,] text) -// Writes a line to the logger. -// The one-argument version logs to infostream. -// The two-argument version accept a log level: error, action, info, or verbose. -static int l_log(lua_State *L) -{ - std::string text; - LogMessageLevel level = LMT_INFO; - if(lua_isnone(L, 2)) - { - text = lua_tostring(L, 1); - } - else - { - std::string levelname = luaL_checkstring(L, 1); - text = luaL_checkstring(L, 2); - if(levelname == "error") - level = LMT_ERROR; - else if(levelname == "action") - level = LMT_ACTION; - else if(levelname == "verbose") - level = LMT_VERBOSE; - } - log_printline(level, text); - return 0; -} - -// request_shutdown() -static int l_request_shutdown(lua_State *L) -{ - get_server(L)->requestShutdown(); - return 0; -} - -// get_server_status() -static int l_get_server_status(lua_State *L) -{ - lua_pushstring(L, wide_to_narrow(get_server(L)->getStatusString()).c_str()); - return 1; -} - - -// register_biome({lots of stuff}) -static int l_register_biome(lua_State *L) -{ - int index = 1; - luaL_checktype(L, index, LUA_TTABLE); - - BiomeDefManager *bmgr = get_server(L)->getEmergeManager()->biomedef; - if (!bmgr) { - verbosestream << "register_biome: BiomeDefManager not active" << std::endl; - return 0; - } - - enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index, - "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL); - Biome *b = bmgr->createBiome(terrain); - - b->name = getstringfield_default(L, index, "name", ""); - b->top_nodename = getstringfield_default(L, index, "top_node", ""); - b->top_depth = getintfield_default(L, index, "top_depth", 0); - b->filler_nodename = getstringfield_default(L, index, "filler_node", ""); - b->filler_height = getintfield_default(L, index, "filler_height", 0); - b->height_min = getintfield_default(L, index, "height_min", 0); - b->height_max = getintfield_default(L, index, "height_max", 0); - b->heat_point = getfloatfield_default(L, index, "heat_point", 0.); - b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.); - - b->flags = 0; //reserved - b->c_top = CONTENT_IGNORE; - b->c_filler = CONTENT_IGNORE; - bmgr->addBiome(b); - - verbosestream << "register_biome: " << b->name << std::endl; - return 0; -} - - -static int l_register_ore(lua_State *L) -{ - int index = 1; - luaL_checktype(L, index, LUA_TTABLE); - - EmergeManager *emerge = get_server(L)->getEmergeManager(); - - enum OreType oretype = (OreType)getenumfield(L, index, - "ore_type", es_OreType, ORE_SCATTER); - Ore *ore = createOre(oretype); - if (!ore) { - errorstream << "register_ore: ore_type " - << oretype << " not implemented"; - return 0; - } - - ore->ore_name = getstringfield_default(L, index, "ore", ""); - ore->ore_param2 = (u8)getintfield_default(L, index, "ore_param2", 0); - ore->wherein_name = getstringfield_default(L, index, "wherein", ""); - ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1); - ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1); - ore->clust_size = getintfield_default(L, index, "clust_size", 0); - ore->height_min = getintfield_default(L, index, "height_min", 0); - ore->height_max = getintfield_default(L, index, "height_max", 0); - ore->flags = getflagsfield(L, index, "flags", flagdesc_ore); - ore->nthresh = getfloatfield_default(L, index, "noise_threshhold", 0.); - - lua_getfield(L, index, "noise_params"); - ore->np = read_noiseparams(L, -1); - lua_pop(L, 1); - - ore->noise = NULL; - - if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) { - errorstream << "register_ore: clust_scarcity and clust_num_ores" - " must be greater than 0" << std::endl; - delete ore; - return 0; - } - - emerge->ores.push_back(ore); - - verbosestream << "register_ore: ore '" << ore->ore_name - << "' registered" << std::endl; - return 0; -} - - -// setting_set(name, value) -static int l_setting_set(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - const char *value = luaL_checkstring(L, 2); - g_settings->set(name, value); - return 0; -} - -// setting_get(name) -static int l_setting_get(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - try{ - std::string value = g_settings->get(name); - lua_pushstring(L, value.c_str()); - } catch(SettingNotFoundException &e){ - lua_pushnil(L); - } - return 1; -} - -// setting_getbool(name) -static int l_setting_getbool(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - try{ - bool value = g_settings->getBool(name); - lua_pushboolean(L, value); - } catch(SettingNotFoundException &e){ - lua_pushnil(L); - } - return 1; -} - -// setting_save() -static int l_setting_save(lua_State *L) -{ - get_server(L)->saveConfig(); - return 0; -} - -// chat_send_all(text) -static int l_chat_send_all(lua_State *L) -{ - const char *text = luaL_checkstring(L, 1); - // Get server from registry - Server *server = get_server(L); - // Send - server->notifyPlayers(narrow_to_wide(text)); - return 0; -} - -// chat_send_player(name, text, prepend) -static int l_chat_send_player(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - const char *text = luaL_checkstring(L, 2); - bool prepend = true; - if (lua_isboolean(L, 3)) - prepend = lua_toboolean(L, 3); - // Get server from registry - Server *server = get_server(L); - // Send - server->notifyPlayer(name, narrow_to_wide(text), prepend); - return 0; -} - -// get_player_privs(name, text) -static int l_get_player_privs(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - // Get server from registry - Server *server = get_server(L); - // Do it - lua_newtable(L); - int table = lua_gettop(L); - std::set privs_s = server->getPlayerEffectivePrivs(name); - for(std::set::const_iterator - i = privs_s.begin(); i != privs_s.end(); i++){ - lua_pushboolean(L, true); - lua_setfield(L, table, i->c_str()); - } - lua_pushvalue(L, table); - return 1; -} - -// get_player_ip() -static int l_get_player_ip(lua_State *L) -{ - const char * name = luaL_checkstring(L, 1); - Player *player = get_env(L)->getPlayer(name); - if(player == NULL) - { - lua_pushnil(L); // no such player - return 1; - } - try - { - Address addr = get_server(L)->getPeerAddress(get_env(L)->getPlayer(name)->peer_id); - std::string ip_str = addr.serializeString(); - lua_pushstring(L, ip_str.c_str()); - return 1; - } - catch(con::PeerNotFoundException) // unlikely - { - dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; - lua_pushnil(L); // error - return 1; - } -} - -// get_ban_list() -static int l_get_ban_list(lua_State *L) -{ - lua_pushstring(L, get_server(L)->getBanDescription("").c_str()); - return 1; -} - -// get_ban_description() -static int l_get_ban_description(lua_State *L) -{ - const char * ip_or_name = luaL_checkstring(L, 1); - lua_pushstring(L, get_server(L)->getBanDescription(std::string(ip_or_name)).c_str()); - return 1; -} - -// ban_player() -static int l_ban_player(lua_State *L) -{ - const char * name = luaL_checkstring(L, 1); - Player *player = get_env(L)->getPlayer(name); - if(player == NULL) - { - lua_pushboolean(L, false); // no such player - return 1; - } - try - { - Address addr = get_server(L)->getPeerAddress(get_env(L)->getPlayer(name)->peer_id); - std::string ip_str = addr.serializeString(); - get_server(L)->setIpBanned(ip_str, name); - } - catch(con::PeerNotFoundException) // unlikely - { - dstream << __FUNCTION_NAME << ": peer was not found" << std::endl; - lua_pushboolean(L, false); // error - return 1; - } - lua_pushboolean(L, true); - return 1; -} - -// unban_player_or_ip() -static int l_unban_player_of_ip(lua_State *L) -{ - const char * ip_or_name = luaL_checkstring(L, 1); - get_server(L)->unsetIpBanned(ip_or_name); - lua_pushboolean(L, true); - return 1; -} - -// show_formspec(playername,formname,formspec) -static int l_show_formspec(lua_State *L) -{ - const char *playername = luaL_checkstring(L, 1); - const char *formname = luaL_checkstring(L, 2); - const char *formspec = luaL_checkstring(L, 3); - - if(get_server(L)->showFormspec(playername,formspec,formname)) - { - lua_pushboolean(L, true); - }else{ - lua_pushboolean(L, false); - } - return 1; -} - -// get_dig_params(groups, tool_capabilities[, time_from_last_punch]) -static int l_get_dig_params(lua_State *L) -{ - std::map groups; - read_groups(L, 1, groups); - ToolCapabilities tp = read_tool_capabilities(L, 2); - if(lua_isnoneornil(L, 3)) - push_dig_params(L, getDigParams(groups, &tp)); - else - push_dig_params(L, getDigParams(groups, &tp, - luaL_checknumber(L, 3))); - return 1; -} - -// get_hit_params(groups, tool_capabilities[, time_from_last_punch]) -static int l_get_hit_params(lua_State *L) -{ - std::map groups; - read_groups(L, 1, groups); - ToolCapabilities tp = read_tool_capabilities(L, 2); - if(lua_isnoneornil(L, 3)) - push_hit_params(L, getHitParams(groups, &tp)); - else - push_hit_params(L, getHitParams(groups, &tp, - luaL_checknumber(L, 3))); - return 1; -} - -// get_current_modname() -static int l_get_current_modname(lua_State *L) -{ - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); - return 1; -} - -// get_modpath(modname) -static int l_get_modpath(lua_State *L) -{ - std::string modname = luaL_checkstring(L, 1); - // Do it - if(modname == "__builtin"){ - std::string path = get_server(L)->getBuiltinLuaPath(); - lua_pushstring(L, path.c_str()); - return 1; - } - const ModSpec *mod = get_server(L)->getModSpec(modname); - if(!mod){ - lua_pushnil(L); - return 1; - } - lua_pushstring(L, mod->path.c_str()); - return 1; -} - -// get_modnames() -// the returned list is sorted alphabetically for you -static int l_get_modnames(lua_State *L) -{ - // Get a list of mods - std::list mods_unsorted, mods_sorted; - get_server(L)->getModNames(mods_unsorted); - - // Take unsorted items from mods_unsorted and sort them into - // mods_sorted; not great performance but the number of mods on a - // server will likely be small. - for(std::list::iterator i = mods_unsorted.begin(); - i != mods_unsorted.end(); ++i) - { - bool added = false; - for(std::list::iterator x = mods_sorted.begin(); - x != mods_sorted.end(); ++x) - { - // I doubt anybody using Minetest will be using - // anything not ASCII based :) - if((*i).compare(*x) <= 0) - { - mods_sorted.insert(x, *i); - added = true; - break; - } - } - if(!added) - mods_sorted.push_back(*i); - } - - // Get the table insertion function from Lua. - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int insertion_func = lua_gettop(L); - - // Package them up for Lua - lua_newtable(L); - int new_table = lua_gettop(L); - std::list::iterator i = mods_sorted.begin(); - while(i != mods_sorted.end()) - { - lua_pushvalue(L, insertion_func); - lua_pushvalue(L, new_table); - lua_pushstring(L, (*i).c_str()); - if(lua_pcall(L, 2, 0, 0) != 0) - { - script_error(L, "error: %s", lua_tostring(L, -1)); - } - ++i; - } - return 1; -} - -// get_worldpath() -static int l_get_worldpath(lua_State *L) -{ - std::string worldpath = get_server(L)->getWorldPath(); - lua_pushstring(L, worldpath.c_str()); - return 1; -} - -// sound_play(spec, parameters) -static int l_sound_play(lua_State *L) -{ - SimpleSoundSpec spec; - read_soundspec(L, 1, spec); - ServerSoundParams params; - read_server_sound_params(L, 2, params); - s32 handle = get_server(L)->playSound(spec, params); - lua_pushinteger(L, handle); - return 1; -} - -// sound_stop(handle) -static int l_sound_stop(lua_State *L) -{ - int handle = luaL_checkinteger(L, 1); - get_server(L)->stopSound(handle); - return 0; -} - -// is_singleplayer() -static int l_is_singleplayer(lua_State *L) -{ - lua_pushboolean(L, get_server(L)->isSingleplayer()); - return 1; -} - -// get_password_hash(name, raw_password) -static int l_get_password_hash(lua_State *L) -{ - std::string name = luaL_checkstring(L, 1); - std::string raw_password = luaL_checkstring(L, 2); - std::string hash = translatePassword(name, - narrow_to_wide(raw_password)); - lua_pushstring(L, hash.c_str()); - return 1; -} - -// notify_authentication_modified(name) -static int l_notify_authentication_modified(lua_State *L) -{ - std::string name = ""; - if(lua_isstring(L, 1)) - name = lua_tostring(L, 1); - get_server(L)->reportPrivsModified(name); - return 0; -} - -// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds -static int l_rollback_get_last_node_actor(lua_State *L) -{ - v3s16 p = read_v3s16(L, 1); - int range = luaL_checknumber(L, 2); - int seconds = luaL_checknumber(L, 3); - Server *server = get_server(L); - IRollbackManager *rollback = server->getRollbackManager(); - v3s16 act_p; - int act_seconds = 0; - std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds); - lua_pushstring(L, actor.c_str()); - push_v3s16(L, act_p); - lua_pushnumber(L, act_seconds); - return 3; -} - -// rollback_revert_actions_by(actor, seconds) -> bool, log messages -static int l_rollback_revert_actions_by(lua_State *L) -{ - std::string actor = luaL_checkstring(L, 1); - int seconds = luaL_checknumber(L, 2); - Server *server = get_server(L); - IRollbackManager *rollback = server->getRollbackManager(); - std::list actions = rollback->getRevertActions(actor, seconds); - std::list log; - bool success = server->rollbackRevertActions(actions, &log); - // Push boolean result - lua_pushboolean(L, success); - // Get the table insert function and push the log table - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - lua_newtable(L); - int table = lua_gettop(L); - for(std::list::const_iterator i = log.begin(); - i != log.end(); i++) - { - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - lua_pushstring(L, i->c_str()); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } - lua_remove(L, -2); // Remove table - lua_remove(L, -2); // Remove insert - return 2; -} - -static const struct luaL_Reg minetest_f [] = { - {"debug", l_debug}, - {"log", l_log}, - {"request_shutdown", l_request_shutdown}, - {"get_server_status", l_get_server_status}, - {"register_item_raw", l_register_item_raw}, - {"register_alias_raw", l_register_alias_raw}, - {"register_craft", l_register_craft}, - {"register_biome", l_register_biome}, - {"register_ore", l_register_ore}, - {"setting_set", l_setting_set}, - {"setting_get", l_setting_get}, - {"setting_getbool", l_setting_getbool}, - {"setting_save",l_setting_save}, - {"chat_send_all", l_chat_send_all}, - {"chat_send_player", l_chat_send_player}, - {"get_player_privs", l_get_player_privs}, - {"get_player_ip", l_get_player_ip}, - {"get_ban_list", l_get_ban_list}, - {"get_ban_description", l_get_ban_description}, - {"ban_player", l_ban_player}, - {"unban_player_or_ip", l_unban_player_of_ip}, - {"get_inventory", l_get_inventory}, - {"create_detached_inventory_raw", l_create_detached_inventory_raw}, - {"show_formspec", l_show_formspec}, - {"get_dig_params", l_get_dig_params}, - {"get_hit_params", l_get_hit_params}, - {"get_current_modname", l_get_current_modname}, - {"get_modpath", l_get_modpath}, - {"get_modnames", l_get_modnames}, - {"get_worldpath", l_get_worldpath}, - {"sound_play", l_sound_play}, - {"sound_stop", l_sound_stop}, - {"is_singleplayer", l_is_singleplayer}, - {"get_password_hash", l_get_password_hash}, - {"notify_authentication_modified", l_notify_authentication_modified}, - {"get_craft_result", l_get_craft_result}, - {"get_craft_recipe", l_get_craft_recipe}, - {"get_all_craft_recipes", l_get_all_craft_recipes}, - {"rollback_get_last_node_actor", l_rollback_get_last_node_actor}, - {"rollback_revert_actions_by", l_rollback_revert_actions_by}, - {"add_particle", l_add_particle}, - {"add_particlespawner", l_add_particlespawner}, - {"delete_particlespawner", l_delete_particlespawner}, - {NULL, NULL} -}; - - -/* - Main export function -*/ - -void scriptapi_export(lua_State *L, Server *server) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - verbosestream<<"scriptapi_export()"< - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef SCRIPTAPI_HEADER -#define SCRIPTAPI_HEADER - -#include -#include -#include -#include "irr_v3d.h" -#include "irr_v2d.h" - -extern "C" { -#include -} -#include "scriptapi_inventory.h" -#include "scriptapi_nodemeta.h" -#include "scriptapi_entity.h" -#include "scriptapi_object.h" -#include "scriptapi_env.h" -#include "scriptapi_item.h" -#include "scriptapi_node.h" - -#define luamethod(class, name) {#name, class::l_##name} - -class Server; - -void scriptapi_export(lua_State *L, Server *server); -bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, - const std::string &modname); - -// Returns true if script handled message -bool scriptapi_on_chat_message(lua_State *L, const std::string &name, - const std::string &message); - -/* server */ -void scriptapi_on_shutdown(lua_State *L); - -/* misc */ -void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player); -void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player); -bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player); -void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player); -void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player); -bool scriptapi_get_auth(lua_State *L, const std::string &playername, - std::string *dst_password, std::set *dst_privs); -void scriptapi_create_auth(lua_State *L, const std::string &playername, - const std::string &password); -bool scriptapi_set_password(lua_State *L, const std::string &playername, - const std::string &password); - -/* player */ -void scriptapi_on_player_receive_fields(lua_State *L, - ServerActiveObject *player, - const std::string &formname, - const std::map &fields); - -#endif - diff --git a/src/scriptapi_common.cpp b/src/scriptapi_common.cpp deleted file mode 100644 index 2d6f6c72..00000000 --- a/src/scriptapi_common.cpp +++ /dev/null @@ -1,311 +0,0 @@ -/* -Minetest -Copyright (C) 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. -*/ - -#include "scriptapi.h" -#include "scriptapi_common.h" - -extern "C" { -#include "lauxlib.h" -} - -#include "script.h" -#include "scriptapi_types.h" -#include "scriptapi_object.h" - - -Server* get_server(lua_State *L) -{ - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - lua_pop(L, 1); - return server; -} - -ServerEnvironment* get_env(lua_State *L) -{ - // Get environment from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env"); - ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1); - lua_pop(L, 1); - return env; -} - -void warn_if_field_exists(lua_State *L, int table, - const char *fieldname, const std::string &message) -{ - lua_getfield(L, table, fieldname); - if(!lua_isnil(L, -1)){ - infostream<::const_iterator - i = toolcap.groupcaps.begin(); i != toolcap.groupcaps.end(); i++){ - // Create groupcap table - lua_newtable(L); - const std::string &name = i->first; - const ToolGroupCap &groupcap = i->second; - // Create subtable "times" - lua_newtable(L); - for(std::map::const_iterator - i = groupcap.times.begin(); i != groupcap.times.end(); i++){ - int rating = i->first; - float time = i->second; - lua_pushinteger(L, rating); - lua_pushnumber(L, time); - lua_settable(L, -3); - } - // Set subtable "times" - lua_setfield(L, -2, "times"); - // Set simple parameters - setintfield(L, -1, "maxlevel", groupcap.maxlevel); - setintfield(L, -1, "uses", groupcap.uses); - // Insert groupcap table into groupcaps table - lua_setfield(L, -2, name.c_str()); - } - // Set groupcaps table - lua_setfield(L, -2, "groupcaps"); - //Create damage_groups table - lua_newtable(L); - // For each damage group - for(std::map::const_iterator - i = toolcap.damageGroups.begin(); i != toolcap.damageGroups.end(); i++){ - // Create damage group table - lua_pushinteger(L, i->second); - lua_setfield(L, -2, i->first.c_str()); - } - lua_setfield(L, -2, "damage_groups"); -} - -void push_tool_capabilities(lua_State *L, - const ToolCapabilities &prop) -{ - lua_newtable(L); - set_tool_capabilities(L, -1, prop); -} - -void realitycheck(lua_State *L) -{ - int top = lua_gettop(L); - if(top >= 30){ - dstream<<"Stack is over 30:"<str){ - if(str == std::string(esp->str)){ - result = esp->num; - return true; - } - esp++; - } - return false; -} - -/*bool enum_to_string(const EnumString *spec, std::string &result, - int num) -{ - const EnumString *esp = spec; - while(esp){ - if(num == esp->num){ - result = esp->str; - return true; - } - esp++; - } - return false; -}*/ - -int getenumfield(lua_State *L, int table, - const char *fieldname, const EnumString *spec, int default_) -{ - int result = default_; - string_to_enum(spec, result, - getstringfield_default(L, table, fieldname, "")); - return result; -} diff --git a/src/scriptapi_common.h b/src/scriptapi_common.h deleted file mode 100644 index d029b48d..00000000 --- a/src/scriptapi_common.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 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. -*/ - -#ifndef LUA_COMMON_H_ -#define LUA_COMMON_H_ - -extern "C" { -#include -} - -#include "server.h" -#include "environment.h" -#include "nodedef.h" -#include "util/pointedthing.h" -#include "tool.h" - -Server* get_server(lua_State *L); -ServerEnvironment* get_env(lua_State *L); - -void warn_if_field_exists(lua_State *L, int table, - const char *fieldname, const std::string &message); - -ToolCapabilities read_tool_capabilities (lua_State *L, int table); -void push_tool_capabilities (lua_State *L, - const ToolCapabilities &prop); -void set_tool_capabilities (lua_State *L, int table, - const ToolCapabilities &toolcap); - -void realitycheck (lua_State *L); - -void push_pointed_thing (lua_State *L, - const PointedThing& pointed); - -void stackDump (lua_State *L, std::ostream &o); - -class StackUnroller -{ -private: - lua_State *m_lua; - int m_original_top; -public: - StackUnroller(lua_State *L): - m_lua(L), - m_original_top(-1) - { - m_original_top = lua_gettop(m_lua); // store stack height - } - ~StackUnroller() - { - lua_settop(m_lua, m_original_top); // restore stack height - } -}; - -/* definitions */ -// What scriptapi_run_callbacks does with the return values of callbacks. -// Regardless of the mode, if only one callback is defined, -// its return value is the total return value. -// Modes only affect the case where 0 or >= 2 callbacks are defined. -enum RunCallbacksMode -{ - // Returns the return value of the first callback - // Returns nil if list of callbacks is empty - RUN_CALLBACKS_MODE_FIRST, - // Returns the return value of the last callback - // Returns nil if list of callbacks is empty - RUN_CALLBACKS_MODE_LAST, - // If any callback returns a false value, the first such is returned - // Otherwise, the first callback's return value (trueish) is returned - // Returns true if list of callbacks is empty - RUN_CALLBACKS_MODE_AND, - // Like above, but stops calling callbacks (short circuit) - // after seeing the first false value - RUN_CALLBACKS_MODE_AND_SC, - // If any callback returns a true value, the first such is returned - // Otherwise, the first callback's return value (falseish) is returned - // Returns false if list of callbacks is empty - RUN_CALLBACKS_MODE_OR, - // Like above, but stops calling callbacks (short circuit) - // after seeing the first true value - RUN_CALLBACKS_MODE_OR_SC, - // Note: "a true value" and "a false value" refer to values that - // are converted by lua_toboolean to true or false, respectively. -}; - -struct EnumString -{ - int num; - const char *str; -}; - -bool string_to_enum(const EnumString *spec, int &result, - const std::string &str); - -int getenumfield(lua_State *L, int table, - const char *fieldname, const EnumString *spec, int default_); -#endif /* LUA_COMMON_H_ */ diff --git a/src/scriptapi_content.cpp b/src/scriptapi_content.cpp deleted file mode 100644 index 30fd7034..00000000 --- a/src/scriptapi_content.cpp +++ /dev/null @@ -1,327 +0,0 @@ -/* -Minetest -Copyright (C) 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. -*/ - -#include "scriptapi.h" -#include "scriptapi_content.h" -#include "scriptapi_types.h" -#include "scriptapi_common.h" -#include "scriptapi_node.h" - - -NodeBox read_nodebox(lua_State *L, int index) -{ - NodeBox nodebox; - if(lua_istable(L, -1)){ - nodebox.type = (NodeBoxType)getenumfield(L, index, "type", - es_NodeBoxType, NODEBOX_REGULAR); - - lua_getfield(L, index, "fixed"); - if(lua_istable(L, -1)) - nodebox.fixed = read_aabb3f_vector(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_top"); - if(lua_istable(L, -1)) - nodebox.wall_top = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_bottom"); - if(lua_istable(L, -1)) - nodebox.wall_bottom = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - - lua_getfield(L, index, "wall_side"); - if(lua_istable(L, -1)) - nodebox.wall_side = read_aabb3f(L, -1, BS); - lua_pop(L, 1); - } - return nodebox; -} - -/* - SimpleSoundSpec -*/ - -void read_soundspec(lua_State *L, int index, SimpleSoundSpec &spec) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - if(lua_isnil(L, index)){ - } else if(lua_istable(L, index)){ - getstringfield(L, index, "name", spec.name); - getfloatfield(L, index, "gain", spec.gain); - } else if(lua_isstring(L, index)){ - spec.name = lua_tostring(L, index); - } -} - -struct EnumString es_TileAnimationType[] = -{ - {TAT_NONE, "none"}, - {TAT_VERTICAL_FRAMES, "vertical_frames"}, - {0, NULL}, -}; - -/* - TileDef -*/ - -TileDef read_tiledef(lua_State *L, int index) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - - TileDef tiledef; - - // key at index -2 and value at index - if(lua_isstring(L, index)){ - // "default_lava.png" - tiledef.name = lua_tostring(L, index); - } - else if(lua_istable(L, index)) - { - // {name="default_lava.png", animation={}} - tiledef.name = ""; - getstringfield(L, index, "name", tiledef.name); - getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. - tiledef.backface_culling = getboolfield_default( - L, index, "backface_culling", true); - // animation = {} - lua_getfield(L, index, "animation"); - if(lua_istable(L, -1)){ - // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} - tiledef.animation.type = (TileAnimationType) - getenumfield(L, -1, "type", es_TileAnimationType, - TAT_NONE); - tiledef.animation.aspect_w = - getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.aspect_h = - getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.length = - getfloatfield_default(L, -1, "length", 1.0); - } - lua_pop(L, 1); - } - - return tiledef; -} - -/* - ContentFeatures -*/ - -ContentFeatures read_content_features(lua_State *L, int index) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - - ContentFeatures f; - - /* Cache existence of some callbacks */ - lua_getfield(L, index, "on_construct"); - if(!lua_isnil(L, -1)) f.has_on_construct = true; - lua_pop(L, 1); - lua_getfield(L, index, "on_destruct"); - if(!lua_isnil(L, -1)) f.has_on_destruct = true; - lua_pop(L, 1); - lua_getfield(L, index, "after_destruct"); - if(!lua_isnil(L, -1)) f.has_after_destruct = true; - lua_pop(L, 1); - - lua_getfield(L, index, "on_rightclick"); - f.rightclickable = lua_isfunction(L, -1); - lua_pop(L, 1); - - /* Name */ - getstringfield(L, index, "name", f.name); - - /* Groups */ - lua_getfield(L, index, "groups"); - read_groups(L, -1, f.groups); - lua_pop(L, 1); - - /* Visual definition */ - - f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType, - NDT_NORMAL); - getfloatfield(L, index, "visual_scale", f.visual_scale); - - // tiles = {} - lua_getfield(L, index, "tiles"); - // If nil, try the deprecated name "tile_images" instead - if(lua_isnil(L, -1)){ - lua_pop(L, 1); - warn_if_field_exists(L, index, "tile_images", - "Deprecated; new name is \"tiles\"."); - lua_getfield(L, index, "tile_images"); - } - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - int i = 0; - while(lua_next(L, table) != 0){ - // Read tiledef from value - f.tiledef[i] = read_tiledef(L, -1); - // removes value, keeps key for next iteration - lua_pop(L, 1); - i++; - if(i==6){ - lua_pop(L, 1); - break; - } - } - // Copy last value to all remaining textures - if(i >= 1){ - TileDef lasttile = f.tiledef[i-1]; - while(i < 6){ - f.tiledef[i] = lasttile; - i++; - } - } - } - lua_pop(L, 1); - - // special_tiles = {} - lua_getfield(L, index, "special_tiles"); - // If nil, try the deprecated name "special_materials" instead - if(lua_isnil(L, -1)){ - lua_pop(L, 1); - warn_if_field_exists(L, index, "special_materials", - "Deprecated; new name is \"special_tiles\"."); - lua_getfield(L, index, "special_materials"); - } - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - int i = 0; - while(lua_next(L, table) != 0){ - // Read tiledef from value - f.tiledef_special[i] = read_tiledef(L, -1); - // removes value, keeps key for next iteration - lua_pop(L, 1); - i++; - if(i==6){ - lua_pop(L, 1); - break; - } - } - } - lua_pop(L, 1); - - f.alpha = getintfield_default(L, index, "alpha", 255); - - bool usealpha = getboolfield_default(L, index, - "use_texture_alpha", false); - if (usealpha) - f.alpha = 0; - - /* Other stuff */ - - lua_getfield(L, index, "post_effect_color"); - if(!lua_isnil(L, -1)) - f.post_effect_color = readARGB8(L, -1); - lua_pop(L, 1); - - f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", - es_ContentParamType, CPT_NONE); - f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", - es_ContentParamType2, CPT2_NONE); - - // Warn about some deprecated fields - warn_if_field_exists(L, index, "wall_mounted", - "deprecated: use paramtype2 = 'wallmounted'"); - warn_if_field_exists(L, index, "light_propagates", - "deprecated: determined from paramtype"); - warn_if_field_exists(L, index, "dug_item", - "deprecated: use 'drop' field"); - warn_if_field_exists(L, index, "extra_dug_item", - "deprecated: use 'drop' field"); - warn_if_field_exists(L, index, "extra_dug_item_rarity", - "deprecated: use 'drop' field"); - warn_if_field_exists(L, index, "metadata_name", - "deprecated: use on_add and metadata callbacks"); - - // True for all ground-like things like stone and mud, false for eg. trees - getboolfield(L, index, "is_ground_content", f.is_ground_content); - f.light_propagates = (f.param_type == CPT_LIGHT); - getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates); - // This is used for collision detection. - // Also for general solidness queries. - getboolfield(L, index, "walkable", f.walkable); - // Player can point to these - getboolfield(L, index, "pointable", f.pointable); - // Player can dig these - getboolfield(L, index, "diggable", f.diggable); - // Player can climb these - getboolfield(L, index, "climbable", f.climbable); - // Player can build on these - getboolfield(L, index, "buildable_to", f.buildable_to); - // Whether the node is non-liquid, source liquid or flowing liquid - f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", - es_LiquidType, LIQUID_NONE); - // If the content is liquid, this is the flowing version of the liquid. - getstringfield(L, index, "liquid_alternative_flowing", - f.liquid_alternative_flowing); - // If the content is liquid, this is the source version of the liquid. - getstringfield(L, index, "liquid_alternative_source", - f.liquid_alternative_source); - // Viscosity for fluid flow, ranging from 1 to 7, with - // 1 giving almost instantaneous propagation and 7 being - // the slowest possible - f.liquid_viscosity = getintfield_default(L, index, - "liquid_viscosity", f.liquid_viscosity); - getboolfield(L, index, "liquid_renewable", f.liquid_renewable); - // Amount of light the node emits - f.light_source = getintfield_default(L, index, - "light_source", f.light_source); - f.damage_per_second = getintfield_default(L, index, - "damage_per_second", f.damage_per_second); - - lua_getfield(L, index, "node_box"); - if(lua_istable(L, -1)) - f.node_box = read_nodebox(L, -1); - lua_pop(L, 1); - - lua_getfield(L, index, "selection_box"); - if(lua_istable(L, -1)) - f.selection_box = read_nodebox(L, -1); - lua_pop(L, 1); - - // Set to true if paramtype used to be 'facedir_simple' - getboolfield(L, index, "legacy_facedir_simple", f.legacy_facedir_simple); - // Set to true if wall_mounted used to be set to true - getboolfield(L, index, "legacy_wallmounted", f.legacy_wallmounted); - - // Sound table - lua_getfield(L, index, "sounds"); - if(lua_istable(L, -1)){ - lua_getfield(L, -1, "footstep"); - read_soundspec(L, -1, f.sound_footstep); - lua_pop(L, 1); - lua_getfield(L, -1, "dig"); - read_soundspec(L, -1, f.sound_dig); - lua_pop(L, 1); - lua_getfield(L, -1, "dug"); - read_soundspec(L, -1, f.sound_dug); - lua_pop(L, 1); - } - lua_pop(L, 1); - - return f; -} diff --git a/src/scriptapi_craft.h b/src/scriptapi_craft.h deleted file mode 100644 index f28989fd..00000000 --- a/src/scriptapi_craft.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 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. -*/ - -#ifndef LUA_CRAFT_H_ -#define LUA_CRAFT_H_ - -#include - -extern "C" { -#include -} - -#include "craftdef.h" - -/*****************************************************************************/ -/* Mod API */ -/*****************************************************************************/ -int l_register_craft(lua_State *L); -int l_get_craft_recipe(lua_State *L); -int l_get_all_craft_recipes(lua_State *L); -int l_get_craft_result(lua_State *L); - -/*****************************************************************************/ -/* scriptapi internal */ -/*****************************************************************************/ -bool read_craft_replacements(lua_State *L, int index, - CraftReplacements &replacements); -bool read_craft_recipe_shapeless(lua_State *L, int index, - std::vector &recipe); -bool read_craft_recipe_shaped(lua_State *L, int index, - int &width, std::vector &recipe); - -extern struct EnumString es_CraftMethod[]; - -#endif /* LUA_CRAFT_H_ */ diff --git a/src/scriptapi_entity.h b/src/scriptapi_entity.h deleted file mode 100644 index e0874325..00000000 --- a/src/scriptapi_entity.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 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. -*/ - -#ifndef LUA_ENTITY_H_ -#define LUA_ENTITY_H_ - -extern "C" { -#include -} - -#include "object_properties.h" -#include "content_sao.h" -#include "tool.h" - -/*****************************************************************************/ -/* scriptapi internal */ -/*****************************************************************************/ -void luaentity_get(lua_State *L, u16 id); - -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -// Returns true if succesfully added into Lua; false otherwise. -bool scriptapi_luaentity_add(lua_State *L, u16 id, const char *name); -void scriptapi_luaentity_activate(lua_State *L, u16 id, - const std::string &staticdata, u32 dtime_s); -void scriptapi_luaentity_rm(lua_State *L, u16 id); -std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id); -void scriptapi_luaentity_get_properties(lua_State *L, u16 id, - ObjectProperties *prop); -void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime); -void scriptapi_luaentity_punch(lua_State *L, u16 id, - ServerActiveObject *puncher, float time_from_last_punch, - const ToolCapabilities *toolcap, v3f dir); -void scriptapi_luaentity_rightclick(lua_State *L, u16 id, - ServerActiveObject *clicker); - -#endif /* LUA_ENTITY_H_ */ diff --git a/src/scriptapi_env.cpp b/src/scriptapi_env.cpp deleted file mode 100644 index a9d9391a..00000000 --- a/src/scriptapi_env.cpp +++ /dev/null @@ -1,975 +0,0 @@ -/* -Minetest -Copyright (C) 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. -*/ - -#include "scriptapi.h" -#include "scriptapi_env.h" -#include "nodedef.h" -#include "gamedef.h" -#include "map.h" -#include "daynightratio.h" -#include "content_sao.h" -#include "script.h" -#include "treegen.h" -#include "pathfinder.h" -#include "util/pointedthing.h" -#include "scriptapi_types.h" -#include "scriptapi_noise.h" -#include "scriptapi_nodemeta.h" -#include "scriptapi_nodetimer.h" -#include "scriptapi_object.h" -#include "scriptapi_common.h" -#include "scriptapi_item.h" -#include "scriptapi_node.h" - - -//TODO -extern void scriptapi_run_callbacks(lua_State *L, int nargs, - RunCallbacksMode mode); - - -class LuaABM : public ActiveBlockModifier -{ -private: - lua_State *m_lua; - int m_id; - - std::set m_trigger_contents; - std::set m_required_neighbors; - float m_trigger_interval; - u32 m_trigger_chance; -public: - LuaABM(lua_State *L, int id, - const std::set &trigger_contents, - const std::set &required_neighbors, - float trigger_interval, u32 trigger_chance): - m_lua(L), - m_id(id), - m_trigger_contents(trigger_contents), - m_required_neighbors(required_neighbors), - m_trigger_interval(trigger_interval), - m_trigger_chance(trigger_chance) - { - } - virtual std::set getTriggerContents() - { - return m_trigger_contents; - } - virtual std::set getRequiredNeighbors() - { - return m_required_neighbors; - } - virtual float getTriggerInterval() - { - return m_trigger_interval; - } - virtual u32 getTriggerChance() - { - return m_trigger_chance; - } - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, - u32 active_object_count, u32 active_object_count_wider) - { - lua_State *L = m_lua; - - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Get minetest.registered_abms - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_abms"); - luaL_checktype(L, -1, LUA_TTABLE); - int registered_abms = lua_gettop(L); - - // Get minetest.registered_abms[m_id] - lua_pushnumber(L, m_id); - lua_gettable(L, registered_abms); - if(lua_isnil(L, -1)) - assert(0); - - // Call action - luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, "action"); - luaL_checktype(L, -1, LUA_TFUNCTION); - push_v3s16(L, p); - pushnode(L, n, env->getGameDef()->ndef()); - lua_pushnumber(L, active_object_count); - lua_pushnumber(L, active_object_count_wider); - if(lua_pcall(L, 4, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } -}; - -/* - EnvRef -*/ - -int EnvRef::gc_object(lua_State *L) { - EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1)); - delete o; - return 0; -} - -EnvRef* EnvRef::checkobject(lua_State *L, int narg) -{ - luaL_checktype(L, narg, LUA_TUSERDATA); - void *ud = luaL_checkudata(L, narg, className); - if(!ud) luaL_typerror(L, narg, className); - return *(EnvRef**)ud; // unbox pointer -} - -// Exported functions - -// EnvRef:set_node(pos, node) -// pos = {x=num, y=num, z=num} -int EnvRef::l_set_node(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - INodeDefManager *ndef = env->getGameDef()->ndef(); - // parameters - v3s16 pos = read_v3s16(L, 2); - MapNode n = readnode(L, 3, ndef); - // Do it - bool succeeded = env->setNode(pos, n); - lua_pushboolean(L, succeeded); - return 1; -} - -int EnvRef::l_add_node(lua_State *L) -{ - return l_set_node(L); -} - -// EnvRef:remove_node(pos) -// pos = {x=num, y=num, z=num} -int EnvRef::l_remove_node(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - // parameters - v3s16 pos = read_v3s16(L, 2); - // Do it - bool succeeded = env->removeNode(pos); - lua_pushboolean(L, succeeded); - return 1; -} - -// EnvRef:get_node(pos) -// pos = {x=num, y=num, z=num} -int EnvRef::l_get_node(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // Do it - MapNode n = env->getMap().getNodeNoEx(pos); - // Return node - pushnode(L, n, env->getGameDef()->ndef()); - return 1; -} - -// EnvRef:get_node_or_nil(pos) -// pos = {x=num, y=num, z=num} -int EnvRef::l_get_node_or_nil(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // pos - v3s16 pos = read_v3s16(L, 2); - // Do it - try{ - MapNode n = env->getMap().getNode(pos); - // Return node - pushnode(L, n, env->getGameDef()->ndef()); - return 1; - } catch(InvalidPositionException &e) - { - lua_pushnil(L); - return 1; - } -} - -// EnvRef:get_node_light(pos, timeofday) -// pos = {x=num, y=num, z=num} -// timeofday: nil = current time, 0 = night, 0.5 = day -int EnvRef::l_get_node_light(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // Do it - v3s16 pos = read_v3s16(L, 2); - u32 time_of_day = env->getTimeOfDay(); - if(lua_isnumber(L, 3)) - time_of_day = 24000.0 * lua_tonumber(L, 3); - time_of_day %= 24000; - u32 dnr = time_to_daynight_ratio(time_of_day, true); - try{ - MapNode n = env->getMap().getNode(pos); - INodeDefManager *ndef = env->getGameDef()->ndef(); - lua_pushinteger(L, n.getLightBlend(dnr, ndef)); - return 1; - } catch(InvalidPositionException &e) - { - lua_pushnil(L); - return 1; - } -} - -// EnvRef:place_node(pos, node) -// pos = {x=num, y=num, z=num} -int EnvRef::l_place_node(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - v3s16 pos = read_v3s16(L, 2); - MapNode n = readnode(L, 3, env->getGameDef()->ndef()); - - // Don't attempt to load non-loaded area as of now - MapNode n_old = env->getMap().getNodeNoEx(pos); - if(n_old.getContent() == CONTENT_IGNORE){ - lua_pushboolean(L, false); - return 1; - } - // Create item to place - INodeDefManager *ndef = get_server(L)->ndef(); - IItemDefManager *idef = get_server(L)->idef(); - ItemStack item(ndef->get(n).name, 1, 0, "", idef); - // Make pointed position - PointedThing pointed; - pointed.type = POINTEDTHING_NODE; - pointed.node_abovesurface = pos; - pointed.node_undersurface = pos + v3s16(0,-1,0); - // Place it with a NULL placer (appears in Lua as a non-functional - // ObjectRef) - bool success = scriptapi_item_on_place(L, item, NULL, pointed); - lua_pushboolean(L, success); - return 1; -} - -// EnvRef:dig_node(pos) -// pos = {x=num, y=num, z=num} -int EnvRef::l_dig_node(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - v3s16 pos = read_v3s16(L, 2); - - // Don't attempt to load non-loaded area as of now - MapNode n = env->getMap().getNodeNoEx(pos); - if(n.getContent() == CONTENT_IGNORE){ - lua_pushboolean(L, false); - return 1; - } - // Dig it out with a NULL digger (appears in Lua as a - // non-functional ObjectRef) - bool success = scriptapi_node_on_dig(L, pos, n, NULL); - lua_pushboolean(L, success); - return 1; -} - -// EnvRef:punch_node(pos) -// pos = {x=num, y=num, z=num} -int EnvRef::l_punch_node(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - v3s16 pos = read_v3s16(L, 2); - - // Don't attempt to load non-loaded area as of now - MapNode n = env->getMap().getNodeNoEx(pos); - if(n.getContent() == CONTENT_IGNORE){ - lua_pushboolean(L, false); - return 1; - } - // Punch it with a NULL puncher (appears in Lua as a non-functional - // ObjectRef) - bool success = scriptapi_node_on_punch(L, pos, n, NULL); - lua_pushboolean(L, success); - return 1; -} - -// EnvRef:get_meta(pos) -int EnvRef::l_get_meta(lua_State *L) -{ - //infostream<<"EnvRef::l_get_meta()"<m_env; - if(env == NULL) return 0; - // Do it - v3s16 p = read_v3s16(L, 2); - NodeMetaRef::create(L, p, env); - return 1; -} - -// EnvRef:get_node_timer(pos) -int EnvRef::l_get_node_timer(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // Do it - v3s16 p = read_v3s16(L, 2); - NodeTimerRef::create(L, p, env); - return 1; -} - -// EnvRef:add_entity(pos, entityname) -> ObjectRef or nil -// pos = {x=num, y=num, z=num} -int EnvRef::l_add_entity(lua_State *L) -{ - //infostream<<"EnvRef::l_add_entity()"<m_env; - if(env == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // content - const char *name = luaL_checkstring(L, 3); - // Do it - ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, ""); - int objectid = env->addActiveObject(obj); - // If failed to add, return nothing (reads as nil) - if(objectid == 0) - return 0; - // Return ObjectRef - objectref_get_or_create(L, obj); - return 1; -} - -// EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil -// pos = {x=num, y=num, z=num} -int EnvRef::l_add_item(lua_State *L) -{ - //infostream<<"EnvRef::l_add_item()"<m_env; - if(env == NULL) return 0; - // pos - //v3f pos = checkFloatPos(L, 2); - // item - ItemStack item = read_item(L, 3); - if(item.empty() || !item.isKnown(get_server(L)->idef())) - return 0; - // Use minetest.spawn_item to spawn a __builtin:item - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "spawn_item"); - if(lua_isnil(L, -1)) - return 0; - lua_pushvalue(L, 2); - lua_pushstring(L, item.getItemString().c_str()); - if(lua_pcall(L, 2, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - return 1; - /*lua_pushvalue(L, 1); - lua_pushstring(L, "__builtin:item"); - lua_pushstring(L, item.getItemString().c_str()); - return l_add_entity(L);*/ - /*// Do it - ServerActiveObject *obj = createItemSAO(env, pos, item.getItemString()); - int objectid = env->addActiveObject(obj); - // If failed to add, return nothing (reads as nil) - if(objectid == 0) - return 0; - // Return ObjectRef - objectref_get_or_create(L, obj); - return 1;*/ -} - -// EnvRef:add_rat(pos) -// pos = {x=num, y=num, z=num} -int EnvRef::l_add_rat(lua_State *L) -{ - infostream<<"EnvRef::l_add_rat(): C++ mobs have been removed." - <<" Doing nothing."<m_env; - if(env == NULL) return 0; - // Do it - const char *name = luaL_checkstring(L, 2); - Player *player = env->getPlayer(name); - if(player == NULL){ - lua_pushnil(L); - return 1; - } - PlayerSAO *sao = player->getPlayerSAO(); - if(sao == NULL){ - lua_pushnil(L); - return 1; - } - // Put player on stack - objectref_get_or_create(L, sao); - return 1; -} - -// EnvRef:get_objects_inside_radius(pos, radius) -int EnvRef::l_get_objects_inside_radius(lua_State *L) -{ - // Get the table insert function - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - // Get environemnt - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // Do it - v3f pos = checkFloatPos(L, 2); - float radius = luaL_checknumber(L, 3) * BS; - std::set ids = env->getObjectsInsideRadius(pos, radius); - lua_newtable(L); - int table = lua_gettop(L); - for(std::set::const_iterator - i = ids.begin(); i != ids.end(); i++){ - ServerActiveObject *obj = env->getActiveObject(*i); - // Insert object reference into table - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - objectref_get_or_create(L, obj); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } - return 1; -} - -// EnvRef:set_timeofday(val) -// val = 0...1 -int EnvRef::l_set_timeofday(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // Do it - float timeofday_f = luaL_checknumber(L, 2); - assert(timeofday_f >= 0.0 && timeofday_f <= 1.0); - int timeofday_mh = (int)(timeofday_f * 24000.0); - // This should be set directly in the environment but currently - // such changes aren't immediately sent to the clients, so call - // the server instead. - //env->setTimeOfDay(timeofday_mh); - get_server(L)->setTimeOfDay(timeofday_mh); - return 0; -} - -// EnvRef:get_timeofday() -> 0...1 -int EnvRef::l_get_timeofday(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - // Do it - int timeofday_mh = env->getTimeOfDay(); - float timeofday_f = (float)timeofday_mh / 24000.0; - lua_pushnumber(L, timeofday_f); - return 1; -} - - -// EnvRef:find_node_near(pos, radius, nodenames) -> pos or nil -// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" -int EnvRef::l_find_node_near(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - INodeDefManager *ndef = get_server(L)->ndef(); - v3s16 pos = read_v3s16(L, 2); - int radius = luaL_checkinteger(L, 3); - std::set filter; - if(lua_istable(L, 4)){ - int table = 4; - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - ndef->getIds(lua_tostring(L, -1), filter); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, 4)){ - ndef->getIds(lua_tostring(L, 4), filter); - } - - for(int d=1; d<=radius; d++){ - std::list list; - getFacePositions(list, d); - for(std::list::iterator i = list.begin(); - i != list.end(); ++i){ - v3s16 p = pos + (*i); - content_t c = env->getMap().getNodeNoEx(p).getContent(); - if(filter.count(c) != 0){ - push_v3s16(L, p); - return 1; - } - } - } - return 0; -} - -// EnvRef:find_nodes_in_area(minp, maxp, nodenames) -> list of positions -// nodenames: eg. {"ignore", "group:tree"} or "default:dirt" -int EnvRef::l_find_nodes_in_area(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - INodeDefManager *ndef = get_server(L)->ndef(); - v3s16 minp = read_v3s16(L, 2); - v3s16 maxp = read_v3s16(L, 3); - std::set filter; - if(lua_istable(L, 4)){ - int table = 4; - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - ndef->getIds(lua_tostring(L, -1), filter); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, 4)){ - ndef->getIds(lua_tostring(L, 4), filter); - } - - // Get the table insert function - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - - lua_newtable(L); - int table = lua_gettop(L); - for(s16 x=minp.X; x<=maxp.X; x++) - for(s16 y=minp.Y; y<=maxp.Y; y++) - for(s16 z=minp.Z; z<=maxp.Z; z++) - { - v3s16 p(x,y,z); - content_t c = env->getMap().getNodeNoEx(p).getContent(); - if(filter.count(c) != 0){ - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - push_v3s16(L, p); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } - } - return 1; -} - -// EnvRef:get_perlin(seeddiff, octaves, persistence, scale) -// returns world-specific PerlinNoise -int EnvRef::l_get_perlin(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - int seeddiff = luaL_checkint(L, 2); - int octaves = luaL_checkint(L, 3); - float persistence = luaL_checknumber(L, 4); - float scale = luaL_checknumber(L, 5); - - LuaPerlinNoise *n = new LuaPerlinNoise(seeddiff + int(env->getServerMap().getSeed()), octaves, persistence, scale); - *(void **)(lua_newuserdata(L, sizeof(void *))) = n; - luaL_getmetatable(L, "PerlinNoise"); - lua_setmetatable(L, -2); - return 1; -} - -// EnvRef:get_perlin_map(noiseparams, size) -// returns world-specific PerlinNoiseMap -int EnvRef::l_get_perlin_map(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if (env == NULL) - return 0; - - NoiseParams *np = read_noiseparams(L, 2); - if (!np) - return 0; - v3s16 size = read_v3s16(L, 3); - - int seed = (int)(env->getServerMap().getSeed()); - LuaPerlinNoiseMap *n = new LuaPerlinNoiseMap(np, seed, size); - *(void **)(lua_newuserdata(L, sizeof(void *))) = n; - luaL_getmetatable(L, "PerlinNoiseMap"); - lua_setmetatable(L, -2); - return 1; -} - -// EnvRef:clear_objects() -// clear all objects in the environment -int EnvRef::l_clear_objects(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - o->m_env->clearAllObjects(); - return 0; -} - -int EnvRef::l_line_of_sight(lua_State *L) { - float stepsize = 1.0; - - //infostream<<"EnvRef::l_get_node()"<m_env; - if(env == NULL) return 0; - - // read position 1 from lua - v3f pos1 = checkFloatPos(L, 2); - // read position 2 from lua - v3f pos2 = checkFloatPos(L, 2); - //read step size from lua - if(lua_isnumber(L, 3)) - stepsize = lua_tonumber(L, 3); - - lua_pushboolean(L, env->line_of_sight(pos1,pos2,stepsize)); - - return 1; -} - -int EnvRef::l_find_path(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - - if(env == NULL) return 0; - - v3s16 pos1 = read_v3s16(L, 2); - v3s16 pos2 = read_v3s16(L, 3); - unsigned int searchdistance = luaL_checkint(L, 4); - unsigned int max_jump = luaL_checkint(L, 5); - unsigned int max_drop = luaL_checkint(L, 6); - algorithm algo = A_PLAIN_NP; - if(! lua_isnil(L, 7)) { - std::string algorithm = luaL_checkstring(L,7); - - if (algorithm == "A*") - algo = A_PLAIN; - - if (algorithm == "Dijkstra") - algo = DIJKSTRA; - } - - std::vector path = - get_Path(env,pos1,pos2,searchdistance,max_jump,max_drop,algo); - - if (path.size() > 0) - { - lua_newtable(L); - int top = lua_gettop(L); - unsigned int index = 1; - for (std::vector::iterator i = path.begin(); i != path.end();i++) - { - lua_pushnumber(L,index); - push_v3s16(L, *i); - lua_settable(L, top); - index++; - } - return 1; - } - - return 0; -} - -int EnvRef::l_spawn_tree(lua_State *L) -{ - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - v3s16 p0 = read_v3s16(L, 2); - - treegen::TreeDef tree_def; - std::string trunk,leaves,fruit; - INodeDefManager *ndef = env->getGameDef()->ndef(); - - if(lua_istable(L, 3)) - { - getstringfield(L, 3, "axiom", tree_def.initial_axiom); - getstringfield(L, 3, "rules_a", tree_def.rules_a); - getstringfield(L, 3, "rules_b", tree_def.rules_b); - getstringfield(L, 3, "rules_c", tree_def.rules_c); - getstringfield(L, 3, "rules_d", tree_def.rules_d); - getstringfield(L, 3, "trunk", trunk); - tree_def.trunknode=ndef->getId(trunk); - getstringfield(L, 3, "leaves", leaves); - tree_def.leavesnode=ndef->getId(leaves); - tree_def.leaves2_chance=0; - getstringfield(L, 3, "leaves2", leaves); - if (leaves !="") - { - tree_def.leaves2node=ndef->getId(leaves); - getintfield(L, 3, "leaves2_chance", tree_def.leaves2_chance); - } - getintfield(L, 3, "angle", tree_def.angle); - getintfield(L, 3, "iterations", tree_def.iterations); - getintfield(L, 3, "random_level", tree_def.iterations_random_level); - getstringfield(L, 3, "trunk_type", tree_def.trunk_type); - getboolfield(L, 3, "thin_branches", tree_def.thin_branches); - tree_def.fruit_chance=0; - getstringfield(L, 3, "fruit", fruit); - if (fruit != "") - { - tree_def.fruitnode=ndef->getId(fruit); - getintfield(L, 3, "fruit_chance",tree_def.fruit_chance); - } - getintfield(L, 3, "seed", tree_def.seed); - } - else - return 0; - treegen::spawn_ltree (env, p0, ndef, tree_def); - return 1; -} - - -EnvRef::EnvRef(ServerEnvironment *env): - m_env(env) -{ - //infostream<<"EnvRef created"< trigger_contents; - lua_getfield(L, current_abm, "nodenames"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - trigger_contents.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ - trigger_contents.insert(lua_tostring(L, -1)); - } - lua_pop(L, 1); - - std::set required_neighbors; - lua_getfield(L, current_abm, "neighbors"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - required_neighbors.insert(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if(lua_isstring(L, -1)){ - required_neighbors.insert(lua_tostring(L, -1)); - } - lua_pop(L, 1); - - float trigger_interval = 10.0; - getfloatfield(L, current_abm, "interval", trigger_interval); - - int trigger_chance = 50; - getintfield(L, current_abm, "chance", trigger_chance); - - LuaABM *abm = new LuaABM(L, id, trigger_contents, - required_neighbors, trigger_interval, trigger_chance); - - env->addActiveBlockModifier(abm); - - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } - lua_pop(L, 1); -} diff --git a/src/scriptapi_node.cpp b/src/scriptapi_node.cpp deleted file mode 100644 index 2fea50fa..00000000 --- a/src/scriptapi_node.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* -Minetest -Copyright (C) 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. -*/ - -#include "scriptapi.h" -#include "scriptapi_node.h" -#include "util/pointedthing.h" -#include "script.h" -#include "scriptapi_common.h" -#include "scriptapi_types.h" -#include "scriptapi_item.h" -#include "scriptapi_object.h" - - -struct EnumString es_DrawType[] = -{ - {NDT_NORMAL, "normal"}, - {NDT_AIRLIKE, "airlike"}, - {NDT_LIQUID, "liquid"}, - {NDT_FLOWINGLIQUID, "flowingliquid"}, - {NDT_GLASSLIKE, "glasslike"}, - {NDT_GLASSLIKE_FRAMED, "glasslike_framed"}, - {NDT_ALLFACES, "allfaces"}, - {NDT_ALLFACES_OPTIONAL, "allfaces_optional"}, - {NDT_TORCHLIKE, "torchlike"}, - {NDT_SIGNLIKE, "signlike"}, - {NDT_PLANTLIKE, "plantlike"}, - {NDT_FENCELIKE, "fencelike"}, - {NDT_RAILLIKE, "raillike"}, - {NDT_NODEBOX, "nodebox"}, - {0, NULL}, -}; - -struct EnumString es_ContentParamType[] = -{ - {CPT_NONE, "none"}, - {CPT_LIGHT, "light"}, - {0, NULL}, -}; - -struct EnumString es_ContentParamType2[] = -{ - {CPT2_NONE, "none"}, - {CPT2_FULL, "full"}, - {CPT2_FLOWINGLIQUID, "flowingliquid"}, - {CPT2_FACEDIR, "facedir"}, - {CPT2_WALLMOUNTED, "wallmounted"}, - {0, NULL}, -}; - -struct EnumString es_LiquidType[] = -{ - {LIQUID_NONE, "none"}, - {LIQUID_FLOWING, "flowing"}, - {LIQUID_SOURCE, "source"}, - {0, NULL}, -}; - -struct EnumString es_NodeBoxType[] = -{ - {NODEBOX_REGULAR, "regular"}, - {NODEBOX_FIXED, "fixed"}, - {NODEBOX_WALLMOUNTED, "wallmounted"}, - {0, NULL}, -}; - - -bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node, - ServerActiveObject *puncher) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_punch")) - return false; - - // Call function - push_v3s16(L, p); - pushnode(L, node, ndef); - objectref_get_or_create(L, puncher); - if(lua_pcall(L, 3, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - return true; -} - -bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node, - ServerActiveObject *digger) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_dig")) - return false; - - // Call function - push_v3s16(L, p); - pushnode(L, node, ndef); - objectref_get_or_create(L, digger); - if(lua_pcall(L, 3, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - return true; -} - -void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_construct")) - return; - - // Call function - push_v3s16(L, p); - if(lua_pcall(L, 1, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_destruct")) - return; - - // Call function - push_v3s16(L, p); - if(lua_pcall(L, 1, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "after_destruct")) - return; - - // Call function - push_v3s16(L, p); - pushnode(L, node, ndef); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_timer")) - return false; - - // Call function - push_v3s16(L, p); - lua_pushnumber(L,dtime); - if(lua_pcall(L, 2, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if((bool)lua_isboolean(L,-1) && (bool)lua_toboolean(L,-1) == true) - return true; - - return false; -} - -void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, - const std::string &formname, - const std::map &fields, - ServerActiveObject *sender) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - INodeDefManager *ndef = get_server(L)->ndef(); - - // If node doesn't exist, we don't know what callback to call - MapNode node = get_env(L)->getMap().getNodeNoEx(p); - if(node.getContent() == CONTENT_IGNORE) - return; - - // Push callback function on stack - if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_receive_fields")) - return; - - // Call function - // param 1 - push_v3s16(L, p); - // param 2 - lua_pushstring(L, formname.c_str()); - // param 3 - lua_newtable(L); - for(std::map::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; - lua_pushstring(L, name.c_str()); - lua_pushlstring(L, value.c_str(), value.size()); - lua_settable(L, -3); - } - // param 4 - objectref_get_or_create(L, sender); - if(lua_pcall(L, 4, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} diff --git a/src/scriptapi_node.h b/src/scriptapi_node.h deleted file mode 100644 index 665b58bf..00000000 --- a/src/scriptapi_node.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 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. -*/ - -#ifndef LUA_NODE_H_ -#define LUA_NODE_H_ - -#include -#include - -extern "C" { -#include -} - -#include "content_sao.h" -#include "map.h" - -/*****************************************************************************/ -/* Minetest interface */ -/*****************************************************************************/ -bool scriptapi_node_on_punch(lua_State *L, v3s16 p, MapNode node, - ServerActiveObject *puncher); -bool scriptapi_node_on_dig(lua_State *L, v3s16 p, MapNode node, - ServerActiveObject *digger); -void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node); -void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node); -void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node); -bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime); -void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, - const std::string &formname, - const std::map &fields, - ServerActiveObject *sender); - -extern struct EnumString es_DrawType[]; -extern struct EnumString es_ContentParamType[]; -extern struct EnumString es_ContentParamType2[]; -extern struct EnumString es_LiquidType[]; -extern struct EnumString es_NodeBoxType[]; - -#endif /* LUA_NODE_H_ */ diff --git a/src/server.cpp b/src/server.cpp index 0d724b1c..12695e1b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -34,8 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "script.h" -#include "scriptapi.h" +#include "script/cpp_api/scriptapi.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -646,7 +645,7 @@ Server::Server( m_rollback_sink_enabled(true), m_enable_rollback_recording(false), m_emerge(NULL), - m_lua(NULL), + m_script(NULL), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), @@ -761,14 +760,14 @@ Server::Server( // Initialize scripting infostream<<"Server: Initializing Lua"<loadMod(builtinpath, "__builtin"); if(!success){ errorstream<<"Server: Failed to load and run " <loadMod(scriptpath, mod.name); if(!success){ errorstream<<"Server: Failed to load and run " <initMapgens(servermap->getMapgenParams()); // Give environment reference to scripting api - scriptapi_add_environment(m_lua, m_env); + m_script->initializeEnvironment(m_env); // Register us to receive map edit events servermap->addEventReceiver(this); @@ -874,7 +873,7 @@ Server::~Server() /* Execute script shutdown hooks */ - scriptapi_on_shutdown(m_lua); + m_script->on_shutdown(); } { @@ -927,7 +926,7 @@ Server::~Server() // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<getAuth(playername, &checkpwd, NULL); // If no authentication info exists for user, create it if(!has_auth){ @@ -1964,10 +1963,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if (raw_default_password.length() == 0) initial_password = given_password; - scriptapi_create_auth(m_lua, playername, initial_password); + m_script->createAuth(playername, initial_password); } - has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL); + has_auth = m_script->getAuth(playername, &checkpwd, NULL); if(!has_auth){ SendAccessDenied(m_con, peer_id, L"Not allowed to login"); @@ -2469,7 +2468,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::wstring name = narrow_to_wide(player->getName()); // Run script hook - bool ate = scriptapi_on_chat_message(m_lua, player->getName(), + bool ate = m_script->on_chat_message(player->getName(), wide_to_narrow(message)); // If script ate the message, don't proceed if(ate) @@ -2601,7 +2600,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::string playername = player->getName(); std::string checkpwd; - scriptapi_get_auth(m_lua, playername, &checkpwd, NULL); + m_script->getAuth(playername, &checkpwd, NULL); if(oldpwd != checkpwd) { @@ -2611,7 +2610,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } - bool success = scriptapi_set_password(m_lua, playername, newpwd); + bool success = m_script->setPassword(playername, newpwd); if(success){ actionstream<getName()<<" changes password"<enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false); } if(n.getContent() != CONTENT_IGNORE) - scriptapi_node_on_punch(m_lua, p_under, n, playersao); + m_script->node_on_punch(p_under, n, playersao); // Cheat prevention playersao->noCheatDigStart(p_under); } @@ -2925,7 +2924,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) /* Actually dig node */ if(is_valid_dig && n.getContent() != CONTENT_IGNORE) - scriptapi_node_on_dig(m_lua, p_under, n, playersao); + m_script->node_on_dig(p_under, n, playersao); // Send unusual result (that is, node not being removed) if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) @@ -2965,7 +2964,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Do stuff pointed_object->rightClick(playersao); } - else if(scriptapi_item_on_place(m_lua, + else if(m_script->item_OnPlace( item, playersao, pointed)) { // Placement was handled in lua @@ -2997,7 +2996,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) actionstream<getName()<<" uses "<item_OnUse( item, playersao, pointed)) { // Apply returned ItemStack @@ -3056,8 +3055,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Check the target node for rollback data; leave others unnoticed RollbackNode rn_old(&m_env->getMap(), p, this); - scriptapi_node_on_receive_fields(m_lua, p, formname, fields, - playersao); + m_script->node_on_receive_fields(p, formname, fields,playersao); // Report rollback data RollbackNode rn_new(&m_env->getMap(), p, this); @@ -3081,7 +3079,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) fields[fieldname] = fieldvalue; } - scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields); + m_script->on_playerReceiveFields(playersao, formname, fields); } else { @@ -3188,46 +3186,46 @@ void Server::setInventoryModified(const InventoryLocation &loc) } } -std::list Server::getPlayerInfo() -{ - DSTACK(__FUNCTION_NAME); - JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - std::list list; - - std::list players = m_env->getPlayers(); - - std::list::iterator i; - for(i = players.begin(); - i != players.end(); ++i) - { - PlayerInfo info; - - Player *player = *i; - - try{ - // Copy info from connection to info struct - info.id = player->peer_id; - info.address = m_con.GetPeerAddress(player->peer_id); - info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id); - } - catch(con::PeerNotFoundException &e) - { - // Set dummy peer info - info.id = 0; - info.address = Address(0,0,0,0,0); - info.avg_rtt = 0.0; - } - - snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName()); - info.position = player->getPosition(); - - list.push_back(info); - } - - return list; -} +//std::list Server::getPlayerInfo() +//{ +// DSTACK(__FUNCTION_NAME); +// JMutexAutoLock envlock(m_env_mutex); +// JMutexAutoLock conlock(m_con_mutex); +// +// std::list list; +// +// std::list players = m_env->getPlayers(); +// +// std::list::iterator i; +// for(i = players.begin(); +// i != players.end(); ++i) +// { +// PlayerInfo info; +// +// Player *player = *i; +// +// try{ +// // Copy info from connection to info struct +// info.id = player->peer_id; +// info.address = m_con.GetPeerAddress(player->peer_id); +// info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id); +// } +// catch(con::PeerNotFoundException &e) +// { +// // Set dummy peer info +// info.id = 0; +// info.address = Address(0,0,0,0,0); +// info.avg_rtt = 0.0; +// } +// +// snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName()); +// info.position = player->getPosition(); +// +// list.push_back(info); +// } +// +// return list; +//} void Server::peerAdded(con::Peer *peer) @@ -3752,7 +3750,7 @@ void Server::SendPlayerPrivileges(u16 peer_id) return; std::set privs; - scriptapi_get_auth(m_lua, player->getName(), NULL, &privs); + m_script->getAuth(player->getName(), NULL, &privs); std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PRIVILEGES); @@ -4472,7 +4470,7 @@ void Server::DiePlayer(u16 peer_id) playersao->setHP(0); // Trigger scripted stuff - scriptapi_on_dieplayer(m_lua, playersao); + m_script->on_dieplayer(playersao); SendPlayerHP(peer_id); SendDeathscreen(m_con, peer_id, false, v3f(0,0,0)); @@ -4491,7 +4489,7 @@ void Server::RespawnPlayer(u16 peer_id) playersao->setHP(PLAYER_MAX_HP); - bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao); + bool repositioned = m_script->on_respawnplayer(playersao); if(!repositioned){ v3f pos = findSpawnPos(m_env->getServerMap()); playersao->setPos(pos); @@ -4571,7 +4569,7 @@ std::wstring Server::getStatusString() std::set Server::getPlayerEffectivePrivs(const std::string &name) { std::set privs; - scriptapi_get_auth(m_lua, name, NULL, &privs); + m_script->getAuth(name, NULL, &privs); return privs; } @@ -5079,9 +5077,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id) /* Run scripts */ if(newplayer) - scriptapi_on_newplayer(m_lua, playersao); + m_script->on_newplayer(playersao); - scriptapi_on_joinplayer(m_lua, playersao); + m_script->on_joinplayer(playersao); return playersao; } @@ -5176,7 +5174,7 @@ void Server::handlePeerChange(PeerChange &c) PlayerSAO *playersao = player->getPlayerSAO(); assert(playersao); - scriptapi_on_leaveplayer(m_lua, playersao); + m_script->on_leaveplayer(playersao); playersao->disconnected(); } diff --git a/src/server.h b/src/server.h index dcd007d2..3906f43b 100644 --- a/src/server.h +++ b/src/server.h @@ -43,8 +43,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -struct LuaState; -typedef struct lua_State lua_State; class IWritableItemDefManager; class IWritableNodeDefManager; class IWritableCraftDefManager; @@ -52,7 +50,9 @@ class EventManager; class PlayerSAO; class IRollbackManager; class EmergeManager; -//struct HudElement; +//struct HudElement; ????????? +class ScriptApi; + class ServerError : public std::exception { @@ -384,7 +384,7 @@ public: void Receive(); void ProcessData(u8 *data, u32 datasize, u16 peer_id); - std::list getPlayerInfo(); + //std::list getPlayerInfo(); // Environment must be locked when called void setTimeOfDay(u32 time) @@ -492,8 +492,8 @@ public: // Creates or resets inventory Inventory* createDetachedInventory(const std::string &name); - // Envlock and conlock should be locked when using Lua - lua_State *getLua(){ return m_lua; } + // Envlock and conlock should be locked when using scriptapi + ScriptApi *getScriptIface(){ return m_script; } // Envlock should be locked when using the rollback manager IRollbackManager *getRollbackManager(){ return m_rollback; } @@ -746,7 +746,7 @@ private: // Scripting // Envlock and conlock should be locked when using Lua - lua_State *m_lua; + ScriptApi *m_script; // Item definition manager IWritableItemDefManager *m_itemdef; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt new file mode 100644 index 00000000..9cb8a19b --- /dev/null +++ b/src/util/CMakeLists.txt @@ -0,0 +1,8 @@ +set(UTIL_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp + PARENT_SCOPE) From e464f193566b3c4173b44d3bf6c82aa032d9fed7 Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 25 May 2013 19:23:10 +0200 Subject: [PATCH 33/41] Fix itemstack:add item not working correct --- src/script/lua_api/l_item.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index e9997d48..730dfd49 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -227,7 +227,7 @@ int LuaItemStack::l_add_item(lua_State *L) NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - ItemStack newitem = read_item(L,-2, STACK_TO_SERVER(L)); + ItemStack newitem = read_item(L,-1, STACK_TO_SERVER(L)); ItemStack leftover = item.addItem(newitem, STACK_TO_SERVER(L)->idef()); create(L, leftover); return 1; From 730d316efe18c57760cd5ec518a6191ac271ce61 Mon Sep 17 00:00:00 2001 From: PilzAdam Date: Sat, 25 May 2013 19:56:48 +0200 Subject: [PATCH 34/41] Higher resolution menuheader --- README.txt | 14 ++++++++++++++ textures/base/pack/menu_header.png | Bin 56279 -> 156026 bytes 2 files changed, 14 insertions(+) diff --git a/README.txt b/README.txt index f264ba6b..43b31d70 100644 --- a/README.txt +++ b/README.txt @@ -259,6 +259,20 @@ 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 ------------------------------- diff --git a/textures/base/pack/menu_header.png b/textures/base/pack/menu_header.png index b95facbdf0e5511fd53044e2d80d0a3c9aa30704..c22192d0f8d8a7c2e0dfe0b624b31f5c5218a27e 100644 GIT binary patch literal 156026 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

>^4cNlCL0y%plJt8o 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

    yCaY2CO%bhTo? zY>idMebkIrL3paJ?8v+YT$du&9N=Lq&Rya}1+q!edS2LyvVF~SwM^At?#S54f28-qfQ1qmC``S#gVi6KN{{nkDroGD z@Y{t9X{-}?_1cIUTLiJljPK_5|H$S>>I9+nZkQQQ?W~J$(;s%Mm0B=j$zq1E zh!7Z7x2=6_Z-EN?_IKpiiK#r(jDEcjm#%kIM+N!#QQwUszRD^Z&)(VBo4+H(fSqRU zWlyx8JwAiL9($d5JeiPRr$U@|eJQ5RX;(3rO+$5TYk7HGM1(bORlH3-%saL!hvc|q za|-q$L3!ZkyfDc)M(F|bo9t1lc*2EW7rT)9b{lK{O#Pn9;k||X_I)LHuCy#XjG2S% z#N-{y-Va^<#ic(e?@bx+hXL)|cpLkzGw)Fo5Qx)03&U}$i_d7q_EPeq`t)bczj)%q zD;`i(c&1EMy%wgj_g+3X{_Z1s$$@e|5``h11T@xMzCdoaVf^V1HnXRH{2n)7aRS8) zsNRt36+4ktj0nA3rvCPk^SeHbVRku}u+jL;r%I($x#epD-AlvjMvI zr3btk5>tv&^Tu`D*FC4+l_#}-NEDFjhon(Omzk#zio3!}D9TS%W;xCeMwf{Nv2V*u zBB3g_#12E=znx<6RKp67-G^c9zA{3*p`xVnO{JS7CTYP{7dF0e+UdbHo15C{kA0dj z6&3eHyNF+4qUvLeI!(srZ{EAE4E}J|=D3Ldi7Dz3&`==%8Itku2nh1tmzdKsXmA~`1$66a0KHtEEBf$F{g%40pfl@l3JMXuBb z2#?iECi)jlyx)mxEq*e$AGkH?crBNA`lh@3w_bmH%%4GCkY^MiKfvoRM>YevKle?p zKdj+{bo1h;W)CUzNPj))RFj<0^~IY&+JM19)RpGlSY7)$4FLF1?)BEwg0O3yo@N#Y z>jS0{#yKo{i?H^(a`Vw{6kh7?kPMB`#D=c___kmc87Xv8Qxam!ekzd1a?OTq4Pkvs zX6(XKZW+NVz9D}(4sZ5N*mpwc)7A%sD-rIwq2RZQzLyV_C$A#ej4J)Y2fWc`y&rVd6Th1M<2tgTOGAd3IX(XBL1oM`d+A=;c3sh^f}|q?x`mk96kQg z?H8roL@vd`b;VE2EOud(PoBDYdV!OlQ6m$pJA4FzVPj262G>a6?y2V?td2>Y>>5*Y zzqpdO+~G4+jC^}G0{Il-R+(9U27l=atFe>?WJUwQW(R8CjHTJA1O5F9i4{vJ4p|QE z&N+yEaA1>kb~V%X!zB~Fvdc*M6WsRv@T4?!jYDq43pBarx%1RHRTBO)I5v{Fx^`OD zie}6s=H^u)_lR^GBAPr8{^iYgw15W91@7Hpg%Zzai|H1Qq{UV^)Pr;X#^g+}quWMn z`j2c%n;NJ4K7zjrnIDwh_GJF|m2%2*d0q!#<~PLP4)KhMNBsV&X@&7>y<|Nmaz*I{ zIc}?@#$(dHO;2qfoyxE7bONX!tuywwAV9C`9SObME~pR4tLy`(kTmM{dk?lk=?*Zd=*AZ zbl7A6H|eSPm+{->3up#o(H)lbI{}m0z|EuUOUesqzu0)zV08Rw4AU?8N9|0ci=iF- zT)LU(yN`zNHE4fYRoAhMe$8^@_I3M z|7$3QAWU&!nqm}7lX&}|SChA30>4|EH@F`#Px)4H7C7CXAY?t%X3B1P$bMrz3&RXZ z-Ae;M!z2ay{t=Hrc4%&@VVj`ENKXMT&ojC-C@m=(baKvdi@)z@2+FjnBNf!`@?9`Z z!fWCR>K~cx+ShM#y!L0hOxZ@AI^vzgP(snc^6^6F+3{qh z6-voknG!I6k6{3BYhcEwhE@Imye0cSD0}d`|LDinGyuGR5bY>YAdS4wWAF6P^oY7N zeaUv}cI*rBuHvy<;%rDOU%V$jLZm6|{JG_M9mq&xywWHTruF4|m0DCQOlrLJlTKOa zh5pf;)$o$`oE0W$n*mu%C5ew|PqLRxpkBU{`O+-RFqqlxwn1pl+*r&OGVoqnd79?P zQ$KQTe%UbFx$T3@PfC{xSSK063z<$8zbmw4?KS1G8vniLOGt0C2-*pcRuj>5Nz&a% z+-aU?bd0}{cAvOmJTJBMFd?I%Vs7&kA zOx$k~kP+)-6sT~?k@NJOnTw6G-}G)Ts8)~$(N$Zg?@3WEeIRg3{>xqe6`7nRSw2)% z{#zw@{Z0GjTX>N&xr-5(Z?#f5A35E-THKQzbu&wLY^FeJx2k1UgR+FUomF266}6USYZj}^muJz6*^ z%J$~Wy-*$hp0Ti~hUJqW&P-kEPKe46N{3)*3j3*(0K2?J)hm+eo?rkPE3~rcs+(P3 zS0NAd8ZDf&4jxEaas8JqY?lHUwBX!q12-q0+Sok7&#E0k3pOS#<$ft&AlM??n zBAxF>?nWQ{8yR!sQ=qrs7U|gAgICfYKq9Q1I7GwsJU&nrXf1W(PKaaMql6LV9}5Nv z>KoEUzC%?YVdST&I+9W(QIIw7v-{?61>UZ)z?C57m@$hL)RtV|+MC$H&U#v&Q=mJ~ zy^EOjM%FxAZ_W5YjRrcs2aYdh>3LRO{s>tOJ^fA9{2$EpzrkE3_&PT-(cN$ht_W!7RGAXUvJL5w&%;Z z;-CmlMlp%!k#K0>tMFlPyBp@uTW&1tO0btfd2j1$Q=2FCzbuFr?HpRnk=r%66&k

    pjMJi5f`B1v>EpeqX<6v8-IZ$9Yo?ctm{aQ~);rjQWQEPLZB8_*kEF=4z`bdTfPg{wEsbQ8(WF~WIAVFFNRQUxbE zR#)^wFQ|xHB)_wpkN8Zz!-DmuaI5NqtmQkClui}(&C@u{si*T`#p~OpUiOnH!bjvp zWwsvIt6ZFb9q9yV*HnG_wdr)qrd*`NUbQ&`)TO}1ZYgm!26LvRZ_=#&hMx0AM!)d? zZhmcb>GQ;YnHT#Pmm16OE+s=_f_CqdKi~uV(jx!HmkdI|&MpJBHqLb$IundEZiNV^qwFGWs!gAi5Q(0k z%arZhg_T7&a7`j&Ce3d9oxw@~^m|9E-^D%7ek&-cyK|N`t>|z@-}7{$!D63F2h~h> zS`e^*Gph-eyI$cT^-d<@Oghq-2R2~VyeD?vrN4gFp}>juUYlPE!KV;dIT`bO)yO)y zFII_PPW`E;y}9_oIw?lo$sFtMN^scUBG`TIDaEq39+yAGYutzE@&e#^`LwVy?`e7q zZ<%t+u8=&DN)v}M-tdtxy>Pr&gJ`uNQSHml?L~doU9&tG#5lLegI>#|kjS1{FsB5& z3ZfPWh1gABlUk)Id9T;$8q})m<^q{ta)Dkp)tcrj3+e~kdym!-V_W(*Wq?6TJ-VTk zPP^zBy-9b%=U%4*SV)G__Q-CRP!-X1zZm}$ga9j)cdqz?An^_Ar5>r{2>t(O0a6Tw z*JpP}B~X*}CuMVXv?XC5Ren-U2vq^`@8~J)7vJdULZ-3i`fh*lzoL;b1y9x@?J3v! zOhuuQv2kOb^AfzOx^M;df)hqrxU-<*xj6!KKCE43dFDQ)Ch7WinG!5)YIxHaAI5YfVKG?HxT z`KH6^m_ax*3(bO*?u!3Geonl#!Z__lC-Ql9CgdMfJn|JmcWxm`7=_SYE&u0xUvnn@ z^s>l+o*uK3_{$HXI*dicqL4Ga7E&>BHv^T`?;tta`3eVaTRjwIa*Ksvzg+3ev6s*JGPjTg3Wn8O+Kxiiz5*Pe;WV#}y9Zq*L}}TaDOs zaZ@C(2j92_^)wQBtL$U>!ac{Bgf(tJKGCvV;(O}rsVd(`vW~Y_8}9t4dI*iTgf

      vpNHqxi?4+Mr|GiVq4>pg?zEt4cD!=8~U#z=x&~*}a_FAB5{5CUX z!%KqnW$;u6-wxrdUdq$cSjnh3mUy{f5k?>Evv|d5NM_Hkk)t8rV25qk7ErJouPh z9{syMn=felpD4tse8M?v${H(MMVM|5+YxJUZ4E5$t0@q7W^%QK8#;bU4%TpMI+78Z zVhnh_SP7hAsbiB7iH7?$X@^C$Cq{Agce0jxj~l<9HZ7jne-~IOT`#{wb;$lUuN;RU z-0tAN8t%*|${0!(KeDZ924{T6g+d%%20n|2&HB-(*PB^qZbBN5ghFjWLQP^&R-KIt zK&VTBzGNsWm0}25CZVMNo`H>xo%NYRZVo9FW%ZaL2O6}1mM>k!4wr_WFSo^qGgqIT zj$c!o_1|7cgIg>QviN7jD>tXbhwF*C;5pofQ~a}|Oy0RQ|R6tbx&=7 zUB!sTbueP4fUGx0 zLv17tcAKKE3va{R<6*iJD$pfu!Zl;q{;7SX)$GjLj|5XcFP@ETI52y0xshlbh^Yw_l9a7w%{l`r>>L!n)DFhXpd!ADXmIe5(6)<|e11 zi~1tp3685and3Fwyr}!v#jgG{dz4u2gYS4h(ZrPT=CG(3JGFKAm+vOrc(W_6q2Ria zT6e_HZ<^B4x+UA1-ci^=&1ineSA!Mh6UVJJ_9O z{_+%$=;}^M_Nm_@x3vEa$K1b`duDUkN{3H+a?|G$+?xLsx{n!f1uzA#Dp6MPF@3Ax z6(lyTwV%48lnDU1`JPgx&+=A|+{vBzWc*5FVw8UI@7e9q*R6&wGHAUDpG!0u#uaSm z!|F`rP(ez&e8H4+ih8G+>7^aQ+CB3l$=5zDgNs733zN7vBZT`sHrzecu?_O2yIC?j zpYKD`MyFzf)y$JQCB9dUppren>->652bGv2ZSt#h?y1MqFgT;!Fwcdf^N>i07sC|O zN<-D*20f~tB9K{IvhS<%MU$L}0Qh(JSME%4{pJ2Yt!H^D%N__nUHwFdyeIZc?ubuC ze0k?*ehts8$1~f2DNTT2Y*b0I`p>>P0^g#*4a7NEk!sUfEeWa>Xu02Q1T8$9wb z5c8=9s!wVS>gE2f^^fZv8`%+akr7*UzP_=!A>$J4ACZxhPnT5%RIboc_w}2$hgA2= zzBA|Ua+kDc<`FD$woyQ^Yj5;f@5&-u&ki$Y;@}^d;6!)F4?A`JN6X!$9|n_H`u3GZSGwUjIgyvz{syxHDO=ZHnw~7w0{9IA&Kkr5N1O>MSj* z`5%e{S&DAwXr|nPhEw0D*I*LpThHMG>B{U=P>zay^)%dSkf^bN%V2vy7(8n@*Uq z?Ymz1vRq#FFHJVyOtcC(K_k=7VUIgL;tlZ1FhPwTv)V)KTAI6 zfj`nXFd&}eZuY=Cp^r6u!1JlI6UM2Q$Aw$(KIzRgWu>>AmWYzOo@SdxynIji(H-83 zcbC;_l9ywC-bq~;y(e_ylGZoT;D4k?vZjrf#iCE#oMfAzXsXGo z$Sir^04)>Fr(z4adbIR}ft!$QP0&(cCCI3M=T7=aj)IO=6! zwlgCZ;^{%V1>2@^IxdP1zr8a8SMns zxh%;p>p#0mWJXIKF$dQ@+)RmXzINEN^$_9zG?0EJCwUSj*gFSO7u1oMy6b5SBN z;XXJX1yWQ41to?dhle-7BMD{TQ||Po*_7S8KuVU6eNzoWDN?-4wf6l<^ZG&Hp+gwr zj`+cBu1oJ-1A(N+7_tzp8zW-$&m9i8Scw-FijVhzzk<`Kr_v~zBWPL>6$PPr#hZF=dvn6>pbBq?%BlB(>p(C{07DgB%H~YT`D(iJw zBJc9-&pxXCS!e4e4jd3qpY_AOSNO~1Sks*cdeHw|W7Hgurov|K>k|QuZ7mlyRhN=h zpy1>>E%&*>CGcc!pV3X_FwWp4Wl(;Y69@1BKlZ1+$HVhC50-_=6&@;pO!12bhhA)f zAQKH-^XLDG&&?5Fdo?gS&^j}=#N|1D-OceIum>o<8@GYQ`wJP>fFB2~O*F?ZB%-3 zg1+vqAvqc*dK_}`r=SV{iTY6QOYyklwlVSH{I?}^n$C(MpIfM}p*}u}8 zb>C%#Vl}WgX_36rokaf1`~NLn{(4^ccq2opqSN2Y)cEFYwf9+Ok$Cc5WdCs0gpjs} zYW~3Ehf&-i#Ed(m)dTql|7gugc&2hiA>)Crgog_4@RObQ*3(09JQY=9KXcBf^G4Rj_x`VWuj-B%sde872x!I9c|##g}kHNU|%k;bt0kv)}PxGeu|@ z=2?+z?ie^#i*(BDvAVVo>-5tpNQnn}^>%hZ-ECUi>(%Pi7W1W|H0yuIpw0_bZoZUA zKZ++=yQBNGY1(=tE({sm-bFEHXs>yMxB>FhGZUD7NkQ$$1tt zVk(L$(cybip2|;dPr!n&sEkc@Dysc~T^ysVZqhby`)^F{hU|QLstV zs2fVv{o>X=AE}~{BD7Tf@-I?BEyBPD>8{ud7?WTP^5?i##tIy~`>q9*dRFe!V2B-n z_C{xVgwn+!q&B}Zk!@)>g1}@V;H4jG{EmF&*E+|`!c}8#MKDs_Mc}#g3BOYiya5o- zh;PVU1+w~3Ir=jj8U<4m$(-7mMhX$A@QveI;9I=lHlwl~zq4Xql{6G~RDyqg>hG7| zdix~*1f7&1tt#A0)tvcYtTLl{poDnV#_a7+%+P zZrv$9gNRlmk5jao67{m!$nGLG_nyOlO$>XRDQRj`LK)REi^PsXCuG88!~$Tfl$P(sNe2a z!*3U6751wYvQ=K)3|M)GnyQXM_>8}NzT%>^PN910x|0`)D(v;~8a@uynrt;Y(|b(; zEj}ysQtn35SJEkOk<+^(H4=R&hmo{`)hg8L4$dWPnW2E%e}#E_*-VUh$w-}NuRX>^ zDLFtiyXU27YGA-qc+wumSkP^ z(w=e4lT0>KPW!!N06}4VTl^^@Nte^w--KQBojf$=;awL4uCR}uD@f0UqDo`Xn^eWn z9Uce!L~9I(WhJY<861}yNc=;TSioDcUbs89JS9ksYAMx^$O|!FDPbqi0(y7XG&Sdm z=O$Z1lG>ii)g?PfMXBo0MarvEAXpO6r(`5N1-v4ek2H}tzr5*^u1=bU)MBi|BA(_3 zE`QdLuF3Rg{?U*VNEz$8w&enxJX24(nd#H(99D3lDJy+Tu1kYOslRbJlWj<0!kz}s z%PEA&mt>l$GN91PsoT7uM$Me<84$Y8n{KaXN_nCy(WY_mMl)qv%QWS)u&3Q{b;1L5 zL@&`zT#taN8aQXN`rb!fW~Yva8cD7p+`wKmz^GS_kaLO7*^I%*kw^GMAb9Fs`s9l# z{>sHV&sCHv<6RA>?ucm`Bh?Iov@B88C7sbT7%s(4cYD>*ng-Q>?s5@5t)9qK08~bw zw?XJ|RirZk>hh(D*Sfw%r@_(6rfQmVG6P!1JZa}Ov;-&yz`iw0GaW3Ft3ZWehMbM@ zD(hTEx6i~$#im3lpO91VQaTU*-u_7$mD#~}LMg=eMK(O?)wL`&_lzTADf@AjTEdKz zC(U;uk<(#M`*+h#EQ7G_7zJF#SFTc|cZ{E4?I84L{qoV5w3=j#L;wPRJ?8VHF87&A zu$S^lWLvxBEqQ*tf-lpm=v`Lf8&^hZo1SFZ77gyBf7YIE&pG7gz&4scDUGB^LLkr39Ph2y1i!!yVNVWf8SARFK~N(gTp0CddCUL$!ktOPP(Y|P{Sa%6kL zw;zxZ8qYagi(Oem35_D+M0E$}DzcQGk z#JkU&=u*c8lgJMd7y>xkY|1lnDYE#!@>ETk_V=7j_O$QkpM6S{po= z>&4;?JN|qNh&4vvKWE^;zNb3BUR4#WRNimv_{a#lUM#H&pc_gTI^h-AGt2om%sk#` z0qy*srE7p%TnG3f+j08L59Z56^#j)`Y%#t@svGui;fJ225d6(XZiqVtv@E*3l&1od zmUC3S5MY8P$TM3L(X+}}f)5+7-jI6OZ8<0JHIiaYUzp`mVj9}nN4q1UvS;W?lzINb zi9D~bFX(B6urFAQCSn*DT@{Wnjdu1awS^xm${@^C%d9UenHhuEq)fe?HqWauaq zNXaEtiBJSGkHyAtGmN*AfA7rm)@VaV$t4LK=`Q1M~mQsMdfvZD5w@0J)R=^wi9<@Oexlq3dnm4=D&B~_$W8)TMrv7|wFB3I~7+)*|t zpyojw*I^>gH}k>BNq5@)sAg#|R>Esj9kjY_eA%9vQ>Glzo=1tj(Y*Mg zJJ?jnVWGSiX;54{+K-Qk5p*@h-e#7XxtG~fL(ni`8yqXEeLt(~;*06h*8oJEmuDOo z4j;Y3_NMU(4;NR##y39ZU4VucoQvq!T%IJ$qsOjo z-(4i&xzZ^-TuuHh+~<8e&Drqsd8$x}Zy$16G5aVSqAJuB6RGM>vS9M`3aQ`EX+#?F z*skN^uQ-MhdHwYBsdeIZo0(nh=?FbIbhj;U7Lx1`n*7G)Nq42gd72KHM2#f0+D2lo zmQS5N414a}_EZ=248?mE8716Zy;g8{wC&BroN{Q!$Yjt@^g$dvL?`aLuiAOtSsqZR zJr5RMZ)nPOg{2%w~gBR^J-*!(B|;u4QZ!a zs^O)QOv|FcUgFS%By>i-a`zErNtCw0TLONs2Vr;}3_1c{2AlBCua`m79RTHEkoy7` zFZ>x&6uxWNwXok`H3*h5x9_6rpRe zUEj@mGTE*oq3C9`)*=4Y<;1fkJ%u5#inSE)HaN9k6^G&zhb8Xw}x$UXWvG;&*qVX2%o>X(UTC{EA zxxwQDwIeW?{$bvvu+Evm~c)dPkG*qQWX`7{*E3ES1_5+q~kV!;MkMMHASQ zo66^Hu>ofwOfKSp@EqJ?dLAz7SdWtYX(r*EIC&KG#etBw^+)Q7Q07p3A=zx4 z7_GI-T2@Szv100k1iNGIT5AXA2-A<223jfj7oO>U+Kscy7Y=Rg`X2DF^y=S*B*;wC zXW$+NXy=-BG;WaF0l>;si)%^>xV|(WU$%6@xO#Hqd~>?#R8e>pN+1}JUI@Cwc1EJK z^XmHC`6ri5?gma(;p}4h@{w_S#Gs5t`yZ{2(Eva}av=6a)dcbVrz(*MxDofS}R>Csm&~HjszOR69_ZHPv zt4H+wCcX&jnD&NlpQ>0{X?%mrAqJDo?c@1%-U8cA8X$#)FTwF=do|alFu;tBpdson zen0<=rZ|7KcnI6`2z%WK{#Tsdt@>}&e!)mnU9r67iDz7c%^4gOjSgG6-!BOBR}$)Lry=1Q zt|4Q#j$*^3=O&vY9_E&>c(a)&W!Kb(aKFjP99yW|? zcuD!vAmJmWW+C^H+9^;U=Dh#Wgb5cs*eE78eI1?xqsywMIFY;~`iz9&8aQ`FNx!nN zdFrDu+Ppgpf5LI5q6hrvmfHIGXh)6v(Se}dq$8{- zr1Ux1Hbz{v8_E_$hwM*%jyg~qumV;xDYkZ)7pD3bCiBtN>7u39)zZPMRq%d>g<|eO zoT!cd7jcPBo-=q(PxFmhu3##9E!Sgg`JpFUo7+1}M(NVs zyGe&mD)(x(nxc*l=tDaE_Y2Of%U*XE&6p?F96Qq_XSzt{CS$q393+_o@(j|LvUFczNeBU(Z5kDyCUPWS2`%9&gXG=8xQ zI2T@%echqTM0Z=WaVCFrQC_kYrE9a1N%<(_AhoGbV%Q%}T*l!$zRUM)aUMon5QqQE z6;v2=lnUJ|*HJpRAT4fBJLiZuDw|f^#w0=YQ11;B#|aU4m;sbz<)cyqYRv^2KXw>!zf@X4X_Jia?CyTE0tZL_LK)U+mx{ht}U>jKM z@TBex!2NJ2=isMHo0dPuBb~O>MsGr>_&YB7zd}HiTJM6ZVu4*HY~C<^pJ*X|Ey247 zpmG0@@}^Q~@-v$@TY%s01K2vVJ=s{lPQ{lz50l#Y#r*VNo%cL(`)N!4ow1Q1|GK?@ zjL|}Bkivpb!~>L3@3My=mpF!(H@uo)5que}7&)f&OOSc)V3lyDZ0~+0h9!H_nji(t zim#(5eB1aRhDA^yWb0b$4jL)Fil2Mf6qr#0Ozh8srSV@ID z+=kMg&D-mEkrMe2G~)_uw39J*7742~O&-{;cInFrAR7-<@((|VS7cAOTOC++8_n4- z>8MAEf?FbeTNL)M&U~8(zMIPh|}z6P~4@V`FuJqq!a4+}OR>^_vM*#`F7Y zk%uBT&$b`eFEMP7;k?LyyO-ljOy|iDbFMpOTNdDZu$Mx^qb4Ah4xsMBf2rkyO-l}I zS+l{OLQzunfitWK$-~o1Rnj{n*Ckj-*P=_*NkGIN``$UwRsQW1DT+!7g8AaocrHv# zYKv0_&36rg9B^qn=6ZQl_2AraL_0y)kd89krhQC}Qd)f85rn<^u^Bsxt+2cEQ=azy zt`5gxDs5$Q;pd`^506~;|4cI4vwY#5*1n=XQetrBm@qdk`Fszwa`E$dN59mM%%yWX z>}yw%FHo^W)^G1jXu>vHDrrs`esO6RY<{@o1-kmbB&FDj*a`V@Dt9@pis58rbkKKK z+JV9i6^#iaZ+SfI>j+29|J$UAhQAT}LhAM=Z{Be8QkHN3lWD@WEd7jv(QS)PPa_Dh zP3g)43*A;Wn1Y_^`9`U^CqLO;CBg2MZvKJa)AwB44AB+lweDvn_7DlcgK1#Ivc}+@ z-~LVK^B}#GVuFrh83j1nu!x<`{=Ch#9KKRR%pNgaa}gHlF1ME|jc(jltfI3@JKP!{9I|^%w#gfd43%8y>WL?M7&Li|JK1_ zJd7F7t+OSh1+o_pBWeWd=ul3Ut3^F;3j70-s)_!{X&pI#pEysfA0@n0znuo!y|2?$ zmoE#|3rW*D^!NxYHL zy{`)tTAA@SY!+nl>eHh;BB%-x9}EvglHu&!j>c(3VO7KL1ap?6=a=lp_2{L5z9Xur_3!8W-_zJEhTq^l9UvL%mi^)6g1or5dnd( z-+$o#aG!JUJreqJ8OrO$t1K1l7yuQOg?%NJDYAshun~*6Et&Fr2JJC4I2E)J`8I)cV$}2I=}ql zobK8KB2vdGhiSVns$Xobt{w4P)Au^VVcLcAwvWQ+ZVBd+v^VcGKdeBLMblFsy7Sl5 zb}_6@>xX!{Ij7HljI}f4g0`g&03N$GQ_Q$U0ZeL+^qb39f`mEmH)LDXa_PKTaFaqh z51=izGw>p3-ujXt`|=A>dzhVVV3WM#&|oV~NzwI{$^7g{*vgdJ>PC9rE^3V-DE0Tr zr5*Oht$Q=ZuH(hlba@F(yW#_H3k@9eJD8h)W7~LqZ&QFx-LyjVn?UOe)eU1{@+Ud? z6}R)w#ru%l)r-9oqn=g9)}AX*K4v@oonhtslRZ|AI2yCnf~v<7rivPI>_(cV2dRwL z3zLQ>Yn~iw!a`=-7O?ckdoyOJYj>V;Md31elHp1^Y$NSi*M)@W*Y$1Y8T1MFxFB%Fw||KEn1E91S7NBX?kW8y8Q2u!-HuUo!1tz#aQ8xpz*dUnC_sxFw%{Ac)QNztQD2w)V#3KGXXfal{Wvo#>(q8#)la=9EGt9@7<21io1iMcsk%dfwc53_;{g3e(z*VP zUa}DpK9;c)#+ZkB8NvPX?b^TmQZ{WzOhLL0#J|OHk14A#^u_rYeV6PJ6H-3hAvywC z+T+w&eu<)6qG*NsIroQdpO66`a)}E zE1yc>*>H2pAxSPt)gtf+>B0VSj`vhK%fl@Yv<23>B|VK98ys5CUEbF#ri6Kbx{qMQ z{@;RT{Z0KO^=S)UA49h?be})m+K(gdzkh7S?k7yTxBSP&Lu{<2fR2&QoIrL#Wy^8DMojephX2XmYsN&a?^!$IF(nDE@>Vof&li{Liw#6+L2 z4dXdJJr9iY<^|v6NXTK*D~bL)d*d69xDPmKL>u$9kcuCWE9EtXu|Iec+FBjxABXB= zfUH8DUpEWLB`#l6oxrk6BYv95uMyn=y)jDe<^a~F`hpKc$&>-Rlk$w~0LY%?9fIP| zQBIQ3(Zlr@TDV5G(D7yQ1I*Wb4D4)rkA#OnC1@^wX^GaW#|5{sA&#n4s-H!#4u@ z#+ToNV{8`pwDdT&J42bC4kYCX9B}pDfjiHexIahy*<+Bm^=`+JlAL|xh?MjEPsO5! z6;c$)i_fQe);grl;;+KOCH|ZCiz6;sy`s~(RFl?VjT^B)_(suC0-R;@Aabx=GDv2R z#t7f?Q==%UJ8-SlmnAFE1|4FxIzE8q%WG6ywQ&8qv1G;27neXqsU|)T2ky*pi5{s@ z?M?JxaA_$)z>z7S>Y?$5Aq$C`6B=P1oMDfE_(>bNY2*r1Lm^lS7Tq~+9$vCK(QG9> zkW$I=8}iWH$rj}sNdvL@>#eAQGuKxMf|3mk%WqkQaPtO_vp7W&^y)HSmR%^B+o@u?;z%#25E~UZbrr(yh9EvL~VMO zMx6LdzWWkuMfjifz(g|5S0q1JYQNVkPKAz!iLU++85;LGB^Ko!rE=2t{gWCcxjclW z1gZZZ{6c=vpAV%*YpVNhQIPxrs{sAVXd+obw53*4M-WakjE0bkk#o}a)NDb^$)5A$=s0nKc`rKeMd3vf4H{HOC}mq8fz>>S#2P`%MyiDL7rSe=+7L9)TxE z6{>3cS6v5Jj!S>&bY8#&S3=DN^J25?94F((%Ss_Y6>2GxYKDFQNP)p)iUHMp7JyZdgcLswZtdL_zKYeX%@sDw>VmwXR4U3+zv=#)$ z!Q?s64q+uCAxYc?E6h0SS@6i_NWnFg#-|v{M zPcLn8A^UEgG$;utv_JtA4xV9bVRC))sQnGGa?owTu0DdULjNPw-n-8j}yTkBCFs^XL7`eoJT`fZ^q!-i>_|po#pa~ zZ_v|i$#z5-oV8Im1-sjOC_zzP;)=59%iMYZs#0uJo*NfnJ%W?7QYt$k;$}x)X2lAH$NC%*K>{_PhDBRE z_%z^yXe#FpLP`~%#_`v_C~c4imgbZp8(46a_N`0IOU>nvqUWI)$me)Ia)E^DQm6Mj zE>C0`jqsCN#tQV&k4>ed*lP>kEGgI4ExqS*HX?G`9Qp|EOyFZHto2QFd0&Xf0Xh}N z&6EvVUCU*A6*vD^qNvx&Gh5UL<`1ZxAnZ)41C=OORQ63@ZJMx(0vdXMiv(@oV=+-k z_*iy#RC(doSd=Q;(Kh{h(=QsAo3m09QHzcx+;`U>Kg}(%l&Jf!gr8_R&leI@dt9|U z|7;knmj&8>J4gX783$3Uwguz8B%5QzB@Mz}`GQjDtN~j=v-Hf6PROu3??TvWF1pVL z!tF}8B=_k2M1+1|%WI34R~idV$KbW69D2rwxCM81v19aH6>Y1V-|8bRZQMpQ(9}!8 zcYfEn{Hv1Oa}3ruL@i=eyh?1`arUrxN4bJ_59whCmFAiV=#altieDUJtUK{=80bs4 z;0`@#p>ZqH#Ub2(c7OB)NRkEO_4ozvAWLOp3!|Eq6W+`dfx;9kB4S{)Kgi-9_9csx z2hu&gMvfDgW-fV`X?;x#fxOdAbmHR}X6b89 zlJBnxf}eEFC5rMK&D=o2$wR3Pp`&rZHt9qFH@=`hf?2BkVvq=WF=xbBQwYppHzqjPzE$U4fJ}2__dKXe<8GAf255O9R9Es`AA2zQf+OBCy-`Bl`J1RuX(EA1QB2IYVBF5xqHm*(J2 z@5h(6fqMl9$5(!fJ(cTE`H@389%*m&jbSQ(zxl0t2p{g*#KRANce0T4s3_{K4C%<( zlwj&wo{Hz7N<_y-zPKPrGXEjP1L~?=bwbmQ>HJ;ef_!=v2MLs;y?>D2tZhis2^}@F zVA1U;U$tB9Lp=y2t{I$Woh)ADwd171xy*Is=&j+n4^gllt4_D{G0u*bk7FRfn~I>d zxY5Tf#o1|SWP4QSw`y&NY~pAb8K#Db?%3%&T9XPH?RqdT+maz3xMY%i#lc^qC^Bb# zvXib02*p=O59B06)QKSL7LF5YZssj{LtF4=(xvXj=vj&-IiSnGCav z)eRwqyrhxZn5qv96!8Ryaw7;@z6d6>7v&j_ z5muH?$Z&$lm3Lo6dt@TLaeejNLkmNm{CmdrOoc6}<=iV0^QSD%g($zRiBSOqvxHwV z=A1)~B5y+N4E9t?>M31KF_oV6(4pn;heH0US*zdM>oxGLhMAN59Xkwv0hQ9xw%3;q z`2J=Nbrt9#Xueh_*|6RSzSSredVih0g@y=PXZ+40a_?B&=5j^S4qXu(7=J#k*JlOD?uUk&u9NS{!^Ony9R4EALtmwou8 zCLQoNvnu`r$t}jGk{7XE70XSX-h`&(uQqaXq#E|P-_TOzaIA=iHu2=$PBdSlJ#I66 zjk&eC&{RfSE7=mjFxZSYFFXUBgoMWaHXr+*o{r88<=Xucol@t{>tsVsa&78_8QX-U zhh1Z4*U3Q{)>O3j&4`6*fP`Fz$nBQ}B0*(4Ywj<| z9a%&1>Z2odfH^Nb?#7Yo?oEhYGAF<2dKY{`Mhq$2th2=I;UG(yd@reFEgg~kh!__T z^M#r$ZjvhQmyg7MqXgIF$vGG(8!rDy$q{!!z4;8eBf6ON!YdH4{SL~mMg-rXF-2@@|(NR;G87}$02jxHG!WliPm-QR=eFtp#7syotnD0SNH4nd)FUzHblR1J5 z&)rC9TKxxl?>j_J`FxOe*A2~`r5~a(!|xmhQm*<|@*Eq}HBgx43n)$y@!)1TBBF?Y z{bXVCe&bI9it6w-zm)$VweRv9Q%auE28XT*iKg$56Tc_Z9jdlzP}>io(1J zI|b4F(J6TmO74&D1TGxHw4M4*u=#spcl>0*i0m-7+e`lArN)(cY@e(tZO#P~Ag6)0 z@KBU0(b_A60M;hxzAeig;FCe7&G1malsxK}hNu4M|o4o|R%@HthRCIb0TGwO6_f zm)!WH&7o{zCl6q5)jC3T+7oWqk3A?@1QGR)Vi2=tN^Dn6lXiN8k4r#z3 z&zxJoob~I0e>p!I{#BLUTdk@mi|?OZOny;Z(sWK2@%zf5?P=#s!|!!QcTktE+P>fD z=dxV|wTB(K*B+(CT@rwrmgNDwr%W{`KXkyEbur;LH;kjXn7WG5$Hj3?f_S`!rIzZJ z$e*j9UlCvEh{qRexW(bgyW_%FRT^rX6MtBLg2>_V)m&v~@sJH6`19*x)6p;ml>Leg_41Vz!HzL( zo`GIne2AKezv}VnF6%_a`m?M!JGT!Dugo4gM>{8k0Ehmn=H6I_=y`r~BFV-bf-TnVxE$cxp`PJ;nCoHdJ!N*5*DXhPT?$2AQ}Ic~YyRU|W{ z4o0g85u5(ih9$)dbo*jR=|h8V(zSpw9@M5Wv{Q3noy@}72i0hO*14ELS|@#0v07Op zP3B%kfn67?1x9F;_^*gM=T{)K3CHGvwm&7W@fK3pGl>|60W^lGSJMYt^3IDkqtx|%W_ zREDqOVHstKyvEKE6zg)wH4(RkEAyF@RtE7`5^hNv9$>69xSk7gMq84 z$j1~FEe{a$AFk-`#)Dp9or$I+7E#L*HDqW$Wi>T4NDK*`1bYb9svN85!#Z1J4T$SK z2CbuDzG3MZ1O3VCks$jFQljg^us&p^$sPxoWUhA?bU!zgYqNN@Qs!I7#?2KdYKekf zt)ffMkOZN*%^ehfy?rrX-P6Q%t<=WuGYpCKjHZie( zb;I4NHJFv4&`e^*{NEJ=f|wFJUi+eJ&uPt^5W(0s*%F=bUY#=Hn&CRWdun*~fP(r2 z6vhv&Mz|*dPUS469gGoB7sFvayy)U))vZ-Q080)`x~9pPmWIlY6@{j8_`mxH>f0C_#_A4lq_F`QB#rqB`230PmH7i#O{f z%opJ5R%bDDYJW;RWaRALKzT|7A=8l6iK49A53j1OY4aDP>Vu0o(P%&AZ>+z?a!=Iw znm@!J^B;GCg#8J)y{2J__6UpKZ|v;JUmNKV^-s>!q06;l;x6iXvtsUNb!D?u=gm!< zsn#}dOAtWl@#!@e1e(4&OHYEgy1-33GK6aR5<-N-P8t8VJ$anVoQr6!(VhA^_#)?= zCAv4E;Lv)$8;rhxg>kyCVG|QY8o#y1InVaUDvI}%m3neU8z@`Sw^5XYl zqjPm;_458N(QG6lzOic*hZXt~N?K}a5o5@52T69lNexaR>1s{YE$s}v32l?}m?EtI zF0*V=W<%@Tnn|AyLMJKLLpnzVN(hOYN{260y-^xQ`r5lq{KfvJ{sde&yl!A%aQVX7 z)4#dT)NM!pqb;$nF&I47N*hc{DxPod!P0j|H~Q{X zh|wDi8KYjmy!XSo_w%{;cg}O4=Q+{O40Tu-Z!uoEa)m|r$>SGSu3V-6cebFX`xlwu z1||PC8t+HCFX{g+IKBPHD_8ih=swnX8DRB$!L0Pn6hioKu=_%=bN0aPd#pe5pF|7a zN$1%7?lRyski1sEX2^Wnfu}=tdg^s!fGr5Tk2fQt$tOMRN2QnI1p~)#}bvCSii7yDv`rZQ}?_w{YoXN=-r-Ye8 z{~cL--fHe(@x^hpoFy@bu~B+b&%9jHsug|$Qwt^&LJx7^OY%zXuZmStaNCi2XiH1W zS(}TgX`?S*DMmv{|BacQsp|KGadE*4`?Hx?2q$7#d%30<;=6XR2DJ=0S-U(*iU3H$ z&z6yp+FGaaOOClVV1=B;Xj;d6X7?`Vs8Dg!V68o8JH6gmCcjvv$3X0>#_#v3n?Z@3@u|?N|Z7_ zFV)vfDdwpm=0oSSL;IK^{wb1{l(Dbf8)F9tq1+h@vd*VJjTc5dXu32Uz8r{GG`!Zv zhU-3Ed+ls!R|OOd!HH+`$4o?&k(d!+A z#bwsndERfeO|Q}f)H+lZt4&?ICrue1hDE!>h7v`&;Y!MJ-W03noMI9iAzoIdjXaqg zpHcNzXs@-To}L~^ban&Fg;5jI#i+w!jrY~gB<8EZ4b?N6?OP-O#r$`E0SCq=@NvcAT|c`?I@IT>`At=Q-} zVs}1fV#=8g{v{q-f2p>g7ChCwDA(Y0xZdPCSI@FR-|n3v3Co`Qck%Nl-W%&fSrABr z^Hg~g6Q@M#$Mw&SLJdtc(~1`EGu3b0w*QoQS6~i)^5#$!F4uED48ztMzZ581nS5#M ziaOE%6IyAvzlaRk_78E`>jk;<(9QCQ;e9BiO*z%%d~acE>*3v*8i!17UBp$-rQH9p zjh?vq_}#gU%kv`0dDT8*Lj~{xq1YES`0Ll@z{>hy+i7zSxqCj#e^DtQwtFDZyPZ#* z+;*~_;jO16iTpEw5&@j+XBI#x@rPARw^aQ1GJ`46B5tRow)2M2Yw9^|`_95PZDX1afP%A+sw@Q?uHCLv4E#q36>p zaSDrE)#&(r3A*vD%^FnPl8-zNa=HoKKmD7hd8tFwdM!4@|M2hbe()tV@OSHR>%JvU zlR;IR@Vz}tZ9WCkbhEaHamKoMaVEe^1q5rdtqJtA+n=_-m?$>#X8rqoI61Bh*Ovgy z);;iU{;q8GBnL5?1?6f`l1kTg6L(4iN&*%`k2ph#>s5Ar#tr)m!8jxir+|eY3JFB* z-7E^vv%g(FoUNu}n+6__$w_=A!F7J}Qy+AGLX#y!HE5YeF~Wt%`Z%5?GN|qLj%Jje zu|R$B*wnE;;!n|v(vuL}6<$BzUO+-w(zy@(YiS*r_g%?)aofpnhY+0k*5Jv& z%3G<(-WJR(5zWQy*L^V?=iTNytIbNQjaRN=IFI2)oB(EGRc@uBVjzE$fbb=(!@Yxn z-Ddy&^XWc_`axzV2R=8h$kVZfvQ;AZz58?`=f?e*F5!X?p@$REC=nTdNoKRvkF#~^ zni#zDusmgx8W7;htR1MBp%O&GziwG(h;cPBXA z0VJr&ANO;%1q_!XPB|bqGt#G~sU(ALZHP(q7;q>Qu(H5G{x5~LmomJi)Il=?9mAul z;8RC$*ASmF2sdw>q(i{c_ASiNgX{Zo?C=bgeroTF=Yg@FFOdhf3TGqZ+Ix%GYo#)h zs-yz&1wk{{Z39RMoH2w?LN=G z{b6ldPM&awX&~~^jmgu{^QjHK^Rbk@z@_Y9x06L9XltWYZr&~y!5vdYGgQvChwA(6 zqR5=C|qK~XlftGjgCCO@-)>%MxANd03-o>i(=VqJ)fL{vNNM*A`jfaw5GC`KoCr`bid3|@9Ra$l* z{zg_r!v$K%TxEU+Yz*A>ANI7_=wfX%guP{J96{1VUbGOEB&;#O^R$rM8E0n7rNdFh zBxo0Bggjm+cQp=?d6mdoy~DdPsw7YM>JN1R$P%y9W~>?|Z9=G2*e`^Y6UCLIo|##I zfZ7}BOf7UIop)#0>6ITP?FeE07eY?wzjo6tHVBy{6~xFB^LOGT2x}r+VjKqQlmX`~ zdh%FVUV}dt!xA_-UbQo}vhk+XY3HDg)Oc5h?n{h;^;A=ehbxSpkI z@ZjJhgSVbb7K7`FEe8oyqj?tOv)o`F0Zv~HzXxt-LtK<2BIe_vLka+Ik_}RU7n;`C z8SFrMsm=3^HQpx*dMMb|g$yaTF)!_wg)jAhZQOZU#dM}85PqHOi|k5-I_qTQQbpWQ zgAlSc2%or{(4$bj^w8hip5G3^EKYGv5Ld-l@1Kl(w$nf3d^D`;An;#ebV59`Ze&K~ z=naBA9lhCkQQv=ub?uH~td^gBz6?SQ4qi)8s!3B~Up(WB7M2yltB6bduZY$Sh zS*d#jzF9*A+j?|Nu-{rx1S88jxQj&nhAa!V006{xX{GhNQ2fcHs0r8Gu4K#R5Zu|? zFW`=DyWZb`yuRm(NN0xI!LiE)e``q}L`3wTMC4M`%e!#;1#)@JpOE zieAOUSiZ;EU$$zbjft}bo2C14krn#o!F#n9{ky#kFL4tp(+2Hnt7YGfltSNCJDPuz z7|3u}iyS*9y@cpyy2f=SEY2inAKuiswAE0bjt-}jO85In!igZ>3lz!M+K6e$<>@Js z#*t+wT1QjEolkf%$b#nRh;rMM{mO#BM!Ii>2~tvqWEX}C*V(tOal%>Y&n!$hRPip% z%tB_atO4@sc=5mk-w(w{U!^bHTThvV%F zrkpEUez0K$*j{f<^QjT1f>o!#v=IV{PJg_)M0;Z$Ozf}Q(9RadTc8DB6u(#b0MktK;Em|t5korF)pCkyV6K*7ZQbaU zW_MJv%c?*@kaUh-Xll(pHL}Oii2QoNNS6wDpnn|I1o?Hh_J#43oSi7{;|IlP3ci+YKg8&c72uqC6Rp(^#`_*Le({?$VOQjj!tdpZ0lEo9s z^=_MHnQwi_16g7HQ1c!nzQ^repheo;m#>$-)BewIY;`ZshdnIPFt#K*N#lT-*Z4>g z5i-1YIIFRKeY{M%>EKw4IUL!c>o}$AFl_sg^s80VgPV^O&ayW5(^)^K$z*|Q?G=(L z*<4};M0v9||H~~9hYGq>OU;#MEK4*l1A@tntMTk-1|wOH)*XBl`H-h=A-Qpx_$8Ga z;}dk`mmCfntow%iUv&@O$Q%#t^vVaI-KAet0Gn=A++d#7{mfyJCc4;3MVEDr=sNbJ zl*FUhGZZk%F>Bd1!?hbw&48Fx5aB9M%886Tb;zZUyW@2mR6sb1{=B`R4Z4p$z(1&^Q*JRw$?K&;bM=tZBGT1oe(9B z8iq)ZbJ8(3^#c^nN0!?5h+5!Q%8gc=LKa@qh`cfEn9^(vrBjvHz97se@+OKGV;zgA z{VHYYpt8=ui^mQ)qIOAr&Jn^+ui(7@5gkep@f&DcRKNK+%7pj|z*f8Y(f?>)(Z|`l z2^mi@F_g|X%eWaD;^5FbeWaRUvej3Isv@jWmk+!Z%lZwl*FcHaqBNT?dgYR*t9rPa z-wx9HRAh~M7)a8FlnGu|+IR?nXNT}h$cBrp&GHHD^2wT_Z!Sqmyo%mcM`0E!!c1#H zOdobqOWkT+hY~Fdr?}~_-t4@A5e4n;fp)@#Y3{^1z3=zKJ^FBN?dSsBl`#tc^Wld_ zsLn>!lb?)k-d3ch!*{|&7uSSW+3ZrhREyTQo%Xj}ls_Nkt_XT@o!fK>=)8DxIse;1 zTH47yK3sm_iH}<-%B+GI4i+rY0q#Y47r&QM>MIEa$iO+tta32vNThCZ$TJ-pv9ynjOY%8gxzLy!oh z!J}ZK7x)*h?Ft4GKAfgk%b=<(H_083L7*`$r)Q)457ljn6SgXnN6zvLl1!h8qwlr2 zA>|1wU5wy*zx`VaD?RF!FPHKsplHYKZx0QpNDm8iITlxS#z(*XlwV-bNXZiCCyB&3 z!D)xl~NR|5`JA5WzmbG zHbB=)F0NLamWxqV+$)JU?c+bQrdhW0A7eI!Xpt)e*>GBfBEHMm z=URoM4;*10OMq4B86Ou80#r2e^>hxJ<>}OWo@d#XN9jw!~_sQH7!RiN3T7iwXr4nk^6O<@UzZNfnz2!ewTsWze2acpW~|tyFVuC zlv@n=T%BL6=?6TJReC3Qh8}-leDVH{JWB;{p#1C?k-j_e%QDUbkHLVAb$b^*tFvjN zN>oG~=%r6?MewLNv~ zGQq5?C2Hw3wBkyeT@JLVG>%od2BB1skEJ&4f63(>R}a+V>nEod#-kr%g&~Slf5`Gl zl^G#7zBuE)&H|tS{YfR|=niOXss$+z#OGtf8MJ~eihchve11WYxaJ8@?q$kw{-mS*VTd4sUnoXCPC@7H(oc@0C z80x9%@7ud#!rk9zwVE4@s<#*yH%HXU6^}C+I~M$M3m8XcmNf)Go2@Op7NYgAhR^K# z<}_HPjZNS?_Z4Hz3o+xg{NV2iw)y;mh=IB2A3ky630DP=kEoULMU`>H2V-uJqVz=4 zZE&mf=!C2!iwgS&m$k9NEMkO#-e`sTV{rZCRAa9$C&Q6l5aFJ5Q+-KKXj~DDQ?rtv z^R>@N!jMlPBy&XDLB5D+L;IIJ@?5}#=r+`ya`p+`OVxBM=Pe{4aM*gaXX&AFb%!?0 zYcyfFBWY)AAXn0&Ibw3bNIgIMjFGQ5F+8(X5)zQ_z#L+!-=UMe_2?7JQ=}@RnS))) zbU{I)iHe!{;}Pu?u`M3>)zDg-w8^lZaQ57W%{8ZIWj^5#niUgAlc_5a-8S~^0#SE` zzx=RGy%)CH&9w#Pf9iG3ZfofSvG>Te0gE{WC8({JWeO^l8iyne*by$+IZa*qV`TSR zYpbi>9emziQKourL*Ll0X^vt>9NEE#ionQ!$nk+koSbmo4!^zDuYyA&< z$trz@g3*c!8jSqYV`=3>XXAPSo}DLR*X(h6mO|SVts2N=O|>0jq-M_gGzav|rqI1_ zMenVrO-q%Bx7%lH>iVxw;xqwjM=oY)kBSA7Ahj~-1663!%R7>;0hqIcUu>3w4ae^e zq?c{m7s47+T5b*F{SE}d*^@EqjQV7YhTX|SZU>**uU5W=I&4jT(s|>*zA7{||5o(aig12hO(ytM`R#(g6Zang?pd6ZO$m*4rI z-uel>>of#-uUV#@b-leRkS>0$t4%DvwnP)?iY_RT1y;$0+5!{b)=V~{Dpj7>!eJ|cL)HekO*oa+w>=+dLn(qy_L-L>YE~5YZEH; zPw*V)^!0L7!Ce?cu-k0SMg$TYAc*|{V(XJs4lEtLWVcMTyXPJiA_V(kxwrAsSC-;= z(|WGno5dsQ1?!rDS*jB<`tKrV(F%80YHaWLyQ{wAFJj+`#(EMXvZ{pnd>PLOULG8> zyqV>!y_?gYO6~7rEq-Ord0EA~rf}f6SR@QPLxv1)lP8--HDUZdQenV8L6LtL<{D{W zZVe=eD-*C2*s8i&-}Z!?=|iMmiu|zq0(v!rmz)^ab*M3Ae>2tIp3Unhn#sz343IoD zyEM?dvszgA;9%EVzUfP3|9Z=R${v3J$$oF9zq8RO3ElEZ54-yj%;&Pu!(N(kw@>F5 zcZ{3svl$vW-cM>-H_zDN{dz!o{sh{{=wiu$Wxc(>4(7Xz=G-dkF9sYE@|vx;Uv)m~ zj}Z=WtZc5QNH>l63unmJPfgWT`P=}n8Ueu+-S0bL^%!_Y89pZY;?u=vxz{f9v{VbH zN|IRw4S<@f0j7sh()a8pOJFol<+FqeOVB|{kcE0%=!m894CbXoHV=StOom5@)b8_Jkr*uw)n(& zn3K&Ob<;ng`#_mDKgs~>pVm7?XukInsf4i6gLg*U#v^s(xY|$jDf+Lf=YCvg>D^ z5;mv&ckIckI^yNm-fw!oSyD0fxX7>rxj&l46DOw*{r*!xc;p#2ng-$X$wB08fSKvb zklh;+i>q=W_eD(Q$?06`v+uen)R_Y&*@)CiEqxeVU&!T&HkYqPcuw?{yYe<+RP`xbnprJKz>kw0GmX(DGM5|Tg$&Dsy_nY`pA zw(NS3-{_+|s6rUCj|3eQr|Ny~aH?}KB5wB~#j^fewsSenH;~-*z^`!aO4t&tWzIl) zj-T1v)@gTFuQgM;G7|;{2d8>-OPUW-dLe#@Pd zvz89)eJTB&-X^{D?k1lg)x&$gxgw+(7C0$yP2Mo)TVjtFy%SusvE-5SWK>~BT zA++1mktmVS)Y`9LbJezLwCh}9QBDY2Qp!;1jQ#x5ykVhUY1k;^P_8%9^19dIP!>?= zMay7@Bzh2=C2|U!zadC(ON%G1Ut=*gY=5r*Y*vX5;K?F7WnkrT;~HyY$0LuZq5(%w ziX3X-*jQ8Niu^MrPEEbAPgMbV26RI!=><;|c?OozRP7A5_`+|CJ&k>~I~f%(pu+0) z_S?c%c3Y+huRn%-LtBTh3ZZR?*A8=ZNIxu90#wy!>5n;t>m<8hjI{1u(YEJl_jigt zumqQ-ZhLF!R%@$M>tl6H@4gW20ji5&cfbbIzZh3U7jjL*_H14yc*_VAa1+e!h~ z^_zFi5k-C=hvPNp72#hLtpat#Q~U?UKDQbLfyAUZGM&;$XV+>NMA@bN`_6B`rn4&I zXsyJ5-m~v@^(0ait_kz|5sJ@&6>+!CbMcy-h$*jd7(kdOXlSn;`K-A!p4iyNUJO!~ zk9ML$mvuPV*aPto4XeKK`Xg?M%vz3f0x{aj5`4-X%_-)y(9-2y7f*UkT0eYXqLg;C z4Cte!;?i&q*==`2w9FG7h|U!)s;a`Q_EEbkw?96A(pV)B0{>k%@X39Y&3G^g9)9+~ zb-uG-EqnD{z5=TiX8mcX*Ih?ERyVv2Re+*Dna7pEL~f$7TgV*YBlVd1#Iu^aRtELt zEm4zYCcOh$!!38hqlUmAY&BA=_Ig@i*^xWI=FTUVv$4NPBPLP|`FeD<_}6oJF$zJf zUO-AwF)Ut9y+1!$sQff{^c&UP+$%%C zUuTpE;6>LNS3l%&=+=%yP2DlvcsHdpRPLvhqDL3YWtT(S-?}ynyT781WPBG8o19X0 zF&SL%8f=o;F&ys3a@kyflEz>!wZqgB_kU2=LpOb`=}JcLFh$vNQI)qbgkNMvQx|We zsKn;Sl_VU-a5kXM(8p}l?EpXC0zP%i$n@#yTu~h^P?tO+@Mh>xt~;;VZ{ZwE`|CH= zBf!nvP`;u2BNMtGw>Hfz2gLvTu_1=wnpn&laeKNHg6{%j)F7n?5Up&ZJnv;36VplZ z+S+wbhUu?oa+UOLu zE?IxmXP~=&mfZn&6AW7`Dwz_d(tQO}t^#4Rj%5A{)r3$|yJsqNWg#@ODGASwtD9xr zOGbqlhb0=V$0OZ51qD_7ABlg5$5z}kfUo|^VL^YK$Z=2lYH6Jd&K{~XPRPNsgnf;C z!l^j>M`T~Z@o2Y`3A^&dO0oKD5RSK;c!!$1B;&$w-n3moP22Ee=%KSudg`c=&Ys~P z$N#*Ci^!3|vJ3v?Wxp_paa801|_?LZuAfBRRu6 zU}`N(&qGDu;+y{bQS2V?{@t)VT;H1)$!bNeX19VrE^U8y*8WHZvF$)^j}y;E{GHLz z+#4@vb(daxqY9Qy?Z|96&F9-~iOshcVLT)V~2|VNBGJ(+MN*oRf*7kW_(?xaXT$x0t&Oe-VJCC)GDN}wvt8jaJ_iDXz@WphW{QPM$1N^{~O<`k76|ik02Qzz8Ca;`tuHIW5fs)CI5*Hi&4n0+98(}%0 zQ51xv-SpAV59qw^D&6j>&&NHiK3n~3y=%r3>w0mplYfo3B{NQLNRH=L1?95_E80FFSh>j4-$o?C>`a(qeoUyX$F)v~sL2iM#wNky3akf#oJ_S-GJLt6h_ zwJ|VVlIH2N6oo_kvg}n zo61M~I-Vr=oSuU+Y6hWu$rUaT7oPO$zDw=)#_MKkKF~iAjv8qb7tS6?bFZrtcsY9| z%kY_>Xspb=TY~Kbp$xRZux#abLlt32GpC@27ON>JrXtM4z%cxwgO4IeM<-!n9u>nn zyWC+m@W|v~b3BsGibd5+J2{wvRZ3T^^(WE&B3N1IlqB9P*74)eL5> z)0)W=ulo&HTgYOdCE5!0o{1+nrc!PZ2=X!4=O3#LYyijcR7_ zkP}N##f|veRP`>R9I^gikYTR+c^V;7?=c}Hltt!fluQ$Ti$6kHI#J7#jXEgH8h=JI zf|g|~`Ag(@4@x%eb2NC?iL2Y!a7h`r1_w+|5ybX9HoI2}s0L7HTLbwcIXkOS*Ml63 zagx0RwU8YQro!EuF_SdK2}%8!wRbzoNFxKzAxU!2R3~<`;V>$X2t7zie~Zj^um-o% zv)?JgD(#O#;U^p?r|KMhJO3CeWxNf~T<0hK9q9}pA+2z(8(U!piYQNYQT5GtjJ-uI z2t~nvtD!lv7!fm5So?lSxAI=7;ah<3nbsY|XjHLo65r}`>zHyGUTYw*mhj#DbUZrL zaXe+u9trq8^EDwK0NLHy#R>U&?B|MJ%#OUzc<+8b-`etFs9%&Dik+p;5(ssP%!y3O z$>sq2EUQ?aMLQrv2wIx@iOq*ppcr%IF2?LQJ+GBv>;RLX;(r^+!36sXFs=f4i!_$P z+rO^QiqTR)OrJ(_T5;9O8G^C;TehD{AB=BK|kCW?&swe;9G zU#|~dY3C6{UC%dAe)zqy=8BM5)Wc++yZU*;kSkOPd6P0iLD^vwaYDQ!8B1=%8_$ZH zW$2EUn`y76rMDoLLl-~uM)1zOZk2-BWY*BK-t}6BCYxQ`H1G=>Ps+m&B$a@~@H@Zn zakUx7OKg;&KEJv)N!52;CIUWWE>)^}*xSW*ro_x0*Hsi>1`!be%@e*wEUiyuqITj@ z483RfzhO1{91fmq*AI`@)#>}ml6s8tm=+~lNn;}#xiuBtR7JuO!loA*$$PgGsW?bs z5v%d_!Cs$_bs&_{rL|+qVKbLB#h#A4u3!S(=Su?{x+>izq9Ub^I$u1-D8RLy#vYk> z@)uxwfP0^HFP|{+ZcgO#s3@bPCB~8!RN9tQ_xHa+n|I~D|5mAir1!p3SCJ4l^o)|nz;#0pmJ`&225EZ_1^(j#D$BbL;D>wCiFWUdYhU_W<_B zr8L)IeLwW_R8!%=M}rY6ySWk@rM`7SZ#LpRsJ-*l$n0qCW!N5kgmhJQSBv-%{t|y;} z|5|DUgtC+R#tfJ$Na9t>!^RsY!da3h7788uItm>wd07W*M-u@q^-2C(YT`p%&2 zy%@;4tl=pzddd{`Uxn) zWn!ok?p!e9mVPDd4g4y1L2_JnW>}oXS&&g?wwtWmbAC8+={NyF?tY<0cJ!t@&kjjr>Z%4TC{@4zl_lv;hNp+e z0?iNbnayxHYnxflx1c-CemaM2U;Vjtip(wrm`)~9R6~vgYQKVnVAS=d5s%zqg@Kou z2B}A)n0roJV(ZRG0l-XOM23GUU)T$@gdZWozx?1S+s*rL7TLwix|Jh2ha+PaSo;)AoE8 z7R2#B7D5tHvHD4<|yzuQp3h)zu5%>DUNg_;pDsc zqE&Zfg%3cZ=k=cD<@K&qRprD7?onyOpYE6)vpjyN$Q5PHEFfj~#Azzb?rtGlG3J9- zOwG2Y^(T1^k55TA^M99v+@`;X@TkzM`E+Jg-Ku8SP}C123R=CL)KK`J=eGHJiuGpHS5SE5MB7GOPva=1OVxuIlKNi zjY#QM(gwP~aBno;tEZ8cX$acG_q&ZE_8pl+lt>YtsIcG|n))BODq6EmM$x|qlhA5< z_+RauQjcZeUfg&NTbB;+%ob!Xrn&8H^08oxc>fS;H!K?EH6a&RV|(N~!t(Ds(bW7z zkaN*q!KpFD^yc(vOxxdGM-3Ran7#9zA%(eWt+ zTY{8DC)KbWx8Ay-O52BL1bfE*q~YO>w8B6ZIx>UO5VI?I?^6a+W6)7~6- zE{^hM$d}pAlOvxFz}hGr&G#J%BI>qY4xugmKb9B(1A|x5>hw@9xzP5GR?n4gHJ3U) z0;L9gHp1WL?aL1nd52I5Q~-87QQ>oJc3%+^$tHIonahb|IYYvMxFSe%>pRuU!ki0& zc0+3JU_))vc$!%67o9bot^g^4@)<7iz!9Ttsf&*$=hdE1wClqgwtFq?P~wDVK#}7t z0^uc4ppS?oeW|^z!!W%bDowQI$BR zt&=Z2k90znc^5r66Fl;rHyD-kbvOX}Jx$b5%176!E@LCuZKj=`+{Urt@C|2T86i-H z;Z3k`*BXVEnr{ zJAg)rhK|PGgSDSN)v-kIMeXi~(!5c*?{vFdQO{{aW&>7qu15`H4;E$5e9}!&KbRwkZ{p!u4pee z0zfYS`33Ipgv4rqEu-nc zM_t3lChjeP!`UF1N=Qpv%7YWaVHxcsOpILHKq{RAr5BClM9fq~e+aQh>GRl+VYy*k z=pt~A~O4a@$7GG1MYocal#fOl{dA@N`ceW6#8SKBSoJE8Fl|N3S ziZEW{%cZgCKZ+Mo3e59e?LsB2 z;_x^fkk#yFxn-V-tA;uhZ(k&;KPnfMxpVYr(e6US+0BHa0FQDgE>dBxBQe`6${ArDDS6As&B_V8rERFXX>vj4AzmLT zHUdx&v$q{xv*^_udvI6y=#6RqvK0tZCyqX1+V$(5eiHguTzk+iZrpZ2y?1-6;?EeB zrR>tm>yDHiI2#?I18TX!L9%&dh*rUqBHTlRkH{JB>Nybp3zAN++0;VvMla^h zl&1#PM>gYRu?BcW!x{_8}!zLrL6u^^u_P~RyZ_`@D zX}d_a(c0;DaR>FhJwO0q{ZWUF{T8LGlp&)u3(m!dkS<9HxT(yo2Q-Yve(z_ePy=*+ zP8+X&u}oZ^dngQURi%=ZYIYyPI|O98OrmP4<+##$W~SuQ?)dNGldyU}uD|nVcI905 zh~K+;y-Io`Vl46v(>)wsU*e)u8^1CP9sOe*TX!t8D{OTR@?gJccczVa)kgRQ(VEK>hXoWWONxDGAK|u)C27|_+!%LL zyQd(!0)tPOTs@B{2~=2{cJ7OB^};Q-CeWTHAaW0_pmfao$~XN_T6FcJtz8EH;Ugz|z{%_$0I!xCSPJbgrH zz+QQFzd!D;mcvWd$Gjz52xa(CShjp0_M45S7I^B&S$bt|z8~XQ6)~o23ZIVU+nQXi zsAw#P`n-6zH;KelYGt%FP9*xZw>PEvoYhHwR@~XegFb0Gog?AmSl;Pyy)4zLa$+}Cpobw$APVv9IJJEdlM46#cfSl^IzCes7 zXy$a}`KkQ(xr*tIhM)g#R1Up2qpQIL^S#l-Qk4g$I$(X|nBae{wgwvLWg)#)k?m?N zM7HReK%Qc(w)U0u<2^VX}qrI_oA_ z%v$JskgLxC4O0s!mb?Vj_U)n8CfGOA-BfE3CdNPJ{(TeD8kmZ;QB|u zO?QX0!s-<(vwnW}m@vstadZYK27jtvm}j`+s;X?({JkO4{0QIuEO6wz47bg*R#Ao4 z_!eUCrcfkUr$tnfN0}6N-1cw0MLlq5+VaGvy3Rk6+*2BI#De+W&SwC~?4b{=%lI(a zq4`|LkhGdFOi>pgD!B$!-f>B{!^cYmPAmi5(N!$3dqp2?etX}b3DG%EVfpm@+>ykv z;0~eSe=4*QW%pQ2a<*3y3gIdwuvrY4R7>kaDb-_M{`)X1Y5zX`~sB8&PuXWa&2 zXW+G-&*1;2_Rv9{KQ8n+>ie3B#VqobCMGQdcTwkQL6^-6iveemd*o%Njr;4?8w>!Y6699eox@bh0(Erp+e8L0qR#;#>*gR7#Yks{qyMua-Q1 zte^xEyC|h#j_0b~k)_3F07AOs(?aaBh~KO1kb?o8P;xh0XqF9Pf7{i2`-qdiO33Ey z^KBcD^#_6WCtFhW!M*+qOB`XeExNw~?B>~6x%Rl2{RFkWU|7)IU zhtIBJ*WQW%Q(KJ8g5NFHzxj3AdHHt#!TduE;_cZ z{L$zy*;jj3KSJ3$JXRYpJ=c;0^O+4&?|#-`5yiWBb{OX>E@`nMDZ)N>1N2>nxdF5m zr%&x@leEUjUlpr4kLe=)YL}pkTM;4~k4!w!MUdFn7Q=?DJS|jEj@tWdr8?EjuK^=>TvritR4fv5hLhazNA~5ggC(7ilFWo# zzT@)_%ph6Lr801a(tHL@cJN-46h}p`EQFuBdDJY_XBoiB^~Yfg=Gc`h`F1SYt@m!P zf_o<~q&}SOmQSA~YRw`fckksQr1A&)a|-nKQ}~_p?TV~b{QB%n07a!A*%l-_pv}Rg zANWHl%!2ZIWDE(P-O6w@R~^s8Faqlz&2g#g0BGIql(UCimY((blLVaC)UGcEVpk?r zd8{m&JeCh)kgSN!a%zCv#f5)c^G5mEpTVq|lWa@te5wY+KjhCjaL1M6EXxyVp8i+} zY@@GNo-R%yc|Oh{VAGBnKHcV-3&h?F#qn6?{@Fh=0bEKvT58`Lob%#y8TDC0D!?um z{V|6m)Uev|WoCZ+YTl^`2g8&(e}B52=sTEkP39~k;Nus8uorza>Fm!@)H<6qZ){&m z_FGPA+;>5BkTX>oae1L4ExZiLE(!ZT8Eu@c9uf+e?0QsQIrbBcKH0l#R7pXLk2f-| zg1Avcu3VI$H7%b5jdk3kWI9ek&L!b{Eg1nna)> zzKZ>)<5sTHuZFq8)>pW)yR$}DUb0R%Iiwh-`ffdb7GkZ=6d6_qZgwgt;&0bfJ14UL z&a7w_E?js_4^#!UBRoH0c8VdEY7E@e3PDZs@o=A^99UhJyXP}8^yl4~zaJs@WiEuI zO5@6g$V8+1yO!1LI_H~j-~VuXaEECQ<*R3(kz93mAQ-fGog2qAJ8A|#slQUcqoNYCWo}5d=Wmx$1>A_@2ozDs;wOBFslL?0QYm((B z3C?~ahOi@-5A~*+b8@!?6|}6Bmpi^YgL1vLZz*;PI2xn zo=12h4yp|iy(ja-Zk4&IxakK9GC%#ui6@ase%8YO#Y#i(XkME$Ke7^d+Etd?yr&vo zF4b#nOzFtxTj9tKX}E(r&n=W(q{OgYbOG1GdULa~;6oTnFA+EV_?Q@WGCNuzxw}xa zA*$lPL+;YSkbBk)Ldi(M#T|~3f|$tJB+Jn^l9Uw#|F$k^^L-P;x|5&x1M$@R>)I49 z_c3Fny7Ja$6h>aQ`wQBpg3R)I>jY@F-1CPp4zLU<&W6eXJL!i=?+dE{f*U}-;GIdZ zDzUqbi6f-JJbt%@Qr~p+&tX4c&UpRZ8N)n%`?Xa6+gncyp>I;1Q)rF~Rr?6N_W_0W z4P~_oAF1AmY1OG1ahR6h8H7VG_OK@eQ>;SGT&&%KisGUrC0Gr$QCSdzg(2DrgTn|pY3 z5VAJFYA-g*FQ!doO)*0D9GLGcRY7+m(-D-W5V~ zTZ21Z^v9cwWS-^UkHsveiW%7*c(SoR0?|Fp>jOji6j5X1rnmn3=-ctm=+V844cBbb z#Q<#S?x-EfimyHvM@OTB>wN8O=+Q;Ti`l;9Px#Z3eVrX>@hdX8=2u=mWI5mS?i0o2 z;b5}Ug_rNHA31brHSZrh8`&)o`Y5zw*F6_uTr%tFPWGWeTzR53ZKl=CM7X9%+8|K9hF0Sc~j`51KSTu%D zxF#=}3cl*dg0JzVh1YBO2;i}AeB+HDJ@wT8d;H|d@eWLMwJpX4Mqfu4ER}~&rLUb0 z=*<63jH2I_8OC!$?1N&+zCEtI>(y!w#06fGWHE zKvp`aYkzfYNe+Hqtes)-o$q{S_zL-qbFGce-uYoKx@%*zUE$-`VA<9~tFMFmu_HzQ z5r^SL*2W&%@``s#UVib#3+FFg@)^6Wma+JM_Ta&9|Hi%d9>_-x_vhW4xd$J7aP1)o zo?|f*{%lH5He@gKeU#b!L3g&b0HUjQ`ZO0g)0t0bqnmp)$<2=Fy5_TrI#|OG_H>b# zjkT~;(BfasX~wu0-xer#CDAaJ&!g{nK8u4dec=lyvpD!n76&hf6x;i($kBJd)*jBT zyFds-Wdu9pIlBj#YXV}<;Tv=m=p?G2dI}Jf@PMJT0}X%xQ(tx3)jI&9T|oTDfA(iL zVp?eh#baeB)sl#yETdFKIFk=kxQ)JK}Jjj_?$Wbm~M}z8s^5M1$<^ z9duTwkMTg4#Lz>FeB`WA>g;H1(+546O!-UBMM>aoj7N{Ti+B}r`NKwrK777t>i!I=;aqPO#<8FJI_4+__^;t{`eR2!sfv#9tMtmdy}S#M!w-^WWxh( zGSP`H3LeGBJvq7vB0Kz=GqoA}S{7mlpL*)#rK?vxE)F`Qq;o#z*cx72hMmJpId|g3 z{0R%mT&vMmKj0tiP+u9ozz6h6-!qxa&gIBgT%XUoWnNHZ`;|O0OcU!CjM(Nl81kdIC1doli#iTm***)dCt9R#{ACg!i^-J8$f{qA?){L~Xq>>G>G z=wt`QuT1*blYSN_?8N3{?uoql+c85Xd6Ayp>7cVbVv*3@H@d(Mp8T%9LjLANdh+|` zSK{+a2Vd6?Yq6NGC69$9oX#b0_~qfwDHaYeKnt(&@WB@%1KFMPGVM zH`y_KI;<=T_`vL;4n|{uiM%<#)u0z%#}VAo-KYCZHOY3|4w~ z>l>Qg?#8oQL}Zab7y@5o6$!lZ)(OamuL%eZ#Gn-vg3#L7@zj(I|u_OiFIHZ+}V@e z(AO6&2aFxCtM9WD{&)*+HndGjW^(AKko~35&(BW3^u#mI_G12X zKeK1Yjz@0Xxbb5LpL}vDpW6;!8O}3#xfQMPLu#odS~hkjU38;aCb7ArNm8&&GMhKa z|Ir`)(ZB&_!;X?{bf~ZPi3(oY9deE*AqAdrN6SRb&%c?^fWQB(Z(YgcEuj%2kWJpP zF=;6Ed6&(57!@|CwQ9env^U!%4t7)Sozy_YyvZ1bOtKIG%ae2E>| zTU?M)au%QJw9!$Wdvc1I`br;Q_z0iy?V{{jJofy{FMm4{PlO(lkA>-S-huJ{)!1fS zv;Rale+l+&wjMT#Er$K4c3WF=M3?L)T+O#LE}T7o{>3a1{W9&?LC6?EHx@yC;Om08 zao(K3XB%U5Wk=X(7YA@?+|ixQVCI~Uu;$Lb`Q}TqUt|2u4C8V=L(-oo6qJ|plkP7kCwbq(0>Kj|Utc+L^yrJbw{QR3 zJaGK}sUt@=o_zgvFK-X039WJchU*;h7cXi|d>zJWaxA6KnfQmkeEqe|YY%_uLu+`* z0p>ck_JSfD_>CMXpUNqGVZ=%>kq=?&;}YZCi-k8cf8}F=E7>^#l2|0mP@FHHN*;&v zOV2$w9w5eIOIz&XoOfwfcW&Eu^_gd%{mS!)4t+0G{9OF|TDh}>Kg{kVT=Q#==`#s* z!gWEQjuX@oD5TBAE8$AK6jf)^X^`vJ1lqUEITSFOEF^qj0S3@O1YB2D%;PDg+mzZ( z3P4uZC!IPYdgMT_tA27@94P4F<8VzDfk9s&p1zR{;-x}%TYlTSW5cw2nYmwn5NoG|fZsxJO_>Gb*Y{fjMYIdh1TB)qP? zH5;#Qx&A$x4hcX)K-AfxPMf4DIm|!x!V6!`6W77WxGBsg?}dyU$wT8mQ$I?T-=>)S zsD?KFJoPVqGWqSuXT84@n{0;-o%tgClrG}BE1*rhV#R)4MCd1mjAvhY!IR#mrKQ#U zBH%ZUo;dMze8o@XZHNqxkQlOYcsH{LzY64IHo5xDl`CgnJ96ZOSiXsJoHHrdh8#Y^ zp5nZ_iF|==`7VDZZ{J;_7vDEdJstFIzN4dwlTHqP9s1`pX|w-W2cR)ZFi7bf@Qc{J zl$<#?Tbm@kP|&AFAL9XI__{cOz1+br2&d*1(=j-FhG?E42*+WN;JQsejupod- zvFUDN7YOiq+YZ767*tbCAe5w-i4ZITg|Hk!f;Tx+MDS`;CwMpzFpQ7-+_@M4IhA?PHIZe4ekPcHn7QSc#?(qABC#3q8iDAftZ z_)dh{jFsS(E?~&Uu+C(qlQ9Z>x(ncp*VrBnCL@U?v8CI*@Aqfm7z1MgrEhUZhaTRX zwIH&MMV|7Lc$#3zXQDDz0FT6-YrZ`)rl+j%QlGzpFyx~roypGLY$Ji8ReI>VW?MMu z-vt#5jVz{9)^K)HPv%^dCmLTwrlnOX?1Yq>(!NQEsRYv zljIzqiT}+XJ$drQ)cIPP`YsZ3`4gqSzTU@t3v1`@ojX5F$L4Rgh6%f?Yd+=k@Zuld z^>K!ezRi!?^>ZlN^2Y^_y)yCjYx%Ug_44(;zFs`|IxH-h9DKez2(x+U`a1}?QP38z zGe_=)5+?RdR< z&9jqXw-uW(-Q#Z|(6dC@ZLN9llcX>%rlrg#pKZk1J>^K+5+WsCQ&eCG`Yr<8Q-5}CPNni z#;R)|GakoxcA%$E`!O_HohacT$#tTh-Gb9kU>Pg0@_F3&z&O|shpb*`z=T|MS2$aD zDek@Z-ZlE_(X=R2@KvX<4?gr!>>`YkEvhgZ?>#u!iN3WJohEsA^5PZx^yuOV_I$z4 zkvewb%=B&`V51IpT?DWX4AG`b^~0}8rwb2HAKjsnWQz?RbOIaRiZ1l9 zc>GG61&89EKhba4HuBBBgOJXnAD&$tmnS_XPlf#I=|u+j>>%j7s4xMN!_y-fx=O#_*_b?T(r2shacn1R$a963HM}XYlVz-uUy)z@i5ZJR$)2usQfEOL zOkj*i@$N)$J-c7e&eyAlhl9S|RvsUv89f z0>m{u)IIdjLu+}#Vs3Nhg$PJDf!m3wx~A%FlXm^7_3w1APfK+zmh@y>{?4{^uCK2j z(>R#LeU0!nF51ueik%>fpymPj7nb~hpLFh!2Vf0b81fPMgs-_Tp7j&sMoxZflCO@% zr03dy_8tz7#t97IA$d7m51*g*saDRj?lR6rc zCOY^s7Kdjr*Ystq8jZ2gV*VOO?vb4Han+RHu)3R{3;j;$Y~!9x77pZtJuKB(#Gzk*Ko5PmlifZ2jOpa*%ud?KZ@k1x4z_Ui zd9P&FNgjseP>gYHoP_PZdZ9xuv>oKVoc6bssZ9hrxsBzw`jeCIb}~>$7xi|e_zB!h zEc753J=L|oc21k%6{NF)IveD(iRgNh0MXy6P2hJzY{Kl;BL0)_zfN)jAk<0ZK4!Sq z0%1L`+554Wh?8wi^3fQ-5MO+XzwizCiAUJM5H|H4Il*gmx z`x!I5nhbOKQ%e_2CX7y+eUiEwUm2fwlCL>}zp^b`lGj30yBKH;vZ=+k7!ixw`9%G& z@x}M_p<8C?PTYOMCmVgPXZQ8kSJ~^;YoDDkyEw#e$+sBDZ?OVF3h~oCP$$Sj{$uf&YW5w=)ql*KK*FTsHN|V|1Euw1RGNIr z#4+*JZk%L}Ct7%+Czu<+=pHz5V2rb3CM(C*S058qjY~ef=qMR5bUzk>OB06nCN;de zNKsEFyEBnvB-2Fbn6&Y!zGT$bxCUJp2gWO$h+M|fwF@fA2S(L3zarTkgz+Xk+QWf! z_=*_&sViQcH1UN;CvlkQ2V13+9J%PNkhu#3aw&MhhRynM0KdYwc%X@&1HQ(hSsjZx zdbP2@>KMAhufAej<+KZ-FHFc$nT*lTHx-s+XJ22MmLa*mbYWlq!x!q;FzPOveh#vj z*w_JW*pbzEb@U};eHMO^S8&b-ECFW|V3QHi-f0qer}o*Wh;FHO*Fsy9BKi`Lwtg;q ztp&n*UX{yPf4|JKDTMPA9FxU2ysNhY*+n2b<4090 zJG6HZajkc5&|i;l@5KyX&ri54cnjA|K6%)PZRrjNwuYfa7F^ZKA?juyfL;4|m7kac zx`5Pg2(hq04^RDYD|WT*=rTIKy=bQ=z3Ixo`u;(8RJvH3r8TImUm&ajj}u4d&=hn` zli5&`;*{hRCP~5TUH~Z^s)-aG$ypEy7Waawz%?;=S}@+zp-H5ZVkdfzXaeVi-Lmc5 z%?GpVpbjlt(1MmsXnM*t##lNiUBRK*{+yk@uB>3}b1(bqN~+zol+i6@2~@WfAH2MZhW=`A3Bm#eXu(ZWH7n1tGe189XRP<-z1lMW6;+|FSKDnPWU+nUa_-xGL+zyF6oqi z)%PvH9lmtxaPxMMADl))t^0|&-)}{!zr!iO?1wJf8wYJPReZX_qy>q zdw;DYfkG2SCkaWRld85pRY@lJOCnZ*as; z^Ir;Gu5~?aUOUa|OmFljcG}?2oJ=0DPA5l+Lo%}kUHPkb3+P66I+0(!qMsOITYcnH zxlyk7zLeO|f8>k}8|cQ*>Dk55wQPoT{d@4a#{Ko`TEBh~(1{RUc*21^}{4%`GZ`;-gM>z{DUvUi_gPl2pAuR4XoAo9V!^Y)cEGhF(*vq^m*~@ zLh`5i$!y%ypPw7wMdEcA2V`8oKu|YP-l}Dq90i5M$XNu1V4-ZErUbOWFS$^eCO zgGu_kca{VI1Arz=ee~@_p??4V{cA~s<4Gb-_!b)`*(Pp*C)n^%hZY)r5*N&Ur$BNV z$t9&@)OAAR#Ejg@vWo=t@IjN&@z;m$urOXj|E}11kiyn@kFCMP7rO((wfTcqn z`sfKXxYkLubG!zjU`2->`gfrMA3PhxWNI+eJN$>k`@G$epWGee^(V7}F6U*>+PSjp zLk0yV3La!PhJKwyD?fSk!Nbd@Xc()W9`J=J3{H0^y8A* zCsKM{OWs)=`eap1#$qUgynryic1tGqW@9|do4>l|6YNb^eyUE+X&#_U-=k7TcMHei z$0p55(x!u_Lkk5uO;)Njm=t=zmh7%)_0q%rwQSyg)!CVi)teNrM&U8e?ioGd%kSyY ze5g)7;@_^tk#8!nr=4Cpi4(CTZs~8wfuFPYpc(r4oZbY*&*{l#U6^%I2mk6ptZS8G zpzV4(uh*A-9qo^w`%aqt-BE03@x9iyIHTB<93yC|l&LtC+0N9HFN2z4@Fo?M&A zIfH`|bsEZaC!x+yaX~B~DAmb~(tTR9N)k}oC50wqeY&7fr=L1ch{keMU+n`ZSp-)n z9ef2V&>crtiFVxSBl3Lk_q%3F(IpY}MhRkH8%XC-?X}cjsjP{{6Mvo-+lLu z{*|9S*g+laYeSd|9{Q_qBC|*+Hu|tPp7iO8U0oM2#@9^u> z6uIE7jlc6ViyZe2_|jDeD>^>=?6X4_IK!cSmG3y;^1*xW{o^Fh!})=pqnY&1=k<9% z_v`01SA9=C+rXFeRoK_^x%-8TyL$Ta<*Q5iH8}hCN6w$WoLHC*_D%ppk9B1>ajs9q zZ393j`>}wX9!Om;S+-4oPgVBX&qe;LYzHGg0Ux%VwUPWG-fMHQq%ZvC1QQWEw+?=2 zPLmbAJ(5Y7eIjkrBU8()FS7vl001BWNkldS^eq(%OpZB9{WX7M&_5VJE zIBR2DHm+^!o5nO3h)X!|4YAn;>$a_1?~DEaaTvdrZ%>?9$WM*u+Gn`q2ODzjyU^#( zT)OmnK)IZc1h0n1N^F1SWb7ZGzML2}(M|&Stp&iE@-r8;xyj9hxUyk+d3>)wat!(4 zEW-ssM=A#&xnG9DoHII>z8*GBBayg|36KnzXfd#ZM>o4TMfa z^l+F&^kpP`oCS5CcJWj24ou@vIx$veXS6y|Id`(xchFf`kx&tX_gI+0lK!Qm+2q!p z0+`ZGJKXRCGmU%P8t@7mC13yUFgqop1_{p$~cNTNaaU_MIHrwcdNurb~>Eb!@KmEN$# z$9VG45f2!_&$We$-)$c{gwAXs7rO9;4c_{>MvKpA>(do_-B~iZbW*^J4J8e-D*1`& zxj+5$KfgO||8;gSyiI=b{MoblqjMKl^Q&pAvGMq&lUVOc@Hm$9rTlG*@#8k(e>GoU z9gBo~c6=sRM@$;|^~2A6?yFz@tB~)6@iu*?H?v8l?M)UEoEu{>U^l)D2Y&9{ID{Mh*$zFqBs)rTcfIF59|=bP zA9eTgY{_+<2OhVoPxYH+tYZ&F&}Tb*u> z59S*(;XR#Rkwl{v0jEzuIJ9#ORxX9+fBDbf`qm$IE({0APKYtU_f`}t~bDQ01BZ1Sv7?yfJ8z11Dk+b8V%U)0x8Rq z6~!nVu#Yf0zVWJ5z{8tD$!IzUSR2RWh*$#z@PckCdWSaceKC?8bY)D(HjYAPvQT%QmA`UngC_-F z>eb;DFZnNhs|go~BnTN?J38;(wsyu;Z{MYOwoq3+uq~Ym805pZYqhU)y0np1(cU-$ zX?dmta=?sZ<7h*NwHte)tG@4Ipy#?iwNLeh9yx3h5VmMW@0=dXFMXCT$EUo*u*RJ+ z$mIJGbflfc2CoX6qGR0huDv>AkO42`$D?Db5Ntwk+S;r8-+ur1r%!Zq{Jwkdy2JOlpKStjc-Ic^lAJ&OumAPS`R6l#l24JZeup_6-&ENSGz~b{ z3Ow8O&R5PDcg%{&h1{8%$i^@0Q)F=78w=--6?UL2eR?B7XSFL`YXd9u%b&}4MSWD@ zE~^Q`b%WnpBW;8CPMTAfidkuccf8Owof}_$W18b8hs9)lstJVTM|)uEAu;u~3| zBX8}b3)&cf-8T~_ z#%E8Sd@wlppU{A1zTtl&kX;>F=>k3cz>I4?h3!!g9=t<@5Bh^K|2M%L(%Kys|fil?c6ou+j|`10?aCStbV3V_@hg8 z3QKx?c1{w+=N<;uoAG|2~YU>3MF9oUR z!1%J7;Jm?GJIDBG7N@_`37ZU@p+TxnE@4sUaR)g#glnCo^l#u8L#`*waf z$fUk|xi7FtiR&L_{DN<&*u1LU;Mxm-(*|9L^iym zqrXHGX5a-5$Uz^)Ygb*_*MBdZevua@?@gZvGKsKz{4T@B)hq4aw88OVc9YM>`m6{suSa|4;dPZ$W`0Gw0KH3G;Cs;m_a`J z;@>8AI7Y88b`}H9#T|6m-SI~vD?a>mTXOMSZkY1;ZFu=uk}Y$VM9#zS9HoX!E|9-|VVL7YGYWOVg}Cn%vz06;`A!dhyZ zz!t%PMnR_;Cr3g9Ep4@9P)hXgHv!4!dX?LW5iNC`P%;2)`g6$AWCWmNtR^kmaZ-*9 zI0_y)d{++`wgE|<>;3Oszmes@r?bTEZh*Ya`tTuv@m!HH8I`k=MLRS*`5Lp1hSxAY zfvHPp^lN&C#hbIAuj8<5B;nIkv8tcql5kDm7OYl%Jo23s(wtj6zT zMUU*ENXUGpa>>)@KKHrdOS=X}dN8()Gn3CGNPmJJosfr3zW2TF4R8DV`x_h3kv5VC zZO~?4eFO=Zp$Gb`?&gjon=#<9&ZeDb*7~cXce)}Utl+V_F%BKkBb_#QMZndsJ?K4r zec=mV7>vWOcJ$kXmYfRvpi^DoK|618!%zF*kBnU`WoZmU!FB|Dkam0!HT z96hK&g)s|#-K8aEIrki3Lj>T2&P zk_z;jP^%YX*n_SCEi%OQwuLxzcsnv(LF2F$gmD%c zT)$yG#sYkXC6tMr5it!AglJG_C;>nn0kvxesc$zz3mlXpC{T)`zkhTC7(TA^wXc0` zt~c}6hB5&cU1Q)2PtJi!dwYCvz|vKRPf3CRUO=NkMs$Hm8#LA9lQCTfQky!o3uNt# zZ;vlBksm+$sp~ak8mH%R=mG2I=XGnHkwHIWR7d1+&RH99@o5a_oK4Z5Zfi3^yJdQ-HOWYIgExrSF`H-N$zzU;f9Nyo;-V`Vlze)Xpd*NQDR)6Uou z^amn`HyiU&8on(_=$*!qgT)AU?Sei?U7ncp{zM-9&S!iDPW;!m*qZIhOYdZ6kK&1+ z!G#2jt<}2@`(go|3=x{9UjCKcgf4i>oDC+tdEeZ&x(gu zWy1p;lLNl^PH(Ox@~~TfYkX_0Lqi?E#5XSc7yN4wf?Om=FlaUE}N z-0&nW@Lh9V<0rk5pN_Ppdq2SrqnCYa#ZPT|m~F4m@l9SmEJ2vAv?e1JjDR2W4ATi0 z;{*pzfL8=WplJ|jCaQ0Pr*@2rCg-Ol3PFeSbfO22X4F7LS(FZ7ju{;oDAJhtG!A-< zq5sp#@ZI_KQ@!{!uKvJ|miC?8(a_IzMkb@BDfPgwt-2b`d7X%p8%v#W=z`2Zt$&lK z8oq%eV~tl@W~);c81{-iT^P&Q^kQV8hG5zTQCXQ={qF!( z+Ou2bZon)5#-p!3&Owh% z?glJBnS>wpc)%}Pln;FbIeWCxWG8$S%jMfO#jKibCJC~jZ+v_^wvxpL#_F#;=mAaR zz+ryPEIGv+9pl;NxXD*-%Z}uO3m9yndwAB4uE;uf%j?*)Ygq)w z@UM)`VFwoQCBEQ+-qn)}K58>{`q|h?J$~7!{hQ>w5h8D|qpd$Ii#^&Ki;uv(aa^$Ke`)(`P9jzie1}B?W6-;CjBN}wZC1iuhg&|=cn^oJ2~THqpyrbW(f-!@n&VjIn2+>N8*8aNIv|-7&)6*(u+6)2NT2150@XOUDK9#!Vk7- z&fvZs$ru#N5S=7@PAJM?0Kif2oB}8ZV1c8K?>Z$hJU~ze!wDW<%rh1zF^)n}CJ^+1 z(KU4SspCtG_^NbF7XbTvy5?o*B2}T*8q&Xblaq;LCGGV0B@P0(YQCnjCf<41`qmC*QAo(>5|@yKVzX^UeMJKkL=aL7Wj7!ulo6a6?2c6dWBUyMx*=M zne6&FM-R`=JHEX{N+G}Ycow|yOE&s}ANX`k9tksjsB?@Co0vq|M?j_vHbc{OH0)sp z?Kd-Fq#VmH9lTLyNuw(WM0wu=3npT-OVMJ)S*?;319VQ7CbalB(J5?2r(}jfk9V}E zV>&RAz$87*PjuHmhug7i;do3xwqgUkG;ZNhd|`jQTh3PS1PAO~KO#GMJm!PP-sI?4 z|C8XuAN%oLSVzA}cjK$}#)c*1piL)qgEzQ1x8g}+$o_r~cRl;V!^oL~d47)9Ca5rm zc4L)MG`WU6oTS^h%q#$a+Pe1xpd^+Bt# zi(b)!Av`$87yj^H|IjZdCqj#EBng%^=3aQEf*I;o%Eq7alHwfCc-h#WEcnC={>?$^ z8wcRy$+Luv_qC zQpcAG7T>`0mybO*nVIDq*Sr{}pD{VTjbZWB$EVKww> znUjwEn`E{&PMsQX@obV5G!*>N3tbyS(T8J`!Sx18zYcgI0i&QtmtNV89(`Wa-4a^b z=t?GeW~<{K68 zQ5zVaeVuD-L!T`4LUuNBY=Va|ZH$cvZH-gE!7F|$ufFvS@@XeXsHb~v`G9Xm&^>$@ zgT1x08D~D1d-0;Ll=UuhwLCFed;dz%mGpP@9}(${{B*>wWWqmtcOtB;Kl|CwhDq|$ z9~qy{Y9KvnOGY&87v+kRBTDJRWydmO&P@`3EUx)}?Pd&^aE*M9@v77(@XY3}v8}#n zYS$RjxF}A2<};reTf7&cdBfhV^epKr=)d{$ z%g5e+^Ub}!Z~{1eDXd1PTn+_v3^O1IIBj)EJPAI|1sqAlPm`GZ;5*-u#`~{Sb4MNL z^1IxW zT|LKkUC=NVy%K_Gf3LS5ZnXr8Ci`edP7^7Iqk{%U zW8<3)0)j*ez}0nvf(QNa(aG?Myrh@yAn#ZlXj`Y(*JPYK_Py_Z``df}$3OdLn>(@E zAMeIOuYt?4NkuY%UplUQu-4$BjY5ZbHXd7OqrGD^U|E3GZX9uX#S=3@U{>F*QfZ?< zEOoh*?u=!lZ+C-mgD~iv4Yv`IgDjYRtE%3k#lYU;tZg?g)ypjRF=$Ag|iT;WaZTI*0 z2k)@2kM{bZrC-|m9|CtIXhxTQUbF}QY`{)1j|bQh6UYKP?Ain#uXxnfb#^CH{R{8x z<{Iq5O=Fpr0^{%}g&&KfFi||KgQsE{9p}cM_!caL2Rf0-yF;%1VPIQVt2m&2c=D4< za>1@WyO`MV2V-nuEIf2VBP;&I;L1u~t333kA707LAMmIi9pkVgd5uFB7(mO|@J=o| zr`P(i*y-j6Jv16~nyFFyO^v(J7rb*tOZwLF~j*FN!y-_7s3KLuPA zNU><(1z0GslYSEme7N38wUZx518jH6+UB>a=b!ue&;R)Cciwqnp~8a)Km6gpm*1TK zuL14dcr=FM7$0~b5988Bom^cftLldQ#_HtC$@QTx1&>!m?#QT4KRRl_2X1ugl#aDg z=)8%EK!p$O@I_X9(h)rQK5Vec&f3-iwNpp`92Sr8!%hz;L+8AmbSw{!p2Dl-#c}-r9piVL+7&PQvxQ@{ z`3U{dkvcSbYDmq~mw8!#f$tLH-tXbYf2*O)@0s2!^B!|xhS{{cQA!dbn8EkjZa2A!1Bbd@Tje0 zZEe+wm_ujiVW+m&pC2`WK(k4Rw)DoA6fq0t^_4KX-dME1zrRUHnsCsgwvAm)D$$rZ zDE=9yvp{|PW^}Vq(~S>9^SI?EST8o%zAHtNKlWr>{FsmEf&BPw{8Hz*`gL8MpS$Kg zk?o&n-k7l?wrGNI1%Sd-{w>vN62U0tGLXQ8S?7QNpg=((4Py44GzI(yXA><$0=6K2 zCIIsDlq`jwdH%&0pMuS8S$^fAhrV2Ja2#Vc(0Aek8cH-7b`o@FrXNG7BZDGvqv7j; zFTMEUg;(Ev^QqTffBpGfs{EI-s&PF3`S481h2p0nXqpSe@Z z7zqNacMvkDHsrz&+prZr>5>iIwE{L>qNS}pd9=lwwq3=Mu{$QC@f^Q)|NWQVI(_w^Lh|03HuXuCC`>+4MpD>?7nUq3b<9l{rRV4mLa zSY389!bTG>a>7~@EOui9w&!c?ExAi_aQSiBj^)TKGP=7jJe$n$z?O7Op85f-*4~W+ ze6d)DGjfxc?X5UCXIDO^Et>4g-o-2(@dX>%wPSB>jIY%H@kKW;LqQ-7iJ+O9E!ykx;UjKU%Q^t0j5O@e(_S(_lxpEl%FM;GIR;iLM{hWunh zgU-;QTYce#?D{O}Id9Pxzw|CaFoEBaZZ?!|;$V&Ik6IZC-;IHELr-{YB2S*;iM(WU z9ZsCHC7si;7)-~JYde141XD+ALR(i2dT`>zfU6*(WWsXFX7Ly<&;=|;UJEgXYo=cS z>Cf2e1yx@J3p$4T2%f6RdaqalOY(3QH~>%3r##MTk1kL(5dbtw#e?>OH=d9!ui7xy z>456BS6;c4Wp8h+E$=>L(({aiv*>3j3ti(fAVUIR9U1U|mh;*Q)XIw&?K{~Cf&w29 z%-!l*2Vr!;QvfwEph@qFvGpT6o~j2yhz<0`v*NmXe71P6B!OU_t9lb3+MZ60 zSFl!QeBj-=py=3G>W#~;b%63=Y;`HM?NxVr@4fr(S#L-A;u-s(YceH29d-#(9eSN) zVT^5!Tf1{Q!L-R3{`4U)+~TWz!vVR`qgU-DjxFK(SRQ-r(Xnf_4Ib$g?u>7bri7+} z%XoA|cJ@c37||Y<*${Tfr49b{!@oN6HM!JRy|MWNdSpXiJzXiV5k}qq&UgG%b(L># z1ibSH#%9`s2EW?Ic>_Ehl9gA^8ul8f%0H*k_0E_^iVP8RD2=(~w&@>jgW7g=D3 z-SG?)uB+1(CgA}-@vqK#?aSuq&;@L1(>PlE(t&IA0Au=^RE!BD_$of#y!cX~zIl{b z<*>o^Ffi9L#(|e}=^v)Zfj&JKXZHL>y_+@i6`xI->;J{NdOYKu{OFNeJMyBb4gG0D zUV7BdIL^g!=i0%y$-e&L9G&W+dc<2NWaHYjov%(NVa>D7>g9U6xg~go(9B(U#{1dW z2%k#l27CUCAGmG|p@-tVD}oZW#-*-$Zaq%h8SmE<)NFE9AUTnjT;d&(W2Icc=EMrH zS`q?$u_}#*001BWNklINt#RnD4%VQEk52erC<&F_-tP_tefqQ+mmX~RMc-B#B@Z&}7B+whp9bi^A&Mx@p$d0u|KkpsPuNbDR?jZBe*)D&=noQbj+oTLHYzCX`f`6q>Up)31 zEj%jvFIOOHFZB3^V&7w9(}RBGYXBvqvFLg19A2WM_oN9Su>#Ccd;d##_5u8)2;nVu z5JPNqN-yN8EoxuqC;* zAlwK8gOBuoH;K^sHnuIv3(u0=#4yN zwl%rYw!Yz*JQ8Q^^`#f%qi0Nsfp%o2W2?2W-*H^S7k(VmGpzK6bP_efhKVblW$zL= zp-omd8s)3ltxnYA-#MMqftxbAt$yJ~oYIEf*;S&tnE+Nizc0SsM+_ZobePIny2HFr zeiFKOYz)>cnKA0b1Q)yndB$wQAfN+=>p=GxfAJS{O>oZ=;k=a?`tM}7{LfS}hhw=* zcsbw2yzunXPv8BOuY6^U4@77wo%A>oKrFNMW{?0y;m+~OIQeHdfciqNN%72nmox7C z``GXmb}kK%+A9srXrMYE3 zv{%qq&~uDed~hho`q7d0`l97}3}N+PY|G%DID zZJ_C0DOm(;Izo#Llk`d5Pum}p@a`XLAV!A>ViI-@7;Yg-s3qjvhS4=i6f^iIyA!qv}@*%=@3?3mu! zM!}alc4I$2sx6y!H3^^WgLl`+s=a>bkW+yLa;b+?<2V*;>BL@pi6J`lV0@+ik&k1| z|02kEmzTn(#WfQ}cG`RS)TuY}sP#X7=mQ`4m*4;MKmPzee((2wZ}^5!Jk}rKmV7XZ zk51TZ*|={&_4<(kJ;&tk4Oab_tj3_9Cf#&q9DUh;WAB_dQPW3zn^=rqZ=9Mq;92{Q znN`zHvb*w$4V}RuhABI8;;-Yw#>Dz;#X4VZeUpXii{87jH(iIeZ8ZUmN#CE+1mOxN z^9DD8G3lp=z({DyR_y6QAf^NY5VFBgy`ZO$Ym~)koB(h-p?}C-@#^2&_TRO-(-dJ1 zbmH{cv(Mxuw)Zl5-0e+rLAZB)lxa-+f&*PnV?1C5G9WJBCL26G`t5Ju<=un4%p*bQ ze}H3bQ?NC`dE>tQ>Ih_a%+LzpDU7bY4UcsW znVO7{s}4q{f(+e`ua3<6HQ;h8_4;coY1`l5_hn3DLF)XE0l|c`>uWOx`RcIhT(6w2 zv9~%rHmK97IxCHupc>uA7D^=^D)*^FCk;Ss-R*`v?T=vy1@$%Ib6f$-|N3l}c`7I-7o z`*tvx&i@VY@=xBaTCLcSnXH}I$X|@&-Sw`Fkb}%!iPVm~Ht%kb*Gr1ob{RD5ixTN$ zi4S{jkc@>NJT_5p0)Q{rVFPxpEy?v!Ds)2dt%-w&z3@IM}Z{6GKtr$2pvK1ck2e($^Ay`LZc&hPxr z$jkrf9sm5gzTg@@#k76|jBK?lKKUE_*Jt>HHt3M6_($72Ozcl5bgIs=KKL;%I`;5S zAKK6_9iWM}zS=d>HjZsRgS`5M$F35Djb1ahKt8|1^WIA123_DEj|#ro*kc+zxsjkF z_4wAd^QgY!M(3hT)_qBFSNt!aaQNY&?;ynKjz`d0UZeCHi9nd|QV0MuNV8hkDWcBZ z0H?nf_G}Ud-=_zz{Y)VKJ#7>tL3lfx$-0xr$+yp(c|P9%^VeQ|_0t8ap9wZeU<_jx zY?Q3HvomG^2B_Mp!`rh@J@wYhuf6ueG&L@-?FTbX#K`YNrnwCyTPJ)I5)h(eU&A{` zlarI(X50dw(S1{0e@P4e>CdvaM;)6b?Tm+(_BIoa371U3E~whORtGXJy(oAxCSCL{ z4Nuy-HUO23cn1NHGgt#mWkS2zIDQ2kx<*smf)}9a#s5lxBLiDEF!m)W`lw^M4qN!KSI-Xa{P0(LWN;m? z_GtJPg0b$8U3`OMtx)`p!;?inw2N)_hmHJKxVeTWzu&j?$+iuGO`>W$ys$r+$ZK4F zLZ=Fv?9s`D{KjHib|sf%{pq*<*hJ-}Jd4f?z&-+UXCKn9v~<0{ZZPr;VE!%fU2m)> zHuBLu8`CXp+M8(X$xRl0T!T9@yGBP=_2_o=lY^{I+>v0J4BkOQN-xO!P*8F8M8dKL6Zv z7vDU4cAhj`&p3-~+RcBF{^1i({K*$S`N{k9>$3m-KmCXQa6dl2@r`c`ulQ(eGcF9X z2R_Nr1WCNmUcqOvOfEKtWqb9V7+vEZmEZdm;-7O9sWFVt$H>YqWUU{;hno?!`Io(G zjn~Ch{)aZ6R!e+0shnGA295qb_pFxTT_G1a@r_S=b$BC}I<|!y*VtOozsZ4i+ULP= zd8goOC4INR@{OBz1>uU8$8+}-15Rd6*$r~eJDGF|wFYs`u>b&Aovic+fCl+aYJ}%7 zK_{V`AecxjLF4P*ytsBO|M~uj9DkZ4HcK@QK8yvll-Egs19dVh7Jv&+{uA1B$x@$w z=9yPsf8&j(L)Un+3gdH>ycTinY&36qbPNQH#=$E`VB8*a3eG}4{4j83mvI9rQ0r&I zE4^^^>LUa*Y>b^f8t56@T?Eje4McdsM|mP6`8b<8e036Y4KH*}H|QBJ9T;^cK7;C` z;EODHC$Dp3sY9nWMn@a{(8il2#&zx7RVj|?2;W(Oa+^-iRuGzu)yDUn&v;S%ni(GghPbc`wX>4QAuY{27uHk_UuF)T!ZD^CLb;TbW!Id`jub(pi@zVD(Qplm5 zF^Vte`t0xT&)pTCT~miAI#+c1A2fJa5F5#y744%Z}R?9h()f=l|w!&c)|epQXI9{pqfoMDF~LpM3ILU-;Cg?#XlS zfBeX|zBT`P{5OB|H-|s`;}tLX#se9PLD$(1-+T?`8{r!d z@Y9%T?;5_zO3uzN_$He+E3zxP!Q=AviCyD<#@a52B?$R=`teyVA+X@6P`W_0G?aS6 zxD!$c>3V~&zJ#PS{kyylQ0gc!h0wjjG>OnPzUD=%yzuci_kZw%7qjY+8}8LI`?}wZ zN4aLj!A0hKO!oiUZulT1= zJi}OVM0R@O(~h-QsyprJ#x_6a%cH;uNyZJPcO;{i6z5+>o{KM;8EgBWul=4}NghJ;9xoH;;i~ z>#O3q5kKTLozENXU6aB_jF{7~qHqhkL=qtrK z9vs^m2l<9E=tvz}Vt_i27tW_`>UQZ9mvQHvACW9$`bCz1A0RGNKb;rt)x!Zk#e&Y4 zcrHHF;~jqbI4j1%`}P*~DSOmL`|4yI*+erANQ=&_jO^$NAWw=iLEDuT>`SWSfa7%68u4 z&C-3wP>j9*YrnSlUb5>~-+1HkREhE(vnvRfV!U&qdC||PI55xwdBFzsj{6O5JQopa6(EXzj>b^ayLIg7#(Ix*9E9Y>--$LNus13ua%_`dI-Z#2+%JCdi(@-DrwakiSc)<5f)Dy> z@G>qvph0K!=@>6CQ(Zs)mw$QJb9qFAm&!6^6A+4ZfhPjqb@tS$jFJL(d3Pg8&6}zB z_VlIi{OO-wc;oH2A3OE-+m;?0Opw_11Uhoxs9eaBs7DO+3a9iWX21?Am8{_6&jdel z8=nduVbKJhW41mK8MSHhgx}(KWXMJjD=~9-OrG>Z-|?^HM(z*($v+t#i7W8ZWSg(T z1bpbv|BX$*>cwTY>5YL#CNyxTmOaYS;Ik+!>fmeo<9!d=Qb4TYUkb7T#_0{Ko{L`QQKY79K%EL7i2Om2Q;dx-divzU58w4?fBeU<#e~9#%T4u687z_~%pb>}uTo{r!zyjqRpKy+m2jjxWIleAF+= zOb&dLjhqS`N<2RtOuY5_>(9jge-W84jQz8eoC6Qcuaahi2^c(k_Vv8T{dQVudoKOo z%CX=5n)GjEd)Kvt74c8v?xj)z=45CcfBcCj{`=`OXI{>`Ilo9Bsh`-9KeM_#`_;Dg zlvOxe*Sm}U_`N4jel*5fCAxa9OZC9X(ONU>+4h5ZN9+UQn;T5J=BIp@Ud1VXMz7U# zW2!jB@A(<(Q)NT41tU@l)Ayz8O$PkcI|aOh@73{5bA=Ln4Bm;t@4rfq{E z5eVOj=Ge-}1my{~E_FGcWFa%jxo4h#{zp0X8AGp`Th6#L5T)V%AYnd$(di5_&8BW$pHiibe26~gGV|9+bg-dSo(ht2b?lLJTxal^HSMHX9 z6A%UjhXqrZR;o)h)EP&6HZXw!qdGi0?nG`m6aP`_>K%mjFDLNm&d6#HFe~<)5P!f6 z$m~u&v;{R2+f(_ZtG0qQAUoE!2?u`Qz@!^qB?Lyv`i z`fb5g5DMLs|H~J@_=z~;$0m8pSb{*E%S27zWD;Cptdl#w8(26g{peR2U7z5c`VFwM z+7iBJ6~nUckA4J#tM_c0MA7d!Z;ElrjAoN9bgd}4o7~^uAN`dt?dXRt!|3U!e*W_x z#*V9>r(B-wu7&dk3fGJ6uEN1Gz0xtAFN4bPdWXw~=Nu#>&1@Hp!bl(WIfrF-ppyoF zn5LJ;7WQm{G$vZ;0J$`Zuu5H z(RmXv^;Q(bo318^O|a$8yLa5$z)?QoK7KYaG_vySRu*-q0lZd*mC^XLIoXVAvi_ZO2uX=fw1LLi^BP6JC2tD6X}+5uMd&yZyTtCagow$79IJ zcI2)u=!gx9EA{NAj*S$3v}>T%wl^4|*?-n_s}t9aB=zozw}$7JEo{n`aKe7@#wYOD zn<9Rqi*|T+Y%)_%X7yyKpYl8Q|3n^1U_R~n$ugn4kJ zV1orK3~<9Q>3CvmVuYAS?~;ZG)a z=Rp`ChhqN3Gb~kJzxH#T?qClc(AfHCO-JSZDq#+*I4nW%-~J|BboWOnNr7NoAt)Zh z_O5qDfw2T+g7vPMp#)~_O|S&#Jd8WfH)!W_DQML|R>4b~Ug@Nit{_xFH&Rpqj^6S) zcvle`NC%)1eE!2DwivmYEC7<1Y@C}sHJo!mqzif?p9BFAT+_$oBJcqL(9qR*TfnZ& zGrE*c$Y{}}tg;RwLq=2Sbz>>`!80D|*7(M#vzVN`nV+V6S!$akK|i*|J2}}5eRcNg z$z7YGjdyqGIO-;DIjag&c}KyrR)ZG1a*jz7}O()Fl%g((^pW%w8SGW?OpT#Kuw_8;{8N zFb@6H=EkBUI(26>Nk^9A)4n?BB(WXPm%Q~_ld2{N{G=~}nkbrhE&JZ-;|<@D|56gI zOI|3X7y58_M2GIAOtSR{cwPqm-iWwfT+FKs*&6xAEA}@{>)9emo z#%>aWKlM#);L~-sZ35}1kz|Gma@HMS#&P_T9Xq+#rq`0I_1*K8#x}NxfyTDl|1etW zZ2B9UkMX%~y@rMrJ6K}F9-LFrt*lpTn(Bak14Bd2c?l z4W3&}qU0?I$j_*KyPmNHK7j-<=wx}WeJ6>F?slV_7GTi6n{m((R5$>zH6x|dW|->m z(7V}=IY~&`;R$U?foJ=ikJ4&K!P{7+(?kegO&rM3M28Ic!czh79H^TB)}iS#neTF` zv-Ys>#3rB#Zf@b(-o6O}1<7nCnUF{scbq0N8IOI~gB)bQpKEx-SKDdJ3HupSZAUK% z(`~Lbazr=tz6QO}0j#qUGk{MxA6`haJAg z9yXYjfbm24Z^BK_>}rc%^De1Zk~ThmRDm|JBt9~p$yWHo4-D`vdvdTV-kL0naoW08 zA0*Qx%dxfVZ~%uh*+oumgK2loH)DVDE#-Aw`-_$E;}NR;GPYgI{_vx~oU)my*L6)= z+yO`s_~f$9s@K(%f7@*D4{P5x)Gap6KZ$`2y6;U39lAlPFVm-u-_kWb!W7%6gPr=S z>&n{S{e*o!gtvpT(d#4~NrsKZw!N7)&%sD}u@|TC+W3hN$J%uTSlsdpm&P**j*pfv zc)pfRcOcef659#FT(P`5&lqwngLda6sP;v?t{4!ucTgq-&S5X8GHxeNbsU@$6h`Lw zZ?6J$0bj11H}Cy zr;AQB~~>jeb6og!inS4?mW($j7$Y31BwI#vrRbz?$^XMZdI-X~P@2=tmp;J4TyM*T$)> zYh$u^Vl%0+t8)p$BnH`F^u#0?dwcS?jm!eAiI&5u$9Hw)9G`Voyy1%;Ii9;v?P0W- zeK$H<8N(y!Sw%bM#ij-!Z$!iGQoqg{%+h|^(ieZJ9m{iZV^5&fF>$kYk7T@aF zo!tDEPE2@8#^4{~Tt61ap6pf(OuLk&?Q8$-1hFd!+X=$8?7zfO@7Rpn8#Ht*zcpze zm8{q~RPVjs{Y?CeKDsXL<=FM{^XTcKc;e@hZ0}~0-XvP|FCN%5Y@B5ae%{2s zcu~KOMfqOR<`KhMjN9HV0$TS!u}iZO}Wn%^hPl!A`IS+8%EME|;!b>I9vPnqfOP z4tg_g)|BU=IbiiCi{!8KcjbU=CKyH}8{=!I5J`VQuakhF2FxauK7R4B7ZY{LC|f@? zEyGr)#_N)*I(*m*G-x?)wo0cBM#grX8MXb8wkFq%m7wKK`T0^NJ_W2Mb?9|#B4h$( zJM{vda}z6hVSrrtmOz;B@Jt{0$$Ko5bglI}`dJ@031nkI2o~8ukm@Q?6NHmF@Z!XK z?&~^H;9bFkt&e^>`IKKgOAHkHaI8HU(1LAzKyxW7y>rpd6+PLqWDH{-C~7if!1!!3it^1MDmxAhhMnkXzEF>KR7=gyb& z=iMTYj$ztw>G=yA&>0)RQ1Jp+Fv%XSvj@Ft598>tlYWy7<{gA23A>hU;$Hk@-a+tX zP2;hzG3$5yg}w31-r6j&1TVz|-eHj}_GB)m#wYW$>I@0Y5=1y?Vt2C%LhaAyO$=-Q ziECuKM(MRC1)4c-;@L#uuoZ;uNy_>5)>z&8B^#~d?-G!^QxTP3n0beeVT0c6`4Rom zHNVr2&ign@9H#5WJ#F}?dbXOMF()}lzw7Da8fv;hb$AJLV$|@BFBpKA#wN%3>5U3m z$RxH(hO}>N(58>(y>sPpZH%(yd-pONBI02y2=n_!OVNJfh;e}w!<0S>N!arS^!tCZ6~2MPus#eFX#bY0o3Jlu!3_Nr0eFJ-O>Sir^N}E0aW8@ z=T0?7HmMDdO$d6YX%Ym2-o=oI%#5sFP;2lgV9Bq&z|bIq-iM;cyI1c8n0Yrr1f4$P z))ybn(G#SA%HACadY!<_8;4QHX6m$=51n{3Keml*{z2A&Rl5d0$LwVsec4xk^lC5i zu_L~k6zNxtz(ZFUj4?W;%vGV|<;KQJg9!Vu_uRd&_X5YgF|x$wd>(&I;$?NGyyK6~ z*g+vr?@VD8pZoj!^C2eneX*8p$tnQp^PraxheG~E$of|d!yGbEl@dNjL6ymyg*&t6wxO5lDU&yAd|YGj{x{N4b=V}qzFU3ru-Ln#yNt)3__z1kFXOdier_J}jv0{n_Ws15#y!4c6IbXB zR$OxoC;gR!bN;Q4ZX1W#gKgF^eV2H-f>wk%$cIzrrdfP&_$N(>qY@8q%Mt_lE7JZWQ46UP#C%RbjTUOLn=h%P6n88foT0)2R7 z42x&-3KTYS(1l|lBrCuwKnvImuIOI?J14I;`gJl3opBg<60QRcz_+hDF zx6XoYCug*cp^c!_HW|yJ=_)}8-->GhO(rzRrEY@P+HUIbgBE!jBu%Vr>>B>`QB0cp zPEhxzd4-*;n^K$9)(uWg4w@i%LF#-WsDDBJ-aNMrZMZWYe&`4Pc#*i`zkGFK5zz5T zCotILfIe$8ZP(2B2!kc&WAj#W=)-}+KAqsuf_HdU$UKuv=f3yOyqo=BTY(v7lJRm!xUbfIF4f!-=r)-_^%1 zuyTd`J_@$}W!?tMY~0vGPn}m_z=r3g4DbT$ih4SLJ7wz~gsHpJh2gi2J`bs*Jrl_Y z;ciV9E$|Ysfw0VRdcjb(!x@~*h&WIkLLFssn)YYlIXS#Aplh;Wptrw@*L58V+Aj^Z zJTDkZ^P0gopt|U-;(KE_PTsp3e4F?ftxN0VC7)nsk_AKoPA~yRx*|6@-8u9cgO3g* z9RdE5Z=g1j_-W~vzx?H)-N1;ZF`EseLEn~d9m_Noed*e>c6X}&84jbpb{W zsGV`x%h>vAkI&k(wl_Ap6gqJ?Ups5t6%TcI(l5m5y*ALb$Lvc14Tfyo)r8(zsFSpb z5{~CHyJ*t~{=G-xm>f!1eVW|SJDKQ0d-d#Qt3C1CGJ4wE^KSyvWc8CIj@)C|M#ngG zguecG=rXdqMC0JC$xPeA5nkzw{X^Hnj+xOUVYakiWt~^*ZNuaGV5!$)yLHR`cE9)S zJlH?*9uxnt3EnV92e1rxbV+aY=N!i9#tNA)bR*SY7$-we2>`^FCfMj<|CXUKBbrS;7b3 zj+Z9hM_FBVLa_UzMzOmd%Xm~5Y3Etp^&9REfqbor1+={?=?eAAi^_Q7^-z- z6%3q{f#W;x1nC$UfZASgL4!WgqdSv1!zaj%vy5<|ryV-_YA2|wALbHV=#rlMf>S4K zbo(sb_<-q}Vq!ugck)&cX6e9(p!8wCPF%(b|t4xAN(7$ zybmLXCkgCun2iOc2^uM0ULwp)n$5cDUhp=C@zj$W@4eg8-n9m2j;NhJWMZGTzbEpq z?U1)Qj~$mzk+G9jbwWq_!i4K=*%ymkL)&>1EPBOPu|PlgvFteSp{(@MB)IPoG>IJr zT%&9U|0TY4*}i@aGIy?;5v`7qm!9aSeg&WE`=Xz7wt*#E@d(cfd&8o0aV4KapIeNz zoR#%_>WaO$3NPug9 zSU-%frLmUR50n2!{g<@1={9-qemSXMtKYKzdhM6j%t6MOL?IV0`MEJW(~Z zzR6gz!3QM_bZO;PzpfTEfnyIggu`rfa+uz3MyBhxiSgOl_g^~k!v{X_V1tRi{H_h2 z`>Q_ZFiuXmD5iW&%L|YZa+~klweR)iSX^b46W9I3PQr|{2ODXw#7Fdjbq|!f}zeBWEh*}dL2#>ue`3;_Ug2;&r6uq z^|kD`yxzcJuioD5Fk5uvBUYm$V=2ZVBRS}rj^PVGO_;P-@Q!D;Y(a-D)!*;VbVW43 ztIQ*Y)yI;iY3*2DqF?L_`~m^H;R8>6!!bW;U}HD_gI4b@yaeenhL>YAOl9rywRE=O zf9v2@+h2>$QS0kZ8#;{v@WdW;(&Uq_=%#jpH#)RBWfjjg_^!|C+qiN_{5UG_1D2p` zR#)+>usa!yjUP#ReN}w}I*h4@K|DHlojmZt{>~kn0L2+4SlPC}^OciMi<>IDw0~__ zU%V*>uF6=dJNIar5n`^MZx5T~bm(H71@;Pl3F~BwL>E74Y zQe8bARbN-o$;rGD1E=}|UqcCL7s#{ zE}WBvjBqGuYsYqMn9eI*O`evQ)RIoIJG%6vG*M{4*SB{$)ic@gMo;*`D?OoMJT|uB zMZz<-32*DNl6QsqZGE)O{~z{iWv-UsVTa6g&o9`6-FgE;FYHFI^vza+C;hgL&~!>) zaAnzbN6%%qV-65mwelY+$M@mLrm!iVCWhsjV;jFABcCG!yZaRj%s98|Cu!FPt?9?_ zS7mAIB?fx^Z)?8`3|@-{y{`k{W+iCkoPP=x{j#+L(lNcm58bJ^H5p*1CZBYz%qmmr zXP$};919}3RDO)VZTsnalcjw{4y5O^;^JT95?nVXp$Wh6Kn{3^5wej3Yk0)JHe$X_ ze^|dso_5-7`G{dD>6b`JOeC79B2@5qpB0PbXM%mg$49T9)V#1k5>vAsTu3BlMspUPFa6`)^s=KNeTy0MBd&+X}$O!%SK zWuR!1LNEC2xB>Ns$5@;vgTE z=&lP}Z`=RxR9q~hcWA%CdJg9Ct^|;tn-IbQpAl#Hm^*QaIo-nweTxD0HRp;t*9BR& z<7+#Vp|mDWh3`v#wr)Ip7t8Pi3owj7^5NfJQVIXA^~MvQVwAehse_f>yl-<$;x7{f z9aodpY6an#k5vFRftwj|Qo#@N_Vn$Tp_b01kIBrnr}Mw=^#w@vN=B0=Mxv zAvsNYeY{Y6)gkS*ZK7cO@~f{xp2Z!~)ZJoPX7}?8O>*F=PDKvm(X-=q5Eyy!Oh5M8 zleeoQc%@5$NBtN-?OM_&PZ{>c2dvnzKOFZ?m~F|Aj{bCL!&@<8V!#tWQ0MN^c=YRK zHv0YbFMY|7_vIg=edZG%|M-dgKZw`gednF0?>%58j2^6qec+;>I! z=8t~lg~ruG`mJk!%clb71bc;^B+&Guun$``IiNd}>Bb(>n;>g-imnw{QwEDGx(E50 zU?1g_YdOngB9`U))RGe3b#J9>yee)v~^ z`IkSJM~)BV|FgXE_Qi|O{KCP(yZ6TaBXA1a+b`{ZDv$3Td+af{%e#<$J<|KS!}s#B z+T5JY%iCf{@IlXFi^A^e=uVxa-u1yl+Oq*Y^KtW5Z(7)c-6mG79_3YE_D9v-^q)8A zlOW_jSdM+gaIqIJ;ntblb`>5U&*n^=VRqw+c9+-hlpunfeWzgk`k zc2{qDBgzPr--(Vut~Ut~00hrYSo$!U!0N5~t}--mFBsjHDJO-=%M+M&x9c%kzKD*?+3k+U+i{(7Uao1wECp0xv94#>%Xo&CrvKpR(E zEBM%2VKA{{gXkTCPZX$ z%s%;7((~(x&96!%$@yG_@z{i1_Qr5L^5xtZ)rn(4iu`m$FUHlr*YNGS%me=9BFls_7iz8U3w=8+c&dXaVp=Njo5n!A9~<{SO3d5zxj;I(`k2Q{5bkN@SX*| zbTzw9~z=D(f&>G{xhdXHc69zXv4L;L%u z&b;!<7vDK^<^eVdpI!vWGq%-NUg;s@u9TfR*GBQ$sE=HZt-3^E|8q_r=yQW(RE%oy*u$R`O%55%nr#%NK4}yuJh}?^>$Z&u5-|!~11k2ct=~)q2BppK3)*?x4b)UNB5smxHDmtb8!3f3z5YVyl>5C&`OiH5yl2*3EFd&O~sI(-+D zCN}mh<{CG(MTh*_ODg`wzy8<9Bkz5$vF+Kj_h+K~N5;ko`#FZ23^eC3i4MH6y)heK z>80_8uFeE&>a?49a_-!zd+xh0|5!S|6o|j(9i;#C){8Ix#}7a7z#o3{v!DHi4~8%Q z_B@H|T}fEX0`oW&PETe#aq?t-?4L<2^@_Ol+Uu|9BPH|py&wOSk#{NXRK3wcLmvqsiNm$H*70IJL?1)EjHevr_pf^_9unqgey}ejVHnBlpwza{K$Ik1FYB^6HfzzAs zdd)`+B4mNz0M|PJU}caRox&@aUoPlZ(@njl7%{CQaVj(4jjcpt+CJugm;kD}~No z*O#7v+g?AD4nBZ}V;G-9aTbMLj6?6nG={r1x<_}w%jMU10IuV=0jti3mO8ScO;!Pt zOylH{gI@5cu0avF=|T`QzU4M!rom2t%`O6xF^n%G7X*yUneZZ^!MCEFI=ZuG3%onl zk1pAdO!~B+zN4qqNfi5CiB15nqv1Loz&$w|lO)>AJ)Zgp(~qs@CD`og;-Lga!5v2QqmS9=nl=)M30V0*Qzih* z{l@>l|L_lg`=#fe`$UG>b%HQ}7TD+zk4h8G;uALU!4~jHE;fQ=GN^}h`XDboqXh?p zt@S%YA-&7kA@;KKV( zr*hmCL-=9SDL=#rJgDved-bY|q-#tQPegT_l9 zn~lwjWo_7<%yh}G>UVA!9kaEh9({gdo`DJU@J|l9?=m*wF8L-vrO<~-*+f7ubVeT>(K#cVyd49AYk;GFwiV&%aJ{@e;HEpl zfG+910klCtQ0kp4diXL<|3xUC)d43EKK$^*lc|pk8H>$0x^d8M5W!zp018yJ*h_o% z6TH#nq~#SoDjT;?*zOFcDE=~JiX^i7OAS>RC+q|?5T)8XGk8Q})$)MNVEIB8f!o;5pdcuDz6YzZyN8q@1%4dd4)CKE|>vZWV!k z$RozUD_+SYNnlHf=p-U(r$2u5Hy88+SlU%4ZJT(}JO5Ge(xd@a@TCu1pog~2*!VGC zCtA8BKfI|=hwTKRc0c-l9qjPuki8tkE`CQR(ZQ2XJ~=qnhOUY+dopRGtz$ISKD~|g zUE}^x`InE1G#KY_fO&vr5DXF&;{*akg+VAE=oCiy>%ac%yMFxRA8!IQAvp>|qC*LS zI$GFCU28Mw3-BxoxJKyrp9v(-3BXIFs5_1!|yBMO<5-h_5IDnzBJtJ z9y1=^1$esQ_xM#u5BO|?gm;^Iw&7davnRR91IOYGo55t5y&X&)Ce9}Ixi}B1V{f`r z=*^^DU!+g^<|8nVH#~P^1NQAi#*USh?#TnkY|>$tUSuRj^s z)|ljl-`+G8>*RG^J6{)^L?z?!2fS2v@{*Bmx}gCt+Vk!Dz6lsTJQx>E{7Y)GG_;){ z92M`=KfHLp5Xh`_C}z+dd%%7lbJSPy%C@jwUu{f9gI*e&=trA%IV!)Ub5r}nRuHBm z}X@Ecw%o^UZJIDQB%$VCM{UCQ!~2WZD`F$kAe#W}8h6 z@GGfkFra(j#QTbT?6{h&-5pIa2KfXUbjWBN^w~-I=}&(;^7L^AxdjD$;DJtDCo5+~ z6EEm`0gpb8ScVa)1_umdqilU0`nvtql!RX^jgt7L&~wIQ?d>C8B2v!RLP^Pm6x@IhDf2Qzf054-oyM{v|e zJ^tCJ%kIW#5&@&yXzSQG#%7oLZG$y=*-gFcvxI!^++HRJKMBRChyVZ^MoC0LR4r&M zXaGH`;a2^_935JkCWj55bXa{lXFKQYg-7zz0Uhe&I^F0;MxfaG=*`M*m;WAx|G}K4 zePi<}n5RefVmJE7r@}6eKKkhVLo|3W5e}ZB-};Di1^;}bHy-NPP9GC5TNz8CuqXP}4H{OP=&L-?aUVC=bwI>&^+g;MmnmVs{Emob?l4Yyk=kkrw7NtM_v;WK$3^NfCvOilZNWZ1SDwC z8;2<9)tj(^eM)rh7;xy;IDoE>-W}sZ=>&kECITFblYJ&H_u#d`M&RL0Xe>*era_0( z0%tRR?F)9n#)<{da(489*Z94gBM+L(pY2TGI{Fgu$~NIGIvr(sE-GNq<(Kj^K{~@< z11a4JGJ=&LN(XetCS=slHMs3^D4DKG%{lHmL7-D(lCkzd4{g`FLcj(vKsUxJ<{h&k z`)K2OZA~V$@j%D)hJWn@KsHjhzrR1YkhJMvp7e)5{n2r(9lRMI{+fuOshz%S``ndJ zPG+blA4&N=RW2eK?1C*l=FG=a%aj+Ms zjA{I_-)aTnsJI?Jx61sy!x&vvc1(VJOQM=Q>E~MY3M=G+iTXQTz!IG{Nr-Kid|wr= zwi(*Rxdh?Rff&g~5DF$7BPtA}ZfS51ub7x>AlLQNULfT_0IV<^rKF-l-wC75ST|87 zG0L_IHuDRz%b!~2kP;lSD2&H&_GGg(MjmyX)!xbhV=K;Ov6W(BE~mo#R8BI>h`R^Oe)@#I|{<>cDp{pHDFOjr=1kdf_~u`qZapeEKmqhikCm zpmeBSFlwMCXD3wpQGEK^T@}?#MT%wSIyybj729xTW4Hzzc!3Y}>BzWE8tKv44G7xn z%ih|P0e^Ifo^kjSIn?2GKgmJwG|@wYPiYSe4L)SWqk@knhK{wxgK@`4*)Z)=*91X? zZjx|Rn~hJAPjUA~CwQP2e9-~EO=kF(WWkW@a7hpSx89Wt-<_C3>mavm%R9oA|K9!$ zN9~U5;oU?-_xzUbU>#muVElx`=c?ly_}150w&J7qc8zWt_h70C6dmBz zjf}DMC7bxz1WY_mqUt&R_9KSD=PpWaEHBZ6o0Y%%tG^nZ6~E#z9_fbd_#-)I4osnM zNrZYbl8v4loA91yNA<~D7PBh|n}8S!ajCB?!a1≫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 From 96fe1de8322a57ad82fcab3540c1eb44f74b9989 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Sat, 4 May 2013 02:08:52 +0200 Subject: [PATCH 35/41] Add ObjectRef.hud_set_hotbar_itemcount and add TOCLIENT_HUD_SET_PARAM --- doc/lua_api.txt | 2 ++ src/client.cpp | 17 +++++++++++++++++ src/clientserver.h | 8 ++++++++ src/game.cpp | 4 ++-- src/hud.cpp | 2 +- src/hud.h | 6 +++++- src/player.cpp | 2 ++ src/player.h | 1 + src/script/lua_api/l_object.cpp | 18 ++++++++++++++++++ src/script/lua_api/l_object.h | 3 +++ src/server.cpp | 28 ++++++++++++++++++++++++++++ src/server.h | 2 ++ 12 files changed, 89 insertions(+), 4 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 260a6276..5143c2ef 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1399,6 +1399,8 @@ Player-only: (no-op for other objects) ^ flags: (is visible) hotbar, healthbar, crosshair, wielditem ^ pass a table containing a true/false value of each flag to be set or unset ^ if a flag is nil, the flag is not modified +- hud_set_hotbar_itemcount(count): sets number of items in builtin hotbar + ^ count: number of items, must be between 1 and 23 InvRef: Reference to an inventory methods: diff --git a/src/client.cpp b/src/client.cpp index e4ca52dd..6b1789fe 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -2136,6 +2136,23 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) player->hud_flags &= ~mask; player->hud_flags |= flags; } + else if(command == TOCLIENT_HUD_SET_PARAM) + { + std::string datastring((char *)&data[2], datasize - 2); + std::istringstream is(datastring, std::ios_base::binary); + + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + + u16 param = readU16(is); + std::string value = deSerializeString(is); + + if(param == HUD_PARAM_HOTBAR_ITEMCOUNT && value.size() == 4){ + s32 hotbar_itemcount = readS32((u8*) value.c_str()); + if(hotbar_itemcount > 0 && hotbar_itemcount <= HUD_HOTBAR_ITEMCOUNT_MAX) + player->hud_hotbar_itemcount = hotbar_itemcount; + } + } else { infostream<<"Client: Ignoring unknown command " diff --git a/src/clientserver.h b/src/clientserver.h index ee9271bb..a43baab2 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -480,6 +480,14 @@ enum ToClientCommand u32 flags u32 mask */ + + TOCLIENT_HUD_SET_PARAM = 0x4d, + /* + u16 command + u16 param + u16 len + u8[len] value + */ }; enum ToServerCommand diff --git a/src/game.cpp b/src/game.cpp index 921cce32..4f0d1b66 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1987,7 +1987,7 @@ void the_game( { s32 wheel = input->getMouseWheel(); u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE-1, - hud.hotbar_itemcount-1); + player->hud_hotbar_itemcount-1); if(wheel < 0) { @@ -2011,7 +2011,7 @@ void the_game( const KeyPress *kp = NumberKey + (i + 1) % 10; if(input->wasKeyDown(*kp)) { - if(i < PLAYER_INVENTORY_SIZE && i < hud.hotbar_itemcount) + if(i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount) { new_playeritem = i; diff --git a/src/hud.cpp b/src/hud.cpp index 65c78ca5..a3ae38bc 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -43,7 +43,6 @@ Hud::Hud(video::IVideoDriver *driver, gui::IGUIEnvironment* guienv, screensize = v2u32(0, 0); displaycenter = v2s32(0, 0); hotbar_imagesize = 48; - hotbar_itemcount = 8; tsrc = gamedef->getTextureSource(); @@ -286,6 +285,7 @@ void Hud::drawHotbar(v2s32 centerlowerpos, s32 halfheartcount, u16 playeritem) { return; } + s32 hotbar_itemcount = player->hud_hotbar_itemcount; s32 padding = hotbar_imagesize / 12; s32 width = hotbar_itemcount * (hotbar_imagesize + padding * 2); v2s32 pos = centerlowerpos - v2s32(width / 2, hotbar_imagesize + padding * 2); diff --git a/src/hud.h b/src/hud.h index 54b7e7f3..fa9d33f8 100644 --- a/src/hud.h +++ b/src/hud.h @@ -36,6 +36,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #define HUD_FLAG_CROSSHAIR_VISIBLE (1 << 2) #define HUD_FLAG_WIELDITEM_VISIBLE (1 << 3) +#define HUD_PARAM_HOTBAR_ITEMCOUNT 1 + +#define HUD_HOTBAR_ITEMCOUNT_DEFAULT 8 +#define HUD_HOTBAR_ITEMCOUNT_MAX 23 + class Player; enum HudElementType { @@ -102,7 +107,6 @@ public: v2u32 screensize; v2s32 displaycenter; s32 hotbar_imagesize; - s32 hotbar_itemcount; video::SColor crosshair_argb; video::SColor selectionbox_argb; diff --git a/src/player.cpp b/src/player.cpp index 34f0fda0..4eb5955c 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -81,6 +81,8 @@ Player::Player(IGameDef *gamedef): hud_flags = HUD_FLAG_HOTBAR_VISIBLE | HUD_FLAG_HEALTHBAR_VISIBLE | HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE; + + hud_hotbar_itemcount = HUD_HOTBAR_ITEMCOUNT_DEFAULT; } Player::~Player() diff --git a/src/player.h b/src/player.h index 63e166b4..d3738fd5 100644 --- a/src/player.h +++ b/src/player.h @@ -250,6 +250,7 @@ public: std::vector hud; u32 hud_flags; + s32 hud_hotbar_itemcount; protected: IGameDef *m_gamedef; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 6dec3db8..1e45610a 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -978,6 +978,23 @@ int ObjectRef::l_hud_set_flags(lua_State *L) return 1; } +// hud_set_hotbar_itemcount(self, hotbar_itemcount) +int ObjectRef::l_hud_set_hotbar_itemcount(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if (player == NULL) + return 0; + + s32 hotbar_itemcount = lua_tonumber(L, 2); + + if (!STACK_TO_SERVER(L)->hudSetHotbarItemcount(player, hotbar_itemcount)) + return 0; + + lua_pushboolean(L, true); + return 1; +} + ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) { @@ -1089,6 +1106,7 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, hud_change), luamethod(ObjectRef, hud_get), luamethod(ObjectRef, hud_set_flags), + luamethod(ObjectRef, hud_set_hotbar_itemcount), {0,0} }; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 88f980a2..57dac0e6 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -209,6 +209,9 @@ private: // hud_set_flags(self, flags) static int l_hud_set_flags(lua_State *L); + // hud_set_hotbar_itemcount(self, hotbar_itemcount) + static int l_hud_set_hotbar_itemcount(lua_State *L); + public: ObjectRef(ServerActiveObject *object); diff --git a/src/server.cpp b/src/server.cpp index 12695e1b..4268bb80 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3687,6 +3687,22 @@ void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) m_con.Send(peer_id, 0, data, true); } +void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_HUD_SET_PARAM); + writeU16(os, param); + os< data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + void Server::BroadcastChatMessage(const std::wstring &message) { for(std::map::iterator @@ -4684,6 +4700,18 @@ bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) { return true; } +bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) { + if (!player) + return false; + if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX) + return false; + + std::ostringstream os(std::ios::binary); + writeS32(os, hotbar_itemcount); + SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str()); + return true; +} + void Server::notifyPlayers(const std::wstring msg) { BroadcastChatMessage(msg); diff --git a/src/server.h b/src/server.h index 3906f43b..edc5c489 100644 --- a/src/server.h +++ b/src/server.h @@ -541,6 +541,7 @@ public: bool hudRemove(Player *player, u32 id); bool hudChange(Player *player, u32 id, HudElementStat stat, void *value); bool hudSetFlags(Player *player, u32 flags, u32 mask); + bool hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount); private: @@ -585,6 +586,7 @@ private: void SendHUDRemove(u16 peer_id, u32 id); void SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value); void SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask); + void SendHUDSetParam(u16 peer_id, u16 param, const std::string &value); /* Send a node removal/addition event to all clients except ignore_id. From f62ddf9022588f6c93fe6cdf2244640dd96dc186 Mon Sep 17 00:00:00 2001 From: MetaDucky Date: Thu, 23 May 2013 19:27:00 +0200 Subject: [PATCH 36/41] Fixed wrongly named invref:get_location() table members for nodes. Makes the result compatible with the minetest.get_inventory(location) param. --- src/script/lua_api/l_inventory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 884b33e6..6dfb7a28 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -306,10 +306,10 @@ int InvRef::l_get_location(lua_State *L) return 1; case InventoryLocation::NODEMETA: lua_newtable(L); - lua_pushstring(L, "nodemeta"); + lua_pushstring(L, "node"); lua_setfield(L, -2, "type"); push_v3s16(L, loc.p); - lua_setfield(L, -2, "name"); + lua_setfield(L, -2, "pos"); return 1; case InventoryLocation::DETACHED: lua_newtable(L); From efc75db76458dc473043043ca72c18701436dec8 Mon Sep 17 00:00:00 2001 From: sapier Date: Sun, 7 Apr 2013 16:34:35 +0200 Subject: [PATCH 37/41] Bugfix: Update formname on showing new formspec while already one shown --- src/game.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 4f0d1b66..ffc69d73 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -134,6 +134,10 @@ struct TextDestPlayerInventory : public TextDest m_client->sendInventoryFields(m_formname, fields); } + void setFormName(std::string formname) { + m_formname = formname; + } + Client *m_client; std::string m_formname; }; @@ -920,6 +924,7 @@ void the_game( ) { FormspecFormSource* current_formspec = 0; + TextDestPlayerInventory* current_textdest = 0; video::IVideoDriver* driver = device->getVideoDriver(); scene::ISceneManager* smgr = device->getSceneManager(); @@ -2236,19 +2241,23 @@ void the_game( if (current_formspec == 0) { /* Create menu */ + /* Note: FormspecFormSource and TextDestPlayerInventory + * are deleted by guiFormSpecMenu */ current_formspec = new FormspecFormSource(*(event.show_formspec.formspec),¤t_formspec); - + current_textdest = new TextDestPlayerInventory(&client,*(event.show_formspec.formname)); GUIFormSpecMenu *menu = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr, &client, gamedef); menu->setFormSource(current_formspec); - menu->setTextDest(new TextDestPlayerInventory(&client,*(event.show_formspec.formname))); + menu->setTextDest(current_textdest); menu->drop(); } else { + assert(current_textdest != 0); /* update menu */ + current_textdest->setFormName(*(event.show_formspec.formname)); current_formspec->setForm(*(event.show_formspec.formspec)); } delete(event.show_formspec.formspec); From 6b2c46c4316cb5d2d153c13565c894a1ebec085c Mon Sep 17 00:00:00 2001 From: proller Date: Fri, 31 May 2013 22:57:59 +0400 Subject: [PATCH 38/41] Liquid adjusting: continue to drop --- src/content_abm.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++--- src/map.cpp | 2 +- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/content_abm.cpp b/src/content_abm.cpp index f19caf74..3704fe83 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -191,17 +191,58 @@ public: virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) { ServerMap *map = &env->getServerMap(); - if (map->transforming_liquid_size() < 500) - map->transforming_liquid_add(p); + if (map->transforming_liquid_size() > 500) + return; + map->transforming_liquid_add(p); //if ((*map).m_transforming_liquid.size() < 500) (*map).m_transforming_liquid.push_back(p); } }; +class LiquidDropABM : public ActiveBlockModifier +{ +private: + std::set contents; + +public: + LiquidDropABM(ServerEnvironment *env, INodeDefManager *nodemgr) + { + std::set liquids; + nodemgr->getIds("group:liquid", liquids); + for(std::set::const_iterator k = liquids.begin(); k != liquids.end(); k++) + contents.insert(nodemgr->get(*k).liquid_alternative_source); + } + virtual std::set getTriggerContents() + { return contents; } + virtual std::set getRequiredNeighbors() + { + std::set neighbors; + neighbors.insert("mapgen_air"); + return neighbors; + } + virtual float getTriggerInterval() + { return 20.0; } + virtual u32 getTriggerChance() + { return 10; } + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) + { + ServerMap *map = &env->getServerMap(); + if (map->transforming_liquid_size() > 500) + return; + //todo: look around except top + MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0)); + if (n_below.getContent() != CONTENT_AIR) + return; + map->transforming_liquid_add(p); + } +}; + void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { env->addActiveBlockModifier(new GrowGrassABM()); env->addActiveBlockModifier(new RemoveGrassABM()); env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef)); - if (g_settings->getBool("liquid_finite")) + if (g_settings->getBool("liquid_finite")) { env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef)); + env->addActiveBlockModifier(new LiquidDropABM(env, nodedef)); + } } diff --git a/src/map.cpp b/src/map.cpp index d5ab8eb1..43502253 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1755,7 +1755,7 @@ void Map::transformLiquidsFinite(std::map & modified_blocks) } //relax up - if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && + if (relax && ((p0.Y == water_level) || (fast_flood && p0.Y <= water_level)) && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level- (can_liquid_same_level - relax) && From 7721948a2fbd101bc8530e4d3ae7c1552bdf9490 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Sat, 1 Jun 2013 01:49:59 +0200 Subject: [PATCH 39/41] Generate debug HUD text with ostringstream instead of snprintf. This should fix #730. --- src/game.cpp | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index ffc69d73..22bd8c42 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -66,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "sound_openal.h" #endif #include "event_manager.h" +#include #include #include "util/directiontables.h" @@ -2961,21 +2962,20 @@ void the_game( static float endscenetime_avg = 0; endscenetime_avg = endscenetime_avg * 0.95 + (float)endscenetime*0.05;*/ - char temptext[300]; - snprintf(temptext, 300, "%s (" - "R: range_all=%i" - ")" - " drawtime=%.0f, dtime_jitter = % .1f %%" - ", v_range = %.1f, RTT = %.3f", - program_name_and_version, - draw_control.range_all, - drawtime_avg, - dtime_jitter1_max_fraction * 100.0, - draw_control.wanted_range, - client.getRTT() - ); - - guitext->setText(narrow_to_wide(temptext).c_str()); + std::ostringstream os(std::ios_base::binary); + os<setText(narrow_to_wide(temptext).c_str()); + std::ostringstream os(std::ios_base::binary); + os<setText(narrow_to_wide(os.str()).c_str()); guitext2->setVisible(true); } else From a61aa6690b5a5bf804c6de22eed95a68ba480e7d Mon Sep 17 00:00:00 2001 From: MetaDucky Date: Mon, 27 May 2013 19:33:33 +0200 Subject: [PATCH 40/41] Fix editbox default text being pre-selected in some cases --- src/guiCreateWorld.cpp | 3 +++ src/guiFormSpecMenu.cpp | 5 ++++- src/guiTextInputMenu.cpp | 3 +++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/guiCreateWorld.cpp b/src/guiCreateWorld.cpp index 9fc76421..f9afe959 100644 --- a/src/guiCreateWorld.cpp +++ b/src/guiCreateWorld.cpp @@ -135,6 +135,9 @@ void GUICreateWorld::regenerateGui(v2u32 screensize) evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.Key = KEY_END; evt.KeyInput.PressedDown = true; + evt.KeyInput.Char = 0; + evt.KeyInput.Control = 0; + evt.KeyInput.Shift = 0; e->OnEvent(evt); } { diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 6ada7769..3e0d7fd4 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -418,9 +418,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT); } else { irr::SEvent evt; - evt.KeyInput.Key = KEY_END; evt.EventType = EET_KEY_INPUT_EVENT; + evt.KeyInput.Key = KEY_END; evt.KeyInput.PressedDown = true; + evt.KeyInput.Char = 0; + evt.KeyInput.Control = 0; + evt.KeyInput.Shift = 0; e->OnEvent(evt); } diff --git a/src/guiTextInputMenu.cpp b/src/guiTextInputMenu.cpp index c15eeedc..d5229f41 100644 --- a/src/guiTextInputMenu.cpp +++ b/src/guiTextInputMenu.cpp @@ -121,6 +121,9 @@ void GUITextInputMenu::regenerateGui(v2u32 screensize) evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.Key = KEY_END; evt.KeyInput.PressedDown = true; + evt.KeyInput.Char = 0; + evt.KeyInput.Control = 0; + evt.KeyInput.Shift = 0; e->OnEvent(evt); } changeCtype(""); From 64627817fcca52f20948c24b60ce192b218f6ce2 Mon Sep 17 00:00:00 2001 From: MetaDucky Date: Sun, 26 May 2013 11:27:06 +0200 Subject: [PATCH 41/41] Fix minetest.get_inventory(loc) always returning nil for "node" type loc --- src/script/lua_api/l_inventory.cpp | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 6dfb7a28..1404c3c8 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -416,9 +416,18 @@ int ModApiInventory::l_get_inventory(lua_State *L) std::string type = checkstringfield(L, 1, "type"); - if(type != "pos"){ - NO_MAP_LOCK_REQUIRED; + if(type == "node"){ + lua_getfield(L, 1, "pos"); + v3s16 pos = check_v3s16(L, -1); + loc.setNodeMeta(pos); + if(getServer(L)->getInventory(loc) != NULL) + InvRef::create(L, loc); + else + lua_pushnil(L); + return 1; + } else { + NO_MAP_LOCK_REQUIRED; if(type == "player"){ std::string name = checkstringfield(L, 1, "name"); loc.setPlayer(name); @@ -431,22 +440,9 @@ int ModApiInventory::l_get_inventory(lua_State *L) InvRef::create(L, loc); else lua_pushnil(L); - - return 1; + return 1; + // END NO_MAP_LOCK_REQUIRED; } - else { - if(type == "node"){ - lua_getfield(L, 1, "pos"); - v3s16 pos = check_v3s16(L, -1); - loc.setNodeMeta(pos); - } - if(getServer(L)->getInventory(loc) != NULL) - InvRef::create(L, loc); - else - lua_pushnil(L); - return 1; - } - } // create_detached_inventory_raw(name)