diff options
author | Weblate <42@minetest.ru> | 2013-03-30 19:49:52 +0100 |
---|---|---|
committer | Weblate <42@minetest.ru> | 2013-03-30 19:49:52 +0100 |
commit | a0566270d9fa075afa36a7e3e68c690b1b23ba90 (patch) | |
tree | 6bc88127ee67b7bf4d6a2d6f9d0a9e5f997d1dd1 | |
parent | 0d83bdc3aa9f1077836aacb833ac4ad9bbb9a4f4 (diff) | |
parent | 1aa50b13622c1e264044839ecdf0152670dae6ce (diff) | |
download | minetest-a0566270d9fa075afa36a7e3e68c690b1b23ba90.tar.gz minetest-a0566270d9fa075afa36a7e3e68c690b1b23ba90.tar.bz2 minetest-a0566270d9fa075afa36a7e3e68c690b1b23ba90.zip |
Merge remote branch 'origin/master'
162 files changed, 15518 insertions, 12403 deletions
diff --git a/.gitignore b/.gitignore index 353032637..21e2371a8 100644 --- a/.gitignore +++ b/.gitignore @@ -4,13 +4,16 @@ *bak* tags *.vim +*.orig +*.rej ## Non-static Minetest directories /bin/ /games/* !/games/minimal/ /cache/ -/textures/ +/textures/* +!/textures/base/ /sounds/ /mods/* !/mods/minetest/ @@ -41,7 +44,9 @@ src/cguittfont/libcguittfont.a src/cguittfont/cmake_install.cmake src/cguittfont/Makefile src/json/CMakeFiles/ -src/json/libjson.a +src/json/libjsoncpp.a +src/sqlite/CMakeFiles/* +src/sqlite/libsqlite3.a CMakeCache.txt CPackConfig.cmake CPackSourceConfig.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index fbf46d059..437d31cf5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Also remember to set PROTOCOL_VERSION in clientserver.h when releasing set(VERSION_MAJOR 0) set(VERSION_MINOR 4) -set(VERSION_PATCH 4-d1) +set(VERSION_PATCH 5) if(VERSION_EXTRA) set(VERSION_PATCH ${VERSION_PATCH}-${VERSION_EXTRA}) endif() @@ -133,6 +133,11 @@ 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 20917d772..74940a147 100644 --- a/README.txt +++ b/README.txt @@ -9,9 +9,10 @@ and contributors (see source file comments and the version control log) In case you downloaded the source code: --------------------------------------- If you downloaded the Minetest Engine source code in which this file is -contained, you probably want to download the minetest_game project too: +contained, you probably want to download these projects too: + https://github.com/minetest/common/ https://github.com/minetest/minetest_game/ -See the README.txt in it. +See the README.txt in them. Further documentation ---------------------- @@ -80,17 +81,24 @@ Compiling on GNU/Linux: ----------------------- Install dependencies. Here's an example for Debian/Ubuntu: -$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev +$ apt-get install build-essential libirrlicht-dev cmake libbz2-dev libpng12-dev libjpeg8-dev libxxf86vm-dev libgl1-mesa-dev libsqlite3-dev libogg-dev libvorbis-dev libopenal-dev libcurl4-gnutls-dev libfreetype6-dev Download source, extract (this is the URL to the latest of source repository, which might not work at all times): $ wget https://github.com/minetest/minetest/tarball/master -O master.tar.gz $ tar xf master.tar.gz $ cd minetest-minetest-286edd4 (or similar) +Download 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 master.tar.gz -$ tar xf master.tar.gz +$ wget https://github.com/minetest/minetest_game/tarball/master -O minetest_game.tar.gz +$ tar xf minetest_game.tar.gz $ mv minetest-minetest_game-* minetest_game $ cd .. @@ -107,11 +115,12 @@ $ ./minetest - You can build a bare server or a bare client by specifying -DBUILD_CLIENT=0 or -DBUILD_SERVER=0 - You can select between Release and Debug build by -DCMAKE_BUILD_TYPE=<Debug or Release> - Debug build is slower, but gives much more useful output in a debugger +- If you build a bare server, you don't need to have Irrlicht installed. In that case use -DIRRLICHT_SOURCE_DIR=/the/irrlicht/source Compiling on Windows: --------------------- - This section is outdated. In addition to what is described here: - - In addition to minetest, you need to download minetest_game. + - In addition to minetest, you need to download common and minetest_game. - If you wish to have sound support, you need libogg, libvorbis and libopenal - You need: diff --git a/builtin/falling.lua b/builtin/falling.lua index d3af36f29..1c09f9856 100644 --- a/builtin/falling.lua +++ b/builtin/falling.lua @@ -57,6 +57,10 @@ minetest.register_entity("__builtin:falling_node", { -- 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) + 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) @@ -80,6 +84,7 @@ minetest.register_entity("__builtin:falling_node", { -- Create node and remove entity minetest.env:add_node(np, {name=self.nodename}) self.object:remove() + nodeupdate(np) else -- Do nothing end @@ -144,7 +149,8 @@ function nodeupdate_single(p) n_bottom = minetest.env: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 then + (not minetest.registered_nodes[n_bottom.name].walkable or + minetest.registered_nodes[n_bottom.name].buildable_to) then minetest.env:remove_node(p) spawn_falling_node(p, n.name) nodeupdate(p) diff --git a/builtin/item.lua b/builtin/item.lua index 1349fdf63..8e2f75a1a 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -129,11 +129,18 @@ function minetest.item_place_node(itemstack, placer, pointed_thing) end local under = pointed_thing.under - local oldnode_under = minetest.env:get_node(under) + local oldnode_under = minetest.env:get_node_or_nil(under) + local above = pointed_thing.above + local oldnode_above = minetest.env:get_node_or_nil(above) + + if not oldnode_under or not oldnode_above then + minetest.log("info", placer:get_player_name() .. " tried to place" + .. " node in unloaded position " .. minetest.pos_to_string(above)) + return itemstack + end + local olddef_under = ItemStack({name=oldnode_under.name}):get_definition() olddef_under = olddef_under or minetest.nodedef_default - local above = pointed_thing.above - local oldnode_above = minetest.env:get_node(above) local olddef_above = ItemStack({name=oldnode_above.name}):get_definition() olddef_above = olddef_above or minetest.nodedef_default diff --git a/builtin/item_entity.lua b/builtin/item_entity.lua index 1699cb03c..50ce7eafe 100644 --- a/builtin/item_entity.lua +++ b/builtin/item_entity.lua @@ -111,6 +111,7 @@ minetest.register_entity("__builtin:item", { if self.itemstring ~= '' then local left = hitter:get_inventory():add_item("main", self.itemstring) if not left:is_empty() then + self.itemstring = left:to_string() return end end diff --git a/builtin/misc.lua b/builtin/misc.lua index e018aff85..8308b3d6b 100644 --- a/builtin/misc.lua +++ b/builtin/misc.lua @@ -14,14 +14,14 @@ minetest.register_globalstep(function(dtime) for index, timer in ipairs(minetest.timers) do timer.time = timer.time - dtime if timer.time <= 0 then - timer.func(timer.param) + timer.func(unpack(timer.args or {})) table.remove(minetest.timers,index) end end end) -function minetest.after(time, func, param) - table.insert(minetest.timers_to_add, {time=time, func=func, param=param}) +function minetest.after(time, func, ...) + table.insert(minetest.timers_to_add, {time=time, func=func, args={...}}) end function minetest.check_player_privs(name, privs) @@ -99,3 +99,10 @@ function minetest.setting_get_pos(name) return minetest.string_to_pos(value) end +function minetest.formspec_escape(str) + str = string.gsub(str, "\\", "\\\\") + str = string.gsub(str, "%[", "\\[") + str = string.gsub(str, "%]", "\\]") + return str +end + diff --git a/client/shaders/test_shader_2/opengl_vertex.glsl b/client/shaders/test_shader_2/opengl_vertex.glsl index 80fd6d427..2881bad21 100644 --- a/client/shaders/test_shader_2/opengl_vertex.glsl +++ b/client/shaders/test_shader_2/opengl_vertex.glsl @@ -8,9 +8,7 @@ varying vec3 vPosition; void main(void)
{
- vec4 pos = gl_Vertex;
- pos.y -= 2.0;
- gl_Position = mWorldViewProj * pos;
+ gl_Position = mWorldViewProj * gl_Vertex;
vPosition = (mWorldViewProj * gl_Vertex).xyz;
diff --git a/cmake/Modules/FindJson.cmake b/cmake/Modules/FindJson.cmake index bc4e71a29..a9178a225 100644 --- a/cmake/Modules/FindJson.cmake +++ b/cmake/Modules/FindJson.cmake @@ -2,17 +2,17 @@ #FIND_PATH(JSON_INCLUDE_DIR json.h) -#FIND_LIBRARY(JSON_LIBRARY NAMES json) +#FIND_LIBRARY(JSON_LIBRARY NAMES jsoncpp) #IF(JSON_LIBRARY AND JSON_INCLUDE_DIR) # SET( JSON_FOUND TRUE ) #ENDIF(JSON_LIBRARY AND JSON_INCLUDE_DIR) #IF(JSON_FOUND) -# MESSAGE(STATUS "Found system json header file in ${JSON_INCLUDE_DIR}") -# MESSAGE(STATUS "Found system json library ${JSON_LIBRARY}") +# MESSAGE(STATUS "Found system jsoncpp header file in ${JSON_INCLUDE_DIR}") +# MESSAGE(STATUS "Found system jsoncpp library ${JSON_LIBRARY}") #ELSE(JSON_FOUND) SET(JSON_INCLUDE_DIR ${PROJECT_SOURCE_DIR}/json) - SET(JSON_LIBRARY json) - MESSAGE(STATUS "Using project json library") + SET(JSON_LIBRARY jsoncpp) + MESSAGE(STATUS "Using project jsoncpp library") #ENDIF(JSON_FOUND) diff --git a/cmake/Modules/FindOpenGLES2.cmake b/cmake/Modules/FindOpenGLES2.cmake new file mode 100644 index 000000000..42d31c898 --- /dev/null +++ b/cmake/Modules/FindOpenGLES2.cmake @@ -0,0 +1,130 @@ +#------------------------------------------------------------------- +# This file is stolen from part of the CMake build system for OGRE (Object-oriented Graphics Rendering Engine) http://www.ogre3d.org/ +# +# The contents of this file are placed in the public domain. Feel +# free to make use of it in any way you like. +#------------------------------------------------------------------- + +# - Try to find OpenGLES and EGL +# Once done this will define +# +# OPENGLES2_FOUND - system has OpenGLES +# OPENGLES2_INCLUDE_DIR - the GL include directory +# OPENGLES2_LIBRARIES - Link these to use OpenGLES +# +# EGL_FOUND - system has EGL +# EGL_INCLUDE_DIR - the EGL include directory +# EGL_LIBRARIES - Link these to use EGL + +# win32, apple, android NOT TESED +# linux tested and works + +IF (WIN32) + IF (CYGWIN) + + FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h ) + + FIND_LIBRARY(OPENGLES2_gl_LIBRARY libGLESv2 ) + + ELSE (CYGWIN) + + IF(BORLAND) + SET (OPENGLES2_gl_LIBRARY import32 CACHE STRING "OpenGL ES 2.x library for win32") + ELSE(BORLAND) + # todo + # SET (OPENGLES_gl_LIBRARY ${SOURCE_DIR}/Dependencies/lib/release/libGLESv2.lib CACHE STRING "OpenGL ES 2.x library for win32" + ENDIF(BORLAND) + + ENDIF (CYGWIN) + +ELSE (WIN32) + + IF (APPLE) + + create_search_paths(/Developer/Platforms) + findpkg_framework(OpenGLES2) + set(OPENGLES2_gl_LIBRARY "-framework OpenGLES") + + ELSE(APPLE) + + FIND_PATH(OPENGLES2_INCLUDE_DIR GLES2/gl2.h + /usr/openwin/share/include + /opt/graphics/OpenGL/include /usr/X11R6/include + /usr/include + ) + + FIND_LIBRARY(OPENGLES2_gl_LIBRARY + NAMES GLESv2 + PATHS /opt/graphics/OpenGL/lib + /usr/openwin/lib + /usr/shlib /usr/X11R6/lib + /usr/lib + ) + + IF (NOT BUILD_ANDROID) + FIND_PATH(EGL_INCLUDE_DIR EGL/egl.h + /usr/openwin/share/include + /opt/graphics/OpenGL/include /usr/X11R6/include + /usr/include + ) + + FIND_LIBRARY(EGL_egl_LIBRARY + NAMES EGL + PATHS /opt/graphics/OpenGL/lib + /usr/openwin/lib + /usr/shlib /usr/X11R6/lib + /usr/lib + ) + + # On Unix OpenGL most certainly always requires X11. + # Feel free to tighten up these conditions if you don't + # think this is always true. + # It's not true on OSX. + + IF (OPENGLES2_gl_LIBRARY) + IF(NOT X11_FOUND) + INCLUDE(FindX11) + ENDIF(NOT X11_FOUND) + IF (X11_FOUND) + IF (NOT APPLE) + SET (OPENGLES2_LIBRARIES ${X11_LIBRARIES}) + ENDIF (NOT APPLE) + ENDIF (X11_FOUND) + ENDIF (OPENGLES2_gl_LIBRARY) + ENDIF () + + ENDIF(APPLE) +ENDIF (WIN32) + +#SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) + +IF (BUILD_ANDROID) + IF(OPENGLES2_gl_LIBRARY) + SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) + SET( EGL_LIBRARIES) + SET( OPENGLES2_FOUND "YES" ) + ENDIF(OPENGLES2_gl_LIBRARY) +ELSE () + + SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) + + IF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY) + SET( OPENGLES2_LIBRARIES ${OPENGLES2_gl_LIBRARY} ${OPENGLES2_LIBRARIES}) + SET( EGL_LIBRARIES ${EGL_egl_LIBRARY} ${EGL_LIBRARIES}) + SET( OPENGLES2_FOUND "YES" ) + ENDIF(OPENGLES2_gl_LIBRARY AND EGL_egl_LIBRARY) + +ENDIF () + +MARK_AS_ADVANCED( + OPENGLES2_INCLUDE_DIR + OPENGLES2_gl_LIBRARY + EGL_INCLUDE_DIR + EGL_egl_LIBRARY +) + +IF(OPENGLES2_FOUND) + MESSAGE(STATUS "Found system opengles2 library ${OPENGLES2_LIBRARIES}") +ELSE () + SET(OPENGLES2_LIBRARIES "") +ENDIF () diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 005d7c010..beb70db15 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Modding API Reference 0.4.4 +Minetest Lua Modding API Reference 0.4.5 ========================================== More information at http://c55.me/minetest/ @@ -27,6 +27,39 @@ Startup Mods are loaded during server startup from the mod load paths by running the init.lua scripts in a shared environment. +Paths +----- +RUN_IN_PLACE=1: (Windows release, local build) + $path_user: Linux: <build directory> + Windows: <build directory> + $path_share: Linux: <build directory> + Windows: <build directory> + +RUN_IN_PLACE=0: (Linux release) + $path_share: Linux: /usr/share/minetest + Windows: <install directory>/minetest-0.4.x + $path_user: Linux: ~/.minetest + Windows: C:/users/<user>/AppData/minetest (maybe) + +Games +----- +Games are looked up from: + $path_share/games/gameid/ + $path_user/games/gameid/ +where gameid is unique to each game. + +The game directory contains the file game.conf, which contains these fields: + name = <Human-readable full name of the game> + common_mods = <Comma-separated list of 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. + Mod load path ------------- Generic: @@ -170,18 +203,18 @@ from the available ones of the following files: Examples of sound parameter tables: -- Play locationless on all clients { - gain = 1.0, -- default + gain = 1.0, -- default } -- Play locationless to a player { - to_player = name, - gain = 1.0, -- default + to_player = name, + gain = 1.0, -- default } -- Play in a location { - pos = {x=1,y=2,z=3}, - gain = 1.0, -- default - max_hear_distance = 32, -- default + pos = {x=1,y=2,z=3}, + gain = 1.0, -- default + max_hear_distance = 32, -- default } -- Play connected to an object, looped { @@ -233,11 +266,11 @@ local drawtype = get_nodedef_field(nodename, "drawtype") Example: minetest.get_item_group(name, group) has been implemented as: function minetest.get_item_group(name, group) - if not minetest.registered_items[name] or not - minetest.registered_items[name].groups[group] then - return 0 - end - return minetest.registered_items[name].groups[group] + if not minetest.registered_items[name] or not + minetest.registered_items[name].groups[group] then + return 0 + end + return minetest.registered_items[name].groups[group] end Nodes @@ -277,6 +310,10 @@ param2 is reserved for the engine when any of these are used: paramtype2 == "facedir" ^ The rotation of the node is stored in param2. Furnaces and chests are rotated this way. Can be made by using minetest.dir_to_facedir(). + Values range 0 - 23 + facedir modulo 4 = axisdir + 0 = y+ 1 = z+ 2 = z- 3 = x+ 4 = x- 5 = y- + facedir's two less significant bits are rotation around the axis Nodes can also contain extra data. See "Node Metadata". @@ -335,6 +372,28 @@ A box is defined as: A box of a regular node would look like: {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, +Ore types +--------------- +These tell in what manner the ore is generated. +All default ores are of the uniformly-distributed scatter type. + +- scatter + Randomly chooses a location and generates a cluster of ore. + If noise_params is specified, the ore will be placed if the 3d perlin noise at + that point is greater than the noise_threshhold, giving the ability to create a non-equal + distribution of ore. +- sheet + Creates a sheet of ore in a blob shape according to the 2d perlin noise described by noise_params. + The relative height of the sheet can be controlled by the same perlin noise as well, by specifying + a non-zero 'scale' parameter in noise_params. IMPORTANT: The noise is not transformed by offset or + scale when comparing against the noise threshhold, but scale is used to determine relative height. + The height of the blob is randomly scattered, with a maximum height of clust_size. + clust_scarcity and clust_num_ores are ignored. + This is essentially an improved version of the so-called "stratus" ore seen in some unofficial mods. +- claylike - NOT YET IMPLEMENTED + Places ore if there are no more than clust_scarcity number of specified nodes within a Von Neumann + neighborhood of clust_size radius. + Representations of simple things -------------------------------- Position/vector: @@ -412,9 +471,11 @@ a node is destroyable and how long it takes to destroy by a tool. Groups of entities ------------------- For entities, groups are, as of now, used only for calculating damage. +The rating is the percentage of damage caused by tools with this damage group. +See "Entity damage mechanism". -object.get_armor_groups() -> a group-rating table (eg. {fleshy=3}) -object.set_armor_groups({level=2, fleshy=2, cracky=2}) +object.get_armor_groups() -> a group-rating table (eg. {fleshy=100}) +object.set_armor_groups({fleshy=30, cracky=80}) Groups of tools ---------------- @@ -435,7 +496,7 @@ An example: Make meat soup from any meat, any water and any bowl } An another example: Make red wool from white wool and red dye { - type = 'shapeless', + type = 'shapeless', output = 'wool:red', recipe = {'wool:white', 'group:dye,basecolor_red'}, } @@ -446,7 +507,7 @@ Special groups - level: Can be used to give an additional sense of progression in the game. - A larger level will cause eg. a weapon of a lower level make much less damage, and get weared out much faster, or not be able to get drops - from destroyed nodes. + from destroyed nodes. - 0 is something that is directly accessible at the start of gameplay - There is no upper limit - dig_immediate: (player can always pick up node without tool wear) @@ -463,7 +524,6 @@ Special groups Known damage and digging time defining groups ---------------------------------------------- -Valid ratings for these are 0, 1, 2 and 3, unless otherwise stated. - crumbly: dirt, sand - cracky: tough but crackable stuff like stone. - snappy: something that can be cut using fine tools; eg. leaves, small @@ -516,6 +576,7 @@ groups to enable interaction with tools. * Uses (until the tool breaks) * Maximum level (usually 0, 1, 2 or 3) * Digging times + * Damage groups **Full punch interval**: When used as a weapon, the tool will do full damage if this time is spent @@ -547,17 +608,19 @@ maximum level. result in the tool to be able to dig nodes that have a rating of 2 or 3 for this group, and unable to dig the rating 1, which is the toughest. Unless there is a matching group that enables digging otherwise. - * For entities, damage equals the amount of nodes dug in the time spent - between hits, with a maximum time of ''full_punch_interval''. + +**Damage groups** +List of damage for groups of entities. See "Entity damage mechanism". Example definition of the capabilities of a tool ------------------------------------------------- tool_capabilities = { - full_punch_interval=1.5, - max_drop_level=1, - groupcaps={ - crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} - } + full_punch_interval=1.5, + max_drop_level=1, + groupcaps={ + crumbly={maxlevel=2, uses=20, times={[1]=1.60, [2]=1.20, [3]=0.80}} + } + damage_groups = {fleshy=2}, } This makes the tool be able to dig nodes that fullfill both of these: @@ -588,10 +651,12 @@ Notes: Entity damage mechanism ------------------------ Damage calculation: -- Take the time spent after the last hit -- Limit time to full_punch_interval -- Take the damage groups and imagine a bunch of nodes that have them -- Damage in HP is the amount of nodes destroyed in this time. +damage = 0 +foreach group in cap.damage_groups: + damage += cap.damage_groups[group] * limit(actual_interval / cap.full_punch_interval, 0.0, 1.0) + * (object.armor_groups[group] / 100.0) + -- Where object.armor_groups[group] is 0 for inexisting values +return damage Client predicts damage based on damage groups. Because of this, it is able to give an immediate response when an entity is damaged or dies; the response is @@ -717,7 +782,7 @@ field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>] ^ default is the default value of the field ^ default may contain variable references such as '${text}' which will fill the value from the metadata value 'text' - ^ Note: no extra text or more than a single variable is supported ATM. + ^ Note: no extra text or more than a single variable is supported ATM. field[<name>;<label>;<default>] ^ as above but without position/size units @@ -778,6 +843,9 @@ string:trim() minetest.pos_to_string({x=X,y=Y,z=Z}) -> "(X,Y,Z)" ^ Convert position to a printable string minetest.string_to_pos(string) -> position +^ Same but in reverse +minetest.formspec_escape(string) -> string +^ escapes characters like [, ], and \ that can not be used in formspecs minetest namespace reference ----------------------------- @@ -804,6 +872,7 @@ minetest.register_tool(name, item definition) minetest.register_craftitem(name, item definition) minetest.register_alias(name, convert_to) minetest.register_craft(recipe) +minetest.register_ore(ore definition) Global callback registration functions: (Call these only at load time) minetest.register_globalstep(func(dtime)) @@ -919,12 +988,17 @@ minetest.get_craft_result(input) -> output, decremented_input ^ output.time = number, if unsuccessful: 0 ^ decremented_input = like input minetest.get_craft_recipe(output) -> input +^ returns last registered recipe for output item (node) ^ output is a node or item type such as 'default:torch' ^ input.method = 'normal' or 'cooking' or 'fuel' ^ input.width = for example 3 ^ input.items = for example { stack 1, stack 2, stack 3, stack 4, stack 5, stack 6, stack 7, stack 8, stack 9 } ^ input.items = nil if no recipe found +minetest.get_all_craft_recipes(output) -> table or nil +^ returns table with all registered recipes for output item (node) +^ returns nil if no recipe was found +^ table entries have same format as minetest.get_craft_recipe minetest.handle_node_drops(pos, drops, digger) ^ drops: list of itemstrings ^ Handles drops from nodes after digging: Default action is to put them into @@ -969,9 +1043,9 @@ minetest.sound_play(spec, parameters) -> handle minetest.sound_stop(handle) Timing: -minetest.after(time, func, param) +minetest.after(time, func, ...) ^ Call function after time seconds -^ param is optional; to pass multiple parameters, pass a table. +^ Optional: Variable number of arguments that are passed to func Server: minetest.request_shutdown() -> request for server shutdown @@ -983,6 +1057,37 @@ minetest.get_ban_description(ip_or_name) -> ban description (string) minetest.ban_player(name) -> ban a player minetest.unban_player_or_ip(name) -> unban player or IP address +Particles: +minetest.add_particle(pos, velocity, acceleration, expirationtime, + size, collisiondetection, texture, playername) +^ Spawn particle at pos with velocity and acceleration +^ Disappears after expirationtime seconds +^ collisiondetection: if true collides with physical objects +^ Uses texture (string) +^ Playername is optional, if specified spawns particle only on the player's client + +minetest.add_particlespawner(amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, texture, playername) +^ Add a particlespawner, an object that spawns an amount of particles over time seconds +^ The particle's properties are random values in between the boundings: +^ minpos/maxpos, minvel/maxvel (velocity), minacc/maxacc (acceleration), +^ minsize/maxsize, minexptime/maxexptime (expirationtime) +^ collisiondetection: if true uses collisiondetection +^ Uses texture (string) +^ Playername is optional, if specified spawns particle only on the player's client +^ If time is 0 has infinite lifespan and spawns the amount on a per-second base +^ Returns and id + +minetest.delete_particlespawner(id, player) +^ Delete ParticleSpawner with id (return value from add_particlespawner) +^ If playername is specified, only deletes on the player's client, +^ otherwise on all clients + Random: minetest.get_connected_players() -> list of ObjectRefs minetest.hash_node_position({x=,y=,z=}) -> 48-bit integer @@ -1196,9 +1301,15 @@ methods: - set_wielded_item(item): replaces the wielded item, returns true if successful - set_armor_groups({group1=rating, group2=rating, ...}) - set_animation({x=1,y=1}, frame_speed=15, frame_blend=0) -- set_attach(parent, "", {x=0,y=0,z=0}, {x=0,y=0,z=0}) +- set_attach(parent, bone, position, rotation) + ^ bone = string + ^ position = {x=num, y=num, z=num} (relative) + ^ rotation = {x=num, y=num, z=num} - set_detach() -- set_bone_position("", {x=0,y=0,z=0}, {x=0,y=0,z=0}) +- set_bone_position(bone, position, rotation) + ^ bone = string + ^ position = {x=num, y=num, z=num} (relative) + ^ rotation = {x=num, y=num, z=num} - set_properties(object property table) LuaEntitySAO-only: (no-op for other objects) - setvelocity({x=num, y=num, z=num}) @@ -1220,15 +1331,17 @@ Player-only: (no-op for other objects) - get_look_dir(): get camera direction as a unit vector - get_look_pitch(): pitch in radians - get_look_yaw(): yaw in radians (wraps around pretty randomly as of now) +- set_look_pitch(radians): sets look pitch +- set_look_yaw(radians): sets look yaw - set_inventory_formspec(formspec) ^ Redefine player's inventory form ^ Should usually be called in on_joinplayer - get_inventory_formspec() -> formspec string - get_player_control(): returns table with player pressed keys - {jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool} + {jump=bool,right=bool,left=bool,LMB=bool,RMB=bool,sneak=bool,aux1=bool,down=bool,up=bool} - get_player_control_bits(): returns integer with bit packed player pressed keys - bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB - + bit nr/meaning: 0/up ,1/down ,2/left ,3/right ,4/jump ,5/aux1 ,6/sneak ,7/LMB ,8/RMB + InvRef: Reference to an inventory methods: - is_empty(listname): return true if list is empty @@ -1313,8 +1426,8 @@ Registered entities ^ puncher: ObjectRef (can be nil) ^ time_from_last_punch: Meant for disallowing spamming of clicks (can be nil) ^ tool_capabilities: capability table of used tool (can be nil) - ^ dir: unit vector of direction of punch. Always defined. Points from - the puncher to the punched. + ^ dir: unit vector of direction of punch. Always defined. Points from + the puncher to the punched. - on_rightclick(self, clicker) - get_staticdata(self) ^ Should return a string that will be passed to on_activate when @@ -1389,10 +1502,10 @@ Item definition (register_node, register_craftitem, register_tool) max_drop_level=0, groupcaps={ -- For example: - fleshy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1}, snappy={times={[2]=0.80, [3]=0.40}, maxwear=0.05, maxlevel=1}, choppy={times={[3]=0.90}, maxwear=0.05, maxlevel=0} - } + }, + damage_groups = {groupname=damage}, } node_placement_prediction = nil, ^ If nil and item is node, prediction is made automatically @@ -1401,6 +1514,9 @@ Item definition (register_node, register_craftitem, register_tool) ^ Otherwise should be name of node which the client immediately places on ground when the player places the item. Server will always update actual result to client in a short moment. + sound = { + place = <SimpleSoundSpec>, + } on_place = func(itemstack, placer, pointed_thing), ^ Shall place item and return the leftover itemstack @@ -1435,10 +1551,10 @@ Node definition (register_node) drawtype = "normal", -- See "Node drawtypes" visual_scale = 1.0, tiles = {tile definition 1, def2, def3, def4, def5, def6}, - ^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images) + ^ Textures of node; +Y, -Y, +X, -X, +Z, -Z (old field name: tile_images) ^ List can be shortened to needed length special_tiles = {tile definition 1, Tile definition 2}, - ^ Special textures of node; used rarely (old field name: special_materials) + ^ Special textures of node; used rarely (old field name: special_materials) ^ List can be shortened to needed length alpha = 255, post_effect_color = {a=0, r=0, g=0, b=0}, -- If player is inside node @@ -1468,6 +1584,7 @@ Node definition (register_node) footstep = <SimpleSoundSpec>, dig = <SimpleSoundSpec>, -- "__group" = group-based sound (default) dug = <SimpleSoundSpec>, + place = <SimpleSoundSpec>, }, on_construct = func(pos), @@ -1494,7 +1611,7 @@ Node definition (register_node) can_dig = function(pos,player) ^ returns true if node can be dug, or false if not ^ default: nil - + on_punch = func(pos, node, puncher), ^ default: minetest.node_punch ^ By default: does nothing @@ -1517,32 +1634,32 @@ Node definition (register_node) ^ Called when an UI form (eg. sign text input) returns data ^ default: nil - allow_metadata_inventory_move = func(pos, from_list, from_index, - to_list, to_index, count, player), - ^ Called when a player wants to move items inside the inventory - ^ Return value: number of items allowed to move - - allow_metadata_inventory_put = func(pos, listname, index, stack, player), - ^ Called when a player wants to put something into the inventory - ^ Return value: number of items allowed to put - ^ Return value: -1: Allow and don't modify item count in inventory + allow_metadata_inventory_move = func(pos, from_list, from_index, + to_list, to_index, count, player), + ^ Called when a player wants to move items inside the inventory + ^ Return value: number of items allowed to move + + allow_metadata_inventory_put = func(pos, listname, index, stack, player), + ^ Called when a player wants to put something into the inventory + ^ Return value: number of items allowed to put + ^ Return value: -1: Allow and don't modify item count in inventory - allow_metadata_inventory_take = func(pos, listname, index, stack, player), - ^ Called when a player wants to take something out of the inventory - ^ Return value: number of items allowed to take - ^ Return value: -1: Allow and don't modify item count in inventory - - on_metadata_inventory_move = func(pos, from_list, from_index, - to_list, to_index, count, player), - on_metadata_inventory_put = func(pos, listname, index, stack, player), - on_metadata_inventory_take = func(pos, listname, index, stack, player), - ^ Called after the actual action has happened, according to what was allowed. - ^ No return value + allow_metadata_inventory_take = func(pos, listname, index, stack, player), + ^ Called when a player wants to take something out of the inventory + ^ Return value: number of items allowed to take + ^ Return value: -1: Allow and don't modify item count in inventory + + on_metadata_inventory_move = func(pos, from_list, from_index, + to_list, to_index, count, player), + on_metadata_inventory_put = func(pos, listname, index, stack, player), + on_metadata_inventory_take = func(pos, listname, index, stack, player), + ^ Called after the actual action has happened, according to what was allowed. + ^ No return value - on_blast = func(pos, intensity), - ^ intensity: 1.0 = mid range of regular TNT - ^ If defined, called when an explosion touches the node, instead of - removing the node + on_blast = func(pos, intensity), + ^ intensity: 1.0 = mid range of regular TNT + ^ If defined, called when an explosion touches the node, instead of + removing the node } Recipe for register_craft: (shaped) @@ -1591,6 +1708,28 @@ Recipe for register_craft (furnace fuel) burntime = 1, } +Ore definition (register_ore) +{ + ore_type = "scatter" -- See "Ore types" + ore = "default:stone_with_coal", + wherein = "default:stone", + clust_scarcity = 8*8*8, + ^ Ore has a 1 out of clust_scarcity chance of spawning in a node + ^ This value should be *MUCH* higher than your intuition might tell you! + clust_num_ores = 8, + ^ Number of ores in a cluster + clust_size = 3, + ^ Size of the bounding box of the cluster + ^ In this example, there is a 3x3x3 cluster where 8 out of the 27 nodes are coal ore + height_min = -31000, + height_max = 64, + noise_threshhold = 0.5, + ^ If noise is above this threshhold, ore is placed. Not needed for a uniform distribution + noise_params = {offset=0, scale=1, spread={x=100, y=100, z=100}, seed=23, octaves=3, persist=0.70} + ^ NoiseParams structure describing the perlin noise used for ore distribution. + ^ Needed for sheet ore_type. Omit from scatter ore_type for a uniform ore distribution +} + Chatcommand definition (register_chatcommand) { params = "<name> <privilege>", -- short parameter description @@ -1601,24 +1740,24 @@ Chatcommand definition (register_chatcommand) Detached inventory callbacks { - allow_move = func(inv, from_list, from_index, to_list, to_index, count, player), + allow_move = func(inv, from_list, from_index, to_list, to_index, count, player), ^ Called when a player wants to move items inside the inventory - ^ Return value: number of items allowed to move - + ^ Return value: number of items allowed to move + allow_put = func(inv, listname, index, stack, player), ^ Called when a player wants to put something into the inventory - ^ Return value: number of items allowed to put - ^ Return value: -1: Allow and don't modify item count in inventory + ^ Return value: number of items allowed to put + ^ Return value: -1: Allow and don't modify item count in inventory allow_take = func(inv, listname, index, stack, player), ^ Called when a player wants to take something out of the inventory - ^ Return value: number of items allowed to take - ^ Return value: -1: Allow and don't modify item count in inventory - - on_move = func(inv, from_list, from_index, to_list, to_index, count, player), + ^ Return value: number of items allowed to take + ^ Return value: -1: Allow and don't modify item count in inventory + + on_move = func(inv, from_list, from_index, to_list, to_index, count, player), on_put = func(inv, listname, index, stack, player), on_take = func(inv, listname, index, stack, player), - ^ Called after the actual action has happened, according to what was allowed. - ^ No return value + ^ Called after the actual action has happened, according to what was allowed. + ^ No return value } diff --git a/doc/protocol.txt b/doc/protocol.txt index 160f15226..b151f88d8 100644 --- a/doc/protocol.txt +++ b/doc/protocol.txt @@ -70,3 +70,39 @@ function check_if_minetestserver_up($host, $port) return false; } +- Here's a Python script for checking if a minetest server is up, confirmed working +#!/usr/bin/env python +import sys, time, socket +address = "" +port = 30000 +if len(sys.argv) <= 1: + print("Usage: %s <address>" % sys.argv[0]) + exit() +if ':' in sys.argv[1]: + address = sys.argv[1].split(':')[0] + try: + port = int(sys.argv[1].split(':')[1]) + except ValueError: + print("Please specify a valid port") + exit() +else: + address = sys.argv[1] + +try: + start = time.time() + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.settimeout(2.0) + buf = "\x4f\x45\x74\x03\x00\x00\x00\x01" + sock.sendto(buf, (address, port)) + data, addr = sock.recvfrom(1000) + if data: + peer_id = data[12:14] + buf = "\x4f\x45\x74\x03" + peer_id + "\x00\x00\x03" + sock.sendto(buf, (address, port)) + sock.close() + end = time.time() + print("%s is up (%0.5fms)" % (sys.argv[1],end-start)) + else: + print("%s seems to be down " % sys.argv[1]) +except: + print("%s seems to be down " % sys.argv[1]) diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index fbb481a0c..163998883 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -659,6 +659,8 @@ function default.node_sound_dirt_defaults(table) {name="", gain=0.5} --table.dug = table.dug or -- {name="default_dirt_break", gain=0.5} + table.place = table.place or + {name="default_grass_footstep", gain=0.5} default.node_sound_defaults(table) return table end diff --git a/games/minimal/mods/default/mapgen.lua b/games/minimal/mods/default/mapgen.lua index 115bb1458..478567d0a 100644 --- a/games/minimal/mods/default/mapgen.lua +++ b/games/minimal/mods/default/mapgen.lua @@ -27,52 +27,74 @@ minetest.register_alias("mapgen_mese", "default:mese") -- Ore generation -- -local function generate_ore(name, wherein, minp, maxp, seed, chunks_per_volume, ore_per_chunk, height_min, height_max, param2) - if maxp.y < height_min or minp.y > height_max then - return - end - local y_min = math.max(minp.y, height_min) - local y_max = math.min(maxp.y, height_max) - local volume = (maxp.x-minp.x+1)*(y_max-y_min+1)*(maxp.z-minp.z+1) - local pr = PseudoRandom(seed) - local num_chunks = math.floor(chunks_per_volume * volume) - local chunk_size = 3 - if ore_per_chunk <= 4 then - chunk_size = 2 - end - local inverse_chance = math.floor(chunk_size*chunk_size*chunk_size / ore_per_chunk) - --print("generate_ore num_chunks: "..dump(num_chunks)) - for i=1,num_chunks do - local y0 = pr:next(y_min, y_max-chunk_size+1) - if y0 >= height_min and y0 <= height_max then - local x0 = pr:next(minp.x, maxp.x-chunk_size+1) - local z0 = pr:next(minp.z, maxp.z-chunk_size+1) - local p0 = {x=x0, y=y0, z=z0} - for x1=0,chunk_size-1 do - for y1=0,chunk_size-1 do - for z1=0,chunk_size-1 do - if pr:next(1,inverse_chance) == 1 then - local x2 = x0+x1 - local y2 = y0+y1 - local z2 = z0+z1 - local p2 = {x=x2, y=y2, z=z2} - if minetest.env:get_node(p2).name == wherein then - minetest.env:set_node(p2, {name=name, param2=param2}) - end - end - end - end - end - end - end - --print("generate_ore done") -end +minetest.register_ore({ + ore_type = "scatter", + ore = "default:stone_with_coal", + wherein = "default:stone", + clust_scarcity = 8*8*8, + clust_num_ores = 5, + clust_size = 3, + height_min = -31000, + height_max = 64, +}) + +minetest.register_ore({ + ore_type = "scatter", + ore = "default:stone_with_iron", + wherein = "default:stone", + clust_scarcity = 16*16*16, + clust_num_ores = 5, + clust_size = 3, + height_min = -5, + height_max = 7, +}) + +minetest.register_ore({ + ore_type = "scatter", + ore = "default:stone_with_iron", + wherein = "default:stone", + clust_scarcity = 12*12*12, + clust_num_ores = 5, + clust_size = 3, + height_min = -16, + height_max = -5, +}) + +minetest.register_ore({ + ore_type = "scatter", + ore = "default:stone_with_iron", + wherein = "default:stone", + clust_scarcity = 9*9*9, + clust_num_ores = 5, + clust_size = 3, + height_min = -31000, + height_max = -17, +}) + +-- for float islands and far scaled +minetest.register_ore({ + ore_type = "scatter", + ore = "default:stone_with_coal", + wherein = "default:stone", + clust_scarcity = 8*8*8, + clust_num_ores = 5, + clust_size = 3, + height_min = 200, + height_max = 31000, +}) + +minetest.register_ore({ + ore_type = "scatter", + ore = "default:stone_with_iron", + wherein = "default:stone", + clust_scarcity = 9*9*9, + clust_num_ores = 5, + clust_size = 3, + height_min = 200, + height_max = 31000, +}) minetest.register_on_generated(function(minp, maxp, seed) - generate_ore("default:stone_with_coal", "default:stone", minp, maxp, seed, 1/8/8/8, 5, -31000, 64) - generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+1, 1/16/16/16, 5, -5, 7) - generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+2, 1/12/12/12, 5, -16, -5) - generate_ore("default:stone_with_iron", "default:stone", minp, maxp, seed+3, 1/9/9/9, 5, -31000, -17) -- Generate clay if maxp.y >= 2 and minp.y <= 0 then -- Assume X and Z lengths are equal @@ -110,11 +132,5 @@ minetest.register_on_generated(function(minp, maxp, seed) end end end - if minetest.setting_get("liquid_finite") then - generate_ore("default:water_source", "default:stone", minp, maxp, seed+42, 1/24/24/24, 4, -100, -10, 128) - generate_ore("default:water_source", "default:stone", minp, maxp, seed+42, 1/28/28/28, 3, -10000, -101, 128) - generate_ore("default:lava_source", "default:stone", minp, maxp, seed+43, 1/38/38/38, 2, -500, -100, 128) - generate_ore("default:lava_source", "default:stone", minp, maxp, seed+43, 1/30/30/30, 4, -31000, -501, 128) - end end) diff --git a/minetest.conf.example b/minetest.conf.example index 1f2a764f2..838987c33 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -80,6 +80,7 @@ # when set to higher number than 0 #fsaa = 0 #vsync = false +#fov = 72 # Address to connect to (#blank = start local server) #address = # Enable random user input, for testing @@ -95,9 +96,11 @@ # Update liquids every .. recommend for finite: 0.2 #liquid_update = 1.0 # When finite liquid: relax flowing blocks to source if level near max and N nearby source blocks, more realistic, but not true constant. values: 0,1,2,3,4 : 0 - disable, 1 - most aggresive -#liquid_relax = 1 -# optimization: faster cave flood (and not true constant) +#liquid_relax = 2 +# Optimization: faster cave flood (and not true constant) #liquid_fast_flood = 1 +# Underground water and lava springs, its infnity sources if liquid_finite enabled +#underground_springs = 1 # Enable nice leaves; disable for speed #new_style_leaves = true # Enable smooth lighting with simple ambient occlusion; @@ -125,6 +128,10 @@ #farmesh_distance = 40 # Enable/disable clouds #enable_clouds = true +#cloud_height = 120 +#enable_3d_clouds = true +# Use a cloud animation for the main menu background +#menu_clouds = true # Path for screenshots #screenshot_path = . # Amount of view bobbing (0 = no view bobbing, 1.0 = normal, 2.0 = double) @@ -304,29 +311,45 @@ # Mapgen stuff # -# Name of map generator to be used. Currently only v6 is supported. +# Name of map generator to be used. Currently v6, indev and singlenode are supported. #mg_name = v6 # Water level of map. #water_level = 1 # Size of chunks to be generated. #chunksize = 5 -# Map generation attributes. Currently supported: trees, caves, flat, v6_biome_blend +# Map generation attributes. Currently supported: trees, caves, flat, v6_biome_blend, v6_jungles, dungeons #mg_flags = trees, caves, v6_biome_blend # How large deserts and beaches are #mgv6_freq_desert = 0.45 #mgv6_freq_beach = 0.15 # Perlin noise attributes for different map generation parameters # Offset, scale, spread factor, seed offset, number of octaves, persistence -#mgv6_np_terrain_base = -4, 20, (250.0, 250, 250), 82341, 5, 0.6 +#mgv6_np_terrain_base = -4, 20, (250, 250, 250), 82341, 5, 0.6 #mgv6_np_terrain_higher = 20, 16, (500, 500, 500), 85039, 5, 0.6 #mgv6_np_steepness = 0.85, 0.5, (125, 125, 125), -932, 5, 0.7 #mgv6_np_height_select = 0.5, 1, (250, 250, 250), 4213, 5, 0.69 -#mgv6_np_trees = 0, 1, (125, 125, 125), 2, 4, 0.66 #mgv6_np_mud = 4, 2, (200, 200, 200), 91013, 3, 0.55 #mgv6_np_beach = 0, 1, (250, 250, 250), 59420, 3, 0.50 #mgv6_np_biome = 0, 1, (250, 250, 250), 9130, 3, 0.50 #mgv6_np_cave = 6, 6, (250, 250, 250), 34329, 3, 0.50 +#mgv6_np_humidity = 0.5, 0.5, (500, 500, 500), 72384, 4, 0.66 +#mgv6_np_trees = 0, 1, (125, 125, 125), 2, 4, 0.66 +#mgv6_np_apple_trees = 0, 1, (100, 100, 100), 342902, 3, 0.45 + #mgv7_np_terrain = 10, 12, (350, 350, 350), 82341, 5, 0.6 #mgv7_np_bgroup = 0.5, 0.3125, (350, 350, 350), 5923, 2, 0.6 #mgv7_np_heat = 25, 50, (500, 500, 500), 35293, 1, 0 #mgv7_np_humidity = 50, 31.25, (750, 750, 750), 12094, 2, 0.6 + +# Offset, scale, spread factor, seed offset, number of octaves, persistence, farscale, farspread +#mgindev_np_terrain_base = -4, 20, (250, 250, 250), 82341, 5, 0.6, 10, 10 +#mgindev_np_terrain_higher = 20, 16, (500, 500, 500), 85039, 5, 0.6, 10, 10 +#mgindev_np_steepness = 0.85, 0.5, (125, 125, 125), -932, 5, 0.7, 2, 10 +#mgindev_np_mud = 4, 2, (200, 200, 200), 91013, 3, 0.55, 1, 1 +#mgindev_np_float_islands1 = 0, 1, (64, 64, 64 ), 3683, 5, 0.5, 1, 1.5 +#mgindev_np_float_islands2 = 0, 1, (8, 8, 8 ), 9292, 2, 0.5, 1, 1.5 +#mgindev_np_float_islands3 = 0, 1, (256, 256, 256), 6412, 2, 0.5, 1, 0.5 +#mgindev_np_biome = 0, 1, (250, 250, 250), 9130, 3, 0.50, 1, 10 + +# Float islands starts from height, 0 to disable +#mgindev_float_islands = 500 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d2f080c90..d6182861f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,7 +161,7 @@ else() endif(APPLE) endif(BUILD_CLIENT) find_package(ZLIB REQUIRED) - set(PLATFORM_LIBS -lpthread ${CMAKE_DL_LIBS}) + set(PLATFORM_LIBS -lpthread -lrt ${CMAKE_DL_LIBS}) #set(CLIENT_PLATFORM_LIBS -lXxf86vm) # This way Xxf86vm is found on OpenBSD too find_library(XXF86VM_LIBRARY Xxf86vm) @@ -172,6 +172,7 @@ endif() find_package(Jthread REQUIRED) find_package(Sqlite3 REQUIRED) find_package(Json REQUIRED) +find_package(OpenGLES2) if(USE_FREETYPE) find_package(Freetype REQUIRED) @@ -205,6 +206,20 @@ 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 @@ -212,7 +227,10 @@ set(common_SRCS emerge.cpp mapgen.cpp mapgen_v6.cpp + mapgen_indev.cpp + mapgen_singlenode.cpp treegen.cpp + dungeongen.cpp content_nodemeta.cpp content_mapnode.cpp collision.cpp @@ -371,6 +389,7 @@ if(BUILD_CLIENT) ${SQLITE3_LIBRARY} ${LUA_LIBRARY} ${JSON_LIBRARY} + ${OPENGLES2_LIBRARIES} ${PLATFORM_LIBS} ${CLIENT_PLATFORM_LIBS} ) diff --git a/src/activeobject.h b/src/activeobject.h index e454f2c8c..1a75fba2e 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -61,7 +61,7 @@ public: } virtual u8 getType() const = 0; - + virtual bool getCollisionBox(aabb3f *toset) = 0; protected: u16 m_id; // 0 is invalid, "no id" }; diff --git a/src/chat.cpp b/src/chat.cpp index c3509ae49..1135ccdf7 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -117,8 +117,8 @@ void ChatBuffer::deleteOldest(u32 count) --count; } - m_unformatted.erase(0, del_unformatted); - m_formatted.erase(0, del_formatted); + m_unformatted.erase(m_unformatted.begin(), m_unformatted.begin() + del_unformatted); + m_formatted.erase(m_formatted.begin(), m_formatted.begin() + del_formatted); } void ChatBuffer::deleteByAge(f32 maxAge) @@ -232,10 +232,10 @@ void ChatBuffer::scrollTop() } u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, - core::array<ChatFormattedLine>& destination) const + std::vector<ChatFormattedLine>& destination) const { u32 num_added = 0; - core::array<ChatFormattedFragment> next_frags; + std::vector<ChatFormattedFragment> next_frags; ChatFormattedLine next_line; ChatFormattedFragment temp_frag; u32 out_column = 0; @@ -292,7 +292,7 @@ u32 ChatBuffer::formatChatLine(const ChatLine& line, u32 cols, frag.column = out_column; next_line.fragments.push_back(frag); out_column += frag.text.size(); - next_frags.erase(0, 1); + next_frags.erase(next_frags.begin()); } else { @@ -414,7 +414,7 @@ std::wstring ChatPrompt::submit() if (!line.empty()) m_history.push_back(line); if (m_history.size() > m_history_limit) - m_history.erase(0); + m_history.erase(m_history.begin()); m_history_index = m_history.size(); m_view = 0; m_cursor = 0; @@ -464,7 +464,7 @@ void ChatPrompt::historyNext() } } -void ChatPrompt::nickCompletion(const core::list<std::wstring>& names, bool backwards) +void ChatPrompt::nickCompletion(const std::list<std::wstring>& names, bool backwards) { // Two cases: // (a) m_nick_completion_start == m_nick_completion_end == 0 @@ -492,10 +492,10 @@ void ChatPrompt::nickCompletion(const core::list<std::wstring>& names, bool back std::wstring prefix = m_line.substr(prefix_start, prefix_end - prefix_start); // find all names that start with the selected prefix - core::array<std::wstring> completions; - for (core::list<std::wstring>::ConstIterator + std::vector<std::wstring> completions; + for (std::list<std::wstring>::const_iterator i = names.begin(); - i != names.end(); i++) + i != names.end(); ++i) { if (str_starts_with(*i, prefix, true)) { diff --git a/src/chat.h b/src/chat.h index 27863922c..8a40c7ccf 100644 --- a/src/chat.h +++ b/src/chat.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include <string> +#include <vector> +#include <list> // Chat console related classes, only used by the client @@ -55,7 +57,7 @@ struct ChatFormattedFragment struct ChatFormattedLine { // Array of text fragments - core::array<ChatFormattedFragment> fragments; + std::vector<ChatFormattedFragment> fragments; // true if first line of one formatted ChatLine bool first; }; @@ -110,7 +112,7 @@ public: // Appends the formatted lines to the destination array and // returns the number of formatted lines. u32 formatChatLine(const ChatLine& line, u32 cols, - core::array<ChatFormattedLine>& destination) const; + std::vector<ChatFormattedLine>& destination) const; protected: s32 getTopScrollPos() const; @@ -120,7 +122,7 @@ private: // Scrollback size u32 m_scrollback; // Array of unformatted chat lines - core::array<ChatLine> m_unformatted; + std::vector<ChatLine> m_unformatted; // Number of character columns in console u32 m_cols; @@ -129,7 +131,7 @@ private: // Scroll position (console's top line index into m_formatted) s32 m_scroll; // Array of formatted lines - core::array<ChatFormattedLine> m_formatted; + std::vector<ChatFormattedLine> m_formatted; // Empty formatted line, for error returns ChatFormattedLine m_empty_formatted_line; }; @@ -158,7 +160,7 @@ public: void historyNext(); // Nick completion - void nickCompletion(const core::list<std::wstring>& names, bool backwards); + void nickCompletion(const std::list<std::wstring>& names, bool backwards); // Update console size and reformat the visible portion of the prompt void reformat(u32 cols); @@ -209,7 +211,7 @@ private: // Currently edited line std::wstring m_line; // History buffer - core::array<std::wstring> m_history; + std::vector<std::wstring> m_history; // History index (0 <= m_history_index <= m_history.size()) u32 m_history_index; // Maximum number of history entries diff --git a/src/client.cpp b/src/client.cpp index be35db5de..f27f95d98 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -232,8 +232,8 @@ void * MediaFetchThread::Thread() #if USE_CURL CURL *curl; CURLcode res; - for (core::list<MediaRequest>::Iterator i = m_file_requests.begin(); - i != m_file_requests.end(); i++) { + for (std::list<MediaRequest>::iterator i = m_file_requests.begin(); + i != m_file_requests.end(); ++i) { curl = curl_easy_init(); assert(curl); curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); @@ -360,8 +360,8 @@ Client::~Client() } } - for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin(); - i != m_media_fetch_threads.end(); i++) + for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin(); + i != m_media_fetch_threads.end(); ++i) delete *i; } @@ -585,7 +585,7 @@ void Client::step(float dtime) if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { ScopeProfiler sp(g_profiler, "Client: map timer and unload"); - core::list<v3s16> deleted_blocks; + std::list<v3s16> deleted_blocks; m_env.getMap().timerUpdate(map_timer_and_unload_dtime, g_settings->getFloat("client_unload_unused_data_timeout"), &deleted_blocks); @@ -599,8 +599,8 @@ void Client::step(float dtime) NOTE: This loop is intentionally iterated the way it is. */ - core::list<v3s16>::Iterator i = deleted_blocks.begin(); - core::list<v3s16> sendlist; + std::list<v3s16>::iterator i = deleted_blocks.begin(); + std::list<v3s16> sendlist; for(;;) { if(sendlist.size() == 255 || i == deleted_blocks.end()) @@ -619,9 +619,9 @@ void Client::step(float dtime) writeU16(&reply[0], TOSERVER_DELETEDBLOCKS); reply[2] = sendlist.size(); u32 k = 0; - for(core::list<v3s16>::Iterator + for(std::list<v3s16>::iterator j = sendlist.begin(); - j != sendlist.end(); j++) + j != sendlist.end(); ++j) { writeV3S16(&reply[2+1+6*k], *j); k++; @@ -635,7 +635,7 @@ void Client::step(float dtime) } sendlist.push_back(*i); - i++; + ++i; } } @@ -727,7 +727,7 @@ void Client::step(float dtime) <<std::endl;*/ int num_processed_meshes = 0; - while(m_mesh_update_thread.m_queue_out.size() > 0) + while(!m_mesh_update_thread.m_queue_out.empty()) { num_processed_meshes++; MeshUpdateResult r = m_mesh_update_thread.m_queue_out.pop_front(); @@ -779,10 +779,10 @@ void Client::step(float dtime) */ if (m_media_receive_started) { bool all_stopped = true; - for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin(); - thread != m_media_fetch_threads.end(); thread++) { + for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin(); + thread != m_media_fetch_threads.end(); ++thread) { all_stopped &= !(*thread)->IsRunning(); - while ((*thread)->m_file_data.size() > 0) { + while (!(*thread)->m_file_data.empty()) { std::pair <std::string, std::string> out = (*thread)->m_file_data.pop_front(); ++m_media_received_count; @@ -803,9 +803,9 @@ void Client::step(float dtime) } { - core::map<std::string, std::string>::Node *n; + std::map<std::string, std::string>::iterator n; n = m_media_name_sha1_map.find(out.first); - if(n == NULL) + if(n == m_media_name_sha1_map.end()) errorstream<<"The server sent a file that has not " <<"been announced."<<std::endl; else @@ -814,11 +814,11 @@ void Client::step(float dtime) } } if (all_stopped) { - core::list<MediaRequest> fetch_failed; - for (core::list<MediaFetchThread*>::Iterator thread = m_media_fetch_threads.begin(); - thread != m_media_fetch_threads.end(); thread++) { - for (core::list<MediaRequest>::Iterator request = (*thread)->m_failed.begin(); - request != (*thread)->m_failed.end(); request++) + std::list<MediaRequest> fetch_failed; + for (std::list<MediaFetchThread*>::iterator thread = m_media_fetch_threads.begin(); + thread != m_media_fetch_threads.end(); ++thread) { + for (std::list<MediaRequest>::iterator request = (*thread)->m_failed.begin(); + request != (*thread)->m_failed.end(); ++request) fetch_failed.push_back(*request); (*thread)->m_failed.clear(); } @@ -1015,14 +1015,14 @@ void Client::deletingPeer(con::Peer *peer, bool timeout) string name } */ -void Client::request_media(const core::list<MediaRequest> &file_requests) +void Client::request_media(const std::list<MediaRequest> &file_requests) { std::ostringstream os(std::ios_base::binary); writeU16(os, TOSERVER_REQUEST_MEDIA); writeU16(os, file_requests.size()); - for(core::list<MediaRequest>::ConstIterator i = file_requests.begin(); - i != file_requests.end(); i++) { + for(std::list<MediaRequest>::const_iterator i = file_requests.begin(); + i != file_requests.end(); ++i) { os<<serializeString(i->name); } @@ -1622,7 +1622,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) infostream<<"Client: Received media announcement: packet size: " <<datasize<<std::endl; - core::list<MediaRequest> file_requests; + std::list<MediaRequest> file_requests; for(int i=0; i<num_files; i++) { @@ -1641,7 +1641,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) std::string sha1_hex = hex_encode(sha1_raw); std::ostringstream tmp_os(std::ios_base::binary); bool found_in_cache = m_media_cache.load_sha1(sha1_raw, tmp_os); - m_media_name_sha1_map.set(name, sha1_raw); + m_media_name_sha1_map[name] = sha1_raw; // If found in cache, try to load it from there if(found_in_cache) @@ -1677,16 +1677,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) request_media(file_requests); } else { #if USE_CURL - core::list<MediaFetchThread*>::Iterator cur = m_media_fetch_threads.begin(); - for(core::list<MediaRequest>::Iterator i = file_requests.begin(); - i != file_requests.end(); i++) { + std::list<MediaFetchThread*>::iterator cur = m_media_fetch_threads.begin(); + for(std::list<MediaRequest>::iterator i = file_requests.begin(); + i != file_requests.end(); ++i) { (*cur)->m_file_requests.push_back(*i); cur++; if (cur == m_media_fetch_threads.end()) cur = m_media_fetch_threads.begin(); } - for (core::list<MediaFetchThread*>::Iterator i = m_media_fetch_threads.begin(); - i != m_media_fetch_threads.end(); i++) { + for (std::list<MediaFetchThread*>::iterator i = m_media_fetch_threads.begin(); + i != m_media_fetch_threads.end(); ++i) { (*i)->m_remote_url = remote_media; (*i)->Start(); } @@ -1762,9 +1762,9 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } { - core::map<std::string, std::string>::Node *n; + std::map<std::string, std::string>::iterator n; n = m_media_name_sha1_map.find(name); - if(n == NULL) + if(n == m_media_name_sha1_map.end()) errorstream<<"The server sent a file that has not " <<"been announced."<<std::endl; else @@ -1936,6 +1936,89 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) event.show_formspec.formname = new std::string(formname); m_client_event_queue.push_back(event); } + else if(command == TOCLIENT_SPAWN_PARTICLE) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + v3f pos = readV3F1000(is); + v3f vel = readV3F1000(is); + v3f acc = readV3F1000(is); + float expirationtime = readF1000(is); + float size = readF1000(is); + bool collisiondetection = readU8(is); + std::string texture = deSerializeLongString(is); + + ClientEvent event; + event.type = CE_SPAWN_PARTICLE; + event.spawn_particle.pos = new v3f (pos); + event.spawn_particle.vel = new v3f (vel); + event.spawn_particle.acc = new v3f (acc); + + event.spawn_particle.expirationtime = expirationtime; + event.spawn_particle.size = size; + event.add_particlespawner.collisiondetection = + collisiondetection; + event.spawn_particle.texture = new std::string(texture); + + m_client_event_queue.push_back(event); + } + else if(command == TOCLIENT_ADD_PARTICLESPAWNER) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + u16 amount = readU16(is); + float spawntime = readF1000(is); + v3f minpos = readV3F1000(is); + v3f maxpos = readV3F1000(is); + v3f minvel = readV3F1000(is); + v3f maxvel = readV3F1000(is); + v3f minacc = readV3F1000(is); + v3f maxacc = readV3F1000(is); + float minexptime = readF1000(is); + float maxexptime = readF1000(is); + float minsize = readF1000(is); + float maxsize = readF1000(is); + bool collisiondetection = readU8(is); + std::string texture = deSerializeLongString(is); + u32 id = readU32(is); + + ClientEvent event; + event.type = CE_ADD_PARTICLESPAWNER; + event.add_particlespawner.amount = amount; + event.add_particlespawner.spawntime = spawntime; + + event.add_particlespawner.minpos = new v3f (minpos); + event.add_particlespawner.maxpos = new v3f (maxpos); + event.add_particlespawner.minvel = new v3f (minvel); + event.add_particlespawner.maxvel = new v3f (maxvel); + event.add_particlespawner.minacc = new v3f (minacc); + event.add_particlespawner.maxacc = new v3f (maxacc); + + event.add_particlespawner.minexptime = minexptime; + event.add_particlespawner.maxexptime = maxexptime; + event.add_particlespawner.minsize = minsize; + event.add_particlespawner.maxsize = maxsize; + event.add_particlespawner.collisiondetection = collisiondetection; + event.add_particlespawner.texture = new std::string(texture); + event.add_particlespawner.id = id; + + m_client_event_queue.push_back(event); + } + else if(command == TOCLIENT_DELETE_PARTICLESPAWNER) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + u32 id = readU16(is); + + ClientEvent event; + event.type = CE_DELETE_PARTICLESPAWNER; + event.delete_particlespawner.id = id; + + m_client_event_queue.push_back(event); + } else { infostream<<"Client: Ignoring unknown command " @@ -2231,7 +2314,7 @@ void Client::sendPlayerItem(u16 item) void Client::removeNode(v3s16 p) { - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; try { @@ -2245,12 +2328,11 @@ void Client::removeNode(v3s16 p) // add urgent task to update the modified node addUpdateMeshTaskForNode(p, false, true); - for(core::map<v3s16, MapBlock * >::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock * >::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - addUpdateMeshTaskWithEdge(p); + addUpdateMeshTaskWithEdge(i->first); } } @@ -2258,7 +2340,7 @@ void Client::addNode(v3s16 p, MapNode n) { TimeTaker timer1("Client::addNode()"); - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; try { @@ -2268,12 +2350,11 @@ void Client::addNode(v3s16 p, MapNode n) catch(InvalidPositionException &e) {} - for(core::map<v3s16, MapBlock * >::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock * >::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - addUpdateMeshTaskWithEdge(p); + addUpdateMeshTaskWithEdge(i->first); } } @@ -2373,7 +2454,7 @@ ClientActiveObject * Client::getSelectedActiveObject( core::line3d<f32> shootline_on_map ) { - core::array<DistanceSortedActiveObject> objects; + std::vector<DistanceSortedActiveObject> objects; m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); @@ -2381,7 +2462,7 @@ ClientActiveObject * Client::getSelectedActiveObject( // Sort them. // After this, the closest object is the first in the array. - objects.sort(); + std::sort(objects.begin(), objects.end()); for(u32 i=0; i<objects.size(); i++) { @@ -2420,13 +2501,13 @@ void Client::printDebugInfo(std::ostream &os) <<std::endl;*/ } -core::list<std::wstring> Client::getConnectedPlayerNames() +std::list<std::wstring> Client::getConnectedPlayerNames() { - core::list<Player*> players = m_env.getPlayers(true); - core::list<std::wstring> playerNames; - for(core::list<Player*>::Iterator + std::list<Player*> players = m_env.getPlayers(true); + std::list<std::wstring> playerNames; + for(std::list<Player*>::iterator i = players.begin(); - i != players.end(); i++) + i != players.end(); ++i) { Player *player = *i; playerNames.push_back(narrow_to_wide(player->getName())); diff --git a/src/client.h b/src/client.h index 809e98b81..d476a1d51 100644 --- a/src/client.h +++ b/src/client.h @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "particles.h" #include "util/pointedthing.h" +#include <algorithm> struct MeshMakeData; class MapBlockMesh; @@ -142,9 +143,9 @@ public: void * Thread(); - core::list<MediaRequest> m_file_requests; + std::list<MediaRequest> m_file_requests; MutexedQueue<std::pair<std::string, std::string> > m_file_data; - core::list<MediaRequest> m_failed; + std::list<MediaRequest> m_failed; std::string m_remote_url; IGameDef *m_gamedef; }; @@ -156,7 +157,10 @@ enum ClientEventType CE_PLAYER_FORCE_MOVE, CE_DEATHSCREEN, CE_TEXTURES_UPDATED, - CE_SHOW_FORMSPEC + CE_SHOW_FORMSPEC, + CE_SPAWN_PARTICLE, + CE_ADD_PARTICLESPAWNER, + CE_DELETE_PARTICLESPAWNER }; struct ClientEvent @@ -184,6 +188,35 @@ struct ClientEvent } show_formspec; struct{ } textures_updated; + struct{ + v3f *pos; + v3f *vel; + v3f *acc; + f32 expirationtime; + f32 size; + bool collisiondetection; + std::string *texture; + } spawn_particle; + struct{ + u16 amount; + f32 spawntime; + v3f *minpos; + v3f *maxpos; + v3f *minvel; + v3f *maxvel; + v3f *minacc; + v3f *maxacc; + f32 minexptime; + f32 maxexptime; + f32 minsize; + f32 maxsize; + bool collisiondetection; + std::string *texture; + u32 id; + } add_particlespawner; + struct{ + u32 id; + } delete_particlespawner; }; }; @@ -282,7 +315,7 @@ public: // Prints a line or two of info void printDebugInfo(std::ostream &os); - core::list<std::wstring> getConnectedPlayerNames(); + std::list<std::wstring> getConnectedPlayerNames(); float getAnimationTime(); @@ -347,7 +380,7 @@ private: // Insert a media file appropriately into the appropriate manager bool loadMedia(const std::string &data, const std::string &filename); - void request_media(const core::list<MediaRequest> &file_requests); + void request_media(const std::list<MediaRequest> &file_requests); // Virtual methods from con::PeerHandler void peerAdded(con::Peer *peer); @@ -377,7 +410,7 @@ private: MtEventManager *m_event; MeshUpdateThread m_mesh_update_thread; - core::list<MediaFetchThread*> m_media_fetch_threads; + std::list<MediaFetchThread*> m_media_fetch_threads; ClientEnvironment m_env; con::Connection m_con; IrrlichtDevice *m_device; @@ -387,7 +420,7 @@ private: bool m_inventory_updated; Inventory *m_inventory_from_server; float m_inventory_from_server_age; - core::map<v3s16, bool> m_active_blocks; + std::set<v3s16> m_active_blocks; PacketCounter m_packetcounter; // Block mesh animation parameters float m_animation_time; @@ -405,7 +438,7 @@ private: Queue<ClientEvent> m_client_event_queue; FileCache m_media_cache; // Mapping from media file name to SHA1 checksum - core::map<std::string, std::string> m_media_name_sha1_map; + std::map<std::string, std::string> m_media_name_sha1_map; bool m_media_receive_started; u32 m_media_count; u32 m_media_received_count; diff --git a/src/clientmap.cpp b/src/clientmap.cpp index aa92dfdee..c08068367 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "settings.h" #include "util/mathconstants.h" +#include <algorithm> #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -83,7 +84,7 @@ MapSector * ClientMap::emergeSector(v2s16 p2d) { //JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out - m_sectors.insert(p2d, sector); + m_sectors[p2d] = sector; } return sector; @@ -164,11 +165,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) INodeDefManager *nodemgr = m_gamedef->ndef(); - for(core::map<v3s16, MapBlock*>::Iterator - i = m_drawlist.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = m_drawlist.begin(); + i != m_drawlist.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); + MapBlock *block = i->second; block->refDrop(); } m_drawlist.clear(); @@ -215,11 +216,11 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) // Blocks from which stuff was actually drawn //u32 blocks_without_stuff = 0; - for(core::map<v2s16, MapSector*>::Iterator - si = m_sectors.getIterator(); - si.atEnd() == false; si++) + for(std::map<v2s16, MapSector*>::iterator + si = m_sectors.begin(); + si != m_sectors.end(); ++si) { - MapSector *sector = si.getNode()->getValue(); + MapSector *sector = si->second; v2s16 sp = sector->getPos(); if(m_control.range_all == false) @@ -231,7 +232,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) continue; } - core::list< MapBlock * > sectorblocks; + std::list< MapBlock * > sectorblocks; sector->getBlocks(sectorblocks); /* @@ -240,7 +241,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) u32 sector_blocks_drawn = 0; - core::list< MapBlock * >::Iterator i; + std::list< MapBlock * >::iterator i; for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++) { MapBlock *block = *i; @@ -350,7 +351,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) } // foreach sectorblocks if(sector_blocks_drawn != 0) - m_last_drawn_sectors[sp] = true; + m_last_drawn_sectors.insert(sp); } m_control.blocks_would_have_drawn = blocks_would_have_drawn; @@ -368,12 +369,12 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) struct MeshBufList { video::SMaterial m; - core::list<scene::IMeshBuffer*> bufs; + std::list<scene::IMeshBuffer*> bufs; }; struct MeshBufListList { - core::list<MeshBufList> lists; + std::list<MeshBufList> lists; void clear() { @@ -382,8 +383,8 @@ struct MeshBufListList void add(scene::IMeshBuffer *buf) { - for(core::list<MeshBufList>::Iterator i = lists.begin(); - i != lists.end(); i++){ + for(std::list<MeshBufList>::iterator i = lists.begin(); + i != lists.end(); ++i){ MeshBufList &l = *i; if(l.m == buf->getMaterial()){ l.bufs.push_back(buf); @@ -487,11 +488,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) MeshBufListList drawbufs; - for(core::map<v3s16, MapBlock*>::Iterator - i = m_drawlist.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = m_drawlist.begin(); + i != m_drawlist.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); + MapBlock *block = i->second; // If the mesh of the block happened to get deleted, ignore it if(block->mesh == NULL) @@ -569,11 +570,11 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) } } - core::list<MeshBufList> &lists = drawbufs.lists; + std::list<MeshBufList> &lists = drawbufs.lists; int timecheck_counter = 0; - for(core::list<MeshBufList>::Iterator i = lists.begin(); - i != lists.end(); i++) + for(std::list<MeshBufList>::iterator i = lists.begin(); + i != lists.end(); ++i) { { timecheck_counter++; @@ -595,8 +596,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) driver->setMaterial(list.m); - for(core::list<scene::IMeshBuffer*>::Iterator j = list.bufs.begin(); - j != list.bufs.end(); j++) + for(std::list<scene::IMeshBuffer*>::iterator j = list.bufs.begin(); + j != list.bufs.end(); ++j) { scene::IMeshBuffer *buf = *j; driver->drawMeshBuffer(buf); @@ -769,7 +770,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, float sunlight_min_d = max_d*0.8; if(sunlight_min_d > 35*BS) sunlight_min_d = 35*BS; - core::array<int> values; + std::vector<int> values; for(u32 i=0; i<sizeof(z_directions)/sizeof(*z_directions); i++){ v3f z_dir = z_directions[i]; z_dir.normalize(); @@ -798,7 +799,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, } int brightness_sum = 0; int brightness_count = 0; - values.sort(); + std::sort(values.begin(), values.end()); u32 num_values_to_use = values.size(); if(num_values_to_use >= 10) num_values_to_use -= num_values_to_use/2; diff --git a/src/clientmap.h b/src/clientmap.h index 786f35b77..7f63704d3 100644 --- a/src/clientmap.h +++ b/src/clientmap.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "map.h" +#include <set> +#include <map> struct MapDrawControl { @@ -128,7 +130,7 @@ public: // Check if sector was drawn on last render() bool sectorWasDrawn(v2s16 p) { - return (m_last_drawn_sectors.find(p) != NULL); + return (m_last_drawn_sectors.find(p) != m_last_drawn_sectors.end()); } private: @@ -143,9 +145,9 @@ private: f32 m_camera_fov; JMutex m_camera_mutex; - core::map<v3s16, MapBlock*> m_drawlist; + std::map<v3s16, MapBlock*> m_drawlist; - core::map<v2s16, bool> m_last_drawn_sectors; + std::set<v2s16> m_last_drawn_sectors; }; #endif diff --git a/src/clientobject.cpp b/src/clientobject.cpp index e7c735dac..37f693c5e 100644 --- a/src/clientobject.cpp +++ b/src/clientobject.cpp @@ -43,9 +43,9 @@ ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef, ClientEnvironment *env) { // Find factory function - core::map<u16, Factory>::Node *n; + std::map<u16, Factory>::iterator n; n = m_types.find(type); - if(n == NULL) + if(n == m_types.end()) { // If factory is not found, just return. dstream<<"WARNING: ClientActiveObject: No factory for type=" @@ -53,18 +53,18 @@ ClientActiveObject* ClientActiveObject::create(u8 type, IGameDef *gamedef, return NULL; } - Factory f = n->getValue(); + Factory f = n->second; ClientActiveObject *object = (*f)(gamedef, env); return object; } void ClientActiveObject::registerType(u16 type, Factory f) { - core::map<u16, Factory>::Node *n; + std::map<u16, Factory>::iterator n; n = m_types.find(type); - if(n) + if(n != m_types.end()) return; - m_types.insert(type, f); + m_types[type] = f; } diff --git a/src/clientobject.h b/src/clientobject.h index d1ee366cf..8cbf3d60b 100644 --- a/src/clientobject.h +++ b/src/clientobject.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "activeobject.h" +#include <map> /* @@ -96,7 +97,7 @@ protected: ClientEnvironment *m_env; private: // Used for creating objects based on type - static core::map<u16, Factory> m_types; + static std::map<u16, Factory> m_types; }; struct DistanceSortedActiveObject @@ -110,7 +111,7 @@ struct DistanceSortedActiveObject d = a_d; } - bool operator < (DistanceSortedActiveObject &other) + bool operator < (const DistanceSortedActiveObject &other) const { return d < other.d; } diff --git a/src/clientserver.h b/src/clientserver.h index 769272a68..28b579971 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -79,9 +79,18 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed); Serialization format changes PROTOCOL_VERSION 16: TOCLIENT_SHOW_FORMSPEC + PROTOCOL_VERSION 17: + Serialization format change: include backface_culling flag in TileDef + Added rightclickable field in nodedef + TOCLIENT_SPAWN_PARTICLE + TOCLIENT_ADD_PARTICLESPAWNER + TOCLIENT_DELETE_PARTICLESPAWNER + PROTOCOL_VERSION 18: + damageGroups added to ToolCapabilities + sound_place added to ItemDefinition */ -#define LATEST_PROTOCOL_VERSION 16 +#define LATEST_PROTOCOL_VERSION 18 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 @@ -356,6 +365,7 @@ enum ToClientCommand u8[len] name [2] serialized inventory */ + TOCLIENT_SHOW_FORMSPEC = 0x44, /* [0] u16 command @@ -381,6 +391,46 @@ enum ToClientCommand f1000 movement_liquid_sink f1000 movement_gravity */ + + TOCLIENT_SPAWN_PARTICLE = 0x46, + /* + u16 command + v3f1000 pos + v3f1000 velocity + v3f1000 acceleration + f1000 expirationtime + f1000 size + u8 bool collisiondetection + u32 len + u8[len] texture + */ + + TOCLIENT_ADD_PARTICLESPAWNER = 0x47, + /* + u16 command + u16 amount + f1000 spawntime + v3f1000 minpos + v3f1000 maxpos + v3f1000 minvel + v3f1000 maxvel + v3f1000 minacc + v3f1000 maxacc + f1000 minexptime + f1000 maxexptime + f1000 minsize + f1000 maxsize + u8 bool collisiondetection + u32 len + u8[len] texture + u32 id + */ + + TOCLIENT_DELETE_PARTICLESPAWNER = 0x48, + /* + u16 command + u32 id + */ }; enum ToServerCommand diff --git a/src/clouds.cpp b/src/clouds.cpp index 9f0bc06d8..55ec8965a 100644 --- a/src/clouds.cpp +++ b/src/clouds.cpp @@ -29,7 +29,8 @@ Clouds::Clouds( scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, - u32 seed + u32 seed, + s16 cloudheight ): scene::ISceneNode(parent, mgr, id), m_seed(seed), @@ -45,7 +46,8 @@ Clouds::Clouds( //m_material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - m_cloud_y = BS * g_settings->getS16("cloud_height"); + m_cloud_y = BS * (cloudheight ? cloudheight : + g_settings->getS16("cloud_height")); m_box = core::aabbox3d<f32>(-BS*1000000,m_cloud_y-BS,-BS*1000000, BS*1000000,m_cloud_y+BS,BS*1000000); diff --git a/src/clouds.h b/src/clouds.h index 72923c2c5..8f8b19faf 100644 --- a/src/clouds.h +++ b/src/clouds.h @@ -30,7 +30,8 @@ public: scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, - u32 seed + u32 seed, + s16 cloudheight=0 ); ~Clouds(); diff --git a/src/collision.cpp b/src/collision.cpp index 58517b779..806a3b720 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -23,7 +23,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "gamedef.h" #include "log.h" +#include "environment.h" +#include "serverobject.h" #include <vector> +#include <set> #include "util/timetaker.h" #include "main.h" // g_profiler #include "profiler.h" @@ -186,11 +189,12 @@ bool wouldCollideWithCeiling( } -collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, +collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, v3f &pos_f, v3f &speed_f, v3f &accel_f) { + Map *map = &env->getMap(); //TimeTaker tt("collisionMoveSimple"); ScopeProfiler sp(g_profiler, "collisionMoveSimple avg", SPT_AVG); @@ -215,6 +219,7 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, std::vector<aabb3f> cboxes; std::vector<bool> is_unloaded; std::vector<bool> is_step_up; + std::vector<bool> is_object; std::vector<int> bouncy_values; std::vector<v3s16> node_positions; { @@ -256,6 +261,7 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, is_step_up.push_back(false); bouncy_values.push_back(n_bouncy_value); node_positions.push_back(p); + is_object.push_back(false); } } catch(InvalidPositionException &e) @@ -267,14 +273,72 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, is_step_up.push_back(false); bouncy_values.push_back(0); node_positions.push_back(p); + is_object.push_back(false); } } } // tt2 + { + ScopeProfiler sp(g_profiler, "collisionMoveSimple objects avg", SPT_AVG); + //TimeTaker tt3("collisionMoveSimple collect object boxes"); + + /* add object boxes to cboxes */ + + + std::list<ActiveObject*> objects; +#ifndef SERVER + ClientEnvironment *c_env = dynamic_cast<ClientEnvironment*>(env); + if (c_env != 0) + { + f32 distance = speed_f.getLength(); + std::vector<DistanceSortedActiveObject> clientobjects; + c_env->getActiveObjects(pos_f,distance * 1.5,clientobjects); + for (int i=0; i < clientobjects.size(); i++) + { + objects.push_back((ActiveObject*)clientobjects[i].obj); + } + } + else +#endif + { + ServerEnvironment *s_env = dynamic_cast<ServerEnvironment*>(env); + if (s_env != 0) + { + f32 distance = speed_f.getLength(); + std::set<u16> s_objects = s_env->getObjectsInsideRadius(pos_f,distance * 1.5); + for (std::set<u16>::iterator iter = s_objects.begin(); iter != s_objects.end(); iter++) + { + ServerActiveObject *current = s_env->getActiveObject(*iter); + objects.push_back((ActiveObject*)current); + } + } + } + + for (std::list<ActiveObject*>::const_iterator iter = objects.begin();iter != objects.end(); ++iter) + { + ActiveObject *object = *iter; + + if (object != NULL) + { + aabb3f object_collisionbox; + if (object->getCollisionBox(&object_collisionbox)) + { + cboxes.push_back(object_collisionbox); + is_unloaded.push_back(false); + is_step_up.push_back(false); + bouncy_values.push_back(0); + node_positions.push_back(v3s16(0,0,0)); + is_object.push_back(true); + } + } + } + } //tt3 + assert(cboxes.size() == is_unloaded.size()); assert(cboxes.size() == is_step_up.size()); assert(cboxes.size() == bouncy_values.size()); assert(cboxes.size() == node_positions.size()); + assert(cboxes.size() == is_object.size()); /* Collision detection @@ -386,7 +450,11 @@ collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, is_collision = false; CollisionInfo info; - info.type = COLLISION_NODE; + if (is_object[nearest_boxindex]) { + info.type = COLLISION_OBJECT; + } + else + info.type = COLLISION_NODE; info.node_p = node_positions[nearest_boxindex]; info.bouncy = bouncy; info.old_speed = speed_f; diff --git a/src/collision.h b/src/collision.h index 38cc3efb3..117818456 100644 --- a/src/collision.h +++ b/src/collision.h @@ -25,10 +25,12 @@ with this program; if not, write to the Free Software Foundation, Inc., class Map; class IGameDef; +class Environment; enum CollisionType { - COLLISION_NODE + COLLISION_NODE, + COLLISION_OBJECT, }; struct CollisionInfo @@ -65,7 +67,7 @@ struct collisionMoveResult }; // Moves using a single iteration; speed should not exceed pos_max_d/dtime -collisionMoveResult collisionMoveSimple(Map *map, IGameDef *gamedef, +collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, v3f &pos_f, v3f &speed_f, v3f &accel_f); diff --git a/src/connection.cpp b/src/connection.cpp index 7a3018bfd..7bff5b113 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -76,19 +76,20 @@ SharedBuffer<u8> makeOriginalPacket( return b; } -core::list<SharedBuffer<u8> > makeSplitPacket( +std::list<SharedBuffer<u8> > makeSplitPacket( SharedBuffer<u8> data, u32 chunksize_max, u16 seqnum) { // Chunk packets, containing the TYPE_SPLIT header - core::list<SharedBuffer<u8> > chunks; + std::list<SharedBuffer<u8> > chunks; u32 chunk_header_size = 7; u32 maximum_data_size = chunksize_max - chunk_header_size; u32 start = 0; u32 end = 0; u32 chunk_num = 0; + u16 chunk_count = 0; do{ end = start + maximum_data_size - 1; if(end > data.getSize() - 1) @@ -106,16 +107,15 @@ core::list<SharedBuffer<u8> > makeSplitPacket( memcpy(&chunk[chunk_header_size], &data[start], payload_size); chunks.push_back(chunk); + chunk_count++; start = end + 1; chunk_num++; } while(end != data.getSize() - 1); - u16 chunk_count = chunks.getSize(); - - core::list<SharedBuffer<u8> >::Iterator i = chunks.begin(); - for(; i != chunks.end(); i++) + for(std::list<SharedBuffer<u8> >::iterator i = chunks.begin(); + i != chunks.end(); ++i) { // Write chunk_count writeU16(&((*i)[3]), chunk_count); @@ -124,13 +124,13 @@ core::list<SharedBuffer<u8> > makeSplitPacket( return chunks; } -core::list<SharedBuffer<u8> > makeAutoSplitPacket( +std::list<SharedBuffer<u8> > makeAutoSplitPacket( SharedBuffer<u8> data, u32 chunksize_max, u16 &split_seqnum) { u32 original_header_size = 1; - core::list<SharedBuffer<u8> > list; + std::list<SharedBuffer<u8> > list; if(data.getSize() + original_header_size > chunksize_max) { list = makeSplitPacket(data, chunksize_max, split_seqnum); @@ -170,11 +170,13 @@ SharedBuffer<u8> makeReliablePacket( ReliablePacketBuffer */ +ReliablePacketBuffer::ReliablePacketBuffer(): m_list_size(0) {} + void ReliablePacketBuffer::print() { - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); - for(; i != m_list.end(); i++) + for(std::list<BufferedPacket>::iterator i = m_list.begin(); + i != m_list.end(); + ++i) { u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1])); dout_con<<s<<" "; @@ -186,13 +188,12 @@ bool ReliablePacketBuffer::empty() } u32 ReliablePacketBuffer::size() { - return m_list.getSize(); + return m_list_size; } RPBSearchResult ReliablePacketBuffer::findPacket(u16 seqnum) { - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); - for(; i != m_list.end(); i++) + std::list<BufferedPacket>::iterator i = m_list.begin(); + for(; i != m_list.end(); ++i) { u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1])); /*dout_con<<"findPacket(): finding seqnum="<<seqnum @@ -218,8 +219,8 @@ BufferedPacket ReliablePacketBuffer::popFirst() if(empty()) throw NotFoundException("Buffer is empty"); BufferedPacket p = *m_list.begin(); - core::list<BufferedPacket>::Iterator i = m_list.begin(); - m_list.erase(i); + m_list.erase(m_list.begin()); + --m_list_size; return p; } BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum) @@ -231,6 +232,7 @@ BufferedPacket ReliablePacketBuffer::popSeqnum(u16 seqnum) } BufferedPacket p = *r; m_list.erase(r); + --m_list_size; return p; } void ReliablePacketBuffer::insert(BufferedPacket &p) @@ -240,6 +242,7 @@ void ReliablePacketBuffer::insert(BufferedPacket &p) assert(type == TYPE_RELIABLE); u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); + ++m_list_size; // Find the right place for the packet and insert it there // If list is empty, just add it @@ -250,12 +253,12 @@ void ReliablePacketBuffer::insert(BufferedPacket &p) return; } // Otherwise find the right place - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); + std::list<BufferedPacket>::iterator i = m_list.begin(); // Find the first packet in the list which has a higher seqnum - for(; i != m_list.end(); i++){ + for(; i != m_list.end(); ++i){ u16 s = readU16(&(i->data[BASE_HEADER_SIZE+1])); if(s == seqnum){ + --m_list_size; throw AlreadyExistsException("Same seqnum in list"); } if(seqnum_higher(s, seqnum)){ @@ -271,14 +274,14 @@ void ReliablePacketBuffer::insert(BufferedPacket &p) return; } // Insert before i - m_list.insert_before(i, p); + m_list.insert(i, p); } void ReliablePacketBuffer::incrementTimeouts(float dtime) { - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); - for(; i != m_list.end(); i++){ + for(std::list<BufferedPacket>::iterator i = m_list.begin(); + i != m_list.end(); ++i) + { i->time += dtime; i->totaltime += dtime; } @@ -286,9 +289,9 @@ void ReliablePacketBuffer::incrementTimeouts(float dtime) void ReliablePacketBuffer::resetTimedOuts(float timeout) { - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); - for(; i != m_list.end(); i++){ + for(std::list<BufferedPacket>::iterator i = m_list.begin(); + i != m_list.end(); ++i) + { if(i->time >= timeout) i->time = 0.0; } @@ -296,21 +299,20 @@ void ReliablePacketBuffer::resetTimedOuts(float timeout) bool ReliablePacketBuffer::anyTotaltimeReached(float timeout) { - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); - for(; i != m_list.end(); i++){ + for(std::list<BufferedPacket>::iterator i = m_list.begin(); + i != m_list.end(); ++i) + { if(i->totaltime >= timeout) return true; } return false; } -core::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout) +std::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout) { - core::list<BufferedPacket> timed_outs; - core::list<BufferedPacket>::Iterator i; - i = m_list.begin(); - for(; i != m_list.end(); i++) + std::list<BufferedPacket> timed_outs; + for(std::list<BufferedPacket>::iterator i = m_list.begin(); + i != m_list.end(); ++i) { if(i->time >= timeout) timed_outs.push_back(*i); @@ -324,11 +326,10 @@ core::list<BufferedPacket> ReliablePacketBuffer::getTimedOuts(float timeout) IncomingSplitBuffer::~IncomingSplitBuffer() { - core::map<u16, IncomingSplitPacket*>::Iterator i; - i = m_buf.getIterator(); - for(; i.atEnd() == false; i++) + for(std::map<u16, IncomingSplitPacket*>::iterator i = m_buf.begin(); + i != m_buf.end(); ++i) { - delete i.getNode()->getValue(); + delete i->second; } } /* @@ -346,7 +347,7 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable) u16 chunk_num = readU16(&p.data[BASE_HEADER_SIZE+5]); // Add if doesn't exist - if(m_buf.find(seqnum) == NULL) + if(m_buf.find(seqnum) == m_buf.end()) { IncomingSplitPacket *sp = new IncomingSplitPacket(); sp->chunk_count = chunk_count; @@ -369,7 +370,7 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable) // If chunk already exists, ignore it. // Sometimes two identical packets may arrive when there is network // lag and the server re-sends stuff. - if(sp->chunks.find(chunk_num) != NULL) + if(sp->chunks.find(chunk_num) != sp->chunks.end()) return SharedBuffer<u8>(); // Cut chunk data out of packet @@ -386,11 +387,10 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable) // Calculate total size u32 totalsize = 0; - core::map<u16, SharedBuffer<u8> >::Iterator i; - i = sp->chunks.getIterator(); - for(; i.atEnd() == false; i++) + for(std::map<u16, SharedBuffer<u8> >::iterator i = sp->chunks.begin(); + i != sp->chunks.end(); ++i) { - totalsize += i.getNode()->getValue().getSize(); + totalsize += i->second.getSize(); } SharedBuffer<u8> fulldata(totalsize); @@ -407,34 +407,32 @@ SharedBuffer<u8> IncomingSplitBuffer::insert(BufferedPacket &p, bool reliable) } // Remove sp from buffer - m_buf.remove(seqnum); + m_buf.erase(seqnum); delete sp; return fulldata; } void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout) { - core::list<u16> remove_queue; - core::map<u16, IncomingSplitPacket*>::Iterator i; - i = m_buf.getIterator(); - for(; i.atEnd() == false; i++) + std::list<u16> remove_queue; + for(std::map<u16, IncomingSplitPacket*>::iterator i = m_buf.begin(); + i != m_buf.end(); ++i) { - IncomingSplitPacket *p = i.getNode()->getValue(); + IncomingSplitPacket *p = i->second; // Reliable ones are not removed by timeout if(p->reliable == true) continue; p->time += dtime; if(p->time >= timeout) - remove_queue.push_back(i.getNode()->getKey()); + remove_queue.push_back(i->first); } - core::list<u16>::Iterator j; - j = remove_queue.begin(); - for(; j != remove_queue.end(); j++) + for(std::list<u16>::iterator j = remove_queue.begin(); + j != remove_queue.end(); ++j) { dout_con<<"NOTE: Removing timed out unreliable split packet" <<std::endl; delete m_buf[*j]; - m_buf.remove(*j); + m_buf.erase(*j); } } @@ -556,12 +554,11 @@ Connection::~Connection() { stop(); // Delete peers - for(core::map<u16, Peer*>::Iterator - j = m_peers.getIterator(); - j.atEnd() == false; j++) + for(std::map<u16, Peer*>::iterator + j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); - delete peer; + delete j->second; } } @@ -591,7 +588,7 @@ void * Connection::Thread() runTimeouts(dtime); - while(m_command_queue.size() != 0){ + while(!m_command_queue.empty()){ ConnectionCommand c = m_command_queue.pop_front(); processCommand(c); } @@ -648,18 +645,18 @@ void Connection::processCommand(ConnectionCommand &c) void Connection::send(float dtime) { - for(core::map<u16, Peer*>::Iterator - j = m_peers.getIterator(); - j.atEnd() == false; j++) + for(std::map<u16, Peer*>::iterator + j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; peer->m_sendtime_accu += dtime; peer->m_num_sent = 0; peer->m_max_num_sent = peer->m_sendtime_accu * peer->m_max_packets_per_second; } Queue<OutgoingPacket> postponed_packets; - while(m_outgoing_queue.size() != 0){ + while(!m_outgoing_queue.empty()){ OutgoingPacket packet = m_outgoing_queue.pop_front(); Peer *peer = getPeerNoEx(packet.peer_id); if(!peer) @@ -674,14 +671,14 @@ void Connection::send(float dtime) postponed_packets.push_back(packet); } } - while(postponed_packets.size() != 0){ + while(!postponed_packets.empty()){ m_outgoing_queue.push_back(postponed_packets.pop_front()); } - for(core::map<u16, Peer*>::Iterator - j = m_peers.getIterator(); - j.atEnd() == false; j++) + for(std::map<u16, Peer*>::iterator + j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; peer->m_sendtime_accu -= (float)peer->m_num_sent / peer->m_max_packets_per_second; if(peer->m_sendtime_accu > 10. / peer->m_max_packets_per_second) @@ -751,11 +748,11 @@ void Connection::receive() Allow only entries that have has_sent_with_id==false. */ - core::map<u16, Peer*>::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + std::map<u16, Peer*>::iterator j; + j = m_peers.begin(); + for(; j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; if(peer->has_sent_with_id) continue; if(peer->address == sender) @@ -766,14 +763,14 @@ void Connection::receive() If no peer was found with the same address and port, we shall assume it is a new peer and create an entry. */ - if(j.atEnd()) + if(j == m_peers.end()) { // Pass on to adding the peer } // Else: A peer was found. else { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; peer_id = peer->id; PrintInfo(derr_con); derr_con<<"WARNING: Assuming unknown peer to be " @@ -797,7 +794,7 @@ void Connection::receive() for(;;) { // Check if exists - if(m_peers.find(peer_id_new) == NULL) + if(m_peers.find(peer_id_new) == m_peers.end()) break; // Check for overflow if(peer_id_new == 65535){ @@ -817,7 +814,7 @@ void Connection::receive() // Create a peer Peer *peer = new Peer(peer_id_new, sender); - m_peers.insert(peer->id, peer); + m_peers[peer->id] = peer; // Create peer addition event ConnectionEvent e; @@ -837,9 +834,9 @@ void Connection::receive() // Go on and process whatever it sent } - core::map<u16, Peer*>::Node *node = m_peers.find(peer_id); + std::map<u16, Peer*>::iterator node = m_peers.find(peer_id); - if(node == NULL) + if(node == m_peers.end()) { // Peer not found // This means that the peer id of the sender is not PEER_ID_INEXISTENT @@ -849,7 +846,7 @@ void Connection::receive() throw InvalidIncomingDataException("Peer not found (possible timeout)"); } - Peer *peer = node->getValue(); + Peer *peer = node->second; // Validate peer address if(peer->address != sender) @@ -902,12 +899,11 @@ void Connection::runTimeouts(float dtime) float congestion_control_min_rate = g_settings->getFloat("congestion_control_min_rate"); - core::list<u16> timeouted_peers; - core::map<u16, Peer*>::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + std::list<u16> timeouted_peers; + for(std::map<u16, Peer*>::iterator j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; // Update congestion control values peer->congestion_control_aim_rtt = congestion_control_aim_rtt; @@ -934,8 +930,7 @@ void Connection::runTimeouts(float dtime) float resend_timeout = peer->resend_timeout; for(u16 i=0; i<CHANNEL_COUNT; i++) { - core::list<BufferedPacket> timed_outs; - core::list<BufferedPacket>::Iterator j; + std::list<BufferedPacket> timed_outs; Channel *channel = &peer->channels[i]; @@ -966,8 +961,8 @@ void Connection::runTimeouts(float dtime) channel->outgoing_reliables.resetTimedOuts(resend_timeout); - j = timed_outs.begin(); - for(; j != timed_outs.end(); j++) + for(std::list<BufferedPacket>::iterator j = timed_outs.begin(); + j != timed_outs.end(); ++j) { u16 peer_id = readPeerId(*(j->data)); u8 channel = readChannel(*(j->data)); @@ -1012,8 +1007,8 @@ nextpeer: } // Remove timed out peers - core::list<u16>::Iterator i = timeouted_peers.begin(); - for(; i != timeouted_peers.end(); i++) + for(std::list<u16>::iterator i = timeouted_peers.begin(); + i != timeouted_peers.end(); ++i) { PrintInfo(derr_con); derr_con<<"RunTimeouts(): Removing peer "<<(*i)<<std::endl; @@ -1041,13 +1036,13 @@ void Connection::connect(Address address) dout_con<<getDesc()<<" connecting to "<<address.serializeString() <<":"<<address.getPort()<<std::endl; - core::map<u16, Peer*>::Node *node = m_peers.find(PEER_ID_SERVER); - if(node != NULL){ + std::map<u16, Peer*>::iterator node = m_peers.find(PEER_ID_SERVER); + if(node != m_peers.end()){ throw ConnectionException("Already connected to a server"); } Peer *peer = new Peer(PEER_ID_SERVER, address); - m_peers.insert(peer->id, peer); + m_peers[peer->id] = peer; // Create event ConnectionEvent e; @@ -1072,22 +1067,20 @@ void Connection::disconnect() writeU8(&data[1], CONTROLTYPE_DISCO); // Send to all - core::map<u16, Peer*>::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + for(std::map<u16, Peer*>::iterator j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; rawSendAsPacket(peer->id, 0, data, false); } } void Connection::sendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable) { - core::map<u16, Peer*>::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + for(std::map<u16, Peer*>::iterator j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; send(peer->id, channelnum, data, reliable); } } @@ -1108,13 +1101,12 @@ void Connection::send(u16 peer_id, u8 channelnum, if(reliable) chunksize_max -= RELIABLE_HEADER_SIZE; - core::list<SharedBuffer<u8> > originals; + std::list<SharedBuffer<u8> > originals; originals = makeAutoSplitPacket(data, chunksize_max, channel->next_outgoing_split_seqnum); - core::list<SharedBuffer<u8> >::Iterator i; - i = originals.begin(); - for(; i != originals.end(); i++) + for(std::list<SharedBuffer<u8> >::iterator i = originals.begin(); + i != originals.end(); ++i) { SharedBuffer<u8> original = *i; @@ -1187,40 +1179,39 @@ void Connection::rawSend(const BufferedPacket &packet) Peer* Connection::getPeer(u16 peer_id) { - core::map<u16, Peer*>::Node *node = m_peers.find(peer_id); + std::map<u16, Peer*>::iterator node = m_peers.find(peer_id); - if(node == NULL){ + if(node == m_peers.end()){ throw PeerNotFoundException("GetPeer: Peer not found (possible timeout)"); } // Error checking - assert(node->getValue()->id == peer_id); + assert(node->second->id == peer_id); - return node->getValue(); + return node->second; } Peer* Connection::getPeerNoEx(u16 peer_id) { - core::map<u16, Peer*>::Node *node = m_peers.find(peer_id); + std::map<u16, Peer*>::iterator node = m_peers.find(peer_id); - if(node == NULL){ + if(node == m_peers.end()){ return NULL; } // Error checking - assert(node->getValue()->id == peer_id); + assert(node->second->id == peer_id); - return node->getValue(); + return node->second; } -core::list<Peer*> Connection::getPeers() +std::list<Peer*> Connection::getPeers() { - core::list<Peer*> list; - core::map<u16, Peer*>::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + std::list<Peer*> list; + for(std::map<u16, Peer*>::iterator j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; list.push_back(peer); } return list; @@ -1228,11 +1219,10 @@ core::list<Peer*> Connection::getPeers() bool Connection::getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst) { - core::map<u16, Peer*>::Iterator j; - j = m_peers.getIterator(); - for(; j.atEnd() == false; j++) + for(std::map<u16, Peer*>::iterator j = m_peers.begin(); + j != m_peers.end(); ++j) { - Peer *peer = j.getNode()->getValue(); + Peer *peer = j->second; for(u16 i=0; i<CHANNEL_COUNT; i++) { Channel *channel = &peer->channels[i]; @@ -1538,7 +1528,7 @@ SharedBuffer<u8> Connection::processPacket(Channel *channel, bool Connection::deletePeer(u16 peer_id, bool timeout) { - if(m_peers.find(peer_id) == NULL) + if(m_peers.find(peer_id) == m_peers.end()) return false; Peer *peer = m_peers[peer_id]; @@ -1549,7 +1539,7 @@ bool Connection::deletePeer(u16 peer_id, bool timeout) putEvent(e); delete m_peers[peer_id]; - m_peers.remove(peer_id); + m_peers.erase(peer_id); return true; } @@ -1557,7 +1547,7 @@ bool Connection::deletePeer(u16 peer_id, bool timeout) ConnectionEvent Connection::getEvent() { - if(m_event_queue.size() == 0){ + if(m_event_queue.empty()){ ConnectionEvent e; e.type = CONNEVENT_NONE; return e; @@ -1602,8 +1592,8 @@ bool Connection::Connected() if(m_peers.size() != 1) return false; - core::map<u16, Peer*>::Node *node = m_peers.find(PEER_ID_SERVER); - if(node == NULL) + std::map<u16, Peer*>::iterator node = m_peers.find(PEER_ID_SERVER); + if(node == m_peers.end()) return false; if(m_peer_id == PEER_ID_INEXISTENT) diff --git a/src/connection.h b/src/connection.h index 05b1ca2e8..486cf331f 100644 --- a/src/connection.h +++ b/src/connection.h @@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/thread.h" #include <iostream> #include <fstream> +#include <list> +#include <map> namespace con { @@ -142,14 +144,14 @@ SharedBuffer<u8> makeOriginalPacket( SharedBuffer<u8> data); // Split data in chunks and add TYPE_SPLIT headers to them -core::list<SharedBuffer<u8> > makeSplitPacket( +std::list<SharedBuffer<u8> > makeSplitPacket( SharedBuffer<u8> data, u32 chunksize_max, u16 seqnum); // Depending on size, make a TYPE_ORIGINAL or TYPE_SPLIT packet // Increments split_seqnum if a split packet is made -core::list<SharedBuffer<u8> > makeAutoSplitPacket( +std::list<SharedBuffer<u8> > makeAutoSplitPacket( SharedBuffer<u8> data, u32 chunksize_max, u16 &split_seqnum); @@ -167,7 +169,7 @@ struct IncomingSplitPacket reliable = false; } // Key is chunk number, value is data without headers - core::map<u16, SharedBuffer<u8> > chunks; + std::map<u16, SharedBuffer<u8> > chunks; u32 chunk_count; float time; // Seconds from adding bool reliable; // If true, isn't deleted on timeout @@ -268,12 +270,12 @@ with a buffer in the receiving and transmitting end. for fast access to the smallest one. */ -typedef core::list<BufferedPacket>::Iterator RPBSearchResult; +typedef std::list<BufferedPacket>::iterator RPBSearchResult; class ReliablePacketBuffer { public: - + ReliablePacketBuffer(); void print(); bool empty(); u32 size(); @@ -286,10 +288,11 @@ public: void incrementTimeouts(float dtime); void resetTimedOuts(float timeout); bool anyTotaltimeReached(float timeout); - core::list<BufferedPacket> getTimedOuts(float timeout); + std::list<BufferedPacket> getTimedOuts(float timeout); private: - core::list<BufferedPacket> m_list; + std::list<BufferedPacket> m_list; + u16 m_list_size; }; /* @@ -310,7 +313,7 @@ public: private: // Key is seqnum - core::map<u16, IncomingSplitPacket*> m_buf; + std::map<u16, IncomingSplitPacket*> m_buf; }; class Connection; @@ -589,7 +592,7 @@ private: void rawSend(const BufferedPacket &packet); Peer* getPeer(u16 peer_id); Peer* getPeerNoEx(u16 peer_id); - core::list<Peer*> getPeers(); + std::list<Peer*> getPeers(); bool getFromBuffers(u16 &peer_id, SharedBuffer<u8> &dst); // Returns next data from a buffer if possible // If found, returns true; if not, false. @@ -619,7 +622,7 @@ private: UDPSocket m_socket; u16 m_peer_id; - core::map<u16, Peer*> m_peers; + std::map<u16, Peer*> m_peers; JMutex m_peers_mutex; // Backwards compatibility diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 03fc82ed4..ccd9ca19c 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -94,11 +94,22 @@ public: 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"); + } + virtual std::set<std::string> getTriggerContents() { std::set<std::string> s; s.insert("sapling"); + s.insert("junglesapling"); return s; } virtual float getTriggerInterval() @@ -111,37 +122,46 @@ public: INodeDefManager *ndef = env->getGameDef()->ndef(); ServerMap *map = &env->getServerMap(); - actionstream<<"A sapling grows into a tree at " - <<PP(p)<<std::endl; + MapNode n_below = map->getNodeNoEx(p - v3s16(0, 1, 0)); + if (n_below.getContent() != c_dirt && + n_below.getContent() != c_dirt_with_grass) + return; + + bool is_jungle_tree = n.getContent() == c_junglesapling; + + actionstream <<"A " << (is_jungle_tree ? "jungle " : "") + << "sapling grows into a tree at " + << PP(p) << std::endl; - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; v3s16 tree_p = p; ManualMapVoxelManipulator vmanip(map); v3s16 tree_blockp = getNodeBlockPos(tree_p); vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,1,1)); - bool is_apple_tree = myrand()%4 == 0; - treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand()); + + if (is_jungle_tree) { + treegen::make_jungletree(vmanip, tree_p, ndef, myrand()); + } else { + bool is_apple_tree = myrand() % 4 == 0; + treegen::make_tree(vmanip, tree_p, is_apple_tree, ndef, myrand()); + } + vmanip.blitBackAll(&modified_blocks); // update lighting - core::map<v3s16, MapBlock*> lighting_modified_blocks; - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) - { - lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue()); - } + std::map<v3s16, MapBlock*> lighting_modified_blocks; + lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); map->updateLighting(lighting_modified_blocks, modified_blocks); // Send a MEET_OTHER event MapEditEvent event; event.type = MEET_OTHER; - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) +// event.modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - event.modified_blocks.insert(p, true); + event.modified_blocks.insert(i->first); } map->dispatchEvent(&event); } @@ -182,7 +202,7 @@ void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { env->addActiveBlockModifier(new GrowGrassABM()); env->addActiveBlockModifier(new RemoveGrassABM()); - env->addActiveBlockModifier(new MakeTreesFromSaplingsABM()); + env->addActiveBlockModifier(new MakeTreesFromSaplingsABM(env, nodedef)); if (g_settings->getBool("liquid_finite")) env->addActiveBlockModifier(new LiquidFlowABM(env, nodedef)); } diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 5e5cb38ae..ee1009b6c 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -50,7 +50,7 @@ struct ToolCapabilities; #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -core::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types; +std::map<u16, ClientActiveObject::Factory> ClientActiveObject::m_types; /* SmoothTranslator @@ -174,6 +174,7 @@ public: void processMessage(const std::string &data); + bool getCollisionBox(aabb3f *toset) { return false; } private: scene::IMeshSceneNode *m_node; v3f m_position; @@ -329,6 +330,7 @@ public: std::string infoText() {return m_infotext;} + bool getCollisionBox(aabb3f *toset) { return false; } private: core::aabbox3d<f32> m_selection_box; scene::IMeshSceneNode *m_node; @@ -643,6 +645,22 @@ public: ClientActiveObject::registerType(getType(), create); } + bool getCollisionBox(aabb3f *toset) { + if (m_prop.physical) { + aabb3f retval; + //update collision box + toset->MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; + + toset->MinEdge += m_position; + toset->MaxEdge += m_position; + + return true; + } + + return false; + } + void initialize(const std::string &data) { infostream<<"GenericCAO: Got init data"<<std::endl; @@ -1127,8 +1145,7 @@ public: v3f p_pos = m_position; v3f p_velocity = m_velocity; v3f p_acceleration = m_acceleration; - IGameDef *gamedef = env->getGameDef(); - moveresult = collisionMoveSimple(&env->getMap(), gamedef, + moveresult = collisionMoveSimple(env,env->getGameDef(), pos_max_d, box, stepheight, dtime, p_pos, p_velocity, p_acceleration); // Apply results diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 0d80dc173..84d5f067c 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -42,14 +42,16 @@ with this program; if not, write to the Free Software Foundation, Inc., // (compatible with ContentFeatures). If you specified 0,0,1,1 // for each face, that would be the same as passing NULL. void makeCuboid(MeshCollector *collector, const aabb3f &box, - const TileSpec *tiles, int tilecount, + TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc) { assert(tilecount >= 1 && tilecount <= 6); v3f min = box.MinEdge; v3f max = box.MaxEdge; - + + + if(txc == NULL) { static const f32 txc_default[24] = { @@ -97,15 +99,70 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]), }; - for(s32 j=0; j<24; j++) + v2f t; + for(int i = 0; i < tilecount; i++) + { + switch (tiles[i].rotation) + { + case 0: + break; + case 1: //R90 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + break; + case 2: //R180 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0)); + break; + case 3: //R270 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + break; + case 4: //FXR90 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + + tiles[i].texture.pos.Y += tiles[i].texture.size.Y; + tiles[i].texture.size.Y *= -1; + break; + case 5: //FXR270 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + t=vertices[i*4].TCoords; + tiles[i].texture.pos.Y += tiles[i].texture.size.Y; + tiles[i].texture.size.Y *= -1; + break; + case 6: //FYR90 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + tiles[i].texture.pos.X += tiles[i].texture.size.X; + tiles[i].texture.size.X *= -1; + break; + case 7: //FYR270 + for (int x = 0; x < 4; x++) + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + tiles[i].texture.pos.X += tiles[i].texture.size.X; + tiles[i].texture.size.X *= -1; + break; + case 8: //FX + tiles[i].texture.pos.Y += tiles[i].texture.size.Y; + tiles[i].texture.size.Y *= -1; + break; + case 9: //FY + tiles[i].texture.pos.X += tiles[i].texture.size.X; + tiles[i].texture.size.X *= -1; + break; + default: + break; + } + } + for(s32 j=0; j<24; j++) { int tileindex = MYMIN(j/4, tilecount-1); vertices[j].TCoords *= tiles[tileindex].texture.size; vertices[j].TCoords += tiles[tileindex].texture.pos; } - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector for(s32 j=0; j<24; j+=4) { @@ -160,18 +217,145 @@ void mapblock_mesh_generate_special(MeshMakeData *data, Add water sources to mesh if using new style */ TileSpec tile_liquid = f.special_tiles[0]; + TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data); AtlasPointer &pa_liquid = tile_liquid.texture; - bool top_is_air = false; - MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); - if(n.getContent() == CONTENT_AIR) - top_is_air = true; - - if(top_is_air == false) - continue; + bool top_is_same_liquid = false; + MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); + content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing); + content_t c_source = nodedef->getId(f.liquid_alternative_source); + if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) + top_is_same_liquid = true; u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(f.alpha, l, decode_light(f.light_source)); + + /* + Generate sides + */ + v3s16 side_dirs[4] = { + v3s16(1,0,0), + v3s16(-1,0,0), + v3s16(0,0,1), + v3s16(0,0,-1), + }; + for(u32 i=0; i<4; i++) + { + v3s16 dir = side_dirs[i]; + + MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir); + content_t neighbor_content = neighbor.getContent(); + const ContentFeatures &n_feat = nodedef->get(neighbor_content); + MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0)); + content_t n_top_c = n_top.getContent(); + + if(neighbor_content == CONTENT_IGNORE) + continue; + + /* + If our topside is liquid and neighbor's topside + is liquid, don't draw side face + */ + if(top_is_same_liquid && (n_top_c == c_flowing || + n_top_c == c_source || n_top_c == CONTENT_IGNORE)) + continue; + + // Don't draw face if neighbor is blocking the view + if(n_feat.solidness == 2) + continue; + + bool neighbor_is_same_liquid = (neighbor_content == c_source + || neighbor_content == c_flowing); + + // Don't draw any faces if neighbor same is liquid and top is + // same liquid + if(neighbor_is_same_liquid && !top_is_same_liquid) + continue; + + // Use backface culled material if neighbor doesn't have a + // solidness of 0 + const TileSpec *current_tile = &tile_liquid; + if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) + current_tile = &tile_liquid_bfculled; + + video::S3DVertex vertices[4] = + { + video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, + pa_liquid.x0(), pa_liquid.y1()), + video::S3DVertex(BS/2,0,BS/2,0,0,0, c, + pa_liquid.x1(), pa_liquid.y1()), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, + pa_liquid.x1(), pa_liquid.y0()), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, + pa_liquid.x0(), pa_liquid.y0()), + }; + + /* + If our topside is liquid, set upper border of face + at upper border of node + */ + if(top_is_same_liquid) + { + vertices[2].Pos.Y = 0.5*BS; + vertices[3].Pos.Y = 0.5*BS; + } + /* + Otherwise upper position of face is liquid level + */ + else + { + vertices[2].Pos.Y = (node_liquid_level-0.5)*BS; + vertices[3].Pos.Y = (node_liquid_level-0.5)*BS; + } + /* + If neighbor is liquid, lower border of face is liquid level + */ + if(neighbor_is_same_liquid) + { + vertices[0].Pos.Y = (node_liquid_level-0.5)*BS; + vertices[1].Pos.Y = (node_liquid_level-0.5)*BS; + } + /* + If neighbor is not liquid, lower border of face is + lower border of node + */ + else + { + vertices[0].Pos.Y = -0.5*BS; + vertices[1].Pos.Y = -0.5*BS; + } + + for(s32 j=0; j<4; j++) + { + if(dir == v3s16(0,0,1)) + vertices[j].Pos.rotateXZBy(0); + if(dir == v3s16(0,0,-1)) + vertices[j].Pos.rotateXZBy(180); + if(dir == v3s16(-1,0,0)) + vertices[j].Pos.rotateXZBy(90); + if(dir == v3s16(1,0,-0)) + vertices[j].Pos.rotateXZBy(-90); + + // Do this to not cause glitches when two liquids are + // side-by-side + /*if(neighbor_is_same_liquid == false){ + vertices[j].Pos.X *= 0.98; + vertices[j].Pos.Z *= 0.98; + }*/ + + vertices[j].Pos += intToFloat(p, BS); + } + + u16 indices[] = {0,1,2,2,3,0}; + // Add to mesh collector + collector.append(*current_tile, vertices, 4, indices, 6); + } + + /* + Generate top + */ + if(top_is_same_liquid) + continue; video::S3DVertex vertices[4] = { @@ -185,7 +369,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, pa_liquid.x0(), pa_liquid.y0()), }; - v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z); + v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS); for(s32 i=0; i<4; i++) { vertices[i].Pos += offset; @@ -230,9 +414,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Neighbor liquid levels (key = relative position) // Includes current node - core::map<v3s16, f32> neighbor_levels; - core::map<v3s16, content_t> neighbor_contents; - core::map<v3s16, u8> neighbor_flags; + std::map<v3s16, f32> neighbor_levels; + std::map<v3s16, content_t> neighbor_contents; + std::map<v3s16, u8> neighbor_flags; const u8 neighborflag_top_is_same_liquid = 0x01; v3s16 neighbor_dirs[9] = { v3s16(0,0,0), @@ -273,9 +457,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data, flags |= neighborflag_top_is_same_liquid; } - neighbor_levels.insert(neighbor_dirs[i], level); - neighbor_contents.insert(neighbor_dirs[i], content); - neighbor_flags.insert(neighbor_dirs[i], flags); + neighbor_levels[neighbor_dirs[i]] = level; + neighbor_contents[neighbor_dirs[i]] = content; + neighbor_flags[neighbor_dirs[i]] = flags; } // Corner heights (average between four liquids) @@ -324,7 +508,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } } if(air_count >= 2) - cornerlevel = -0.5*BS+0.1; + cornerlevel = -0.5*BS+0.2; else if(valid_count > 0) cornerlevel /= valid_count; corner_levels[i] = cornerlevel; @@ -1027,14 +1211,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16(0, 0, 1), v3s16(0, 0, -1) }; - TileSpec tiles[6]; - for(int i = 0; i < 6; i++) - { - // Handles facedir rotation for textures - tiles[i] = getNodeTile(n, p, tile_dirs[i], data); - } - + u16 l = getInteriorLight(n, 0, data); video::SColor c = MapBlock_LightColor(255, l, decode_light(f.light_source)); @@ -1045,17 +1223,43 @@ void mapblock_mesh_generate_special(MeshMakeData *data, i = boxes.begin(); i != boxes.end(); i++) { + for(int j = 0; j < 6; j++) + { + // Handles facedir rotation for textures + tiles[j] = getNodeTile(n, p, tile_dirs[j], data); + } aabb3f box = *i; box.MinEdge += pos; box.MaxEdge += pos; + + f32 temp; + if (box.MinEdge.X > box.MaxEdge.X) + { + temp=box.MinEdge.X; + box.MinEdge.X=box.MaxEdge.X; + box.MaxEdge.X=temp; + } + if (box.MinEdge.Y > box.MaxEdge.Y) + { + temp=box.MinEdge.Y; + box.MinEdge.Y=box.MaxEdge.Y; + box.MaxEdge.Y=temp; + } + if (box.MinEdge.Z > box.MaxEdge.Z) + { + temp=box.MinEdge.Z; + box.MinEdge.Z=box.MaxEdge.Z; + box.MaxEdge.Z=temp; + } + // // Compute texture coords - f32 tx1 = (i->MinEdge.X/BS)+0.5; - f32 ty1 = (i->MinEdge.Y/BS)+0.5; - f32 tz1 = (i->MinEdge.Z/BS)+0.5; - f32 tx2 = (i->MaxEdge.X/BS)+0.5; - f32 ty2 = (i->MaxEdge.Y/BS)+0.5; - f32 tz2 = (i->MaxEdge.Z/BS)+0.5; + f32 tx1 = (box.MinEdge.X/BS)+0.5; + f32 ty1 = (box.MinEdge.Y/BS)+0.5; + f32 tz1 = (box.MinEdge.Z/BS)+0.5; + f32 tx2 = (box.MaxEdge.X/BS)+0.5; + f32 ty2 = (box.MaxEdge.Y/BS)+0.5; + f32 tz2 = (box.MaxEdge.Z/BS)+0.5; f32 txc[24] = { // up tx1, 1-tz2, tx2, 1-tz1, @@ -1070,7 +1274,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // front tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, tiles, 6, c, txc); } break;} diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 718a42dff..ae08b4260 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "genericobject.h" #include "util/serialize.h" -core::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types; +std::map<u16, ServerActiveObject::Factory> ServerActiveObject::m_types; /* DummyLoadSAO @@ -64,6 +64,10 @@ public: infostream<<"DummyLoadSAO step"<<std::endl; } + bool getCollisionBox(aabb3f *toset) { + return false; + } + private: }; @@ -132,6 +136,10 @@ public: } } + bool getCollisionBox(aabb3f *toset) { + return false; + } + private: float m_timer1; float m_age; @@ -208,8 +216,7 @@ public: v3f pos_f_old = pos_f; v3f accel_f = v3f(0,0,0); f32 stepheight = 0; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, + moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), pos_max_d, box, stepheight, dtime, pos_f, m_speed_f, accel_f); @@ -314,6 +321,10 @@ public: return 0; } + bool getCollisionBox(aabb3f *toset) { + return false; + } + private: std::string m_itemstring; @@ -370,8 +381,7 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, } // Initialize something to armor groups - m_armor_groups["fleshy"] = 3; - m_armor_groups["snappy"] = 2; + m_armor_groups["fleshy"] = 100; } LuaEntitySAO::~LuaEntitySAO() @@ -490,8 +500,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended) v3f p_pos = m_base_position; v3f p_velocity = m_velocity; v3f p_acceleration = m_acceleration; - IGameDef *gamedef = m_env->getGameDef(); - moveresult = collisionMoveSimple(&m_env->getMap(), gamedef, + moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), pos_max_d, box, stepheight, dtime, p_pos, p_velocity, p_acceleration); // Apply results @@ -880,6 +889,22 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) m_messages_out.push_back(aom); } +bool LuaEntitySAO::getCollisionBox(aabb3f *toset) { + if (m_prop.physical) + { + //update collision box + toset->MinEdge = m_prop.collisionbox.MinEdge * BS; + toset->MaxEdge = m_prop.collisionbox.MaxEdge * BS; + + toset->MinEdge += m_base_position; + toset->MaxEdge += m_base_position; + + return true; + } + + return false; +} + /* PlayerSAO */ @@ -916,8 +941,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_, assert(m_peer_id != 0); setBasePosition(m_player->getPosition()); m_inventory = &m_player->inventory; - m_armor_groups["choppy"] = 2; - m_armor_groups["fleshy"] = 3; + m_armor_groups["fleshy"] = 100; m_prop.hp_max = PLAYER_MAX_HP; m_prop.physical = false; @@ -1230,6 +1254,20 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) m_moved = true; } +void PlayerSAO::setYaw(float yaw) +{ + m_player->setYaw(yaw); + // Force change on client + m_moved = true; +} + +void PlayerSAO::setPitch(float pitch) +{ + m_player->setPitch(pitch); + // Force change on client + m_moved = true; +} + int PlayerSAO::punch(v3f dir, const ToolCapabilities *toolcap, ServerActiveObject *puncher, @@ -1420,3 +1458,7 @@ std::string PlayerSAO::getPropertyPacket() return gob_cmd_set_properties(m_prop); } +bool PlayerSAO::getCollisionBox(aabb3f *toset) { + //player collision handling is already done clientside no need to do it twice + return false; +} diff --git a/src/content_sao.h b/src/content_sao.h index 2fd1034eb..60ca8f319 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -78,6 +78,7 @@ public: void setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch); std::string getName(); + bool getCollisionBox(aabb3f *toset); private: std::string getPropertyPacket(); void sendPosition(bool do_interpolate, bool is_movement_end); @@ -147,6 +148,8 @@ public: void setBasePosition(const v3f &position); void setPos(v3f pos); void moveTo(v3f pos, bool continuous); + void setYaw(float); + void setPitch(float); /* Interaction interface @@ -233,6 +236,8 @@ public: m_is_singleplayer = is_singleplayer; } + bool getCollisionBox(aabb3f *toset); + private: std::string getPropertyPacket(); diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 99e3fcc3d..c79408f99 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -987,6 +987,43 @@ public: } return false; } + virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output, + IGameDef *gamedef) const + { + std::vector<CraftDefinition*> recipes_list; + CraftInput input; + CraftOutput tmpout; + tmpout.item = ""; + tmpout.time = 0; + + for(std::vector<CraftDefinition*>::const_reverse_iterator + i = m_craft_definitions.rbegin(); + i != m_craft_definitions.rend(); i++) + { + CraftDefinition *def = *i; + + /*infostream<<"Checking "<<input.dump()<<std::endl + <<" against "<<def->dump()<<std::endl;*/ + + try { + tmpout = def->getOutput(input, gamedef); + if(tmpout.item.substr(0,output.item.length()) == output.item) + { + // Get output, then decrement input (if requested) + input = def->getInput(output, gamedef); + recipes_list.push_back(*i); + } + } + catch(SerializationError &e) + { + errorstream<<"getCraftResult: ERROR: " + <<"Serialization error in recipe " + <<def->dump()<<std::endl; + // then go on with the next craft definition + } + } + return recipes_list; + } virtual std::string dump() const { std::ostringstream os(std::ios::binary); diff --git a/src/craftdef.h b/src/craftdef.h index eb3cd7e39..14dc53003 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -358,6 +358,8 @@ public: bool decrementInput, IGameDef *gamedef) const=0; virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output, IGameDef *gamedef) const=0; + virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output, + IGameDef *gamedef) const=0; // Print crafting recipes for debugging virtual std::string dump() const=0; @@ -376,6 +378,8 @@ public: bool decrementInput, IGameDef *gamedef) const=0; virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output, IGameDef *gamedef) const=0; + virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output, + IGameDef *gamedef) const=0; // Print crafting recipes for debugging virtual std::string dump() const=0; diff --git a/src/debug.cpp b/src/debug.cpp index e32cceb86..2e4992a78 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -130,7 +130,7 @@ void DebugStack::print(std::ostream &os, bool everything) os<<"Probably overflown."<<std::endl; } -core::map<threadid_t, DebugStack*> g_debug_stacks; +std::map<threadid_t, DebugStack*> g_debug_stacks; JMutex g_debug_stacks_mutex; void debug_stacks_init() @@ -144,12 +144,11 @@ void debug_stacks_print_to(std::ostream &os) os<<"Debug stacks:"<<std::endl; - for(core::map<threadid_t, DebugStack*>::Iterator - i = g_debug_stacks.getIterator(); - i.atEnd() == false; i++) + for(std::map<threadid_t, DebugStack*>::iterator + i = g_debug_stacks.begin(); + i != g_debug_stacks.end(); ++i) { - DebugStack *stack = i.getNode()->getValue(); - stack->print(os, false); + i->second->print(os, false); } } @@ -159,11 +158,11 @@ void debug_stacks_print() DEBUGPRINT("Debug stacks:\n"); - for(core::map<threadid_t, DebugStack*>::Iterator - i = g_debug_stacks.getIterator(); - i.atEnd() == false; i++) + for(std::map<threadid_t, DebugStack*>::iterator + i = g_debug_stacks.begin(); + i != g_debug_stacks.end(); ++i) { - DebugStack *stack = i.getNode()->getValue(); + DebugStack *stack = i->second; for(int i=0; i<DEBUGSTREAM_COUNT; i++) { @@ -179,18 +178,18 @@ DebugStacker::DebugStacker(const char *text) JMutexAutoLock lock(g_debug_stacks_mutex); - core::map<threadid_t, DebugStack*>::Node *n; + std::map<threadid_t, DebugStack*>::iterator n; n = g_debug_stacks.find(threadid); - if(n != NULL) + if(n != g_debug_stacks.end()) { - m_stack = n->getValue(); + m_stack = n->second; } else { /*DEBUGPRINT("Creating new debug stack for thread %x\n", (unsigned int)threadid);*/ m_stack = new DebugStack(threadid); - g_debug_stacks.insert(threadid, m_stack); + g_debug_stacks[threadid] = m_stack; } if(m_stack->stack_i >= DEBUG_STACK_SIZE) @@ -224,7 +223,7 @@ DebugStacker::~DebugStacker() /*DEBUGPRINT("Deleting debug stack for thread %x\n", (unsigned int)threadid);*/ delete m_stack; - g_debug_stacks.remove(threadid); + g_debug_stacks.erase(threadid); } } diff --git a/src/debug.h b/src/debug.h index 56952427c..1b14c4e0a 100644 --- a/src/debug.h +++ b/src/debug.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threads.h" #include "gettime.h" #include "exceptions.h" +#include <map> #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN @@ -165,7 +166,7 @@ struct DebugStack int stack_max_i; // Highest i that was seen }; -extern core::map<threadid_t, DebugStack*> g_debug_stacks; +extern std::map<threadid_t, DebugStack*> g_debug_stacks; extern JMutex g_debug_stacks_mutex; extern void debug_stacks_init(); @@ -205,42 +206,42 @@ public: void add(u16 command) { - core::map<u16, u16>::Node *n = m_packets.find(command); - if(n == NULL) + std::map<u16, u16>::iterator n = m_packets.find(command); + if(n == m_packets.end()) { m_packets[command] = 1; } else { - n->setValue(n->getValue()+1); + n->second++; } } void clear() { - for(core::map<u16, u16>::Iterator - i = m_packets.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, u16>::iterator + i = m_packets.begin(); + i != m_packets.end(); ++i) { - i.getNode()->setValue(0); + i->second = 0; } } void print(std::ostream &o) { - for(core::map<u16, u16>::Iterator - i = m_packets.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, u16>::iterator + i = m_packets.begin(); + i != m_packets.end(); ++i) { - o<<"cmd "<<i.getNode()->getKey() - <<" count "<<i.getNode()->getValue() + o<<"cmd "<<i->first + <<" count "<<i->second <<std::endl; } } private: // command, count - core::map<u16, u16> m_packets; + std::map<u16, u16> m_packets; }; /* diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index a8954be72..592c6bcca 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -109,6 +109,7 @@ void set_default_settings(Settings *settings) settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("enable_3d_clouds", "true"); settings->setDefault("cloud_height", "120"); + settings->setDefault("menu_clouds", "true"); settings->setDefault("opaque_water", "false"); settings->setDefault("console_color", "(0,0,0)"); settings->setDefault("console_alpha", "200"); @@ -202,34 +203,57 @@ void set_default_settings(Settings *settings) settings->setDefault("movement_liquid_fluidity_smooth", "0.5"); settings->setDefault("movement_liquid_sink", "10"); settings->setDefault("movement_gravity", "9.81"); - + //liquid stuff settings->setDefault("liquid_finite", "false"); settings->setDefault("liquid_update", "1.0"); - settings->setDefault("liquid_relax", "1"); + settings->setDefault("liquid_relax", "2"); settings->setDefault("liquid_fast_flood", "1"); + settings->setDefault("underground_springs", "1"); //mapgen stuff settings->setDefault("mg_name", "v6"); - settings->setDefault("water_level", "1"); + settings->setDefault("water_level", "1"); settings->setDefault("chunksize", "5"); settings->setDefault("mg_flags", "trees, caves, v6_biome_blend"); settings->setDefault("mgv6_freq_desert", "0.45"); settings->setDefault("mgv6_freq_beach", "0.15"); - settings->setDefault("mgv6_np_terrain_base", "-4, 20, (250.0, 250, 250), 82341, 5, 0.6"); + settings->setDefault("mgv6_np_terrain_base", "-4, 20, (250, 250, 250), 82341, 5, 0.6"); settings->setDefault("mgv6_np_terrain_higher", "20, 16, (500, 500, 500), 85039, 5, 0.6"); settings->setDefault("mgv6_np_steepness", "0.85, 0.5, (125, 125, 125), -932, 5, 0.7"); settings->setDefault("mgv6_np_height_select", "0.5, 1, (250, 250, 250), 4213, 5, 0.69"); - settings->setDefault("mgv6_np_trees", "0, 1, (125, 125, 125), 2, 4, 0.66"); settings->setDefault("mgv6_np_mud", "4, 2, (200, 200, 200), 91013, 3, 0.55"); settings->setDefault("mgv6_np_beach", "0, 1, (250, 250, 250), 59420, 3, 0.50"); settings->setDefault("mgv6_np_biome", "0, 1, (250, 250, 250), 9130, 3, 0.50"); settings->setDefault("mgv6_np_cave", "6, 6, (250, 250, 250), 34329, 3, 0.50"); + settings->setDefault("mgv6_np_humidity", "0.5, 0.5, (500, 500, 500), 72384, 4, 0.66"); + settings->setDefault("mgv6_np_trees", "0, 1, (125, 125, 125), 2, 4, 0.66"); + settings->setDefault("mgv6_np_apple_trees", "0, 1, (100, 100, 100), 342902, 3, 0.45"); settings->setDefault("mgv7_np_terrain", "10, 12, (350, 350, 350), 82341, 5, 0.6"); settings->setDefault("mgv7_np_bgroup", "0.5, 0.3125, (350, 350, 350), 5923, 2, 0.6"); settings->setDefault("mgv7_np_heat", "25, 50, (500, 500, 500), 35293, 1, 0"); settings->setDefault("mgv7_np_humidity", "50, 31.25, (750, 750, 750), 12094, 2, 0.6"); + + settings->setDefault("mgindev_np_terrain_base", "-4, 20, (250, 250, 250), 82341, 5, 0.6, 10, 10"); + settings->setDefault("mgindev_np_terrain_higher", "20, 16, (500, 500, 500), 85039, 5, 0.6, 10, 10"); + settings->setDefault("mgindev_np_steepness", "0.85, 0.5, (125, 125, 125), -932, 5, 0.7, 2, 10"); + settings->setDefault("mgindev_np_mud", "4, 2, (200, 200, 200), 91013, 3, 0.55, 1, 1"); + settings->setDefault("mgindev_np_float_islands1", "0, 1, (64, 64, 64 ), 3683, 5, 0.5, 1, 1.5"); + settings->setDefault("mgindev_np_float_islands2", "0, 1, (8, 8, 8 ), 9292, 2, 0.5, 1, 1.5"); + settings->setDefault("mgindev_np_float_islands3", "0, 1, (256, 256, 256), 6412, 2, 0.5, 1, 0.5"); + settings->setDefault("mgindev_np_biome", "0, 1, (250, 250, 250), 9130, 3, 0.50, 1, 10"); + settings->setDefault("mgindev_float_islands", "500"); + +} + +void override_default_settings(Settings *settings, Settings *from) +{ + std::vector<std::string> names = from->getNames(); + for(size_t i=0; i<names.size(); i++){ + const std::string &name = names[i]; + settings->setDefault(name, from->get(name)); + } } diff --git a/src/defaultsettings.h b/src/defaultsettings.h index 37e3f717f..00aacad87 100644 --- a/src/defaultsettings.h +++ b/src/defaultsettings.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Settings; void set_default_settings(Settings *settings); +void override_default_settings(Settings *settings, Settings *from); #endif diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp new file mode 100644 index 000000000..9528b4132 --- /dev/null +++ b/src/dungeongen.cpp @@ -0,0 +1,634 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "dungeongen.h" +#include "mapgen.h" +#include "voxel.h" +#include "noise.h" +#include "mapblock.h" +#include "mapnode.h" +#include "map.h" +#include "nodedef.h" +#include "profiler.h" +#include "settings.h" // For g_settings +#include "main.h" // For g_profiler + +NoiseParams nparams_dungeon_rarity = + {0.0, 1.0, v3f(500.0, 500.0, 500.0), 0, 2, 0.8}; +NoiseParams nparams_dungeon_wetness = + {0.0, 1.0, v3f(40.0, 40.0, 40.0), 32474, 4, 1.1}; +NoiseParams nparams_dungeon_density = + {0.0, 1.0, v3f(2.5, 2.5, 2.5), 0, 2, 1.4}; + + +/////////////////////////////////////////////////////////////////////////////// + + +DungeonGen::DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel) { + this->ndef = ndef; + this->mapseed = seed; + this->water_level = waterlevel; + + np_rarity = &nparams_dungeon_rarity; + np_wetness = &nparams_dungeon_wetness; + np_density = &nparams_dungeon_density; + /* + cid_water_source = ndef->getId("mapgen_water_source"); + cid_cobble = ndef->getId("mapgen_cobble"); + cid_mossycobble = ndef->getId("mapgen_mossycobble"); + cid_torch = ndef->getId("default:torch"); + */ +} + + +void DungeonGen::generate(ManualMapVoxelManipulator *vm, u32 bseed, + v3s16 nmin, v3s16 nmax) { + //TimeTaker t("gen dungeons"); + int approx_groundlevel = 10 + water_level; + + if ((nmin.Y + nmax.Y) / 2 >= approx_groundlevel || + NoisePerlin3D(np_rarity, nmin.X, nmin.Y, nmin.Z, mapseed) < 0.2) + return; + + this->vmanip = vm; + this->blockseed = bseed; + random.seed(bseed + 2); + + cid_water_source = ndef->getId("mapgen_water_source"); + cid_cobble = ndef->getId("mapgen_cobble"); + cid_mossycobble = ndef->getId("mapgen_mossycobble"); + //cid_torch = ndef->getId("default:torch"); + cid_cobblestair = ndef->getId("stairs:stair_cobble"); + + // Dungeon generator doesn't modify places which have this set + vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE); + + // Set all air and water to be untouchable to make dungeons open + // to caves and open air + for (s16 z = nmin.Z; z <= nmax.Z; z++) { + for (s16 y = nmin.Y; y <= nmax.Y; y++) { + u32 i = vmanip->m_area.index(nmin.X, y, z); + for (s16 x = nmin.X; x <= nmax.X; x++) { + content_t c = vmanip->m_data[i].getContent(); + if (c == CONTENT_AIR || c == cid_water_source) + vmanip->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; + i++; + } + } + } + + // Add it + makeDungeon(v3s16(1,1,1) * MAP_BLOCKSIZE); + + // Convert some cobble to mossy cobble + for (s16 z = nmin.Z; z <= nmax.Z; z++) { + for (s16 y = nmin.Y; y <= nmax.Y; y++) { + u32 i = vmanip->m_area.index(nmin.X, y, z); + for (s16 x = nmin.X; x <= nmax.X; x++) { + if (vmanip->m_data[i].getContent() == cid_cobble) { + float wetness = NoisePerlin3D(np_wetness, x, y, z, mapseed); + float density = NoisePerlin3D(np_density, x, y, z, blockseed); + if (density < wetness / 3.0) + vmanip->m_data[i].setContent(cid_mossycobble); + } + i++; + } + } + } + + //printf("== gen dungeons: %dms\n", t.stop()); +} + + +void DungeonGen::makeDungeon(v3s16 start_padding) +{ + v3s16 areasize = vmanip->m_area.getExtent(); + v3s16 roomsize; + v3s16 roomplace; + + /* + Find place for first room + */ + bool fits = false; + for (u32 i = 0; i < 100; i++) + { + bool is_large_room = ((random.next() & 3) == 1); + roomsize = is_large_room ? + v3s16(random.range(8, 16),random.range(8, 16),random.range(8, 16)) : + v3s16(random.range(4, 8),random.range(4, 6),random.range(4, 8)); + + // start_padding is used to disallow starting the generation of + // a dungeon in a neighboring generation chunk + roomplace = vmanip->m_area.MinEdge + start_padding + v3s16( + random.range(0,areasize.X-roomsize.X-1-start_padding.X), + random.range(0,areasize.Y-roomsize.Y-1-start_padding.Y), + random.range(0,areasize.Z-roomsize.Z-1-start_padding.Z)); + + /* + Check that we're not putting the room to an unknown place, + otherwise it might end up floating in the air + */ + fits = true; + for (s16 z = 1; z < roomsize.Z - 1; z++) + for (s16 y = 1; y < roomsize.Y - 1; y++) + for (s16 x = 1; x < roomsize.X - 1; x++) + { + v3s16 p = roomplace + v3s16(x, y, z); + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) + { + fits = false; + break; + } + if (vmanip->m_data[vi].getContent() == CONTENT_IGNORE) + { + fits = false; + break; + } + } + if (fits) + break; + } + // No place found + if (fits == false) + return; + + /* + Stores the center position of the last room made, so that + a new corridor can be started from the last room instead of + the new room, if chosen so. + */ + v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); + + u32 room_count = random.range(2, 16); + for (u32 i = 0; i < room_count; i++) + { + // Make a room to the determined place + makeRoom(roomsize, roomplace); + + v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); + + // Place torch at room center (for testing) + //vmanip->m_data[vmanip->m_area.index(room_center)] = MapNode(cid_torch); + + // Quit if last room + if (i == room_count - 1) + break; + + // Determine walker start position + + bool start_in_last_room = (random.range(0, 2) != 0); + + v3s16 walker_start_place; + + if(start_in_last_room) + { + walker_start_place = last_room_center; + } + else + { + walker_start_place = room_center; + // Store center of current room as the last one + last_room_center = room_center; + } + + // Create walker and find a place for a door + v3s16 doorplace; + v3s16 doordir; + + m_pos = walker_start_place; + bool r = findPlaceForDoor(doorplace, doordir); + if (r == false) + return; + + if (random.range(0,1) == 0) + // Make the door + makeDoor(doorplace, doordir); + else + // Don't actually make a door + doorplace -= doordir; + + // Make a random corridor starting from the door + v3s16 corridor_end; + v3s16 corridor_end_dir; + makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir); + + // Find a place for a random sized room + roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); + m_pos = corridor_end; + m_dir = corridor_end_dir; + r = findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace); + if (r == false) + return; + + if (random.range(0,1) == 0) + // Make the door + makeDoor(doorplace, doordir); + else + // Don't actually make a door + roomplace -= doordir; + + } +} + + +void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) +{ + MapNode n_cobble(cid_cobble); + MapNode n_air(CONTENT_AIR); + + // Make +-X walls + for (s16 z = 0; z < roomsize.Z; z++) + for (s16 y = 0; y < roomsize.Y; y++) + { + { + v3s16 p = roomplace + v3s16(0, y, z); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + continue; + vmanip->m_data[vi] = n_cobble; + } + { + v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + continue; + vmanip->m_data[vi] = n_cobble; + } + } + + // Make +-Z walls + for (s16 x = 0; x < roomsize.X; x++) + for (s16 y = 0; y < roomsize.Y; y++) + { + { + v3s16 p = roomplace + v3s16(x, y, 0); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + continue; + vmanip->m_data[vi] = n_cobble; + } + { + v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + continue; + vmanip->m_data[vi] = n_cobble; + } + } + + // Make +-Y walls (floor and ceiling) + for (s16 z = 0; z < roomsize.Z; z++) + for (s16 x = 0; x < roomsize.X; x++) + { + { + v3s16 p = roomplace + v3s16(x, 0, z); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + continue; + vmanip->m_data[vi] = n_cobble; + } + { + v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + continue; + vmanip->m_data[vi] = n_cobble; + } + } + + // Fill with air + for (s16 z = 1; z < roomsize.Z - 1; z++) + for (s16 y = 1; y < roomsize.Y - 1; y++) + for (s16 x = 1; x < roomsize.X - 1; x++) + { + v3s16 p = roomplace + v3s16(x, y, z); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + vmanip->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; + vmanip->m_data[vi] = n_air; + } +} + + +void DungeonGen::makeFill(v3s16 place, v3s16 size, + u8 avoid_flags, MapNode n, u8 or_flags) +{ + for (s16 z = 0; z < size.Z; z++) + for (s16 y = 0; y < size.Y; y++) + for (s16 x = 0; x < size.X; x++) + { + v3s16 p = place + v3s16(x, y, z); + if (vmanip->m_area.contains(p) == false) + continue; + u32 vi = vmanip->m_area.index(p); + if (vmanip->m_flags[vi] & avoid_flags) + continue; + vmanip->m_flags[vi] |= or_flags; + vmanip->m_data[vi] = n; + } +} + + +void DungeonGen::makeHole(v3s16 place) +{ + makeFill(place, v3s16(1, 2, 1), 0, MapNode(CONTENT_AIR), + VMANIP_FLAG_DUNGEON_INSIDE); +} + + +void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir) +{ + makeHole(doorplace); + // Place torch (for testing) + //vmanip->m_data[vmanip->m_area.index(doorplace)] = MapNode(cid_torch); +} + + +void DungeonGen::makeCorridor(v3s16 doorplace, + v3s16 doordir, v3s16 &result_place, v3s16 &result_dir) +{ + makeHole(doorplace); + v3s16 p0 = doorplace; + v3s16 dir = doordir; + u32 length; + /*if (random.next() % 2) + length = random.range(1, 13); + else + length = random.range(1, 6);*/ + length = random.range(1, 13); + u32 partlength = random.range(1, 13); + u32 partcount = 0; + s16 make_stairs = 0; + + if (random.next() % 2 == 0 && partlength >= 3) + make_stairs = random.next() % 2 ? 1 : -1; + + for (u32 i = 0; i < length; i++) { + v3s16 p = p0 + dir; + if (partcount != 0) + p.Y += make_stairs; + + if (vmanip->m_area.contains(p) == true && + vmanip->m_area.contains(p + v3s16(0, 1, 0)) == true) { + if (make_stairs) { + makeFill(p + v3s16(-1, -1, -1), v3s16(3, 5, 3), + VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0); + makeHole(p); + makeHole(p - dir); + + // TODO: fix stairs code so it works 100% (quite difficult) + + // exclude stairs from the bottom step + if (((make_stairs == 1) && i != 0) || + ((make_stairs == -1) && i != length - 1)) { + // rotate face 180 deg if making stairs backwards + int facedir = dir_to_facedir(dir * make_stairs); + + u32 vi = vmanip->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z); + if (vmanip->m_data[vi].getContent() == cid_cobble) + vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir); + + vi = vmanip->m_area.index(p.X, p.Y, p.Z); + if (vmanip->m_data[vi].getContent() == cid_cobble) + vmanip->m_data[vi] = MapNode(cid_cobblestair, 0, facedir); + } + } else { + makeFill(p + v3s16(-1, -1, -1), v3s16(3, 4, 3), + VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(cid_cobble), 0); + makeHole(p); + } + + p0 = p; + } else { + // Can't go here, turn away + dir = turn_xz(dir, random.range(0, 1)); + make_stairs = -make_stairs; + partcount = 0; + partlength = random.range(1, length); + continue; + } + + partcount++; + if (partcount >= partlength) { + partcount = 0; + + dir = random_turn(random, dir); + + partlength = random.range(1,length); + + make_stairs = 0; + if (random.next() % 2 == 0 && partlength >= 3) + make_stairs = random.next() % 2 ? 1 : -1; + } + } + result_place = p0; + result_dir = dir; +} + + +bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) +{ + for (u32 i = 0; i < 100; i++) + { + v3s16 p = m_pos + m_dir; + v3s16 p1 = p + v3s16(0, 1, 0); + if (vmanip->m_area.contains(p) == false + || vmanip->m_area.contains(p1) == false + || i % 4 == 0) + { + randomizeDir(); + continue; + } + if (vmanip->getNodeNoExNoEmerge(p).getContent() == cid_cobble + && vmanip->getNodeNoExNoEmerge(p1).getContent() == cid_cobble) + { + // Found wall, this is a good place! + result_place = p; + result_dir = m_dir; + // Randomize next direction + randomizeDir(); + return true; + } + /* + Determine where to move next + */ + // Jump one up if the actual space is there + if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == cid_cobble + && vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == CONTENT_AIR + && vmanip->getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() == CONTENT_AIR) + p += v3s16(0,1,0); + // Jump one down if the actual space is there + if (vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() == cid_cobble + && vmanip->getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() == CONTENT_AIR + && vmanip->getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() == CONTENT_AIR) + p += v3s16(0,-1,0); + // Check if walking is now possible + if (vmanip->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR + || vmanip->getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() != CONTENT_AIR) + { + // Cannot continue walking here + randomizeDir(); + continue; + } + // Move there + m_pos = p; + } + return false; +} + + +bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, + v3s16 &result_doordir, v3s16 &result_roomplace) +{ + for (s16 trycount = 0; trycount < 30; trycount++) + { + v3s16 doorplace; + v3s16 doordir; + bool r = findPlaceForDoor(doorplace, doordir); + if (r == false) + continue; + v3s16 roomplace; + // X east, Z north, Y up +#if 1 + if (doordir == v3s16(1, 0, 0)) // X+ + roomplace = doorplace + + v3s16(0, -1, random.range(-roomsize.Z + 2, -2)); + if (doordir == v3s16(-1, 0, 0)) // X- + roomplace = doorplace + + v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2)); + if (doordir == v3s16(0, 0, 1)) // Z+ + roomplace = doorplace + + v3s16(random.range(-roomsize.X + 2, -2), -1, 0); + if (doordir == v3s16(0, 0, -1)) // Z- + roomplace = doorplace + + v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1); +#endif +#if 0 + if (doordir == v3s16(1, 0, 0)) // X+ + roomplace = doorplace + v3s16(0, -1, -roomsize.Z / 2); + if (doordir == v3s16(-1, 0, 0)) // X- + roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z / 2); + if (doordir == v3s16(0, 0, 1)) // Z+ + roomplace = doorplace + v3s16(-roomsize.X / 2, -1, 0); + if (doordir == v3s16(0, 0, -1)) // Z- + roomplace = doorplace + v3s16(-roomsize.X / 2, -1, -roomsize.Z + 1); +#endif + + // Check fit + bool fits = true; + for (s16 z = 1; z < roomsize.Z - 1; z++) + for (s16 y = 1; y < roomsize.Y - 1; y++) + for (s16 x = 1; x < roomsize.X - 1; x++) + { + v3s16 p = roomplace + v3s16(x, y, z); + if (vmanip->m_area.contains(p) == false) + { + fits = false; + break; + } + if (vmanip->m_flags[vmanip->m_area.index(p)] + & VMANIP_FLAG_DUNGEON_INSIDE) + { + fits = false; + break; + } + } + if(fits == false) + { + // Find new place + continue; + } + result_doorplace = doorplace; + result_doordir = doordir; + result_roomplace = roomplace; + return true; + } + return false; +} + + +v3s16 rand_ortho_dir(PseudoRandom &random) +{ + if (random.next() % 2 == 0) + return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0); + else + return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1); +} + + +v3s16 turn_xz(v3s16 olddir, int t) +{ + v3s16 dir; + if (t == 0) + { + // Turn right + dir.X = olddir.Z; + dir.Z = -olddir.X; + dir.Y = olddir.Y; + } + else + { + // Turn left + dir.X = -olddir.Z; + dir.Z = olddir.X; + dir.Y = olddir.Y; + } + return dir; +} + + +v3s16 random_turn(PseudoRandom &random, v3s16 olddir) +{ + int turn = random.range(0, 2); + v3s16 dir; + if (turn == 0) + { + // Go straight + dir = olddir; + } + else if (turn == 1) + // Turn right + dir = turn_xz(olddir, 0); + else + // Turn left + dir = turn_xz(olddir, 1); + return dir; +} + + +int dir_to_facedir(v3s16 d) { + if (abs(d.X) > abs(d.Z)) + return d.X < 0 ? 3 : 1; + else + return d.Z < 0 ? 2 : 0; +} diff --git a/src/dungeongen.h b/src/dungeongen.h new file mode 100644 index 000000000..4be3df4aa --- /dev/null +++ b/src/dungeongen.h @@ -0,0 +1,128 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef DUNGEONGEN_HEADER +#define DUNGEONGEN_HEADER + +#include "voxel.h" +#include "noise.h" + +#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1 +#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2 +#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\ + VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE) + +class ManualMapVoxelManipulator; +class INodeDefManager; + +v3s16 rand_ortho_dir(PseudoRandom &random); +v3s16 turn_xz(v3s16 olddir, int t); +v3s16 random_turn(PseudoRandom &random, v3s16 olddir); +int dir_to_facedir(v3s16 d); + +class DungeonGen { +public: + u32 blockseed; + u64 mapseed; + ManualMapVoxelManipulator *vmanip; + INodeDefManager *ndef; + PseudoRandom random; + v3s16 csize; + s16 water_level; + + NoiseParams *np_rarity; + NoiseParams *np_wetness; + NoiseParams *np_density; + + content_t cid_water_source; + content_t cid_cobble; + content_t cid_mossycobble; + content_t cid_torch; + content_t cid_cobblestair; + + //RoomWalker + v3s16 m_pos; + v3s16 m_dir; + + DungeonGen(INodeDefManager *ndef, u64 seed, s16 waterlevel); + void generate(ManualMapVoxelManipulator *vm, u32 bseed, + v3s16 full_node_min, v3s16 full_node_max); + //void generate(v3s16 full_node_min, v3s16 full_node_max, u32 bseed); + + void makeDungeon(v3s16 start_padding); + void makeRoom(v3s16 roomsize, v3s16 roomplace); + void makeCorridor(v3s16 doorplace, v3s16 doordir, + v3s16 &result_place, v3s16 &result_dir); + void makeDoor(v3s16 doorplace, v3s16 doordir); + void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags); + void makeHole(v3s16 place); + + bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir); + bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, + v3s16 &result_doordir, v3s16 &result_roomplace); + + void randomizeDir() + { + m_dir = rand_ortho_dir(random); + } +}; + +class RoomWalker +{ +public: + + RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random, + INodeDefManager *ndef): + vmanip(vmanip_), + m_pos(pos), + m_random(random), + m_ndef(ndef) + { + randomizeDir(); + } + + void randomizeDir() + { + m_dir = rand_ortho_dir(m_random); + } + + void setPos(v3s16 pos) + { + m_pos = pos; + } + + void setDir(v3s16 dir) + { + m_dir = dir; + } + + //bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir); + //bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, + // v3s16 &result_doordir, v3s16 &result_roomplace); + +private: + VoxelManipulator &vmanip; + v3s16 m_pos; + v3s16 m_dir; + PseudoRandom &m_random; + INodeDefManager *m_ndef; +}; + + +#endif diff --git a/src/emerge.cpp b/src/emerge.cpp index 6c6863eff..e4bd997cb 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -39,6 +39,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "biome.h" #include "emerge.h" #include "mapgen_v6.h" +#include "mapgen_indev.h" +#include "mapgen_singlenode.h" /////////////////////////////// Emerge Manager //////////////////////////////// @@ -46,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc., EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) { //register built-in mapgens registerMapgen("v6", new MapgenFactoryV6()); + registerMapgen("indev", new MapgenFactoryIndev()); + registerMapgen("singlenode", new MapgenFactorySinglenode()); this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef); this->params = NULL; @@ -359,7 +363,7 @@ void *EmergeThread::Thread() { */ BlockMakeData data; MapBlock *block = NULL; - core::map<v3s16, MapBlock *> modified_blocks; + std::map<v3s16, MapBlock *> modified_blocks; if (getBlockOrStartGen(p, &block, &data, allow_generate)) { { @@ -415,13 +419,13 @@ void *EmergeThread::Thread() { JMutexAutoLock lock(m_server->m_con_mutex); // Add the originally fetched block to the modified list if (block) - modified_blocks.insert(p, block); + modified_blocks[p] = block; // Set the modified blocks unsent for all the clients - for (core::map<u16, RemoteClient*>::Iterator - i = m_server->m_clients.getIterator(); - i.atEnd() == false; i++) { - RemoteClient *client = i.getNode()->getValue(); + for (std::map<u16, RemoteClient*>::iterator + i = m_server->m_clients.begin(); + i != m_server->m_clients.end(); ++i) { + RemoteClient *client = i->second; if (modified_blocks.size() > 0) { // Remove block from sent history client->SetBlocksNotSent(modified_blocks); diff --git a/src/emerge.h b/src/emerge.h index 70b67e731..3d717bce3 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -63,8 +63,9 @@ public: std::map<v3s16, BlockEmergeData *> blocks_enqueued; std::map<u16, u16> peer_queue_count; - //biome manager + //Mapgen-related structures BiomeDefManager *biomedef; + std::vector<Ore *> ores; EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef); ~EmergeManager(); diff --git a/src/environment.cpp b/src/environment.cpp index e939672e7..07cdb24d1 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -58,8 +58,8 @@ Environment::Environment(): Environment::~Environment() { // Deallocate players - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { delete (*i); } @@ -86,8 +86,8 @@ void Environment::removePlayer(u16 peer_id) { DSTACK(__FUNCTION_NAME); re_search: - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(player->peer_id != peer_id) @@ -103,8 +103,8 @@ re_search: Player * Environment::getPlayer(u16 peer_id) { - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(player->peer_id == peer_id) @@ -115,8 +115,8 @@ Player * Environment::getPlayer(u16 peer_id) Player * Environment::getPlayer(const char *name) { - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(strcmp(player->getName(), name) == 0) @@ -127,12 +127,12 @@ Player * Environment::getPlayer(const char *name) Player * Environment::getRandomConnectedPlayer() { - core::list<Player*> connected_players = getPlayers(true); + std::list<Player*> connected_players = getPlayers(true); u32 chosen_one = myrand() % connected_players.size(); u32 j = 0; - for(core::list<Player*>::Iterator + for(std::list<Player*>::iterator i = connected_players.begin(); - i != connected_players.end(); i++) + i != connected_players.end(); ++i) { if(j == chosen_one) { @@ -146,12 +146,12 @@ Player * Environment::getRandomConnectedPlayer() Player * Environment::getNearestConnectedPlayer(v3f pos) { - core::list<Player*> connected_players = getPlayers(true); + std::list<Player*> connected_players = getPlayers(true); f32 nearest_d = 0; Player *nearest_player = NULL; - for(core::list<Player*>::Iterator + for(std::list<Player*>::iterator i = connected_players.begin(); - i != connected_players.end(); i++) + i != connected_players.end(); ++i) { Player *player = *i; f32 d = player->getPosition().getDistanceFrom(pos); @@ -164,17 +164,17 @@ Player * Environment::getNearestConnectedPlayer(v3f pos) return nearest_player; } -core::list<Player*> Environment::getPlayers() +std::list<Player*> Environment::getPlayers() { return m_players; } -core::list<Player*> Environment::getPlayers(bool ignore_disconnected) +std::list<Player*> Environment::getPlayers(bool ignore_disconnected) { - core::list<Player*> newlist; - for(core::list<Player*>::Iterator + std::list<Player*> newlist; + for(std::list<Player*>::iterator i = m_players.begin(); - i != m_players.end(); i++) + i != m_players.end(); ++i) { Player *player = *i; @@ -193,7 +193,7 @@ core::list<Player*> Environment::getPlayers(bool ignore_disconnected) void Environment::printPlayers(std::ostream &o) { o<<"Players in environment:"<<std::endl; - for(core::list<Player*>::Iterator i = m_players.begin(); + for(std::list<Player*>::iterator i = m_players.begin(); i != m_players.end(); i++) { Player *player = *i; @@ -251,7 +251,7 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_): ActiveBlockList */ -void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list) +void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list) { v3s16 p; for(p.X=p0.X-r; p.X<=p0.X+r; p.X++) @@ -259,21 +259,21 @@ void fillRadiusBlock(v3s16 p0, s16 r, core::map<v3s16, bool> &list) for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++) { // Set in list - list[p] = true; + list.insert(p); } } -void ActiveBlockList::update(core::list<v3s16> &active_positions, +void ActiveBlockList::update(std::list<v3s16> &active_positions, s16 radius, - core::map<v3s16, bool> &blocks_removed, - core::map<v3s16, bool> &blocks_added) + std::set<v3s16> &blocks_removed, + std::set<v3s16> &blocks_added) { /* Create the new list */ - core::map<v3s16, bool> newlist; - for(core::list<v3s16>::Iterator i = active_positions.begin(); - i != active_positions.end(); i++) + std::set<v3s16> newlist; + for(std::list<v3s16>::iterator i = active_positions.begin(); + i != active_positions.end(); ++i) { fillRadiusBlock(*i, radius, newlist); } @@ -282,37 +282,37 @@ void ActiveBlockList::update(core::list<v3s16> &active_positions, Find out which blocks on the old list are not on the new list */ // Go through old list - for(core::map<v3s16, bool>::Iterator i = m_list.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator i = m_list.begin(); + i != m_list.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; // If not on new list, it's been removed - if(newlist.find(p) == NULL) - blocks_removed.insert(p, true); + if(newlist.find(p) == newlist.end()) + blocks_removed.insert(p); } /* Find out which blocks on the new list are not on the old list */ // Go through new list - for(core::map<v3s16, bool>::Iterator i = newlist.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator i = newlist.begin(); + i != newlist.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; // If not on old list, it's been added - if(m_list.find(p) == NULL) - blocks_added.insert(p, true); + if(m_list.find(p) == m_list.end()) + blocks_added.insert(p); } /* Update m_list */ m_list.clear(); - for(core::map<v3s16, bool>::Iterator i = newlist.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator i = newlist.begin(); + i != newlist.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - m_list.insert(p, true); + v3s16 p = *i; + m_list.insert(p); } } @@ -348,8 +348,8 @@ ServerEnvironment::~ServerEnvironment() m_map->drop(); // Delete ActiveBlockModifiers - for(core::list<ABMWithState>::Iterator - i = m_abms.begin(); i != m_abms.end(); i++){ + for(std::list<ABMWithState>::iterator + i = m_abms.begin(); i != m_abms.end(); ++i){ delete i->abm; } } @@ -370,7 +370,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir) std::string players_path = savedir + "/players"; fs::CreateDir(players_path); - core::map<Player*, bool> saved_players; + std::set<Player*> saved_players; std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path); for(u32 i=0; i<player_files.size(); i++) @@ -419,15 +419,15 @@ void ServerEnvironment::serializePlayers(const std::string &savedir) continue; } player->serialize(os); - saved_players.insert(player, true); + saved_players.insert(player); } } - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; - if(saved_players.find(player) != NULL) + if(saved_players.find(player) != saved_players.end()) { /*infostream<<"Player "<<player->getName() <<" was already saved."<<std::endl;*/ @@ -473,7 +473,7 @@ void ServerEnvironment::serializePlayers(const std::string &savedir) continue; } player->serialize(os); - saved_players.insert(player, true); + saved_players.insert(player); } } @@ -484,8 +484,6 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir) { std::string players_path = savedir + "/players"; - core::map<Player*, bool> saved_players; - std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path); for(u32 i=0; i<player_files.size(); i++) { @@ -627,7 +625,7 @@ private: ServerEnvironment *m_env; std::map<content_t, std::list<ActiveABM> > m_aabms; public: - ABMHandler(core::list<ABMWithState> &abms, + ABMHandler(std::list<ABMWithState> &abms, float dtime_s, ServerEnvironment *env, bool use_timers): m_env(env) @@ -635,8 +633,8 @@ public: if(dtime_s < 0.001) return; INodeDefManager *ndef = env->getGameDef()->ndef(); - for(core::list<ABMWithState>::Iterator - i = abms.begin(); i != abms.end(); i++){ + for(std::list<ABMWithState>::iterator + i = abms.begin(); i != abms.end(); ++i){ ActiveBlockModifier *abm = i->abm; float trigger_interval = abm->getTriggerInterval(); if(trigger_interval < 0.001) @@ -862,12 +860,12 @@ bool ServerEnvironment::removeNode(v3s16 p) std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius) { std::set<u16> objects; - for(core::map<u16, ServerActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, ServerActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i.getNode()->getValue(); - u16 id = i.getNode()->getKey(); + ServerActiveObject* obj = i->second; + u16 id = i->first; v3f objectpos = obj->getBasePosition(); if(objectpos.getDistanceFrom(pos) > radius) continue; @@ -880,16 +878,16 @@ void ServerEnvironment::clearAllObjects() { infostream<<"ServerEnvironment::clearAllObjects(): " <<"Removing all active objects"<<std::endl; - core::list<u16> objects_to_remove; - for(core::map<u16, ServerActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + std::list<u16> objects_to_remove; + for(std::map<u16, ServerActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i.getNode()->getValue(); + ServerActiveObject* obj = i->second; if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) continue; - u16 id = i.getNode()->getKey(); - v3f objectpos = obj->getBasePosition(); + 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); @@ -919,13 +917,13 @@ void ServerEnvironment::clearAllObjects() objects_to_remove.push_back(id); } // Remove references from m_active_objects - for(core::list<u16>::Iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); i++) + for(std::list<u16>::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { - m_active_objects.remove(*i); + m_active_objects.erase(*i); } - core::list<v3s16> loadable_blocks; + std::list<v3s16> loadable_blocks; infostream<<"ServerEnvironment::clearAllObjects(): " <<"Listing all loadable blocks"<<std::endl; m_map->listAllLoadableBlocks(loadable_blocks); @@ -937,8 +935,8 @@ void ServerEnvironment::clearAllObjects() u32 num_blocks_checked = 0; u32 num_blocks_cleared = 0; u32 num_objs_cleared = 0; - for(core::list<v3s16>::Iterator i = loadable_blocks.begin(); - i != loadable_blocks.end(); i++) + for(std::list<v3s16>::iterator i = loadable_blocks.begin(); + i != loadable_blocks.end(); ++i) { v3s16 p = *i; MapBlock *block = m_map->emergeBlock(p, false); @@ -1002,8 +1000,8 @@ void ServerEnvironment::step(float dtime) */ { ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG); - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; @@ -1027,10 +1025,10 @@ void ServerEnvironment::step(float dtime) /* Get player block positions */ - core::list<v3s16> players_blockpos; - for(core::list<Player*>::Iterator + std::list<v3s16> players_blockpos; + for(std::list<Player*>::iterator i = m_players.begin(); - i != m_players.end(); i++) + i != m_players.end(); ++i) { Player *player = *i; // Ignore disconnected players @@ -1045,8 +1043,8 @@ void ServerEnvironment::step(float dtime) Update list of active blocks, collecting changes */ const s16 active_block_range = g_settings->getS16("active_block_range"); - core::map<v3s16, bool> blocks_removed; - core::map<v3s16, bool> blocks_added; + std::set<v3s16> blocks_removed; + std::set<v3s16> blocks_added; m_active_blocks.update(players_blockpos, active_block_range, blocks_removed, blocks_added); @@ -1057,11 +1055,11 @@ void ServerEnvironment::step(float dtime) // Convert active objects that are no more in active blocks to static deactivateFarObjects(false); - for(core::map<v3s16, bool>::Iterator - i = blocks_removed.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator + i = blocks_removed.begin(); + i != blocks_removed.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") became inactive"<<std::endl;*/ @@ -1078,11 +1076,11 @@ void ServerEnvironment::step(float dtime) Handle added blocks */ - for(core::map<v3s16, bool>::Iterator - i = blocks_added.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator + i = blocks_added.begin(); + i != blocks_added.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") became active"<<std::endl;*/ @@ -1091,7 +1089,7 @@ void ServerEnvironment::step(float dtime) if(block==NULL){ // Block needs to be fetched first m_emerger->queueBlockEmerge(p, false); - m_active_blocks.m_list.remove(p); + m_active_blocks.m_list.erase(p); continue; } @@ -1108,11 +1106,11 @@ void ServerEnvironment::step(float dtime) float dtime = 1.0; - for(core::map<v3s16, bool>::Iterator - i = m_active_blocks.m_list.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator + i = m_active_blocks.m_list.begin(); + i != m_active_blocks.m_list.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") being handled"<<std::endl;*/ @@ -1163,11 +1161,11 @@ void ServerEnvironment::step(float dtime) // Initialize handling of ActiveBlockModifiers ABMHandler abmhandler(m_abms, abm_interval, this, true); - for(core::map<v3s16, bool>::Iterator - i = m_active_blocks.m_list.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator + i = m_active_blocks.m_list.begin(); + i != m_active_blocks.m_list.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") being handled"<<std::endl;*/ @@ -1216,11 +1214,11 @@ void ServerEnvironment::step(float dtime) send_recommended = true; } - for(core::map<u16, ServerActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, ServerActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i.getNode()->getValue(); + ServerActiveObject* obj = i->second; // Remove non-peaceful mobs on peaceful mode if(g_settings->getBool("only_peaceful_mobs")){ if(!obj->isPeaceful()) @@ -1232,7 +1230,7 @@ void ServerEnvironment::step(float dtime) // Step object obj->step(dtime, send_recommended); // Read messages from object - while(obj->m_messages_out.size() > 0) + while(!obj->m_messages_out.empty()) { m_active_object_messages.push_back( obj->m_messages_out.pop_front()); @@ -1255,31 +1253,24 @@ void ServerEnvironment::step(float dtime) ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) { - core::map<u16, ServerActiveObject*>::Node *n; + std::map<u16, ServerActiveObject*>::iterator n; n = m_active_objects.find(id); - if(n == NULL) + if(n == m_active_objects.end()) return NULL; - return n->getValue(); + return n->second; } bool isFreeServerActiveObjectId(u16 id, - core::map<u16, ServerActiveObject*> &objects) + std::map<u16, ServerActiveObject*> &objects) { if(id == 0) return false; - - for(core::map<u16, ServerActiveObject*>::Iterator - i = objects.getIterator(); - i.atEnd()==false; i++) - { - if(i.getNode()->getKey() == id) - return false; - } - return true; + + return objects.find(id) == objects.end(); } u16 getFreeServerActiveObjectId( - core::map<u16, ServerActiveObject*> &objects) + std::map<u16, ServerActiveObject*> &objects) { u16 new_id = 1; for(;;) @@ -1351,8 +1342,8 @@ bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj) inside a radius around a position */ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, - core::map<u16, bool> ¤t_objects, - core::map<u16, bool> &added_objects) + std::set<u16> ¤t_objects, + std::set<u16> &added_objects) { v3f pos_f = intToFloat(pos, BS); f32 radius_f = radius * BS; @@ -1363,13 +1354,13 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, - discard objects that are found in current_objects. - add remaining objects to added_objects */ - for(core::map<u16, ServerActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, ServerActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - u16 id = i.getNode()->getKey(); + u16 id = i->first; // Get object - ServerActiveObject *object = i.getNode()->getValue(); + ServerActiveObject *object = i->second; if(object == NULL) continue; // Discard if removed @@ -1382,12 +1373,12 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, continue; } // Discard if already on current_objects - core::map<u16, bool>::Node *n; + std::set<u16>::iterator n; n = current_objects.find(id); - if(n != NULL) + if(n != current_objects.end()) continue; // Add to added_objects - added_objects.insert(id, false); + added_objects.insert(id); } } @@ -1396,8 +1387,8 @@ void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius, inside a radius around a position */ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, - core::map<u16, bool> ¤t_objects, - core::map<u16, bool> &removed_objects) + std::set<u16> ¤t_objects, + std::set<u16> &removed_objects) { v3f pos_f = intToFloat(pos, BS); f32 radius_f = radius * BS; @@ -1409,23 +1400,23 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, - object has m_removed=true, or - object is too far away */ - for(core::map<u16, bool>::Iterator - i = current_objects.getIterator(); - i.atEnd()==false; i++) + for(std::set<u16>::iterator + i = current_objects.begin(); + i != current_objects.end(); ++i) { - u16 id = i.getNode()->getKey(); + u16 id = *i; ServerActiveObject *object = getActiveObject(id); if(object == NULL){ infostream<<"ServerEnvironment::getRemovedActiveObjects():" <<" object in current_objects is NULL"<<std::endl; - removed_objects.insert(id, false); + removed_objects.insert(id); continue; } if(object->m_removed) { - removed_objects.insert(id, false); + removed_objects.insert(id); continue; } @@ -1437,7 +1428,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, if(distance_f >= radius_f) { - removed_objects.insert(id, false); + removed_objects.insert(id); continue; } @@ -1447,7 +1438,7 @@ void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius, ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() { - if(m_active_object_messages.size() == 0) + if(m_active_object_messages.empty()) return ActiveObjectMessage(0); return m_active_object_messages.pop_front(); @@ -1488,7 +1479,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, /*infostream<<"ServerEnvironment::addActiveObjectRaw(): " <<"added (id="<<object->getId()<<")"<<std::endl;*/ - m_active_objects.insert(object->getId(), object); + m_active_objects[object->getId()] = object; verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " <<"Added id="<<object->getId()<<"; there are now " @@ -1512,7 +1503,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); if(block) { - block->m_static_objects.m_active.insert(object->getId(), s_obj); + block->m_static_objects.m_active[object->getId()] = s_obj; object->m_static_exists = true; object->m_static_block = blockpos; @@ -1536,13 +1527,13 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, */ void ServerEnvironment::removeRemovedObjects() { - core::list<u16> objects_to_remove; - for(core::map<u16, ServerActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + std::list<u16> objects_to_remove; + for(std::map<u16, ServerActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - u16 id = i.getNode()->getKey(); - ServerActiveObject* obj = i.getNode()->getValue(); + u16 id = i->first; + ServerActiveObject* obj = i->second; // This shouldn't happen but check it if(obj == NULL) { @@ -1593,10 +1584,10 @@ void ServerEnvironment::removeRemovedObjects() objects_to_remove.push_back(id); } // Remove references from m_active_objects - for(core::list<u16>::Iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); i++) + for(std::list<u16>::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { - m_active_objects.remove(*i); + m_active_objects.erase(*i); } } @@ -1663,11 +1654,11 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) } // A list for objects that couldn't be converted to active for some // reason. They will be stored back. - core::list<StaticObject> new_stored; + std::list<StaticObject> new_stored; // Loop through stored static objects - for(core::list<StaticObject>::Iterator + for(std::list<StaticObject>::iterator i = block->m_static_objects.m_stored.begin(); - i != block->m_static_objects.m_stored.end(); i++) + i != block->m_static_objects.m_stored.end(); ++i) { /*infostream<<"Server: Creating an active object from " <<"static data"<<std::endl;*/ @@ -1696,9 +1687,9 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) // Clear stored list block->m_static_objects.m_stored.clear(); // Add leftover failed stuff to stored list - for(core::list<StaticObject>::Iterator + for(std::list<StaticObject>::iterator i = new_stored.begin(); - i != new_stored.end(); i++) + i != new_stored.end(); ++i) { StaticObject &s_obj = *i; block->m_static_objects.m_stored.push_back(s_obj); @@ -1726,12 +1717,12 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) */ void ServerEnvironment::deactivateFarObjects(bool force_delete) { - core::list<u16> objects_to_remove; - for(core::map<u16, ServerActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + std::list<u16> objects_to_remove; + for(std::map<u16, ServerActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i.getNode()->getValue(); + ServerActiveObject* obj = i->second; assert(obj); // Do not deactivate if static data creation not allowed @@ -1742,7 +1733,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(!force_delete && obj->m_pending_deactivation) continue; - u16 id = i.getNode()->getKey(); + u16 id = i->first; v3f objectpos = obj->getBasePosition(); // The block in which the object resides in @@ -1778,10 +1769,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - core::map<u16, StaticObject>::Node *n = + std::map<u16, StaticObject>::iterator n = block->m_static_objects.m_active.find(id); - if(n){ - StaticObject static_old = n->getValue(); + if(n != block->m_static_objects.m_active.end()){ + StaticObject static_old = n->second; float save_movem = obj->getMinimumSavedMovement(); @@ -1840,7 +1831,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) // This shouldn't happen, but happens rarely for some // unknown reason. Unsuccessful attempts have been made to // find said reason. - if(new_id && block->m_static_objects.m_active.find(new_id)){ + if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){ infostream<<"ServerEnv: WARNING: Performing hack #83274" <<std::endl; block->m_static_objects.remove(new_id); @@ -1900,10 +1891,10 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } // Remove references from m_active_objects - for(core::list<u16>::Iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); i++) + for(std::list<u16>::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { - m_active_objects.remove(*i); + m_active_objects.erase(*i); } } @@ -1930,15 +1921,15 @@ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, ClientEnvironment::~ClientEnvironment() { // delete active objects - for(core::map<u16, ClientActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, ClientActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - delete i.getNode()->getValue(); + delete i->second; } - for(core::list<ClientSimpleObject*>::Iterator - i = m_simple_objects.begin(); i != m_simple_objects.end(); i++) + for(std::list<ClientSimpleObject*>::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { delete *i; } @@ -1971,8 +1962,8 @@ void ClientEnvironment::addPlayer(Player *player) LocalPlayer * ClientEnvironment::getLocalPlayer() { - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; if(player->isLocal()) @@ -1996,7 +1987,7 @@ void ClientEnvironment::step(float dtime) LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); // collision info queue - core::list<CollisionInfo> player_collisions; + std::list<CollisionInfo> player_collisions; /* Get the speed the player is going @@ -2105,7 +2096,7 @@ void ClientEnvironment::step(float dtime) Move the lplayer. This also does collision detection. */ - lplayer->move(dtime_part, *m_map, position_max_increment, + lplayer->move(dtime_part, this, position_max_increment, &player_collisions); } } @@ -2113,9 +2104,9 @@ void ClientEnvironment::step(float dtime) //std::cout<<"Looped "<<loopcount<<" times."<<std::endl; - for(core::list<CollisionInfo>::Iterator + for(std::list<CollisionInfo>::iterator i = player_collisions.begin(); - i != player_collisions.end(); i++) + i != player_collisions.end(); ++i) { CollisionInfo &info = *i; v3f speed_diff = info.new_speed - info.old_speed;; @@ -2179,8 +2170,8 @@ void ClientEnvironment::step(float dtime) /* Stuff that can be done in an arbitarily large dtime */ - for(core::list<Player*>::Iterator i = m_players.begin(); - i != m_players.end(); i++) + for(std::list<Player*>::iterator i = m_players.begin(); + i != m_players.end(); ++i) { Player *player = *i; v3f playerpos = player->getPosition(); @@ -2214,11 +2205,11 @@ void ClientEnvironment::step(float dtime) */ bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); - for(core::map<u16, ClientActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, ClientActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - ClientActiveObject* obj = i.getNode()->getValue(); + ClientActiveObject* obj = i->second; // Step object obj->step(dtime, this); @@ -2242,12 +2233,12 @@ void ClientEnvironment::step(float dtime) /* Step and handle simple objects */ - for(core::list<ClientSimpleObject*>::Iterator + for(std::list<ClientSimpleObject*>::iterator i = m_simple_objects.begin(); i != m_simple_objects.end();) { ClientSimpleObject *simple = *i; - core::list<ClientSimpleObject*>::Iterator cur = i; - i++; + std::list<ClientSimpleObject*>::iterator cur = i; + ++i; simple->step(dtime); if(simple->m_to_be_removed){ delete simple; @@ -2263,31 +2254,24 @@ void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) { - core::map<u16, ClientActiveObject*>::Node *n; + std::map<u16, ClientActiveObject*>::iterator n; n = m_active_objects.find(id); - if(n == NULL) + if(n == m_active_objects.end()) return NULL; - return n->getValue(); + return n->second; } bool isFreeClientActiveObjectId(u16 id, - core::map<u16, ClientActiveObject*> &objects) + std::map<u16, ClientActiveObject*> &objects) { if(id == 0) return false; - - for(core::map<u16, ClientActiveObject*>::Iterator - i = objects.getIterator(); - i.atEnd()==false; i++) - { - if(i.getNode()->getKey() == id) - return false; - } - return true; + + return objects.find(id) == objects.end(); } u16 getFreeClientActiveObjectId( - core::map<u16, ClientActiveObject*> &objects) + std::map<u16, ClientActiveObject*> &objects) { u16 new_id = 1; for(;;) @@ -2326,7 +2310,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) } infostream<<"ClientEnvironment::addActiveObject(): " <<"added (id="<<object->getId()<<")"<<std::endl; - m_active_objects.insert(object->getId(), object); + m_active_objects[object->getId()] = object; object->addToScene(m_smgr, m_texturesource, m_irr); { // Update lighting immediately u8 light = 0; @@ -2389,7 +2373,7 @@ void ClientEnvironment::removeActiveObject(u16 id) } obj->removeFromScene(true); delete obj; - m_active_objects.remove(id); + m_active_objects.erase(id); } void ClientEnvironment::processActiveObjectMessage(u16 id, @@ -2445,13 +2429,13 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) */ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, - core::array<DistanceSortedActiveObject> &dest) + std::vector<DistanceSortedActiveObject> &dest) { - for(core::map<u16, ClientActiveObject*>::Iterator - i = m_active_objects.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, ClientActiveObject*>::iterator + i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { - ClientActiveObject* obj = i.getNode()->getValue(); + ClientActiveObject* obj = i->second; f32 d = (obj->getPosition() - origin).getLength(); @@ -2466,7 +2450,7 @@ void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, ClientEnvEvent ClientEnvironment::getClientEvent() { - if(m_client_event_queue.size() == 0) + if(m_client_event_queue.empty()) { ClientEnvEvent event; event.type = CEE_NONE; diff --git a/src/environment.h b/src/environment.h index 07a4d7635..02301e5d3 100644 --- a/src/environment.h +++ b/src/environment.h @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include <set> +#include <list> #include "irrlichttypes_extrabloated.h" #include "player.h" #include <ostream> @@ -73,8 +74,8 @@ public: Player * getPlayer(const char *name); Player * getRandomConnectedPlayer(); Player * getNearestConnectedPlayer(v3f pos); - core::list<Player*> getPlayers(); - core::list<Player*> getPlayers(bool ignore_disconnected); + std::list<Player*> getPlayers(); + std::list<Player*> getPlayers(bool ignore_disconnected); void printPlayers(std::ostream &o); u32 getDayNightRatio(); @@ -102,7 +103,7 @@ public: protected: // peer_ids in here should be unique, except that there may be many 0s - core::list<Player*> m_players; + std::list<Player*> m_players; // Time of day in milli-hours (0-23999); determines day and night u32 m_time_of_day; // Time of day in 0...1 @@ -156,20 +157,20 @@ struct ABMWithState class ActiveBlockList { public: - void update(core::list<v3s16> &active_positions, + void update(std::list<v3s16> &active_positions, s16 radius, - core::map<v3s16, bool> &blocks_removed, - core::map<v3s16, bool> &blocks_added); + std::set<v3s16> &blocks_removed, + std::set<v3s16> &blocks_added); bool contains(v3s16 p){ - return (m_list.find(p) != NULL); + return (m_list.find(p) != m_list.end()); } void clear(){ m_list.clear(); } - core::map<v3s16, bool> m_list; + std::set<v3s16> m_list; private: }; @@ -249,16 +250,16 @@ public: inside a radius around a position */ void getAddedActiveObjects(v3s16 pos, s16 radius, - core::map<u16, bool> ¤t_objects, - core::map<u16, bool> &added_objects); + std::set<u16> ¤t_objects, + std::set<u16> &added_objects); /* Find out what new objects have been removed from inside a radius around a position */ void getRemovedActiveObjects(v3s16 pos, s16 radius, - core::map<u16, bool> ¤t_objects, - core::map<u16, bool> &removed_objects); + std::set<u16> ¤t_objects, + std::set<u16> &removed_objects); /* Get the next message emitted by some active object. @@ -350,7 +351,7 @@ private: // Background block emerger (the server, in practice) IBackgroundBlockEmerger *m_emerger; // Active object list - core::map<u16, ServerActiveObject*> m_active_objects; + std::map<u16, ServerActiveObject*> m_active_objects; // Outgoing network message buffer for active objects Queue<ActiveObjectMessage> m_active_object_messages; // Some timers @@ -368,7 +369,7 @@ private: u32 m_game_time; // A helper variable for incrementing the latter float m_game_time_fraction_counter; - core::list<ABMWithState> m_abms; + std::list<ABMWithState> m_abms; // An interval for generally sending object positions and stuff float m_recommended_send_interval; }; @@ -463,7 +464,7 @@ public: // Get all nearby objects void getActiveObjects(v3f origin, f32 max_d, - core::array<DistanceSortedActiveObject> &dest); + std::vector<DistanceSortedActiveObject> &dest); // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); @@ -476,8 +477,8 @@ private: ITextureSource *m_texturesource; IGameDef *m_gamedef; IrrlichtDevice *m_irr; - core::map<u16, ClientActiveObject*> m_active_objects; - core::list<ClientSimpleObject*> m_simple_objects; + std::map<u16, ClientActiveObject*> m_active_objects; + std::list<ClientSimpleObject*> m_simple_objects; Queue<ClientEnvEvent> m_client_event_queue; IntervalLimiter m_active_object_light_update_interval; IntervalLimiter m_lava_hurt_interval; diff --git a/src/farmesh.cpp b/src/farmesh.cpp index 443e2b3bf..ecf01ee07 100644 --- a/src/farmesh.cpp +++ b/src/farmesh.cpp @@ -112,13 +112,13 @@ struct HeightPoint float have_sand; float tree_amount; }; -core::map<v2s16, HeightPoint> g_heights; +std::map<v2s16, HeightPoint> g_heights; HeightPoint ground_height(u64 seed, v2s16 p2d) { - core::map<v2s16, HeightPoint>::Node *n = g_heights.find(p2d); - if(n) - return n->getValue(); + std::map<v2s16, HeightPoint>::iterator n = g_heights.find(p2d); + if(n != g_heights.end()) + return n->second; HeightPoint hp; s16 level = Mapgen::find_ground_level_from_noise(seed, p2d, 3); hp.gh = (level-4)*BS; diff --git a/src/game.cpp b/src/game.cpp index 1ae29b13b..5e4e06148 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2186,6 +2186,47 @@ void the_game( { update_wielded_item_trigger = true; } + else if(event.type == CE_SPAWN_PARTICLE) + { + LocalPlayer* player = client.getEnv().getLocalPlayer(); + AtlasPointer ap = + gamedef->tsrc()->getTexture(*(event.spawn_particle.texture)); + + new Particle(gamedef, smgr, player, client.getEnv(), + *event.spawn_particle.pos, + *event.spawn_particle.vel, + *event.spawn_particle.acc, + event.spawn_particle.expirationtime, + event.spawn_particle.size, + event.spawn_particle.collisiondetection, ap); + } + else if(event.type == CE_ADD_PARTICLESPAWNER) + { + LocalPlayer* player = client.getEnv().getLocalPlayer(); + AtlasPointer ap = + gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture)); + + new ParticleSpawner(gamedef, smgr, player, + event.add_particlespawner.amount, + event.add_particlespawner.spawntime, + *event.add_particlespawner.minpos, + *event.add_particlespawner.maxpos, + *event.add_particlespawner.minvel, + *event.add_particlespawner.maxvel, + *event.add_particlespawner.minacc, + *event.add_particlespawner.maxacc, + event.add_particlespawner.minexptime, + event.add_particlespawner.maxexptime, + event.add_particlespawner.minsize, + event.add_particlespawner.maxsize, + event.add_particlespawner.collisiondetection, + ap, + event.add_particlespawner.id); + } + else if(event.type == CE_DELETE_PARTICLESPAWNER) + { + delete_particlespawner (event.delete_particlespawner.id); + } } } @@ -2353,11 +2394,6 @@ void the_game( } } - // We can't actually know, but assume the sound of right-clicking - // to be the sound of placing a node - soundmaker.m_player_rightpunch_sound.gain = 0.5; - soundmaker.m_player_rightpunch_sound.name = "default_place_node"; - /* Handle digging */ @@ -2415,7 +2451,8 @@ void the_game( const ContentFeatures &features = client.getNodeDefManager()->get(n); addPunchingParticles - (gamedef, smgr, player, nodepos, features.tiles); + (gamedef, smgr, player, client.getEnv(), + nodepos, features.tiles); } } @@ -2453,7 +2490,8 @@ void the_game( const ContentFeatures &features = client.getNodeDefManager()->get(wasnode); addDiggingParticles - (gamedef, smgr, player, nodepos, features.tiles); + (gamedef, smgr, player, client.getEnv(), + nodepos, features.tiles); } dig_time = 0; @@ -2574,6 +2612,9 @@ void the_game( <<") - Position not loaded"<<std::endl; } }while(0); + + // Read the sound + soundmaker.m_player_rightpunch_sound = def.sound_place; } } } @@ -2734,6 +2775,7 @@ void the_game( */ allparticles_step(dtime, client.getEnv()); + allparticlespawners_step(dtime, client.getEnv()); /* Fog @@ -3220,6 +3262,7 @@ void the_game( clouds->drop(); if(gui_chat_console) gui_chat_console->drop(); + clear_particles (); /* Draw a "shutting down" screen, which will be shown while the map diff --git a/src/game.h b/src/game.h index fef777fea..a2c1fc09c 100644 --- a/src/game.h +++ b/src/game.h @@ -23,17 +23,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include <string> #include "keycode.h" +#include <list> -class KeyList : protected core::list<KeyPress> +class KeyList : protected std::list<KeyPress> { - typedef core::list<KeyPress> super; - typedef super::Iterator Iterator; - typedef super::ConstIterator ConstIterator; + typedef std::list<KeyPress> super; + typedef super::iterator iterator; + typedef super::const_iterator const_iterator; - virtual ConstIterator find(const KeyPress &key) const + virtual const_iterator find(const KeyPress &key) const { - ConstIterator f(begin()); - ConstIterator e(end()); + const_iterator f(begin()); + const_iterator e(end()); while (f!=e) { if (*f == key) return f; @@ -42,10 +43,10 @@ class KeyList : protected core::list<KeyPress> return e; } - virtual Iterator find(const KeyPress &key) + virtual iterator find(const KeyPress &key) { - Iterator f(begin()); - Iterator e(end()); + iterator f(begin()); + iterator e(end()); while (f!=e) { if (*f == key) return f; @@ -65,14 +66,14 @@ public: void unset(const KeyPress &key) { - Iterator p(find(key)); + iterator p(find(key)); if (p != end()) erase(p); } void toggle(const KeyPress &key) { - Iterator p(this->find(key)); + iterator p(this->find(key)); if (p != end()) erase(p); else diff --git a/src/gettime.h b/src/gettime.h index 611906559..cde1471e5 100644 --- a/src/gettime.h +++ b/src/gettime.h @@ -31,7 +31,15 @@ with this program; if not, write to the Free Software Foundation, Inc., Normal build: main.cpp Server build: servermain.cpp */ +enum TimePrecision { + PRECISION_SECONDS, + PRECISION_MILLI, + PRECISION_MICRO, + PRECISION_NANO +}; + extern u32 getTimeMs(); +extern u32 getTime(TimePrecision prec); /* Timestamp stuff diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index f522af01f..5fc576cf8 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -535,7 +535,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) { // Tab or Shift-Tab pressed // Nick completion - core::list<std::wstring> names = m_client->getConnectedPlayerNames(); + std::list<std::wstring> names = m_client->getConnectedPlayerNames(); bool backwards = event.KeyInput.Shift; m_chat_backend->getPrompt().nickCompletion(names, backwards); return true; diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 120d6629a..1754422d0 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -207,18 +207,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) Strfnd f(m_formspec_string); while(f.atend() == false) { - std::string type = trim(f.next("[")); + std::string type = trim(f.next_esc("[")); if(type == "invsize" || type == "size") { v2f invsize; - invsize.X = stof(f.next(",")); + invsize.X = stof(f.next_esc(",")); if(type == "size") { - invsize.Y = stof(f.next("]")); + invsize.Y = stof(f.next_esc("]")); } else{ - invsize.Y = stof(f.next(";")); - f.next("]"); + invsize.Y = stof(f.next_esc(";")); + f.next_esc("]"); } infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl; @@ -242,24 +242,24 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } else if(type == "list") { - std::string name = f.next(";"); + std::string name = f.next_esc(";"); InventoryLocation loc; if(name == "context" || name == "current_name") loc = m_current_inventory_location; else loc.deSerialize(name); - std::string listname = f.next(";"); + std::string listname = f.next_esc(";"); v2s32 pos = basepos; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; v2s32 geom; - geom.X = stoi(f.next(",")); - geom.Y = stoi(f.next(";")); + geom.X = stoi(f.next_esc(",")); + geom.Y = stoi(f.next_esc(";")); infostream<<"list inv="<<name<<", listname="<<listname <<", pos=("<<pos.X<<","<<pos.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")" <<std::endl; - std::string start_i_s = f.next("]"); + std::string start_i_s = f.next_esc("]"); s32 start_i = 0; if(start_i_s != "") start_i = stoi(start_i_s); @@ -270,12 +270,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "image") { v2s32 pos = basepos; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; v2s32 geom; - geom.X = stof(f.next(",")) * (float)imgsize.X; - geom.Y = stof(f.next(";")) * (float)imgsize.Y; - std::string name = f.next("]"); + geom.X = stof(f.next_esc(",")) * (float)imgsize.X; + geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y; + std::string name = f.next_esc("]"); infostream<<"image name="<<name <<", pos=("<<pos.X<<","<<pos.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")" @@ -287,12 +287,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "item_image") { v2s32 pos = basepos; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; v2s32 geom; - geom.X = stof(f.next(",")) * (float)imgsize.X; - geom.Y = stof(f.next(";")) * (float)imgsize.Y; - std::string name = f.next("]"); + geom.X = stof(f.next_esc(",")) * (float)imgsize.X; + geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y; + std::string name = f.next_esc("]"); infostream<<"item name="<<name <<", pos=("<<pos.X<<","<<pos.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")" @@ -304,12 +304,12 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "background") { v2s32 pos = basepos; - pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2; - pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2; + pos.X += stof(f.next_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2; v2s32 geom; - geom.X = stof(f.next(",")) * (float)spacing.X; - geom.Y = stof(f.next(";")) * (float)spacing.Y; - std::string name = f.next("]"); + geom.X = stof(f.next_esc(",")) * (float)spacing.X; + geom.Y = stof(f.next_esc(";")) * (float)spacing.Y; + std::string name = f.next_esc("]"); infostream<<"image name="<<name <<", pos=("<<pos.X<<","<<pos.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")" @@ -320,8 +320,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } else if(type == "field" || type == "textarea") { - std::string fname = f.next(";"); - std::string flabel = f.next(";"); + std::string fname = f.next_esc(";"); + std::string flabel = f.next_esc(";"); if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos) { @@ -372,14 +372,14 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) - fname = f.next(";"); - flabel = f.next(";"); + fname = f.next_esc(";"); + flabel = f.next_esc(";"); if(bp_set != 2) errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl; } - std::string odefault = f.next("]"); + std::string odefault = f.next_esc("]"); std::string fdefault; // fdefault may contain a variable reference, which @@ -389,6 +389,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else fdefault = odefault; + fdefault = unescape_string(fdefault); + flabel = unescape_string(flabel); + FieldSpec spec = FieldSpec( narrow_to_wide(fname.c_str()), narrow_to_wide(flabel.c_str()), @@ -434,15 +437,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "label") { v2s32 pos = padding; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15)); - std::string flabel = f.next("]"); + std::string flabel = f.next_esc("]"); if(bp_set != 2) errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl; + flabel = unescape_string(flabel); + FieldSpec spec = FieldSpec( narrow_to_wide(""), narrow_to_wide(flabel.c_str()), @@ -455,19 +460,21 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "button" || type == "button_exit") { v2s32 pos = padding; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; v2s32 geom; - geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X); - pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2; + geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X); + pos.Y += (stof(f.next_esc(";")) * (float)imgsize.Y)/2; rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15); - std::string fname = f.next(";"); - std::string flabel = f.next("]"); + std::string fname = f.next_esc(";"); + std::string flabel = f.next_esc("]"); if(bp_set != 2) errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl; + flabel = unescape_string(flabel); + FieldSpec spec = FieldSpec( narrow_to_wide(fname.c_str()), narrow_to_wide(flabel.c_str()), @@ -483,20 +490,22 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "image_button" || type == "image_button_exit") { v2s32 pos = padding; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; v2s32 geom; - geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X); - geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y); + geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X); + geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y); rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - std::string fimage = f.next(";"); - std::string fname = f.next(";"); - std::string flabel = f.next("]"); + std::string fimage = f.next_esc(";"); + std::string fname = f.next_esc(";"); + std::string flabel = f.next_esc("]"); if(bp_set != 2) errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl; + flabel = unescape_string(flabel); + FieldSpec spec = FieldSpec( narrow_to_wide(fname.c_str()), narrow_to_wide(flabel.c_str()), @@ -519,15 +528,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else if(type == "item_image_button") { v2s32 pos = padding; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; + pos.X += stof(f.next_esc(",")) * (float)spacing.X; + pos.Y += stof(f.next_esc(";")) * (float)spacing.Y; v2s32 geom; - geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X); - geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y); + geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X); + geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y); rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - std::string fimage = f.next(";"); - std::string fname = f.next(";"); - std::string flabel = f.next("]"); + std::string fimage = f.next_esc(";"); + std::string fname = f.next_esc(";"); + std::string flabel = f.next_esc("]"); if(bp_set != 2) errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl; IItemDefManager *idef = m_gamedef->idef(); @@ -535,6 +544,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) item.deSerialize(fimage, idef); video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); std::string tooltip = item.getDefinition(idef).description; + flabel = unescape_string(flabel); FieldSpec spec = FieldSpec( narrow_to_wide(fname.c_str()), narrow_to_wide(flabel.c_str()), @@ -556,7 +566,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) else { // Ignore others - std::string ts = f.next("]"); + std::string ts = f.next_esc("]"); infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\"" <<std::endl; } diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index aee16736e..17b202b18 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -209,11 +209,11 @@ protected: IFormSource *m_form_src; TextDest *m_text_dst; - core::array<ListDrawSpec> m_inventorylists; - core::array<ImageDrawSpec> m_backgrounds; - core::array<ImageDrawSpec> m_images; - core::array<ImageDrawSpec> m_itemimages; - core::array<FieldSpec> m_fields; + std::vector<ListDrawSpec> m_inventorylists; + std::vector<ImageDrawSpec> m_backgrounds; + std::vector<ImageDrawSpec> m_images; + std::vector<ImageDrawSpec> m_itemimages; + std::vector<FieldSpec> m_fields; ItemSpec *m_selected_item; u32 m_selected_amount; diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index c2e68579e..4c2030039 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -42,6 +42,43 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "subgame.h" +#define ARRAYLEN(x) (sizeof(x) / sizeof((x)[0])) +#define LSTRING(x) LSTRING_(x) +#define LSTRING_(x) L##x + +const wchar_t *contrib_core_strs[] = { + L"Perttu Ahola (celeron55) <celeron55@gmail.com>", + L"Ryan Kwolek (kwolekr) <kwolekr@minetest.net>", + L"PilzAdam <pilzadam@minetest.net>", + L"Ilya Zhuravlev (thexyz) <xyz@minetest.net>", + L"Lisa Milne (darkrose) <lisa@ltmnet.com>", + L"Maciej Kasatkin (RealBadAngel) <mk@realbadangel.pl>", + L"proller <proler@gmail.com>" +}; + +const wchar_t *contrib_active_strs[] = { + L"sfan5 <sfan5@live.de>", + L"sapier <sapier@gmx.net>", + L"Vanessa Ezekowitz (VanessaE) <vanessaezekowitz@gmail.com>", + L"Jurgen Doser (doserj) <jurgen.doser@gmail.com>", + L"Jeija <jeija@mesecons.net>", + L"MirceaKitsune <mirceakitsune@gmail.com>", + L"ShadowNinja", + L"dannydark <the_skeleton_of_a_child@yahoo.co.uk>", + L"0gb.us <0gb.us@0gb.us>" +}; + +const wchar_t *contrib_previous_strs[] = { + L"kahrl <kahrl@gmx.net>", + L"Giuseppe Bilotta (Oblomov) <giuseppe.bilotta@gmail.com>", + L"Jonathan Neuschafer <j.neuschaefer@gmx.net>", + L"Nils Dagsson Moskopp (erlehmann) <nils@dieweltistgarnichtso.net>", + L"Constantin Wenger (SpeedProg) <constantin.wenger@googlemail.com>", + L"matttpt <matttpt@gmail.com>", + L"JacobF <queatz@gmail.com>" +}; + + struct CreateWorldDestMainMenu : public CreateWorldDest { CreateWorldDestMainMenu(GUIMainMenu *menu): @@ -106,6 +143,7 @@ enum GUI_ID_SHADERS_CB, GUI_ID_PRELOAD_ITEM_VISUALS_CB, GUI_ID_ENABLE_PARTICLES_CB, + GUI_ID_LIQUID_FINITE_CB, GUI_ID_DAMAGE_CB, GUI_ID_CREATIVE_CB, GUI_ID_PUBLIC_CB, @@ -119,6 +157,7 @@ enum GUI_ID_SERVERLIST, GUI_ID_SERVERLIST_TOGGLE, GUI_ID_SERVERLIST_DELETE, + GUI_ID_SERVERLIST_TITLE, }; enum @@ -209,7 +248,6 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) changeCtype(""); // Version - //if(m_data->selected_tab != TAB_CREDITS) { core::rect<s32> rect(0, 0, size.X, 40); rect += v2s32(4, 0); @@ -219,7 +257,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) } //v2s32 center(size.X/2, size.Y/2); - v2s32 c800(size.X/2-400, size.Y/2-300); + v2s32 c800(size.X/2-400, size.Y/2-270); m_topleft_client = c800 + v2s32(90, 70+50+30); m_size_client = v2s32(620, 270); @@ -237,7 +275,6 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) m_topleft_server = m_topleft_client + v2s32(0, m_size_client.Y+20); // Tabs -#if 1 { core::rect<s32> rect(0, 0, m_size_client.X, 30); rect += m_topleft_client + v2s32(0, -30); @@ -250,7 +287,6 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) e->addTab(wgettext("Credits")); e->setActiveTab(m_data->selected_tab); } -#endif if(m_data->selected_tab == TAB_SINGLEPLAYER) { @@ -259,7 +295,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 10, m_size_client.Y); rect += m_topleft_client + v2s32(15, 0); //const wchar_t *text = L"H\nY\nB\nR\nI\nD"; - const wchar_t *text = L"T\nA\nP\nE\n\nA\nN\nD\n\nG\nL\nU\nE"; + const wchar_t *text = L"S\nI\nN\nG\nL\nE\n \nP\nL\nA\nY\nE\nR\n"; gui::IGUIStaticText *t = Environment->addStaticText(text, rect, false, true, this, -1); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); @@ -392,13 +428,38 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) changeCtype(""); // Server List { - core::rect<s32> rect(0, 0, 390, 160); - rect += m_topleft_client + v2s32(50, 10); + core::rect<s32> rect(0, 0, 390, 140); + rect += m_topleft_client + v2s32(50, 30); gui::IGUIListBox *e = Environment->addListBox(rect, this, GUI_ID_SERVERLIST); e->setDrawBackground(true); - if (m_data->serverlist_show_available == false) +#if USE_CURL + if(m_data->selected_serverlist == SERVERLIST_FAVORITES) { m_data->servers = ServerList::getLocal(); + { + core::rect<s32> rect(0, 0, 390, 20); + rect += m_topleft_client + v2s32(50, 10); + Environment->addStaticText(wgettext("Favorites:"), + rect, false, true, this, GUI_ID_SERVERLIST_TITLE); + } + } else { + m_data->servers = ServerList::getOnline(); + { + core::rect<s32> rect(0, 0, 390, 20); + rect += m_topleft_client + v2s32(50, 10); + Environment->addStaticText(wgettext("Public Server List:"), + rect, false, true, this, GUI_ID_SERVERLIST_TITLE); + } + } +#else + m_data->servers = ServerList::getLocal(); + { + core::rect<s32> rect(0, 0, 390, 20); + rect += m_topleft_client + v2s32(50, 10); + Environment->addStaticText(wgettext("Favorites:"), + rect, false, true, this, GUI_ID_SERVERLIST_TITLE); + } +#endif updateGuiServerList(); e->setSelected(0); } @@ -435,7 +496,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_TOGGLE, wgettext("Show Public")); e->setIsPushButton(true); - if (m_data->serverlist_show_available) + if (m_data->selected_serverlist == SERVERLIST_PUBLIC) { e->setText(wgettext("Show Favorites")); e->setPressed(); @@ -448,7 +509,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) rect += m_topleft_client + v2s32(50+260+10, 180); gui::IGUIButton *e = Environment->addButton(rect, this, GUI_ID_SERVERLIST_DELETE, wgettext("Delete")); - if (m_data->serverlist_show_available) // Hidden on Show-Online mode + if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // Hidden when on public list e->setVisible(false); } // Start game button @@ -691,6 +752,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) GUI_ID_ENABLE_PARTICLES_CB, wgettext("Enable Particles")); } + { + core::rect<s32> rect(0, 0, option_w+20+20, 30); + rect += m_topleft_client + v2s32(option_x+175*2, option_y+20*3); + Environment->addCheckBox(m_data->liquid_finite, rect, this, + GUI_ID_LIQUID_FINITE_CB, wgettext("Finite liquid")); + } + // Key change button { core::rect<s32> rect(0, 0, 120, 30); @@ -706,7 +774,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) { // CREDITS { - core::rect<s32> rect(0, 0, 10, m_size_client.Y); + core::rect<s32> rect(0, 0, 9, m_size_client.Y); rect += m_topleft_client + v2s32(15, 0); const wchar_t *text = L"C\nR\nE\nD\nI\nT\nS"; gui::IGUIStaticText *t = @@ -714,15 +782,34 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); } { - core::rect<s32> rect(0, 0, 454, 250); - rect += m_topleft_client + v2s32(110, 50+35); - Environment->addStaticText(narrow_to_wide( - "Minetest " VERSION_STRING "\n" - "http://minetest.net/\n" - "\n" - "by Perttu Ahola <celeron55@gmail.com>\n" - "and contributors: PilzAdam, Taoki, tango_, kahrl (kaaaaaahrl?), darkrose, matttpt, erlehmann, SpeedProg, JacobF, teddydestodes, marktraceur, Jonathan Neuschäfer, thexyz, VanessaE, sfan5... and tens of more random people." - ).c_str(), rect, false, true, this, -1); + core::rect<s32> rect(0, 0, 130, 70); + rect += m_topleft_client + v2s32(35, 160); + Environment->addStaticText( + L"Minetest " LSTRING(VERSION_STRING) L"\nhttp://minetest.net/", + rect, false, true, this, -1); + } + { + video::SColor yellow(255, 255, 255, 0); + core::rect<s32> rect(0, 0, 450, 260); + rect += m_topleft_client + v2s32(168, 5); + + irr::gui::IGUIListBox *list = Environment->addListBox(rect, this); + + list->addItem(L"Core Developers"); + list->setItemOverrideColor(list->getItemCount() - 1, yellow); + for (int i = 0; i != ARRAYLEN(contrib_core_strs); i++) + list->addItem(contrib_core_strs[i]); + list->addItem(L""); + list->addItem(L"Active Contributors"); + list->setItemOverrideColor(list->getItemCount() - 1, yellow); + for (int i = 0; i != ARRAYLEN(contrib_active_strs); i++) + list->addItem(contrib_active_strs[i]); + list->addItem(L""); + list->addItem(L"Previous Contributors"); + list->setItemOverrideColor(list->getItemCount() - 1, yellow); + for (int i = 0; i != ARRAYLEN(contrib_previous_strs); i++) + list->addItem(contrib_previous_strs[i]); + list->addItem(L""); } } @@ -786,15 +873,15 @@ void GUIMainMenu::drawMenu() driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); } video::ITexture *logotexture = - driver->getTexture(getTexturePath("menulogo.png").c_str()); + driver->getTexture(getTexturePath("logo.png").c_str()); if(logotexture) { v2s32 logosize(logotexture->getOriginalSize().Width, logotexture->getOriginalSize().Height); - logosize *= 2; + core::rect<s32> rect(0,0,logosize.X,logosize.Y); rect += AbsoluteRect.UpperLeftCorner + m_topleft_client; - rect += v2s32(130, 50); + rect += v2s32(50, 60); driver->draw2DImage(logotexture, rect, core::rect<s32>(core::position2d<s32>(0,0), core::dimension2di(logotexture->getSize())), @@ -919,6 +1006,12 @@ void GUIMainMenu::readInput(MainMenuData *dst) } { + gui::IGUIElement *e = getElementFromId(GUI_ID_LIQUID_FINITE_CB); + if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX) + dst->liquid_finite = ((gui::IGUICheckBox*)e)->isChecked(); + } + + { gui::IGUIElement *e = getElementFromId(GUI_ID_WORLD_LISTBOX); if(e != NULL && e->getType() == gui::EGUIET_LIST_BOX) dst->selected_world = ((gui::IGUIListBox*)e)->getSelected(); @@ -1083,25 +1176,28 @@ bool GUIMainMenu::OnEvent(const SEvent& event) gui::IGUIElement *togglebutton = getElementFromId(GUI_ID_SERVERLIST_TOGGLE); gui::IGUIElement *deletebutton = getElementFromId(GUI_ID_SERVERLIST_DELETE); gui::IGUIListBox *serverlist = (gui::IGUIListBox*)getElementFromId(GUI_ID_SERVERLIST); - if (m_data->serverlist_show_available) // switch to favorite list + gui::IGUIElement *title = getElementFromId(GUI_ID_SERVERLIST_TITLE); + if (m_data->selected_serverlist == SERVERLIST_PUBLIC) // switch to favorite list { m_data->servers = ServerList::getLocal(); togglebutton->setText(wgettext("Show Public")); + title->setText(wgettext("Favorites:")); deletebutton->setVisible(true); updateGuiServerList(); serverlist->setSelected(0); + m_data->selected_serverlist = SERVERLIST_FAVORITES; } else // switch to online list { m_data->servers = ServerList::getOnline(); togglebutton->setText(wgettext("Show Favorites")); + title->setText(wgettext("Public Server List:")); deletebutton->setVisible(false); updateGuiServerList(); serverlist->setSelected(0); + m_data->selected_serverlist = SERVERLIST_PUBLIC; } serverListOnSelected(); - - m_data->serverlist_show_available = !m_data->serverlist_show_available; } #endif } diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h index a21f3b32a..a594ccd41 100644 --- a/src/guiMainMenu.h +++ b/src/guiMainMenu.h @@ -29,6 +29,11 @@ with this program; if not, write to the Free Software Foundation, Inc., class IGameCallback; +enum { + SERVERLIST_FAVORITES, + SERVERLIST_PUBLIC, +}; + struct MainMenuData { // These are in the native format of the gui elements @@ -52,6 +57,7 @@ struct MainMenuData int enable_shaders; bool preload_item_visuals; bool enable_particles; + bool liquid_finite; // Server options bool creative_mode; bool enable_damage; @@ -63,7 +69,7 @@ struct MainMenuData std::string create_world_gameid; bool only_refresh; - bool serverlist_show_available; // if false show local favorites only + int selected_serverlist; std::vector<WorldSpec> worlds; std::vector<SubgameSpec> games; @@ -84,7 +90,7 @@ struct MainMenuData // Actions only_refresh(false), - serverlist_show_available(false) + selected_serverlist(SERVERLIST_FAVORITES) {} }; diff --git a/src/inventory.cpp b/src/inventory.cpp index 7051b611f..d6815d329 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -903,6 +903,10 @@ void Inventory::deSerialize(std::istream &is) m_lists.push_back(list); } + else + { + throw SerializationError("invalid inventory specifier"); + } } } diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 5fd27fca3..72ce0e654 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -75,6 +75,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) } groups = def.groups; node_placement_prediction = def.node_placement_prediction; + sound_place = def.sound_place; return *this; } @@ -107,13 +108,17 @@ void ItemDefinition::reset() tool_capabilities = NULL; } groups.clear(); + sound_place = SimpleSoundSpec(); node_placement_prediction = ""; } -void ItemDefinition::serialize(std::ostream &os) const +void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const { - writeU8(os, 1); // version + if(protocol_version <= 17) + writeU8(os, 1); // version + else + writeU8(os, 2); // version writeU8(os, type); os<<serializeString(name); os<<serializeString(description); @@ -126,7 +131,7 @@ void ItemDefinition::serialize(std::ostream &os) const std::string tool_capabilities_s = ""; if(tool_capabilities){ std::ostringstream tmp_os(std::ios::binary); - tool_capabilities->serialize(tmp_os); + tool_capabilities->serialize(tmp_os, protocol_version); tool_capabilities_s = tmp_os.str(); } os<<serializeString(tool_capabilities_s); @@ -137,6 +142,11 @@ void ItemDefinition::serialize(std::ostream &os) const writeS16(os, i->second); } os<<serializeString(node_placement_prediction); + if(protocol_version > 17){ + //serializeSimpleSoundSpec(sound_place, os); + os<<serializeString(sound_place.name); + writeF1000(os, sound_place.gain); + } } void ItemDefinition::deSerialize(std::istream &is) @@ -146,7 +156,7 @@ void ItemDefinition::deSerialize(std::istream &is) // Deserialize int version = readU8(is); - if(version != 1) + if(version != 1 && version != 2) throw SerializationError("unsupported ItemDefinition version"); type = (enum ItemType)readU8(is); name = deSerializeString(is); @@ -171,10 +181,24 @@ void ItemDefinition::deSerialize(std::istream &is) int value = readS16(is); groups[name] = value; } + if(version == 1){ + // We cant be sure that node_placement_prediction is send in version 1 + try{ + node_placement_prediction = deSerializeString(is); + }catch(SerializationError &e) {}; + // Set the old default sound + sound_place.name = "default_place_node"; + sound_place.gain = 0.5; + } else if(version == 2) { + node_placement_prediction = deSerializeString(is); + //deserializeSimpleSoundSpec(sound_place, is); + sound_place.name = deSerializeString(is); + sound_place.gain = readF1000(is); + } // If you add anything here, insert it primarily inside the try-catch // block to not need to increase the version. try{ - node_placement_prediction = deSerializeString(is); + }catch(SerializationError &e) {}; } @@ -211,8 +235,8 @@ public: virtual ~CItemDefManager() { #ifndef SERVER - const core::list<ClientCached*> &values = m_clientcached.getValues(); - for(core::list<ClientCached*>::ConstIterator + const std::list<ClientCached*> &values = m_clientcached.getValues(); + for(std::list<ClientCached*>::const_iterator i = values.begin(); i != values.end(); ++i) { ClientCached *cc = *i; @@ -547,7 +571,7 @@ public: m_aliases[name] = convert_to; } } - void serialize(std::ostream &os) + void serialize(std::ostream &os, u16 protocol_version) { writeU8(os, 0); // version u16 count = m_item_definitions.size(); @@ -559,7 +583,7 @@ public: ItemDefinition *def = i->second; // Serialize ItemDefinition and write wrapped in a string std::ostringstream tmp_os(std::ios::binary); - def->serialize(tmp_os); + def->serialize(tmp_os, protocol_version); os<<serializeString(tmp_os.str()); } writeU16(os, m_aliases.size()); @@ -599,7 +623,7 @@ public: void processQueue(IGameDef *gamedef) { #ifndef SERVER - while(m_get_clientcached_queue.size() > 0) + while(!m_get_clientcached_queue.empty()) { GetRequest<std::string, ClientCached*, u8, u8> request = m_get_clientcached_queue.pop(); diff --git a/src/itemdef.h b/src/itemdef.h index dd20ba353..08c9c8358 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <iostream> #include <set> #include "itemgroup.h" +#include "sound.h" class IGameDef; struct ToolCapabilities; @@ -66,6 +67,7 @@ struct ItemDefinition // May be NULL. If non-NULL, deleted by destructor ToolCapabilities *tool_capabilities; ItemGroupList groups; + SimpleSoundSpec sound_place; // Client shall immediately place this node when player places the item. // Server will update the precise end result a moment later. @@ -80,7 +82,7 @@ struct ItemDefinition ItemDefinition& operator=(const ItemDefinition &def); ~ItemDefinition(); void reset(); - void serialize(std::ostream &os) const; + void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); private: void resetInitial(); @@ -109,7 +111,7 @@ public: IGameDef *gamedef) const=0; #endif - virtual void serialize(std::ostream &os)=0; + virtual void serialize(std::ostream &os, u16 protocol_version)=0; }; class IWritableItemDefManager : public IItemDefManager @@ -146,7 +148,7 @@ public: virtual void registerAlias(const std::string &name, const std::string &convert_to)=0; - virtual void serialize(std::ostream &os)=0; + virtual void serialize(std::ostream &os, u16 protocol_version)=0; virtual void deSerialize(std::istream &is)=0; // Do stuff asked by threads that can only be done in the main thread diff --git a/src/json/CMakeLists.txt b/src/json/CMakeLists.txt index 0957799aa..5887d523a 100644 --- a/src/json/CMakeLists.txt +++ b/src/json/CMakeLists.txt @@ -6,9 +6,9 @@ else( UNIX ) set(json_platform_LIBS "") endif( UNIX ) -add_library(json ${json_SRCS}) +add_library(jsoncpp ${json_SRCS}) target_link_libraries( - json + jsoncpp ${json_platform_LIBS} ) diff --git a/src/keycode.cpp b/src/keycode.cpp index 8aadab2f1..96631b4ea 100644 --- a/src/keycode.cpp +++ b/src/keycode.cpp @@ -345,17 +345,16 @@ const KeyPress NumberKey[] = { */ // A simple cache for quicker lookup -core::map<std::string, KeyPress> g_key_setting_cache; +std::map<std::string, KeyPress> g_key_setting_cache; KeyPress getKeySetting(const char *settingname) { - core::map<std::string, KeyPress>::Node *n; + std::map<std::string, KeyPress>::iterator n; n = g_key_setting_cache.find(settingname); - if(n) - return n->getValue(); - g_key_setting_cache.insert(settingname, - g_settings->get(settingname).c_str()); - return g_key_setting_cache.find(settingname)->getValue(); + if(n != g_key_setting_cache.end()) + return n->second; + g_key_setting_cache[settingname] = g_settings->get(settingname).c_str(); + return g_key_setting_cache.find(settingname)->second; } void clearKeyCache() diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 0554302e0..ee9b41c58 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "nodedef.h" #include "settings.h" +#include "environment.h" #include "map.h" #include "util/numeric.h" @@ -57,9 +58,10 @@ LocalPlayer::~LocalPlayer() { } -void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, - core::list<CollisionInfo> *collision_info) +void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d, + std::list<CollisionInfo> *collision_info) { + Map *map = &env->getMap(); INodeDefManager *nodemgr = m_gamedef->ndef(); v3f position = getPosition(); @@ -97,15 +99,15 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, if(in_liquid) { v3s16 pp = floatToInt(position + v3f(0,BS*0.1,0), BS); - in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); - liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity; + in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid(); + liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity; } // If not in liquid, the threshold of going in is at lower y else { v3s16 pp = floatToInt(position + v3f(0,BS*0.5,0), BS); - in_liquid = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); - liquid_viscosity = nodemgr->get(map.getNode(pp).getContent()).liquid_viscosity; + in_liquid = nodemgr->get(map->getNode(pp).getContent()).isLiquid(); + liquid_viscosity = nodemgr->get(map->getNode(pp).getContent()).liquid_viscosity; } } catch(InvalidPositionException &e) @@ -118,7 +120,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, */ try{ v3s16 pp = floatToInt(position + v3f(0,0,0), BS); - in_liquid_stable = nodemgr->get(map.getNode(pp).getContent()).isLiquid(); + in_liquid_stable = nodemgr->get(map->getNode(pp).getContent()).isLiquid(); } catch(InvalidPositionException &e) { @@ -132,8 +134,8 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, try { v3s16 pp = floatToInt(position + v3f(0,0.5*BS,0), BS); v3s16 pp2 = floatToInt(position + v3f(0,-0.2*BS,0), BS); - is_climbing = ((nodemgr->get(map.getNode(pp).getContent()).climbable || - nodemgr->get(map.getNode(pp2).getContent()).climbable) && !free_move); + is_climbing = ((nodemgr->get(map->getNode(pp).getContent()).climbable || + nodemgr->get(map->getNode(pp2).getContent()).climbable) && !free_move); } catch(InvalidPositionException &e) { @@ -197,7 +199,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, v3f accel_f = v3f(0,0,0); - collisionMoveResult result = collisionMoveSimple(&map, m_gamedef, + collisionMoveResult result = collisionMoveSimple(env, m_gamedef, pos_max_d, playerbox, player_stepheight, dtime, position, m_speed, accel_f); @@ -219,7 +221,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, */ v3s16 current_node = floatToInt(position - v3f(0,BS/2,0), BS); if(m_sneak_node_exists && - nodemgr->get(map.getNodeNoEx(m_old_node_below)).name == "air" && + nodemgr->get(map->getNodeNoEx(m_old_node_below)).name == "air" && m_old_node_below_type != "air") { // Old node appears to have been removed; that is, @@ -227,7 +229,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, m_need_to_get_new_sneak_node = false; m_sneak_node_exists = false; } - else if(nodemgr->get(map.getNodeNoEx(current_node)).name != "air") + else if(nodemgr->get(map->getNodeNoEx(current_node)).name != "air") { // We are on something, so make sure to recalculate the sneak // node. @@ -267,10 +269,10 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, try{ // The node to be sneaked on has to be walkable - if(nodemgr->get(map.getNode(p)).walkable == false) + if(nodemgr->get(map->getNode(p)).walkable == false) continue; // And the node above it has to be nonwalkable - if(nodemgr->get(map.getNode(p+v3s16(0,1,0))).walkable == true) + if(nodemgr->get(map->getNode(p+v3s16(0,1,0))).walkable == true) continue; } catch(InvalidPositionException &e) @@ -331,7 +333,7 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, { camera_barely_in_ceiling = false; v3s16 camera_np = floatToInt(getEyePosition(), BS); - MapNode n = map.getNodeNoEx(camera_np); + MapNode n = map->getNodeNoEx(camera_np); if(n.getContent() != CONTENT_IGNORE){ if(nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2){ camera_barely_in_ceiling = true; @@ -343,21 +345,21 @@ void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d, Update the node last under the player */ m_old_node_below = floatToInt(position - v3f(0,BS/2,0), BS); - m_old_node_below_type = nodemgr->get(map.getNodeNoEx(m_old_node_below)).name; + m_old_node_below_type = nodemgr->get(map->getNodeNoEx(m_old_node_below)).name; /* Check properties of the node on which the player is standing */ - const ContentFeatures &f = nodemgr->get(map.getNodeNoEx(getStandingNodePos())); + const ContentFeatures &f = nodemgr->get(map->getNodeNoEx(getStandingNodePos())); // Determine if jumping is possible m_can_jump = touching_ground && !in_liquid; if(itemgroup_get(f.groups, "disable_jump")) m_can_jump = false; } -void LocalPlayer::move(f32 dtime, Map &map, f32 pos_max_d) +void LocalPlayer::move(f32 dtime, ClientEnvironment *env, f32 pos_max_d) { - move(dtime, map, pos_max_d, NULL); + move(dtime, env, pos_max_d, NULL); } void LocalPlayer::applyControl(float dtime) diff --git a/src/localplayer.h b/src/localplayer.h index f372c787d..17434d379 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -21,6 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define LOCALPLAYER_HEADER #include "player.h" +#include <list> + +class ClientEnvironment; class LocalPlayer : public Player { @@ -37,9 +40,9 @@ public: v3f overridePosition; - void move(f32 dtime, Map &map, f32 pos_max_d, - core::list<CollisionInfo> *collision_info); - void move(f32 dtime, Map &map, f32 pos_max_d); + void move(f32 dtime, ClientEnvironment *env, f32 pos_max_d, + std::list<CollisionInfo> *collision_info); + void move(f32 dtime, ClientEnvironment *env, f32 pos_max_d); void applyControl(float dtime); diff --git a/src/main.cpp b/src/main.cpp index cfd643ac7..2e57a8c20 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -50,6 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "debug.h" #include "test.h" +#include "clouds.h" #include "server.h" #include "constants.h" #include "porting.h" @@ -132,7 +133,12 @@ MainGameCallback *g_gamecallback = NULL; u32 getTimeMs() { /* Use imprecise system calls directly (from porting.h) */ - return porting::getTimeMs(); + return porting::getTime(PRECISION_MILLI); +} + +u32 getTime(TimePrecision prec) +{ + return porting::getTime(prec); } #else @@ -141,7 +147,7 @@ u32 getTimeMs() class TimeGetter { public: - virtual u32 getTime() = 0; + virtual u32 getTime(TimePrecision prec) = 0; }; // A precise irrlicht one @@ -151,11 +157,15 @@ public: IrrlichtTimeGetter(IrrlichtDevice *device): m_device(device) {} - u32 getTime() - { - if(m_device == NULL) - return 0; - return m_device->getTimer()->getRealTime(); + u32 getTime(TimePrecision prec) + { + if (prec == PRECISION_MILLI) { + if(m_device == NULL) + return 0; + return m_device->getTimer()->getRealTime(); + } else { + return porting::getTime(prec); + } } private: IrrlichtDevice *m_device; @@ -164,9 +174,9 @@ private: class SimpleTimeGetter: public TimeGetter { public: - u32 getTime() + u32 getTime(TimePrecision prec) { - return porting::getTimeMs(); + return porting::getTime(prec); } }; @@ -178,7 +188,13 @@ u32 getTimeMs() { if(g_timegetter == NULL) return 0; - return g_timegetter->getTime(); + return g_timegetter->getTime(PRECISION_MILLI); +} + +u32 getTime(TimePrecision prec) { + if (g_timegetter == NULL) + return 0; + return g_timegetter->getTime(prec); } #endif @@ -596,50 +612,120 @@ private: bool rightreleased; }; -void drawMenuBackground(video::IVideoDriver* driver) -{ +//Draw the tiled menu background +void drawMenuBackground(video::IVideoDriver* driver) { core::dimension2d<u32> screensize = driver->getScreenSize(); + + std::string path = getTexturePath("menubg.png"); + if (path[0]) { + video::ITexture *bgtexture = + driver->getTexture(path.c_str()); + + if (bgtexture) { + s32 scaledsize = 128; - video::ITexture *bgtexture = - driver->getTexture(getTexturePath("menubg.png").c_str()); - if(bgtexture) - { - s32 scaledsize = 128; - - // The important difference between destsize and screensize is - // that destsize is rounded to whole scaled pixels. - // These formulas use component-wise multiplication and division of v2u32. - v2u32 texturesize = bgtexture->getSize(); - v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1); - v2u32 destsize = scaledsize * sourcesize / texturesize; + // The important difference between destsize and screensize is + // that destsize is rounded to whole scaled pixels. + // These formulas use component-wise multiplication and division of v2u32. + v2u32 texturesize = bgtexture->getSize(); + v2u32 sourcesize = texturesize * screensize / scaledsize + v2u32(1,1); + v2u32 destsize = scaledsize * sourcesize / texturesize; - // Default texture wrapping mode in Irrlicht is ETC_REPEAT. - driver->draw2DImage(bgtexture, - core::rect<s32>(0, 0, destsize.X, destsize.Y), - core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), - NULL, NULL, true); + // Default texture wrapping mode in Irrlicht is ETC_REPEAT. + driver->draw2DImage(bgtexture, + core::rect<s32>(0, 0, destsize.X, destsize.Y), + core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y), + NULL, NULL, true); + } } - - video::ITexture *logotexture = - driver->getTexture(getTexturePath("menulogo.png").c_str()); - if(logotexture) - { - v2s32 logosize(logotexture->getOriginalSize().Width, - logotexture->getOriginalSize().Height); - logosize *= 4; - - video::SColor bgcolor(255,50,50,50); - core::rect<s32> bgrect(0, screensize.Height-logosize.Y-20, - screensize.Width, screensize.Height); - driver->draw2DRectangle(bgcolor, bgrect, NULL); - - core::rect<s32> rect(0,0,logosize.X,logosize.Y); - rect += v2s32(screensize.Width/2,screensize.Height-10-logosize.Y); - rect -= v2s32(logosize.X/2, 0); - driver->draw2DImage(logotexture, rect, - core::rect<s32>(core::position2d<s32>(0,0), - core::dimension2di(logotexture->getSize())), - NULL, NULL, true); +} + +//Draw the footer at the bottom of the window +void drawMenuFooter(video::IVideoDriver* driver, bool clouds) { + core::dimension2d<u32> screensize = driver->getScreenSize(); + std::string path = getTexturePath(clouds ? + "menufooter_clouds.png" : "menufooter.png"); + if (path[0]) { + video::ITexture *footertexture = + driver->getTexture(path.c_str()); + + if (footertexture) { + f32 mult = (((f32)screensize.Width)) / + ((f32)footertexture->getOriginalSize().Width); + + v2s32 footersize(((f32)footertexture->getOriginalSize().Width) * mult, + ((f32)footertexture->getOriginalSize().Height) * mult); + + // Don't draw the footer if there isn't enough room + s32 free_space = (((s32)screensize.Height)-320)/2; + if (free_space > footersize.Y) { + core::rect<s32> rect(0,0,footersize.X,footersize.Y); + rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y); + rect -= v2s32(footersize.X/2, 0); + + driver->draw2DImage(footertexture, rect, + core::rect<s32>(core::position2d<s32>(0,0), + core::dimension2di(footertexture->getSize())), + NULL, NULL, true); + } + } + } +} + +// Draw the Header over the main menu +void drawMenuHeader(video::IVideoDriver* driver) { + core::dimension2d<u32> screensize = driver->getScreenSize(); + + std::string path = getTexturePath("menuheader.png"); + if (path[0]) { + video::ITexture *splashtexture = + driver->getTexture(path.c_str()); + + if(splashtexture) { + //v2s32 splashsize((splashtexture->getOriginalSize().Width*100)/ + // splashtexture->getOriginalSize().Height, 80); + + f32 mult = (((f32)screensize.Width / 2)) / + ((f32)splashtexture->getOriginalSize().Width); + + v2s32 splashsize(((f32)splashtexture->getOriginalSize().Width) * mult, + ((f32)splashtexture->getOriginalSize().Height) * mult); + + // Don't draw the header is there isn't enough room + s32 free_space = (((s32)screensize.Height)-320)/2; + if (free_space > splashsize.Y) { + core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y); + splashrect += v2s32((screensize.Width/2)-(splashsize.X/2), + ((free_space/2)-splashsize.Y/2)+10); + + video::SColor bgcolor(255,50,50,50); + + driver->draw2DImage(splashtexture, splashrect, + core::rect<s32>(core::position2d<s32>(0,0), + core::dimension2di(splashtexture->getSize())), + NULL, NULL, true); + } + } + } +} + +// Draw the Splash over the clouds and under the main menu +void drawMenuSplash(video::IVideoDriver* driver) { + core::dimension2d<u32> screensize = driver->getScreenSize(); + if (getTexturePath("menusplash.png") != "") { + video::ITexture *splashtexture = + driver->getTexture(getTexturePath("menusplash.png").c_str()); + + if(splashtexture) { + core::rect<s32> splashrect(0, 0, screensize.Width, screensize.Height); + + video::SColor bgcolor(255,50,50,50); + + driver->draw2DImage(splashtexture, splashrect, + core::rect<s32>(core::position2d<s32>(0,0), + core::dimension2di(splashtexture->getSize())), + NULL, NULL, true); + } } } @@ -700,14 +786,14 @@ void SpeedTests() } { - TimeTaker timer("Testing core::map speed"); + TimeTaker timer("Testing std::map speed"); - core::map<v2s16, f32> map1; + std::map<v2s16, f32> map1; tempf = -324; const s16 ii=300; for(s16 y=0; y<ii; y++){ for(s16 x=0; x<ii; x++){ - map1.insert(v2s16(x,y), tempf); + map1[v2s16(x,y)] = tempf; tempf += 1; } } @@ -734,7 +820,7 @@ void SpeedTests() } } // Do at least 10ms - while(timer.getTime() < 10); + while(timer.getTimerTime() < 10); u32 dtime = timer.stop(); u32 per_ms = n / dtime; @@ -788,48 +874,48 @@ int main(int argc, char *argv[]) */ // List all allowed options - core::map<std::string, ValueSpec> allowed_options; - allowed_options.insert("help", ValueSpec(VALUETYPE_FLAG, - _("Show allowed options"))); - allowed_options.insert("config", ValueSpec(VALUETYPE_STRING, - _("Load configuration from specified file"))); - allowed_options.insert("port", ValueSpec(VALUETYPE_STRING, - _("Set network port (UDP)"))); - allowed_options.insert("disable-unittests", ValueSpec(VALUETYPE_FLAG, - _("Disable unit tests"))); - allowed_options.insert("enable-unittests", ValueSpec(VALUETYPE_FLAG, - _("Enable unit tests"))); - allowed_options.insert("map-dir", ValueSpec(VALUETYPE_STRING, - _("Same as --world (deprecated)"))); - allowed_options.insert("world", ValueSpec(VALUETYPE_STRING, - _("Set world path (implies local game) ('list' lists all)"))); - allowed_options.insert("worldname", ValueSpec(VALUETYPE_STRING, - _("Set world by name (implies local game)"))); - allowed_options.insert("info", ValueSpec(VALUETYPE_FLAG, - _("Print more information to console"))); - allowed_options.insert("verbose", ValueSpec(VALUETYPE_FLAG, - _("Print even more information to console"))); - allowed_options.insert("trace", ValueSpec(VALUETYPE_FLAG, - _("Print enormous amounts of information to log and console"))); - allowed_options.insert("logfile", ValueSpec(VALUETYPE_STRING, - _("Set logfile path ('' = no logging)"))); - allowed_options.insert("gameid", ValueSpec(VALUETYPE_STRING, - _("Set gameid (\"--gameid list\" prints available ones)"))); + std::map<std::string, ValueSpec> allowed_options; + allowed_options.insert(std::make_pair("help", ValueSpec(VALUETYPE_FLAG, + _("Show allowed options")))); + allowed_options.insert(std::make_pair("config", ValueSpec(VALUETYPE_STRING, + _("Load configuration from specified file")))); + allowed_options.insert(std::make_pair("port", ValueSpec(VALUETYPE_STRING, + _("Set network port (UDP)")))); + allowed_options.insert(std::make_pair("disable-unittests", ValueSpec(VALUETYPE_FLAG, + _("Disable unit tests")))); + allowed_options.insert(std::make_pair("enable-unittests", ValueSpec(VALUETYPE_FLAG, + _("Enable unit tests")))); + allowed_options.insert(std::make_pair("map-dir", ValueSpec(VALUETYPE_STRING, + _("Same as --world (deprecated)")))); + allowed_options.insert(std::make_pair("world", ValueSpec(VALUETYPE_STRING, + _("Set world path (implies local game) ('list' lists all)")))); + allowed_options.insert(std::make_pair("worldname", ValueSpec(VALUETYPE_STRING, + _("Set world by name (implies local game)")))); + allowed_options.insert(std::make_pair("info", ValueSpec(VALUETYPE_FLAG, + _("Print more information to console")))); + allowed_options.insert(std::make_pair("verbose", ValueSpec(VALUETYPE_FLAG, + _("Print even more information to console")))); + allowed_options.insert(std::make_pair("trace", ValueSpec(VALUETYPE_FLAG, + _("Print enormous amounts of information to log and console")))); + allowed_options.insert(std::make_pair("logfile", ValueSpec(VALUETYPE_STRING, + _("Set logfile path ('' = no logging)")))); + allowed_options.insert(std::make_pair("gameid", ValueSpec(VALUETYPE_STRING, + _("Set gameid (\"--gameid list\" prints available ones)")))); #ifndef SERVER - allowed_options.insert("speedtests", ValueSpec(VALUETYPE_FLAG, - _("Run speed tests"))); - allowed_options.insert("address", ValueSpec(VALUETYPE_STRING, - _("Address to connect to. ('' = local game)"))); - allowed_options.insert("random-input", ValueSpec(VALUETYPE_FLAG, - _("Enable random user input, for testing"))); - allowed_options.insert("server", ValueSpec(VALUETYPE_FLAG, - _("Run dedicated server"))); - allowed_options.insert("name", ValueSpec(VALUETYPE_STRING, - _("Set player name"))); - allowed_options.insert("password", ValueSpec(VALUETYPE_STRING, - _("Set password"))); - allowed_options.insert("go", ValueSpec(VALUETYPE_FLAG, - _("Disable main menu"))); + allowed_options.insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG, + _("Run speed tests")))); + allowed_options.insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING, + _("Address to connect to. ('' = local game)")))); + allowed_options.insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG, + _("Enable random user input, for testing")))); + allowed_options.insert(std::make_pair("server", ValueSpec(VALUETYPE_FLAG, + _("Run dedicated server")))); + allowed_options.insert(std::make_pair("name", ValueSpec(VALUETYPE_STRING, + _("Set player name")))); + allowed_options.insert(std::make_pair("password", ValueSpec(VALUETYPE_STRING, + _("Set password")))); + allowed_options.insert(std::make_pair("go", ValueSpec(VALUETYPE_FLAG, + _("Disable main menu")))); #endif Settings cmd_args; @@ -839,20 +925,20 @@ int main(int argc, char *argv[]) if(ret == false || cmd_args.getFlag("help") || cmd_args.exists("nonopt1")) { dstream<<_("Allowed options:")<<std::endl; - for(core::map<std::string, ValueSpec>::Iterator - i = allowed_options.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, ValueSpec>::iterator + i = allowed_options.begin(); + i != allowed_options.end(); ++i) { std::ostringstream os1(std::ios::binary); - os1<<" --"<<i.getNode()->getKey(); - if(i.getNode()->getValue().type == VALUETYPE_FLAG) + os1<<" --"<<i->first; + if(i->second.type == VALUETYPE_FLAG) {} else os1<<_(" <value>"); dstream<<padStringRight(os1.str(), 24); - if(i.getNode()->getValue().help != NULL) - dstream<<i.getNode()->getValue().help; + if(i->second.help != NULL) + dstream<<i->second.help; dstream<<std::endl; } @@ -953,7 +1039,7 @@ int main(int argc, char *argv[]) } else { - core::array<std::string> filenames; + std::vector<std::string> filenames; filenames.push_back(porting::path_user + DIR_DELIM + "minetest.conf"); // Legacy configuration file location @@ -1279,6 +1365,14 @@ int main(int argc, char *argv[]) driverType = video::EDT_DIRECT3D9; else if(driverstring == "opengl") driverType = video::EDT_OPENGL; +#ifdef _IRR_COMPILE_WITH_OGLES1_ + else if(driverstring == "ogles1") + driverType = video::EDT_OGLES1; +#endif +#ifdef _IRR_COMPILE_WITH_OGLES2_ + else if(driverstring == "ogles2") + driverType = video::EDT_OGLES2; +#endif else { errorstream<<"WARNING: Invalid video_driver specified; defaulting " @@ -1462,6 +1556,8 @@ int main(int argc, char *argv[]) MainMenuData menudata; if(g_settings->exists("selected_mainmenu_tab")) menudata.selected_tab = g_settings->getS32("selected_mainmenu_tab"); + if(g_settings->exists("selected_serverlist")) + menudata.selected_serverlist = g_settings->getS32("selected_serverlist"); menudata.address = narrow_to_wide(address); menudata.name = narrow_to_wide(playername); menudata.port = narrow_to_wide(itos(port)); @@ -1478,6 +1574,7 @@ int main(int argc, char *argv[]) menudata.enable_shaders = g_settings->getS32("enable_shaders"); menudata.preload_item_visuals = g_settings->getBool("preload_item_visuals"); menudata.enable_particles = g_settings->getBool("enable_particles"); + menudata.liquid_finite = g_settings->getBool("liquid_finite"); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, menudata.mip_map); menudata.creative_mode = g_settings->getBool("creative_mode"); menudata.enable_damage = g_settings->getBool("enable_damage"); @@ -1518,7 +1615,7 @@ int main(int argc, char *argv[]) if(skip_main_menu == false) { video::IVideoDriver* driver = device->getVideoDriver(); - + float fps_max = g_settings->getFloat("fps_max"); infostream<<"Waiting for other menus"<<std::endl; while(device->run() && kill == false) { @@ -1540,6 +1637,22 @@ int main(int argc, char *argv[]) &g_menumgr, &menudata, g_gamecallback); menu->allowFocusRemoval(true); + // Clouds for the main menu + bool cloud_menu_background = false; + Clouds *clouds = NULL; + if (g_settings->getBool("menu_clouds")) { + cloud_menu_background = true; + clouds = new Clouds(smgr->getRootSceneNode(), + smgr, -1, rand(), 100); + clouds->update(v2f(0, 0), video::SColor(255,200,200,255)); + + // A camera to see the clouds + scene::ICameraSceneNode* camera; + camera = smgr->addCameraSceneNode(0, + v3f(0,0,0), v3f(0, 60, 100)); + camera->setFarValue(10000); + } + if(error_message != L"") { verbosestream<<"error_message = " @@ -1552,6 +1665,9 @@ int main(int argc, char *argv[]) error_message = L""; } + // Time is in milliseconds, for clouds + u32 lasttime = device->getTimer()->getTime(); + infostream<<"Created main menu"<<std::endl; while(device->run() && kill == false) @@ -1559,26 +1675,75 @@ int main(int argc, char *argv[]) if(menu->getStatus() == true) break; - //driver->beginScene(true, true, video::SColor(255,0,0,0)); - driver->beginScene(true, true, video::SColor(255,128,128,128)); + // Time calc for the clouds + f32 dtime; // in seconds + if (cloud_menu_background) { + u32 time = device->getTimer()->getTime(); + if(time > lasttime) + dtime = (time - lasttime) / 1000.0; + else + dtime = 0; + lasttime = time; + } - drawMenuBackground(driver); + //driver->beginScene(true, true, video::SColor(255,0,0,0)); + driver->beginScene(true, true, video::SColor(255,140,186,250)); + + if (cloud_menu_background) { + // *3 otherwise the clouds would move very slowly + clouds->step(dtime*3); + clouds->render(); + smgr->drawAll(); + drawMenuSplash(driver); + drawMenuFooter(driver, true); + drawMenuHeader(driver); + } else { + drawMenuBackground(driver); + drawMenuFooter(driver, false); + } guienv->drawAll(); - + driver->endScene(); // On some computers framerate doesn't seem to be // automatically limited - sleep_ms(25); + if (cloud_menu_background) { + // Time of frame without fps limit + float busytime; + u32 busytime_u32; + // not using getRealTime is necessary for wine + u32 time = device->getTimer()->getTime(); + if(time > lasttime) + busytime_u32 = time - lasttime; + else + busytime_u32 = 0; + busytime = busytime_u32 / 1000.0; + + // FPS limiter + u32 frametime_min = 1000./fps_max; + + if(busytime_u32 < frametime_min) { + u32 sleeptime = frametime_min - busytime_u32; + device->sleep(sleeptime); + } + } else { + sleep_ms(25); + } } infostream<<"Dropping main menu"<<std::endl; menu->drop(); + if (cloud_menu_background) { + clouds->drop(); + smgr->clear(); + } } playername = wide_to_narrow(menudata.name); + if (playername == "") + playername = std::string("Guest") + itos(myrand_range(1000,9999)); password = translatePassword(playername, menudata.password); //infostream<<"Main: password hash: '"<<password<<"'"<<std::endl; @@ -1589,6 +1754,7 @@ int main(int argc, char *argv[]) simple_singleplayer_mode = menudata.simple_singleplayer_mode; // Save settings g_settings->setS32("selected_mainmenu_tab", menudata.selected_tab); + g_settings->setS32("selected_serverlist", menudata.selected_serverlist); g_settings->set("new_style_leaves", itos(menudata.fancy_trees)); g_settings->set("smooth_lighting", itos(menudata.smooth_lighting)); g_settings->set("enable_3d_clouds", itos(menudata.clouds_3d)); @@ -1602,6 +1768,7 @@ int main(int argc, char *argv[]) g_settings->setS32("enable_shaders", menudata.enable_shaders); g_settings->set("preload_item_visuals", itos(menudata.preload_item_visuals)); g_settings->set("enable_particles", itos(menudata.enable_particles)); + g_settings->set("liquid_finite", itos(menudata.liquid_finite)); g_settings->set("creative_mode", itos(menudata.creative_mode)); g_settings->set("enable_damage", itos(menudata.enable_damage)); diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h index ce7684f11..a3133686b 100644 --- a/src/mainmenumanager.h +++ b/src/mainmenumanager.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" // assert #include "modalMenu.h" #include "guiPauseMenu.h" //For IGameCallback +#include <list> extern gui::IGUIEnvironment* guienv; extern gui::IGUIStaticText *guiroot; @@ -37,15 +38,15 @@ class MainMenuManager : public IMenuManager public: virtual void createdMenu(GUIModalMenu *menu) { - for(core::list<GUIModalMenu*>::Iterator + for(std::list<GUIModalMenu*>::iterator i = m_stack.begin(); - i != m_stack.end(); i++) + i != m_stack.end(); ++i) { assert(*i != menu); } if(m_stack.size() != 0) - (*m_stack.getLast())->setVisible(false); + m_stack.back()->setVisible(false); m_stack.push_back(menu); } @@ -55,9 +56,9 @@ public: bool removed_entry; do{ removed_entry = false; - for(core::list<GUIModalMenu*>::Iterator + for(std::list<GUIModalMenu*>::iterator i = m_stack.begin(); - i != m_stack.end(); i++) + i != m_stack.end(); ++i) { if(*i == menu) { @@ -73,7 +74,7 @@ public: m_stack.erase(i);*/ if(m_stack.size() != 0) - (*m_stack.getLast())->setVisible(true); + m_stack.back()->setVisible(true); } u32 menuCount() @@ -81,7 +82,7 @@ public: return m_stack.size(); } - core::list<GUIModalMenu*> m_stack; + std::list<GUIModalMenu*> m_stack; }; extern MainMenuManager g_menumgr; diff --git a/src/map.cpp b/src/map.cpp index 4be094326..5d6b79fb0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "rollback_interface.h" #include "emerge.h" #include "mapgen_v6.h" +#include "mapgen_indev.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -73,34 +74,30 @@ Map::~Map() /* Free all MapSectors */ - core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator(); - for(; i.atEnd() == false; i++) + for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin(); + i != m_sectors.end(); ++i) { - MapSector *sector = i.getNode()->getValue(); - delete sector; + delete i->second; } } void Map::addEventReceiver(MapEventReceiver *event_receiver) { - m_event_receivers.insert(event_receiver, false); + m_event_receivers.insert(event_receiver); } void Map::removeEventReceiver(MapEventReceiver *event_receiver) { - if(m_event_receivers.find(event_receiver) == NULL) - return; - m_event_receivers.remove(event_receiver); + m_event_receivers.erase(event_receiver); } void Map::dispatchEvent(MapEditEvent *event) { - for(core::map<MapEventReceiver*, bool>::Iterator - i = m_event_receivers.getIterator(); - i.atEnd()==false; i++) + for(std::set<MapEventReceiver*>::iterator + i = m_event_receivers.begin(); + i != m_event_receivers.end(); ++i) { - MapEventReceiver* event_receiver = i.getNode()->getKey(); - event_receiver->onMapEditEvent(event); + (*i)->onMapEditEvent(event); } } @@ -111,12 +108,12 @@ MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p) return sector; } - core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p); + std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p); - if(n == NULL) + if(n == m_sectors.end()) return NULL; - MapSector *sector = n->getValue(); + MapSector *sector = n->second; // Cache the last result m_sector_cache_p = p; @@ -236,9 +233,9 @@ void Map::setNode(v3s16 p, MapNode & n) values of from_nodes are lighting values. */ void Map::unspreadLight(enum LightBank bank, - core::map<v3s16, u8> & from_nodes, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, MapBlock*> & modified_blocks) + std::map<v3s16, u8> & from_nodes, + std::set<v3s16> & light_sources, + std::map<v3s16, MapBlock*> & modified_blocks) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -256,9 +253,7 @@ void Map::unspreadLight(enum LightBank bank, u32 blockchangecount = 0; - core::map<v3s16, u8> unlighted_nodes; - core::map<v3s16, u8>::Iterator j; - j = from_nodes.getIterator(); + std::map<v3s16, u8> unlighted_nodes; /* Initialize block cache @@ -268,9 +263,10 @@ void Map::unspreadLight(enum LightBank bank, // Cache this a bit, too bool block_checked_in_modified = false; - for(; j.atEnd() == false; j++) + for(std::map<v3s16, u8>::iterator j = from_nodes.begin(); + j != from_nodes.end(); ++j) { - v3s16 pos = j.getNode()->getKey(); + v3s16 pos = j->first; v3s16 blockpos = getNodeBlockPos(pos); // Only fetch a new block if the block position has changed @@ -297,7 +293,7 @@ void Map::unspreadLight(enum LightBank bank, // Get node straight from the block MapNode n = block->getNode(relpos); - u8 oldlight = j.getNode()->getValue(); + u8 oldlight = j->second; // Loop through 6 neighbors for(u16 i=0; i<6; i++) @@ -354,7 +350,7 @@ void Map::unspreadLight(enum LightBank bank, n2.setLight(bank, 0, nodemgr); block->setNode(relpos, n2); - unlighted_nodes.insert(n2pos, current_light); + unlighted_nodes[n2pos] = current_light; changed = true; /* @@ -373,16 +369,16 @@ void Map::unspreadLight(enum LightBank bank, light_sources.remove(n2pos);*/ } else{ - light_sources.insert(n2pos, true); + light_sources.insert(n2pos); } // Add to modified_blocks if(changed == true && block_checked_in_modified == false) { // If the block is not found in modified_blocks, add. - if(modified_blocks.find(blockpos) == NULL) + if(modified_blocks.find(blockpos) == modified_blocks.end()) { - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; } block_checked_in_modified = true; } @@ -408,11 +404,11 @@ void Map::unspreadLight(enum LightBank bank, */ void Map::unLightNeighbors(enum LightBank bank, v3s16 pos, u8 lightwas, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, MapBlock*> & modified_blocks) + std::set<v3s16> & light_sources, + std::map<v3s16, MapBlock*> & modified_blocks) { - core::map<v3s16, u8> from_nodes; - from_nodes.insert(pos, lightwas); + std::map<v3s16, u8> from_nodes; + from_nodes[pos] = lightwas; unspreadLight(bank, from_nodes, light_sources, modified_blocks); } @@ -422,8 +418,8 @@ void Map::unLightNeighbors(enum LightBank bank, goes on recursively. */ void Map::spreadLight(enum LightBank bank, - core::map<v3s16, bool> & from_nodes, - core::map<v3s16, MapBlock*> & modified_blocks) + std::set<v3s16> & from_nodes, + std::map<v3s16, MapBlock*> & modified_blocks) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -441,9 +437,7 @@ void Map::spreadLight(enum LightBank bank, u32 blockchangecount = 0; - core::map<v3s16, bool> lighted_nodes; - core::map<v3s16, bool>::Iterator j; - j = from_nodes.getIterator(); + std::set<v3s16> lighted_nodes; /* Initialize block cache @@ -453,12 +447,10 @@ void Map::spreadLight(enum LightBank bank, // Cache this a bit, too bool block_checked_in_modified = false; - for(; j.atEnd() == false; j++) - //for(; j != from_nodes.end(); j++) + for(std::set<v3s16>::iterator j = from_nodes.begin(); + j != from_nodes.end(); ++j) { - v3s16 pos = j.getNode()->getKey(); - //v3s16 pos = *j; - //infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl; + v3s16 pos = *j; v3s16 blockpos = getNodeBlockPos(pos); // Only fetch a new block if the block position has changed @@ -525,8 +517,7 @@ void Map::spreadLight(enum LightBank bank, */ if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight)) { - lighted_nodes.insert(n2pos, true); - //lighted_nodes.push_back(n2pos); + lighted_nodes.insert(n2pos); changed = true; } /* @@ -539,8 +530,7 @@ void Map::spreadLight(enum LightBank bank, { n2.setLight(bank, newlight, nodemgr); block->setNode(relpos, n2); - lighted_nodes.insert(n2pos, true); - //lighted_nodes.push_back(n2pos); + lighted_nodes.insert(n2pos); changed = true; } } @@ -549,9 +539,9 @@ void Map::spreadLight(enum LightBank bank, if(changed == true && block_checked_in_modified == false) { // If the block is not found in modified_blocks, add. - if(modified_blocks.find(blockpos) == NULL) + if(modified_blocks.find(blockpos) == modified_blocks.end()) { - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; } block_checked_in_modified = true; } @@ -577,10 +567,10 @@ void Map::spreadLight(enum LightBank bank, */ void Map::lightNeighbors(enum LightBank bank, v3s16 pos, - core::map<v3s16, MapBlock*> & modified_blocks) + std::map<v3s16, MapBlock*> & modified_blocks) { - core::map<v3s16, bool> from_nodes; - from_nodes.insert(pos, true); + std::set<v3s16> from_nodes; + from_nodes.insert(pos); spreadLight(bank, from_nodes, modified_blocks); } @@ -635,7 +625,7 @@ v3s16 Map::getBrightestNeighbour(enum LightBank bank, v3s16 p) Mud is turned into grass in where the sunlight stops. */ s16 Map::propagateSunlight(v3s16 start, - core::map<v3s16, MapBlock*> & modified_blocks) + std::map<v3s16, MapBlock*> & modified_blocks) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -662,7 +652,7 @@ s16 Map::propagateSunlight(v3s16 start, n.setLight(LIGHTBANK_DAY, LIGHT_SUN, nodemgr); block->setNode(relpos, n); - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; } else { @@ -674,8 +664,8 @@ s16 Map::propagateSunlight(v3s16 start, } void Map::updateLighting(enum LightBank bank, - core::map<v3s16, MapBlock*> & a_blocks, - core::map<v3s16, MapBlock*> & modified_blocks) + std::map<v3s16, MapBlock*> & a_blocks, + std::map<v3s16, MapBlock*> & modified_blocks) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -688,22 +678,21 @@ void Map::updateLighting(enum LightBank bank, //bool debug=true; //u32 count_was = modified_blocks.size(); - core::map<v3s16, MapBlock*> blocks_to_update; + std::map<v3s16, MapBlock*> blocks_to_update; - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; - core::map<v3s16, u8> unlight_from; + std::map<v3s16, u8> unlight_from; int num_bottom_invalid = 0; { //TimeTaker t("first stuff"); - core::map<v3s16, MapBlock*>::Iterator i; - i = a_blocks.getIterator(); - for(; i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator i = a_blocks.begin(); + i != a_blocks.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); + MapBlock *block = i->second; for(;;) { @@ -713,9 +702,8 @@ void Map::updateLighting(enum LightBank bank, v3s16 pos = block->getPos(); v3s16 posnodes = block->getPosRelative(); - modified_blocks.insert(pos, block); - - blocks_to_update.insert(pos, block); + modified_blocks[pos] = block; + blocks_to_update[pos] = block; /* Clear all light from block @@ -735,7 +723,7 @@ void Map::updateLighting(enum LightBank bank, // If node sources light, add to list u8 source = nodemgr->get(n).light_source; if(source != 0) - light_sources[p + posnodes] = true; + light_sources.insert(p + posnodes); // Collect borders for unlighting if((x==0 || x == MAP_BLOCKSIZE-1 @@ -744,7 +732,7 @@ void Map::updateLighting(enum LightBank bank, && oldlight != 0) { v3s16 p_map = p + posnodes; - unlight_from.insert(p_map, oldlight); + unlight_from[p_map] = oldlight; } } catch(InvalidPositionException &e) @@ -912,8 +900,8 @@ void Map::updateLighting(enum LightBank bank, //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl; } -void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks, - core::map<v3s16, MapBlock*> & modified_blocks) +void Map::updateLighting(std::map<v3s16, MapBlock*> & a_blocks, + std::map<v3s16, MapBlock*> & modified_blocks) { updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks); updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks); @@ -921,11 +909,11 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks, /* Update information about whether day and night light differ */ - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); + MapBlock *block = i->second; block->expireDayNightDiff(); } } @@ -933,7 +921,7 @@ void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks, /* */ void Map::addNodeAndUpdate(v3s16 p, MapNode n, - core::map<v3s16, MapBlock*> &modified_blocks) + std::map<v3s16, MapBlock*> &modified_blocks) { INodeDefManager *ndef = m_gamedef->ndef(); @@ -952,7 +940,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, v3s16 bottompos = p + v3s16(0,-1,0); bool node_under_sunlight = true; - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; /* Collect old node for rollback @@ -994,7 +982,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, v3s16 blockpos = getNodeBlockPos(p); MapBlock * block = getBlockNoCreate(blockpos); assert(block != NULL); - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; assert(isValidPosition(p)); @@ -1078,12 +1066,11 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, /* Update information about whether day and night light differ */ - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); - block->expireDayNightDiff(); + i->second->expireDayNightDiff(); } /* @@ -1132,7 +1119,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, /* */ void Map::removeNodeAndUpdate(v3s16 p, - core::map<v3s16, MapBlock*> &modified_blocks) + std::map<v3s16, MapBlock*> &modified_blocks) { INodeDefManager *ndef = m_gamedef->ndef(); @@ -1166,7 +1153,7 @@ void Map::removeNodeAndUpdate(v3s16 p, { } - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; enum LightBank banks[] = { @@ -1214,7 +1201,7 @@ void Map::removeNodeAndUpdate(v3s16 p, v3s16 blockpos = getNodeBlockPos(p); MapBlock * block = getBlockNoCreate(blockpos); assert(block != NULL); - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; /* If the removed node was under sunlight, propagate the @@ -1270,12 +1257,11 @@ void Map::removeNodeAndUpdate(v3s16 p, /* Update information about whether day and night light differ */ - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); - block->expireDayNightDiff(); + i->second->expireDayNightDiff(); } /* @@ -1330,15 +1316,15 @@ bool Map::addNodeWithEvent(v3s16 p, MapNode n) bool succeeded = true; try{ - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; addNodeAndUpdate(p, n, modified_blocks); // Copy modified_blocks to event - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd()==false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - event.modified_blocks.insert(i.getNode()->getKey(), false); + event.modified_blocks.insert(i->first); } } catch(InvalidPositionException &e){ @@ -1358,15 +1344,15 @@ bool Map::removeNodeWithEvent(v3s16 p) bool succeeded = true; try{ - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; removeNodeAndUpdate(p, modified_blocks); // Copy modified_blocks to event - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd()==false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - event.modified_blocks.insert(i.getNode()->getKey(), false); + event.modified_blocks.insert(i->first); } } catch(InvalidPositionException &e){ @@ -1439,33 +1425,31 @@ bool Map::getDayNightDiff(v3s16 blockpos) Updates usage timers */ void Map::timerUpdate(float dtime, float unload_timeout, - core::list<v3s16> *unloaded_blocks) + std::list<v3s16> *unloaded_blocks) { bool save_before_unloading = (mapType() == MAPTYPE_SERVER); // Profile modified reasons Profiler modprofiler; - core::list<v2s16> sector_deletion_queue; + std::list<v2s16> sector_deletion_queue; u32 deleted_blocks_count = 0; u32 saved_blocks_count = 0; u32 block_count_all = 0; - core::map<v2s16, MapSector*>::Iterator si; - beginSave(); - si = m_sectors.getIterator(); - for(; si.atEnd() == false; si++) + for(std::map<v2s16, MapSector*>::iterator si = m_sectors.begin(); + si != m_sectors.end(); ++si) { - MapSector *sector = si.getNode()->getValue(); + MapSector *sector = si->second; bool all_blocks_deleted = true; - core::list<MapBlock*> blocks; + std::list<MapBlock*> blocks; sector->getBlocks(blocks); - for(core::list<MapBlock*>::Iterator i = blocks.begin(); - i != blocks.end(); i++) + for(std::list<MapBlock*>::iterator i = blocks.begin(); + i != blocks.end(); ++i) { MapBlock *block = (*i); @@ -1501,7 +1485,7 @@ void Map::timerUpdate(float dtime, float unload_timeout, if(all_blocks_deleted) { - sector_deletion_queue.push_back(si.getNode()->getKey()); + sector_deletion_queue.push_back(si->first); } } endSave(); @@ -1526,17 +1510,17 @@ void Map::timerUpdate(float dtime, float unload_timeout, } } -void Map::deleteSectors(core::list<v2s16> &list) +void Map::deleteSectors(std::list<v2s16> &list) { - core::list<v2s16>::Iterator j; - for(j=list.begin(); j!=list.end(); j++) + for(std::list<v2s16>::iterator j = list.begin(); + j != list.end(); ++j) { MapSector *sector = m_sectors[*j]; // If sector is in sector cache, remove it from there if(m_sector_cache == sector) m_sector_cache = NULL; // Remove from map and delete - m_sectors.remove(*j); + m_sectors.erase(*j); delete sector; } } @@ -1642,7 +1626,7 @@ const v3s16 g_7dirs[7] = #define D_TOP 6 #define D_SELF 1 -void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) +void Map::transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -1656,19 +1640,16 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) bool fast_flood = g_settings->getS16("liquid_fast_flood"); int water_level = g_settings->getS16("water_level"); - /*if(initial_size != 0) - infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/ - // list of nodes that due to viscosity have not reached their max level height UniqueQueue<v3s16> must_reflow, must_reflow_second; // List of MapBlocks that will require a lighting update (due to lava) - core::map<v3s16, MapBlock*> lighting_modified_blocks; + std::map<v3s16, MapBlock*> lighting_modified_blocks; - while(m_transforming_liquid.size() > 0) + while (m_transforming_liquid.size() > 0) { // This should be done here so that it is done when continue is used - if(loopcount >= initial_size || loopcount >= 1000) + if (loopcount >= initial_size || loopcount >= 1000) break; loopcount++; /* @@ -1676,9 +1657,12 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) */ v3s16 p0 = m_transforming_liquid.pop_front(); u16 total_level = 0; - NodeNeighbor neighbors[7]; // surrounding flowing liquid nodes - s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; // current level of every block - s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; // target levels + // surrounding flowing liquid nodes + NodeNeighbor neighbors[7]; + // current level of every block + s8 liquid_levels[7] = {-1, -1, -1, -1, -1, -1, -1}; + // target levels + s8 liquid_levels_want[7] = {-1, -1, -1, -1, -1, -1, -1}; s8 can_liquid_same_level = 0; content_t liquid_kind = CONTENT_IGNORE; content_t liquid_kind_flowing = CONTENT_IGNORE; @@ -1713,9 +1697,11 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) } break; case LIQUID_SOURCE: - // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + // if this node is not (yet) of a liquid type, + // choose the first liquid type we encounter if (liquid_kind_flowing == CONTENT_IGNORE) - liquid_kind_flowing = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_flowing); + liquid_kind_flowing = nodemgr->getId( + nodemgr->get(nb.n).liquid_alternative_flowing); if (liquid_kind == CONTENT_IGNORE) liquid_kind = nb.n.getContent(); if (nb.n.getContent() == liquid_kind) { @@ -1725,37 +1711,55 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) } break; case LIQUID_FLOWING: - // if this node is not (yet) of a liquid type, choose the first liquid type we encounter + // if this node is not (yet) of a liquid type, + // choose the first liquid type we encounter if (liquid_kind_flowing == CONTENT_IGNORE) liquid_kind_flowing = nb.n.getContent(); if (liquid_kind == CONTENT_IGNORE) - liquid_kind = nodemgr->getId(nodemgr->get(nb.n).liquid_alternative_source); + liquid_kind = nodemgr->getId( + nodemgr->get(nb.n).liquid_alternative_source); if (nb.n.getContent() == liquid_kind_flowing) { liquid_levels[i] = (nb.n.param2 & LIQUID_LEVEL_MASK); nb.l = 1; } break; } - if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) ++can_liquid_same_level; - if (liquid_levels[i] > 0) total_level += liquid_levels[i]; + + if (nb.l && nb.t == NEIGHBOR_SAME_LEVEL) + ++can_liquid_same_level; + if (liquid_levels[i] > 0) + total_level += liquid_levels[i]; /* - infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c="<<nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1="<< (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt="<<nodemgr->get(nb.n.getContent()).liquid_type + infostream << "get node i=" <<(int)i<<" " << PP(npos) << " c=" + << nb.n.getContent() <<" p0="<< (int)nb.n.param0 <<" p1=" + << (int)nb.n.param1 <<" p2="<< (int)nb.n.param2 << " lt=" + << nodemgr->get(nb.n.getContent()).liquid_type //<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing - << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] << " tlevel=" << (int)total_level << " cansame="<<(int)can_liquid_same_level<<std::endl; + << " l="<< nb.l << " inf="<< nb.i << " nlevel=" << (int)liquid_levels[i] + << " tlevel=" << (int)total_level << " cansame=" + << (int)can_liquid_same_level << std::endl; */ } - if (liquid_kind == CONTENT_IGNORE || !neighbors[D_SELF].l || total_level <= 0) + if (liquid_kind == CONTENT_IGNORE || + !neighbors[D_SELF].l || + total_level <= 0) continue; // fill bottom block if (neighbors[D_BOTTOM].l) { - liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level; + liquid_levels_want[D_BOTTOM] = total_level > LIQUID_LEVEL_SOURCE ? + LIQUID_LEVEL_SOURCE : total_level; total_level -= liquid_levels_want[D_BOTTOM]; } - if (relax && p0.Y <= water_level && liquid_levels[D_TOP] == 0 && total_level >= LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 && can_liquid_same_level >= relax + 1) { //relax up + //relax up + if (relax && 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) && + can_liquid_same_level >= relax + 1) { total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level; } @@ -1766,7 +1770,11 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) : total_level / can_liquid_same_level; total_level -= want_level * can_liquid_same_level; - if (relax && p0.Y > water_level && liquid_levels[D_TOP] == 0 && liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && total_level <= can_liquid_same_level - 2 && can_liquid_same_level >= relax + 1) { //relax down + //relax down + if (relax && p0.Y == water_level + 1 && liquid_levels[D_TOP] == 0 && + liquid_levels[D_BOTTOM] == LIQUID_LEVEL_SOURCE && want_level == 0 && + total_level <= (can_liquid_same_level - relax) && + can_liquid_same_level >= relax + 1) { total_level = 0; } @@ -1784,7 +1792,8 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) for (u16 ii = 0; ii < 7; ++ii) { if (total_level < 1) break; - if (liquid_levels_want[ii] >= 0 && liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) { + if (liquid_levels_want[ii] >= 0 && + liquid_levels_want[ii] < LIQUID_LEVEL_SOURCE) { ++liquid_levels_want[ii]; --total_level; } @@ -1792,7 +1801,8 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) // fill top block if can if (neighbors[D_TOP].l) { - liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ; + liquid_levels_want[D_TOP] = total_level > LIQUID_LEVEL_SOURCE ? + LIQUID_LEVEL_SOURCE : total_level; total_level -= liquid_levels_want[D_TOP]; } @@ -1807,7 +1817,15 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) && liquid_levels[D_TOP] >= LIQUID_LEVEL_SOURCE)))) liquid_levels_want[ii] = LIQUID_LEVEL_SOURCE; - //if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl; + /* + if (total_level > 0) //|| flowed != volume) + infostream <<" AFTER level=" << (int)total_level + //<< " flowed="<<flowed<< " volume=" << volume + << " wantsame="<<(int)want_level<< " top=" + << (int)liquid_levels_want[D_TOP]<< " topwas=" + << (int)liquid_levels[D_TOP]<< " bot=" + << (int)liquid_levels_want[D_BOTTOM]<<std::endl; + */ u8 changed = 0; for (u16 i = 0; i < 7; i++) { @@ -1831,8 +1849,10 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) new_node_level = liquid_levels[i] - 1; else if (level_inc > 0) new_node_level = liquid_levels[i] + 1; - } else + } else { new_node_level = liquid_levels_want[i]; + } + if (new_node_level >= LIQUID_LEVEL_SOURCE) new_node_content = liquid_kind; else if (new_node_level > 0) @@ -1841,25 +1861,30 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) new_node_content = CONTENT_AIR; // last level must flow down on stairs - if (liquid_levels_want[i] != liquid_levels[i] && liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && new_node_level >= 1 && new_node_level <= 2) //maybe == 1 // + if (liquid_levels_want[i] != liquid_levels[i] && + liquid_levels[D_TOP] <= 0 && !neighbors[D_BOTTOM].l && + new_node_level >= 1 && new_node_level <= 2) { for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level - if (!neighbors[ii].l) - continue; - must_reflow_second.push_back(p0 + dirs[ii]); + if (neighbors[ii].l) + must_reflow_second.push_back(p0 + dirs[ii]); + } } /* - check if anything has changed. if not, just continue with the next node. + check if anything has changed. + if not, just continue with the next node. */ if ( new_node_content == n0.getContent() && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_FLOWING || ((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level - // &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down + //&& ((n0.param2 & LIQUID_FLOW_DOWN_MASK) == + //LIQUID_FLOW_DOWN_MASK) == flowing_down )) && (nodemgr->get(n0.getContent()).liquid_type != LIQUID_SOURCE || - (((n0.param2 & LIQUID_INFINITY_MASK) == LIQUID_INFINITY_MASK) == neighbors[i].i + (((n0.param2 & LIQUID_INFINITY_MASK) == + LIQUID_INFINITY_MASK) == neighbors[i].i )) ) { continue; @@ -1876,7 +1901,12 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) //n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK); n0.param2 = (neighbors[i].i ? LIQUID_INFINITY_MASK : 0x00); } - //infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl; + /* + infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc=" + <<new_node_content<< " p2="<<(int)n0.param2<< " nl=" + <<(int)new_node_level<<std::endl; + */ + n0.setContent(new_node_content); // Find out whether there is a suspect for this action std::string suspect; @@ -1886,7 +1916,8 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) if(!suspect.empty()){ // Blame suspect - RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true); + RollbackScopeActor rollback_scope(m_gamedef->rollback(), + suspect, true); // Get old node for rollback RollbackNode rollback_oldnode(this, p0, m_gamedef); // Set node @@ -1904,20 +1935,25 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) v3s16 blockpos = getNodeBlockPos(p0); MapBlock *block = getBlockNoCreateNoEx(blockpos); if(block != NULL) { - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; // If node emits light, MapBlock requires lighting update if(nodemgr->get(n0).light_source != 0) lighting_modified_blocks[block->getPos()] = block; } must_reflow.push_back(neighbors[i].p); } - /* //for better relax - if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { // only same level + /* //for better relax only same level + if (changed) for (u16 ii = D_SELF + 1; ii < D_TOP; ++ii) { if (!neighbors[ii].l) continue; must_reflow.push_back(p0 + dirs[ii]); }*/ } - //if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl; + /* + if (loopcount) + infostream<<"Map::transformLiquids(): loopcount="<<loopcount + <<" reflow="<<must_reflow.size() + <<" queue="<< m_transforming_liquid.size()<<std::endl; + */ while (must_reflow.size() > 0) m_transforming_liquid.push_back(must_reflow.pop_front()); while (must_reflow_second.size() > 0) @@ -1925,11 +1961,12 @@ void Map::transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks) updateLighting(lighting_modified_blocks, modified_blocks); } -void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) +void Map::transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks) { - if (g_settings->getBool("liquid_finite")) return Map::transformLiquidsFinite(modified_blocks); - + if (g_settings->getBool("liquid_finite")) + return Map::transformLiquidsFinite(modified_blocks); + INodeDefManager *nodemgr = m_gamedef->ndef(); DSTACK(__FUNCTION_NAME); @@ -1945,7 +1982,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) UniqueQueue<v3s16> must_reflow; // List of MapBlocks that will require a lighting update (due to lava) - core::map<v3s16, MapBlock*> lighting_modified_blocks; + std::map<v3s16, MapBlock*> lighting_modified_blocks; while(m_transforming_liquid.size() != 0) { @@ -2165,7 +2202,7 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks) v3s16 blockpos = getNodeBlockPos(p0); MapBlock *block = getBlockNoCreateNoEx(blockpos); if(block != NULL) { - modified_blocks.insert(blockpos, block); + modified_blocks[blockpos] = block; // If node emits light, MapBlock requires lighting update if(nodemgr->get(n0).light_source != 0) lighting_modified_blocks[block->getPos()] = block; @@ -2460,19 +2497,15 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) bool enable_mapgen_debug_info = m_emerge->mapgen_debug_info; EMERGE_DBG_OUT("initBlockMake(): " PP(blockpos) " - " PP(blockpos)); - //s16 chunksize = 3; - //v3s16 chunk_offset(-1,-1,-1); - //s16 chunksize = 4; - //v3s16 chunk_offset(-1,-1,-1); - s16 chunksize = 5; - v3s16 chunk_offset(-2,-2,-2); + s16 chunksize = m_mgparams->chunksize; + s16 coffset = -chunksize / 2; + v3s16 chunk_offset(coffset, coffset, coffset); v3s16 blockpos_div = getContainerPos(blockpos - chunk_offset, chunksize); v3s16 blockpos_min = blockpos_div * chunksize; v3s16 blockpos_max = blockpos_div * chunksize + v3s16(1,1,1)*(chunksize-1); blockpos_min += chunk_offset; blockpos_max += chunk_offset; - //v3s16 extra_borders(1,1,1); v3s16 extra_borders(1,1,1); // Do nothing if not inside limits (+-1 because of neighbors) @@ -2571,7 +2604,7 @@ bool ServerMap::initBlockMake(BlockMakeData *data, v3s16 blockpos) } MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, - core::map<v3s16, MapBlock*> &changed_blocks) + std::map<v3s16, MapBlock*> &changed_blocks) { v3s16 blockpos_min = data->blockpos_min; v3s16 blockpos_max = data->blockpos_max; @@ -2676,10 +2709,10 @@ MapBlock* ServerMap::finishBlockMake(BlockMakeData *data, /* Go through changed blocks */ - for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator i = changed_blocks.begin(); + i != changed_blocks.end(); ++i) { - MapBlock *block = i.getNode()->getValue(); + MapBlock *block = i->second; assert(block); /* Update day/night difference cache of the MapBlocks @@ -2797,7 +2830,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d) /* Insert to container */ - m_sectors.insert(p2d, sector); + m_sectors[p2d] = sector; return sector; } @@ -2808,7 +2841,7 @@ ServerMapSector * ServerMap::createSector(v2s16 p2d) */ MapBlock * ServerMap::generateBlock( v3s16 p, - core::map<v3s16, MapBlock*> &modified_blocks + std::map<v3s16, MapBlock*> &modified_blocks ) { DSTACKF("%s: p=(%d,%d,%d)", __FUNCTION_NAME, p.X, p.Y, p.Z); @@ -3008,7 +3041,7 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) } /*if(allow_generate) { - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; MapBlock *block = generateBlock(p, modified_blocks); if(block) { @@ -3017,11 +3050,11 @@ MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank) event.p = p; // Copy modified_blocks to event - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd()==false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - event.modified_blocks.insert(i.getNode()->getKey(), false); + event.modified_blocks.insert(i->first); } // Queue event @@ -3262,10 +3295,10 @@ void ServerMap::save(ModifiedState save_level) // Don't do anything with sqlite unless something is really saved bool save_started = false; - core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator(); - for(; i.atEnd() == false; i++) + for(std::map<v2s16, MapSector*>::iterator i = m_sectors.begin(); + i != m_sectors.end(); ++i) { - ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue(); + ServerMapSector *sector = (ServerMapSector*)i->second; assert(sector->getId() == MAPSECTOR_SERVER); if(sector->differs_from_disk || save_level == MOD_STATE_CLEAN) @@ -3273,11 +3306,11 @@ void ServerMap::save(ModifiedState save_level) saveSectorMeta(sector); sector_meta_count++; } - core::list<MapBlock*> blocks; + std::list<MapBlock*> blocks; sector->getBlocks(blocks); - core::list<MapBlock*>::Iterator j; - for(j=blocks.begin(); j!=blocks.end(); j++) + for(std::list<MapBlock*>::iterator j = blocks.begin(); + j != blocks.end(); ++j) { MapBlock *block = *j; @@ -3350,7 +3383,7 @@ v3s16 ServerMap::getIntegerAsBlock(sqlite3_int64 i) return v3s16(x,y,z); } -void ServerMap::listAllLoadableBlocks(core::list<v3s16> &dst) +void ServerMap::listAllLoadableBlocks(std::list<v3s16> &dst) { if(loadFromFolders()){ errorstream<<"Map::listAllLoadableBlocks(): Result will be missing " @@ -3487,7 +3520,7 @@ MapSector* ServerMap::loadSectorMeta(std::string sectordir, bool save_after_load <<" Continuing with a sector with no metadata." <<std::endl;*/ sector = new ServerMapSector(this, p2d, m_gamedef); - m_sectors.insert(p2d, sector); + m_sectors[p2d] = sector; } else { @@ -3987,9 +4020,9 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) u8 flags = 0; MapBlock *block; v3s16 p(x,y,z); - core::map<v3s16, u8>::Node *n; + std::map<v3s16, u8>::iterator n; n = m_loaded_blocks.find(p); - if(n != NULL) + if(n != m_loaded_blocks.end()) continue; bool block_data_inexistent = false; @@ -4017,7 +4050,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) if(block_data_inexistent) { flags |= VMANIP_BLOCK_DATA_INEXIST; - + VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); // Fill with VOXELFLAG_INEXISTENT for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) @@ -4033,7 +4066,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) flags |= VMANIP_BLOCK_CONTAINS_CIGNORE; }*/ - m_loaded_blocks.insert(p, flags); + m_loaded_blocks[p] = flags; } //infostream<<"emerge done"<<std::endl; @@ -4045,7 +4078,7 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) run on background. */ void MapVoxelManipulator::blitBack - (core::map<v3s16, MapBlock*> & modified_blocks) + (std::map<v3s16, MapBlock*> & modified_blocks) { if(m_area.getExtent() == v3s16(0,0,0)) return; @@ -4156,9 +4189,9 @@ void ManualMapVoxelManipulator::initialEmerge( u8 flags = 0; MapBlock *block; v3s16 p(x,y,z); - core::map<v3s16, u8>::Node *n; + std::map<v3s16, u8>::iterator n; n = m_loaded_blocks.find(p); - if(n != NULL) + if(n != m_loaded_blocks.end()) continue; bool block_data_inexistent = false; @@ -4199,12 +4232,12 @@ void ManualMapVoxelManipulator::initialEmerge( flags |= VMANIP_BLOCK_CONTAINS_CIGNORE; }*/ - m_loaded_blocks.insert(p, flags); + m_loaded_blocks[p] = flags; } } void ManualMapVoxelManipulator::blitBackAll( - core::map<v3s16, MapBlock*> * modified_blocks) + std::map<v3s16, MapBlock*> * modified_blocks) { if(m_area.getExtent() == v3s16(0,0,0)) return; @@ -4212,37 +4245,22 @@ void ManualMapVoxelManipulator::blitBackAll( /* Copy data of all blocks */ - for(core::map<v3s16, u8>::Iterator - i = m_loaded_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, u8>::iterator + i = m_loaded_blocks.begin(); + i != m_loaded_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - u8 flags = i.getNode()->getValue(); - - bool existed = !(flags & VMANIP_BLOCK_DATA_INEXIST); - if(existed == false) - { - // The Great Bug was found using this - /*infostream<<"ManualMapVoxelManipulator::blitBackAll: " - <<"Inexistent ("<<p.X<<","<<p.Y<<","<<p.Z<<")" - <<std::endl;*/ - continue; - } - + v3s16 p = i->first; MapBlock *block = m_map->getBlockNoCreateNoEx(p); - if(block == NULL) + bool existed = !(i->second & VMANIP_BLOCK_DATA_INEXIST); + if(existed == false) { - infostream<<"WARNING: "<<__FUNCTION_NAME - <<": got NULL block " - <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" - <<std::endl; continue; } block->copyFrom(*this); - + if(modified_blocks) - modified_blocks->insert(p, block); + (*modified_blocks)[p] = block; } } @@ -25,6 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <jthread.h> #include <iostream> #include <sstream> +#include <set> +#include <map> +#include <list> #include "irrlichttypes_bloated.h" #include "mapnode.h" @@ -47,7 +50,7 @@ class NodeMetadata; class IGameDef; class IRollbackReportSink; class EmergeManager; -class BlockMakeData; +struct BlockMakeData; /* @@ -75,7 +78,7 @@ struct MapEditEvent MapEditEventType type; v3s16 p; MapNode n; - core::map<v3s16, bool> modified_blocks; + std::set<v3s16> modified_blocks; u16 already_known_by_peer; MapEditEvent(): @@ -90,14 +93,7 @@ struct MapEditEvent event->type = type; event->p = p; event->n = n; - for(core::map<v3s16, bool>::Iterator - i = modified_blocks.getIterator(); - i.atEnd()==false; i++) - { - v3s16 p = i.getNode()->getKey(); - bool v = i.getNode()->getValue(); - event->modified_blocks.insert(p, v); - } + event->modified_blocks = modified_blocks; return event; } @@ -117,11 +113,11 @@ struct MapEditEvent case MEET_OTHER: { VoxelArea a; - for(core::map<v3s16, bool>::Iterator - i = modified_blocks.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = *i; v3s16 np1 = p*MAP_BLOCKSIZE; v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1); a.addPoint(np1); @@ -186,7 +182,7 @@ public: */ virtual MapSector * emergeSector(v2s16 p){ return NULL; } virtual MapSector * emergeSector(v2s16 p, - core::map<v3s16, MapBlock*> &changed_blocks){ return NULL; } + std::map<v3s16, MapBlock*> &changed_blocks){ return NULL; } // Returns InvalidPositionException if not found MapBlock * getBlockNoCreate(v3s16 p); @@ -212,42 +208,42 @@ public: MapNode getNodeNoEx(v3s16 p); void unspreadLight(enum LightBank bank, - core::map<v3s16, u8> & from_nodes, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, MapBlock*> & modified_blocks); + std::map<v3s16, u8> & from_nodes, + std::set<v3s16> & light_sources, + std::map<v3s16, MapBlock*> & modified_blocks); void unLightNeighbors(enum LightBank bank, v3s16 pos, u8 lightwas, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, MapBlock*> & modified_blocks); + std::set<v3s16> & light_sources, + std::map<v3s16, MapBlock*> & modified_blocks); void spreadLight(enum LightBank bank, - core::map<v3s16, bool> & from_nodes, - core::map<v3s16, MapBlock*> & modified_blocks); + std::set<v3s16> & from_nodes, + std::map<v3s16, MapBlock*> & modified_blocks); void lightNeighbors(enum LightBank bank, v3s16 pos, - core::map<v3s16, MapBlock*> & modified_blocks); + std::map<v3s16, MapBlock*> & modified_blocks); v3s16 getBrightestNeighbour(enum LightBank bank, v3s16 p); s16 propagateSunlight(v3s16 start, - core::map<v3s16, MapBlock*> & modified_blocks); + std::map<v3s16, MapBlock*> & modified_blocks); void updateLighting(enum LightBank bank, - core::map<v3s16, MapBlock*> & a_blocks, - core::map<v3s16, MapBlock*> & modified_blocks); + std::map<v3s16, MapBlock*> & a_blocks, + std::map<v3s16, MapBlock*> & modified_blocks); - void updateLighting(core::map<v3s16, MapBlock*> & a_blocks, - core::map<v3s16, MapBlock*> & modified_blocks); + void updateLighting(std::map<v3s16, MapBlock*> & a_blocks, + std::map<v3s16, MapBlock*> & modified_blocks); /* These handle lighting but not faces. */ void addNodeAndUpdate(v3s16 p, MapNode n, - core::map<v3s16, MapBlock*> &modified_blocks); + std::map<v3s16, MapBlock*> &modified_blocks); void removeNodeAndUpdate(v3s16 p, - core::map<v3s16, MapBlock*> &modified_blocks); + std::map<v3s16, MapBlock*> &modified_blocks); /* Wrappers for the latter ones. @@ -281,12 +277,12 @@ public: Saves modified blocks before unloading on MAPTYPE_SERVER. */ void timerUpdate(float dtime, float unload_timeout, - core::list<v3s16> *unloaded_blocks=NULL); + std::list<v3s16> *unloaded_blocks=NULL); // Deletes sectors and their blocks from memory // Takes cache into account // If deleted sector is in sector cache, clears cache - void deleteSectors(core::list<v2s16> &list); + void deleteSectors(std::list<v2s16> &list); #if 0 /* @@ -301,8 +297,8 @@ public: // For debug printing. Prints "Map: ", "ServerMap: " or "ClientMap: " virtual void PrintInfo(std::ostream &out); - void transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks); - void transformLiquidsFinite(core::map<v3s16, MapBlock*> & modified_blocks); + void transformLiquids(std::map<v3s16, MapBlock*> & modified_blocks); + void transformLiquidsFinite(std::map<v3s16, MapBlock*> & modified_blocks); /* Node metadata @@ -325,7 +321,7 @@ public: /* Misc. */ - core::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;} + std::map<v2s16, MapSector*> *getSectorsPtr(){return &m_sectors;} /* Variables @@ -340,9 +336,9 @@ protected: IGameDef *m_gamedef; - core::map<MapEventReceiver*, bool> m_event_receivers; + std::set<MapEventReceiver*> m_event_receivers; - core::map<v2s16, MapSector*> m_sectors; + std::map<v2s16, MapSector*> m_sectors; // Be sure to set this to NULL when the cached sector is deleted MapSector *m_sector_cache; @@ -385,13 +381,7 @@ public: */ bool initBlockMake(BlockMakeData *data, v3s16 blockpos); MapBlock *finishBlockMake(BlockMakeData *data, - core::map<v3s16, MapBlock*> &changed_blocks); - - // A non-threaded wrapper to the above - DEFUNCT -/* MapBlock * generateBlock( - v3s16 p, - core::map<v3s16, MapBlock*> &modified_blocks - );*/ + std::map<v3s16, MapBlock*> &changed_blocks); /* Get a block from somewhere. @@ -444,9 +434,7 @@ public: void save(ModifiedState save_level); //void loadAll(); - - void listAllLoadableBlocks(core::list<v3s16> &dst); - + void listAllLoadableBlocks(std::list<v3s16> &dst); // Saves map seed and possibly other stuff void saveMapMeta(); void loadMapMeta(); @@ -538,15 +526,15 @@ public: virtual void emerge(VoxelArea a, s32 caller_id=-1); - void blitBack(core::map<v3s16, MapBlock*> & modified_blocks); - + void blitBack(std::map<v3s16, MapBlock*> & modified_blocks); + +protected: + Map *m_map; /* key = blockpos value = flags describing the block */ - core::map<v3s16, u8> m_loaded_blocks; -protected: - Map *m_map; + std::map<v3s16, u8> m_loaded_blocks; }; class ManualMapVoxelManipulator : public MapVoxelManipulator @@ -563,7 +551,7 @@ public: void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max); // This is much faster with big chunks of generated data - void blitBackAll(core::map<v3s16, MapBlock*> * modified_blocks); + void blitBackAll(std::map<v3s16, MapBlock*> * modified_blocks); protected: bool m_create_area; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index a6e9b3951..dd95ab77f 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -168,7 +168,7 @@ MapNode MapBlock::getNodeParentNoEx(v3s16 p) if black_air_left!=NULL, it is set to true if non-sunlighted air is left in block. */ -bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources, +bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources, bool remove_light, bool *black_air_left) { INodeDefManager *nodemgr = m_gamedef->ndef(); @@ -287,7 +287,7 @@ bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources, if(diminish_light(current_light) != 0) { - light_sources.insert(pos_relative + pos, true); + light_sources.insert(pos_relative + pos); } if(current_light == 0 && stopped_to_solid_object) diff --git a/src/mapblock.h b/src/mapblock.h index 692b54318..05bb944a6 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <jmutex.h> #include <jmutexautolock.h> #include <exception> +#include <set> #include "debug.h" #include "irrlichttypes.h" #include "irr_v3d.h" @@ -352,7 +353,7 @@ public: } // See comments in mapblock.cpp - bool propagateSunlight(core::map<v3s16, bool> & light_sources, + bool propagateSunlight(std::set<v3s16> & light_sources, bool remove_light=false, bool *black_air_left=NULL); // Copies data to VoxelManipulator to getPosRelative() diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index f4d57922a..f68a79e41 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -445,7 +445,7 @@ struct FastFace }; static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, - v3f p, v3s16 dir, v3f scale, u8 light_source, core::array<FastFace> &dest) + v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest) { FastFace face; @@ -455,6 +455,80 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, v3f vertex_pos[4]; v3s16 vertex_dirs[4]; getNodeVertexDirs(dir, vertex_dirs); + v3s16 t; + switch (tile.rotation) + { + case 0: + break; + case 1: //R90 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[3]; + vertex_dirs[3] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[1]; + vertex_dirs[1] = t; + break; + case 2: //R180 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[2]; + vertex_dirs[2] = t; + t = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[3]; + vertex_dirs[3] = t; + break; + case 3: //R270 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[3]; + vertex_dirs[3] = t; + break; + case 4: //FXR90 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[3]; + vertex_dirs[3] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[1]; + vertex_dirs[1] = t; + tile.texture.pos.Y += tile.texture.size.Y; + tile.texture.size.Y *= -1; + break; + case 5: //FXR270 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[3]; + vertex_dirs[3] = t; + tile.texture.pos.Y += tile.texture.size.Y; + tile.texture.size.Y *= -1; + break; + case 6: //FYR90 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[3]; + vertex_dirs[3] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[1]; + vertex_dirs[1] = t; + tile.texture.pos.X += tile.texture.size.X; + tile.texture.size.X *= -1; + break; + case 7: //FYR270 + t = vertex_dirs[0]; + vertex_dirs[0] = vertex_dirs[1]; + vertex_dirs[1] = vertex_dirs[2]; + vertex_dirs[2] = vertex_dirs[3]; + vertex_dirs[3] = t; + tile.texture.pos.X += tile.texture.size.X; + tile.texture.size.X *= -1; + break; + case 8: //FX + tile.texture.pos.Y += tile.texture.size.Y; + tile.texture.size.Y *= -1; + break; + case 9: //FY + tile.texture.pos.X += tile.texture.size.X; + tile.texture.size.X *= -1; + break; + default: + break; + } for(u16 i=0; i<4; i++) { vertex_pos[i] = v3f( @@ -601,60 +675,50 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) // 5 = (0,0,-1) // 6 = (0,-1,0) // 7 = (-1,0,0) - u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7; + u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2; // Get rotation for things like chests u8 facedir = mn.getFaceDir(ndef); - assert(facedir <= 3); - - static const u8 dir_to_tile[4 * 8] = + assert(facedir <= 23); + static const u16 dir_to_tile[24 * 16] = { - // 0 +X +Y +Z 0 -Z -Y -X - 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0 - 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1 - 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2 - 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3 + // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation + 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3 + 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 , + 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 , + 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 , + + 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7 + 0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 , + 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 , + 0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 , + + 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11 + 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 , + 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 , + 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 , + + 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15 + 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 , + 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 , + 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 , + + 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19 + 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 , + 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 , + 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 , + + 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23 + 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 , + 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 , + 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2 + }; - u8 tileindex = dir_to_tile[facedir*8 + dir_i]; - - // If not rotated or is side tile, we're done - if(facedir == 0 || (tileindex != 0 && tileindex != 1)) - return getNodeTileN(mn, p, tileindex, data); - - // This is the top or bottom tile, and it shall be rotated; thus rotate it - TileSpec spec = getNodeTileN(mn, p, tileindex, data); - if(tileindex == 0){ - if(facedir == 1){ // -90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR270"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - else if(facedir == 2){ // 180 - spec.texture.pos += spec.texture.size; - spec.texture.size *= -1; - } - else if(facedir == 3){ // 90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR90"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - } - else if(tileindex == 1){ - if(facedir == 1){ // -90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR90"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - else if(facedir == 2){ // 180 - spec.texture.pos += spec.texture.size; - spec.texture.size *= -1; - } - else if(facedir == 3){ // 90 - std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); - name += "^[transformR270"; - spec.texture = data->m_gamedef->tsrc()->getTexture(name); - } - } + u16 tile_index=facedir*16 + dir_i; + TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data); + spec.rotation=dir_to_tile[tile_index + 1]; + std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id); + spec.texture = data->m_gamedef->tsrc()->getTexture(name); return spec; } @@ -745,7 +809,7 @@ static void updateFastFaceRow( v3f translate_dir_f, v3s16 face_dir, v3f face_dir_f, - core::array<FastFace> &dest) + std::vector<FastFace> &dest) { v3s16 p = startpos; @@ -794,6 +858,7 @@ static void updateFastFaceRow( && next_lights[2] == lights[2] && next_lights[3] == lights[3] && next_tile == tile + && tile.rotation == 0 && next_light_source == light_source) { next_is_different = false; @@ -897,7 +962,7 @@ static void updateFastFaceRow( } static void updateAllFastFaceRows(MeshMakeData *data, - core::array<FastFace> &dest) + std::vector<FastFace> &dest) { /* Go through every y,z and get top(y+) faces in rows of x+ @@ -962,7 +1027,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data): // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated) //TimeTaker timer1("MapBlockMesh()"); - core::array<FastFace> fastfaces_new; + std::vector<FastFace> fastfaces_new; /* We are including the faces of the trailing edges of the block. @@ -1124,8 +1189,8 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data): m_mesh->addMeshBuffer(buf); // Mesh grabbed it buf->drop(); - buf->append(p.vertices.pointer(), p.vertices.size(), - p.indices.pointer(), p.indices.size()); + buf->append(&p.vertices[0], p.vertices.size(), + &p.indices[0], p.indices.size()); } /* diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 5b33990c6..c75984021 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -143,13 +143,13 @@ private: struct PreMeshBuffer { TileSpec tile; - core::array<u16> indices; - core::array<video::S3DVertex> vertices; + std::vector<u16> indices; + std::vector<video::S3DVertex> vertices; }; struct MeshCollector { - core::array<PreMeshBuffer> prebuffers; + std::vector<PreMeshBuffer> prebuffers; void append(const TileSpec &material, const video::S3DVertex *vertices, u32 numVertices, diff --git a/src/mapgen.cpp b/src/mapgen.cpp index ef5da6bf1..b5deaae52 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -39,914 +39,366 @@ FlagDesc flagdesc_mapgen[] = { {"trees", MG_TREES}, {"caves", MG_CAVES}, {"dungeons", MG_DUNGEONS}, - {"v6_forests", MGV6_FORESTS}, + {"v6_jungles", MGV6_JUNGLES}, {"v6_biome_blend", MGV6_BIOME_BLEND}, {"flat", MG_FLAT}, {NULL, 0} }; -/////////////////////////////////////////////////////////////////////////////// - -///////////////////// - -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_trees = settings->getNoiseParams("mgv6_np_trees"); - 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"); - - bool success = - np_terrain_base && np_terrain_higher && np_steepness && - np_height_select && np_trees && np_mud && - np_beach && np_biome && np_cave; - return success; -} - - -void MapgenV6Params::writeParams(Settings *settings) { - settings->setFloat("mgv6_freq_desert", freq_desert); - settings->setFloat("mgv6_freq_beach", freq_beach); - - settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base); - settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); - settings->setNoiseParams("mgv6_np_steepness", np_steepness); - settings->setNoiseParams("mgv6_np_height_select", np_height_select); - settings->setNoiseParams("mgv6_np_trees", np_trees); - settings->setNoiseParams("mgv6_np_mud", np_mud); - settings->setNoiseParams("mgv6_np_beach", np_beach); - settings->setNoiseParams("mgv6_np_biome", np_biome); - settings->setNoiseParams("mgv6_np_cave", np_cave); -} - - -/////////////////////////////////// legacy static functions for farmesh - - -s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { - //just need to return something - s16 level = 5; - return level; -} - - -bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) { - double sandnoise = noise2d_perlin( - 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, - seed+59420, 3, 0.50); - - return (sandnoise > 0.15); -} - - -double Mapgen::tree_amount_2d(u64 seed, v2s16 p) { - double noise = noise2d_perlin( - 0.5+(float)p.X/125, 0.5+(float)p.Y/125, - seed+2, 4, 0.66); - double zeroval = -0.39; - if(noise < zeroval) - return 0; - else - return 0.04 * (noise-zeroval) / (1.0-zeroval); -} - - -#if 0 /// BIG COMMENT -namespace mapgen -{ - -/* - Some helper functions for the map generator -*/ - -#if 1 -// Returns Y one under area minimum if not found -static s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).walkable) - break; - - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; -} - -#if 0 -// Returns Y one under area minimum if not found -static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - if(!vmanip.m_area.contains(v3s16(p2d.X, vmanip.m_area.MaxEdge.Y, p2d.Y))) - return vmanip.m_area.MinEdge.Y-1; - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - content_t c_tree = ndef->getId("mapgen_tree"); - content_t c_leaves = ndef->getId("mapgen_leaves"); - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).walkable - && n.getContent() != c_tree - && n.getContent() != c_leaves) - break; - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; -} -#endif - -// Returns Y one under area minimum if not found -static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - content_t c_stone = ndef->getId("mapgen_stone"); - content_t c_desert_stone = ndef->getId("mapgen_desert_stone"); - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - content_t c = n.getContent(); - if(c != CONTENT_IGNORE && ( - c == c_stone || c == c_desert_stone)) - break; - - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; -} -#endif - - -#if 0 - -static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) -{ - MapNode papyrusnode(ndef->getId("mapgen_papyrus")); - - s16 trunk_h = myrand_range(2, 3); - v3s16 p1 = p0; - for(s16 ii=0; ii<trunk_h; ii++) - { - if(vmanip.m_area.contains(p1)) - vmanip.m_data[vmanip.m_area.index(p1)] = papyrusnode; - p1.Y++; - } -} +/////////////////////////////////////////////////////////////////////////////// -static void make_cactus(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) -{ - MapNode cactusnode(ndef->getId("mapgen_cactus")); - s16 trunk_h = 3; - v3s16 p1 = p0; - for(s16 ii=0; ii<trunk_h; ii++) - { - if(vmanip.m_area.contains(p1)) - vmanip.m_data[vmanip.m_area.index(p1)] = cactusnode; - p1.Y++; +Ore *createOre(OreType type) { + switch (type) { + case ORE_SCATTER: + return new OreScatter; + case ORE_SHEET: + return new OreSheet; + //case ORE_CLAYLIKE: //TODO: implement this! + // return new OreClaylike; + default: + return NULL; } } -#endif -#if 0 -/* - Dungeon making routines -*/ - -#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1 -#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2 -#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\ - VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE) - -static void make_room1(VoxelManipulator &vmanip, v3s16 roomsize, v3s16 roomplace, - INodeDefManager *ndef) -{ - // Make +-X walls - for(s16 z=0; z<roomsize.Z; z++) - for(s16 y=0; y<roomsize.Y; y++) - { - { - v3s16 p = roomplace + v3s16(0,y,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(roomsize.X-1,y,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); - } - } - // Make +-Z walls - for(s16 x=0; x<roomsize.X; x++) - for(s16 y=0; y<roomsize.Y; y++) - { - { - v3s16 p = roomplace + v3s16(x,y,0); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); +void Ore::resolveNodeNames(INodeDefManager *ndef) { + if (ore == CONTENT_IGNORE) { + ore = ndef->getId(ore_name); + if (ore == CONTENT_IGNORE) { + errorstream << "Ore::resolveNodeNames: ore node '" + << ore_name << "' not defined"; + ore = CONTENT_AIR; + wherein = CONTENT_AIR; } } - - // Make +-Y walls (floor and ceiling) - for(s16 z=0; z<roomsize.Z; z++) - for(s16 x=0; x<roomsize.X; x++) - { - { - v3s16 p = roomplace + v3s16(x,0,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); + + if (wherein == CONTENT_IGNORE) { + wherein = ndef->getId(wherein_name); + if (wherein == CONTENT_IGNORE) { + errorstream << "Ore::resolveNodeNames: wherein node '" + << wherein_name << "' not defined"; + ore = CONTENT_AIR; + wherein = CONTENT_AIR; } } - - // Fill with air - for(s16 z=1; z<roomsize.Z-1; z++) - for(s16 y=1; y<roomsize.Y-1; y++) - for(s16 x=1; x<roomsize.X-1; x++) - { - v3s16 p = roomplace + v3s16(x,y,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - vmanip.m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; - vmanip.m_data[vi] = MapNode(CONTENT_AIR); - } } -static void make_fill(VoxelManipulator &vmanip, v3s16 place, v3s16 size, - u8 avoid_flags, MapNode n, u8 or_flags) -{ - for(s16 z=0; z<size.Z; z++) - for(s16 y=0; y<size.Y; y++) - for(s16 x=0; x<size.X; x++) - { - v3s16 p = place + v3s16(x,y,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & avoid_flags) + +void OreScatter::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + if (nmin.Y > height_max || nmax.Y < height_min) + return; + + resolveNodeNames(mg->ndef); + + MapNode n_ore(ore); + ManualMapVoxelManipulator *vm = mg->vm; + PseudoRandom pr(blockseed); + + int ymin = MYMAX(nmin.Y, height_min); + int ymax = MYMIN(nmax.Y, height_max); + if (clust_size >= ymax - ymin + 1) + return; + + int volume = (nmax.X - nmin.X + 1) * + (nmax.Y - nmin.Y + 1) * + (nmax.Z - nmin.Z + 1); + int csize = clust_size; + int orechance = (csize * csize * csize) / clust_num_ores; + int nclusters = volume / clust_scarcity; + + for (int i = 0; i != nclusters; i++) { + int x0 = pr.range(nmin.X, nmax.X - csize + 1); + int y0 = pr.range(ymin, ymax - csize + 1); + int z0 = pr.range(nmin.Z, nmax.Z - csize + 1); + + if (np && (NoisePerlin3D(np, x0, y0, z0, mg->seed) < nthresh)) continue; - vmanip.m_flags[vi] |= or_flags; - vmanip.m_data[vi] = n; + + for (int z1 = 0; z1 != csize; z1++) + for (int y1 = 0; y1 != csize; y1++) + for (int x1 = 0; x1 != csize; x1++) { + if (pr.range(1, orechance) != 1) + continue; + + u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1); + if (vm->m_data[i].getContent() == wherein) + vm->m_data[i] = n_ore; + } } } -static void make_hole1(VoxelManipulator &vmanip, v3s16 place, - INodeDefManager *ndef) -{ - make_fill(vmanip, place, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE); -} -static void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir, - INodeDefManager *ndef) -{ - make_hole1(vmanip, doorplace, ndef); - // Place torch (for testing) - //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(ndef->getId("mapgen_torch")); -} +void OreSheet::generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + if (nmin.Y > height_max || nmax.Y < height_min) + return; -static v3s16 rand_ortho_dir(PseudoRandom &random) -{ - if(random.next()%2==0) - return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0); - else - return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1); -} + resolveNodeNames(mg->ndef); -static v3s16 turn_xz(v3s16 olddir, int t) -{ - v3s16 dir; - if(t == 0) - { - // Turn right - dir.X = olddir.Z; - dir.Z = -olddir.X; - dir.Y = olddir.Y; + MapNode n_ore(ore); + ManualMapVoxelManipulator *vm = mg->vm; + PseudoRandom pr(blockseed + 4234); + + int ymin = MYMAX(nmin.Y, height_min); + int ymax = MYMIN(nmax.Y, height_max); + if (clust_size >= ymax - ymin + 1) + return; + + int x0 = nmin.X; + int z0 = nmin.Z; + + int x1 = nmax.X; + int z1 = nmax.Z; + + int max_height = clust_size; + int y_start = pr.range(ymin, ymax - max_height); + + if (!noise) { + int sx = nmax.X - nmin.X + 1; + int sz = nmax.Z - nmin.Z + 1; + noise = new Noise(np, 0, sx, sz); } - else - { - // Turn left - dir.X = -olddir.Z; - dir.Z = olddir.X; - dir.Y = olddir.Y; + noise->seed = mg->seed + y_start; + noise->perlinMap2D(x0, z0); + + int index = 0; + for (int z = z0; z != z1; z++) + for (int x = x0; x != x1; x++) { + float noiseval = noise->result[index++]; + if (noiseval < nthresh) + continue; + + int height = max_height * (1. / pr.range(1, 3)); + int y0 = y_start + np->scale * noiseval; //pr.range(1, 3) - 1; + int y1 = y0 + height; + for (int y = y0; y != y1; y++) { + u32 i = vm->m_area.index(x, y, z); + if (!vm->m_area.contains(i)) + continue; + + if (vm->m_data[i].getContent() == wherein) + vm->m_data[i] = n_ore; + } } - return dir; } -static v3s16 random_turn(PseudoRandom &random, v3s16 olddir) -{ - int turn = random.range(0,2); - v3s16 dir; - if(turn == 0) - { - // Go straight - dir = olddir; - } - else if(turn == 1) - // Turn right - dir = turn_xz(olddir, 0); - else - // Turn left - dir = turn_xz(olddir, 1); - return dir; -} -static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, - v3s16 doordir, v3s16 &result_place, v3s16 &result_dir, - PseudoRandom &random, INodeDefManager *ndef) -{ - make_hole1(vmanip, doorplace, ndef); - v3s16 p0 = doorplace; - v3s16 dir = doordir; - u32 length; - if(random.next()%2) - length = random.range(1,13); - else - length = random.range(1,6); - length = random.range(1,13); - u32 partlength = random.range(1,13); - u32 partcount = 0; - s16 make_stairs = 0; - if(random.next()%2 == 0 && partlength >= 3) - make_stairs = random.next()%2 ? 1 : -1; - for(u32 i=0; i<length; i++) - { - v3s16 p = p0 + dir; - if(partcount != 0) - p.Y += make_stairs; +void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax) { + bool isliquid, wasliquid; + v3s16 em = vm->m_area.getExtent(); - /*// If already empty - if(vmanip.getNodeNoExNoEmerge(p).getContent() - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - == CONTENT_AIR) - { - }*/ + for (s16 z = nmin.Z; z <= nmax.Z; z++) { + for (s16 x = nmin.X; x <= nmax.X; x++) { + wasliquid = true; + + u32 i = vm->m_area.index(x, nmax.Y, z); + for (s16 y = nmax.Y; y >= nmin.Y; y--) { + isliquid = ndef->get(vm->m_data[i]).isLiquid(); + + // there was a change between liquid and nonliquid, add to queue + if (isliquid != wasliquid) + trans_liquid->push_back(v3s16(x, y, z)); - if(vmanip.m_area.contains(p) == true - && vmanip.m_area.contains(p+v3s16(0,1,0)) == true) - { - if(make_stairs) - { - make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,5,3), - VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0); - make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE); - make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE); + wasliquid = isliquid; + vm->m_area.add_y(em, i, -1); } - else - { - make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3), - VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0); - make_hole1(vmanip, p, ndef); - /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE);*/ - } - - p0 = p; - } - else - { - // Can't go here, turn away - dir = turn_xz(dir, random.range(0,1)); - make_stairs = -make_stairs; - partcount = 0; - partlength = random.range(1,length); - continue; - } - - partcount++; - if(partcount >= partlength) - { - partcount = 0; - - dir = random_turn(random, dir); - - partlength = random.range(1,length); - - make_stairs = 0; - if(random.next()%2 == 0 && partlength >= 3) - make_stairs = random.next()%2 ? 1 : -1; } } - result_place = p0; - result_dir = dir; } -class RoomWalker -{ -public: - RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random, - INodeDefManager *ndef): - vmanip(vmanip_), - m_pos(pos), - m_random(random), - m_ndef(ndef) - { - randomizeDir(); - } +void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, + nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); - void randomizeDir() - { - m_dir = rand_ortho_dir(m_random); + for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { + for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { + u32 i = vm->m_area.index(a.MinEdge.X, y, z); + for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) + vm->m_data[i].param1 = light; + } } +} - void setPos(v3s16 pos) - { - m_pos = pos; - } - void setDir(v3s16 dir) - { - m_dir = dir; - } +void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { + if (light <= 1 || !a.contains(p)) + return; + + u32 vi = vm->m_area.index(p); + MapNode &nn = vm->m_data[vi]; - bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) - { - for(u32 i=0; i<100; i++) - { - v3s16 p = m_pos + m_dir; - v3s16 p1 = p + v3s16(0,1,0); - if(vmanip.m_area.contains(p) == false - || vmanip.m_area.contains(p1) == false - || i % 4 == 0) - { - randomizeDir(); - continue; - } - if(vmanip.getNodeNoExNoEmerge(p).getContent() - == m_ndef->getId("mapgen_cobble") - && vmanip.getNodeNoExNoEmerge(p1).getContent() - == m_ndef->getId("mapgen_cobble")) - { - // Found wall, this is a good place! - result_place = p; - result_dir = m_dir; - // Randomize next direction - randomizeDir(); - return true; - } - /* - Determine where to move next - */ - // Jump one up if the actual space is there - if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() - == m_ndef->getId("mapgen_cobble") - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() - == CONTENT_AIR) - p += v3s16(0,1,0); - // Jump one down if the actual space is there - if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - == m_ndef->getId("mapgen_cobble") - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() - == CONTENT_AIR) - p += v3s16(0,-1,0); - // Check if walking is now possible - if(vmanip.getNodeNoExNoEmerge(p).getContent() - != CONTENT_AIR - || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - != CONTENT_AIR) - { - // Cannot continue walking here - randomizeDir(); + light--; + // should probably compare masked, but doesn't seem to make a difference + if (light <= nn.param1 || !ndef->get(nn).light_propagates) + return; + + nn.param1 = light; + + lightSpread(a, p + v3s16(0, 0, 1), light); + lightSpread(a, p + v3s16(0, 1, 0), light); + lightSpread(a, p + v3s16(1, 0, 0), light); + lightSpread(a, p - v3s16(0, 0, 1), light); + lightSpread(a, p - v3s16(0, 1, 0), light); + lightSpread(a, p - v3s16(1, 0, 0), light); +} + + +void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { + VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, + nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + bool block_is_underground = (water_level >= nmax.Y); + + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + //TimeTaker t("updateLighting"); + + // first, send vertical rays of sunshine downward + v3s16 em = vm->m_area.getExtent(); + for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { + for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) { + // see if we can get a light value from the overtop + u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z); + if (vm->m_data[i].getContent() == CONTENT_IGNORE) { + if (block_is_underground) + continue; + } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN) { continue; } - // Move there - m_pos = p; - } - return false; - } - - bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, - v3s16 &result_doordir, v3s16 &result_roomplace) - { - for(s16 trycount=0; trycount<30; trycount++) - { - v3s16 doorplace; - v3s16 doordir; - bool r = findPlaceForDoor(doorplace, doordir); - if(r == false) - continue; - v3s16 roomplace; - // X east, Z north, Y up -#if 1 - if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + - v3s16(0,-1,m_random.range(-roomsize.Z+2,-2)); - if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + - v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2)); - if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + - v3s16(m_random.range(-roomsize.X+2,-2),-1,0); - if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + - v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1); -#endif -#if 0 - if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2); - if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2); - if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + v3s16(-roomsize.X/2,-1,0); - if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1); -#endif + vm->m_area.add_y(em, i, -1); - // Check fit - bool fits = true; - for(s16 z=1; z<roomsize.Z-1; z++) - for(s16 y=1; y<roomsize.Y-1; y++) - for(s16 x=1; x<roomsize.X-1; x++) - { - v3s16 p = roomplace + v3s16(x,y,z); - if(vmanip.m_area.contains(p) == false) - { - fits = false; - break; - } - if(vmanip.m_flags[vmanip.m_area.index(p)] - & VMANIP_FLAG_DUNGEON_INSIDE) - { - fits = false; + for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) { + MapNode &n = vm->m_data[i]; + if (!ndef->get(n).sunlight_propagates) break; - } - } - if(fits == false) - { - // Find new place - continue; + n.param1 = LIGHT_SUN; + vm->m_area.add_y(em, i, -1); } - result_doorplace = doorplace; - result_doordir = doordir; - result_roomplace = roomplace; - return true; } - return false; } - -private: - VoxelManipulator &vmanip; - v3s16 m_pos; - v3s16 m_dir; - PseudoRandom &m_random; - INodeDefManager *m_ndef; -}; - -static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random, - INodeDefManager *ndef) -{ - v3s16 areasize = vmanip.m_area.getExtent(); - v3s16 roomsize; - v3s16 roomplace; - - /* - Find place for first room - */ - bool fits = false; - for(u32 i=0; i<100; i++) - { - roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); - roomplace = vmanip.m_area.MinEdge + v3s16( - random.range(0,areasize.X-roomsize.X-1), - random.range(0,areasize.Y-roomsize.Y-1), - random.range(0,areasize.Z-roomsize.Z-1)); - /* - Check that we're not putting the room to an unknown place, - otherwise it might end up floating in the air - */ - fits = true; - for(s16 z=1; z<roomsize.Z-1; z++) - for(s16 y=1; y<roomsize.Y-1; y++) - for(s16 x=1; x<roomsize.X-1; x++) - { - v3s16 p = roomplace + v3s16(x,y,z); - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_INSIDE) - { - fits = false; - break; - } - if(vmanip.m_data[vi].getContent() == CONTENT_IGNORE) - { - fits = false; - break; + + // now spread the sunlight and light up any sources + for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { + for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { + u32 i = vm->m_area.index(a.MinEdge.X, y, z); + for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) { + MapNode &n = vm->m_data[i]; + if (n.getContent() == CONTENT_IGNORE || + !ndef->get(n).light_propagates) + continue; + + u8 light_produced = ndef->get(n).light_source & 0x0F; + if (light_produced) + n.param1 = light_produced; + + u8 light = n.param1 & 0x0F; + if (light) { + lightSpread(a, v3s16(x, y, z + 1), light); + lightSpread(a, v3s16(x, y + 1, z ), light); + lightSpread(a, v3s16(x + 1, y, z ), light); + lightSpread(a, v3s16(x, y, z - 1), light); + lightSpread(a, v3s16(x, y - 1, z ), light); + lightSpread(a, v3s16(x - 1, y, z ), light); + } } } - if(fits) - break; } - // No place found - if(fits == false) - return; - - /* - Stores the center position of the last room made, so that - a new corridor can be started from the last room instead of - the new room, if chosen so. - */ - v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2); - - u32 room_count = random.range(2,7); - for(u32 i=0; i<room_count; i++) - { - // Make a room to the determined place - make_room1(vmanip, roomsize, roomplace, ndef); - - v3s16 room_center = roomplace + v3s16(roomsize.X/2,1,roomsize.Z/2); - - // Place torch at room center (for testing) - //vmanip.m_data[vmanip.m_area.index(room_center)] = MapNode(ndef->getId("mapgen_torch")); - - // Quit if last room - if(i == room_count-1) - break; - - // Determine walker start position - - bool start_in_last_room = (random.range(0,2)!=0); - //bool start_in_last_room = true; - - v3s16 walker_start_place; - - if(start_in_last_room) - { - walker_start_place = last_room_center; - } - else - { - walker_start_place = room_center; - // Store center of current room as the last one - last_room_center = room_center; - } + + //printf("updateLighting: %dms\n", t.stop()); +} - // Create walker and find a place for a door - RoomWalker walker(vmanip, walker_start_place, random, ndef); - v3s16 doorplace; - v3s16 doordir; - bool r = walker.findPlaceForDoor(doorplace, doordir); - if(r == false) - return; - if(random.range(0,1)==0) - // Make the door - make_door1(vmanip, doorplace, doordir, ndef); - else - // Don't actually make a door - doorplace -= doordir; +void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax) { + enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; - // Make a random corridor starting from the door - v3s16 corridor_end; - v3s16 corridor_end_dir; - make_corridor(vmanip, doorplace, doordir, corridor_end, - corridor_end_dir, random, ndef); + VoxelArea a(nmin - v3s16(1,0,1) * MAP_BLOCKSIZE, + nmax + v3s16(1,0,1) * MAP_BLOCKSIZE); + bool block_is_underground = (water_level > nmax.Y); + bool sunlight = !block_is_underground; - // Find a place for a random sized room - roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); - walker.setPos(corridor_end); - walker.setDir(corridor_end_dir); - r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace); - if(r == false) - return; + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + + for (int i = 0; i < 2; i++) { + enum LightBank bank = banks[i]; + std::set<v3s16> light_sources; + std::map<v3s16, u8> unlight_from; - if(random.range(0,1)==0) - // Make the door - make_door1(vmanip, doorplace, doordir, ndef); - else - // Don't actually make a door - roomplace -= doordir; + voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef, + light_sources, unlight_from); + voxalgo::propagateSunlight(*vm, a, sunlight, light_sources, ndef); + vm->unspreadLight(bank, unlight_from, light_sources, ndef); + vm->spreadLight(bank, light_sources, ndef); } } -#endif -#if 0 -static void make_nc(VoxelManipulator &vmanip, PseudoRandom &random, - INodeDefManager *ndef) -{ - v3s16 dir; - u8 facedir_i = 0; - s32 r = random.range(0, 3); - if(r == 0){ - dir = v3s16( 1, 0, 0); - facedir_i = 3; - } - if(r == 1){ - dir = v3s16(-1, 0, 0); - facedir_i = 1; - } - if(r == 2){ - dir = v3s16( 0, 0, 1); - facedir_i = 2; - } - if(r == 3){ - dir = v3s16( 0, 0,-1); - facedir_i = 0; - } - v3s16 p = vmanip.m_area.MinEdge + v3s16( - 16+random.range(0,15), - 16+random.range(0,15), - 16+random.range(0,15)); - vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat"), facedir_i); - u32 length = random.range(3,15); - for(u32 j=0; j<length; j++) - { - p -= dir; - vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat_rainbow")); - } -} -#endif -/* - Noise functions. Make sure seed is mangled differently in each one. -*/ +//////////////////////// Mapgen V6 parameter read/write -#if 0 -/* - Scaling the output of the noise function affects the overdrive of the - contour function, which affects the shape of the output considerably. -*/ -#define CAVE_NOISE_SCALE 12.0 -//#define CAVE_NOISE_SCALE 10.0 -//#define CAVE_NOISE_SCALE 7.5 -//#define CAVE_NOISE_SCALE 5.0 -//#define CAVE_NOISE_SCALE 1.0 +bool MapgenV6Params::readParams(Settings *settings) { + freq_desert = settings->getFloat("mgv6_freq_desert"); + freq_beach = settings->getFloat("mgv6_freq_beach"); -//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE) -#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE) + 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"); -NoiseParams get_cave_noise1_params(u64 seed) -{ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7, - 200, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7, - 100, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6, - 100, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3, - 100, CAVE_NOISE_SCALE);*/ - return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5, - 50, CAVE_NOISE_SCALE); - //return NoiseParams(NOISE_CONSTANT_ONE); + 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; + return success; } -NoiseParams get_cave_noise2_params(u64 seed) -{ - /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7, - 200, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7, - 100, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3, - 100, CAVE_NOISE_SCALE);*/ - return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5, - 50, CAVE_NOISE_SCALE); - //return NoiseParams(NOISE_CONSTANT_ONE); -} -NoiseParams get_ground_noise1_params(u64 seed) -{ - return NoiseParams(NOISE_PERLIN, seed+983240, 4, - 0.55, 80.0, 40.0); +void MapgenV6Params::writeParams(Settings *settings) { + settings->setFloat("mgv6_freq_desert", freq_desert); + settings->setFloat("mgv6_freq_beach", freq_beach); + + settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base); + settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); + settings->setNoiseParams("mgv6_np_steepness", np_steepness); + settings->setNoiseParams("mgv6_np_height_select", np_height_select); + settings->setNoiseParams("mgv6_np_mud", np_mud); + settings->setNoiseParams("mgv6_np_beach", np_beach); + settings->setNoiseParams("mgv6_np_biome", np_biome); + settings->setNoiseParams("mgv6_np_cave", np_cave); + settings->setNoiseParams("mgv6_np_humidity", np_humidity); + settings->setNoiseParams("mgv6_np_trees", np_trees); + settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees); } -NoiseParams get_ground_crumbleness_params(u64 seed) -{ - return NoiseParams(NOISE_PERLIN, seed+34413, 3, - 1.3, 20.0, 1.0); -} -NoiseParams get_ground_wetness_params(u64 seed) -{ - return NoiseParams(NOISE_PERLIN, seed+32474, 4, - 1.1, 40.0, 1.0); -} +/////////////////////////////////// legacy static functions for farmesh + -bool is_cave(u64 seed, v3s16 p) -{ - double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z); - double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z); - return d1*d2 > CAVE_NOISE_THRESHOLD; +s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { + //just need to return something + s16 level = 5; + return level; } -/* - Ground density noise shall be interpreted by using this. - TODO: No perlin noises here, they should be outsourced - and buffered - NOTE: The speed of these actually isn't terrible -*/ -bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed) -{ - //return ((double)p.Y < ground_noise1_val); +bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) { + double sandnoise = noise2d_perlin( + 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, + seed+59420, 3, 0.50); - double f = 0.55 + noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Z/250, - seed+920381, 3, 0.45); - if(f < 0.01) - f = 0.01; - else if(f >= 1.0) - f *= 1.6; - double h = WATER_LEVEL + 10 * noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Z/250, - seed+84174, 4, 0.5); - /*double f = 1; - double h = 0;*/ - return ((double)p.Y - h < ground_noise1_val * f); + return (sandnoise > 0.15); } -/* - Queries whether a position is ground or not. -*/ -bool is_ground(u64 seed, v3s16 p) -{ - double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z); - return val_is_ground(val1, p, seed); -} -#endif -// Amount of trees per area in nodes -double tree_amount_2d(u64 seed, v2s16 p) -{ - /*double noise = noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Y/250, - seed+2, 5, 0.66);*/ +double Mapgen::tree_amount_2d(u64 seed, v2s16 p) { double noise = noise2d_perlin( 0.5+(float)p.X/125, 0.5+(float)p.Y/125, seed+2, 4, 0.66); @@ -956,1895 +408,3 @@ double tree_amount_2d(u64 seed, v2s16 p) else return 0.04 * (noise-zeroval) / (1.0-zeroval); } - -#if 0 -double surface_humidity_2d(u64 seed, v2s16 p) -{ - double noise = noise2d_perlin( - 0.5+(float)p.X/500, 0.5+(float)p.Y/500, - seed+72384, 4, 0.66); - noise = (noise + 1.0)/2.0; - if(noise < 0.0) - noise = 0.0; - if(noise > 1.0) - noise = 1.0; - return noise; -} - -/* - Incrementally find ground level from 3d noise -*/ -s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) -{ - // Start a bit fuzzy to make averaging lower precision values - // more useful - s16 level = myrand_range(-precision/2, precision/2); - s16 dec[] = {31000, 100, 20, 4, 1, 0}; - s16 i; - for(i = 1; dec[i] != 0 && precision <= dec[i]; i++) - { - // First find non-ground by going upwards - // Don't stop in caves. - { - s16 max = level+dec[i-1]*2; - v3s16 p(p2d.X, level, p2d.Y); - for(; p.Y < max; p.Y += dec[i]) - { - if(!is_ground(seed, p)) - { - level = p.Y; - break; - } - } - } - // Then find ground by going downwards from there. - // Go in caves, too, when precision is 1. - { - s16 min = level-dec[i-1]*2; - v3s16 p(p2d.X, level, p2d.Y); - for(; p.Y>min; p.Y-=dec[i]) - { - bool ground = is_ground(seed, p); - /*if(dec[i] == 1 && is_cave(seed, p)) - ground = false;*/ - if(ground) - { - level = p.Y; - break; - } - } - } - } - - // This is more like the actual ground level - level += dec[i-1]/2; - - return level; -} - -double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = 0; - a += find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p); - a /= 5; - return a; -} - -double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = -31000; - // Corners - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - // Center - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); - // Side middle points - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - return a; -} - -double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = 31000; - // Corners - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - // Center - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); - // Side middle points - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - return a; -} -#endif - -// Required by mapgen.h -bool block_is_underground(u64 seed, v3s16 blockpos) -{ - /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level( - seed, v2s16(blockpos.X, blockpos.Z));*/ - // Nah, this is just a heuristic, just return something - s16 minimum_groundlevel = WATER_LEVEL; - - if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel) - return true; - else - return false; -} - -#define AVERAGE_MUD_AMOUNT 4 - -double base_rock_level_2d(u64 seed, v2s16 p) -{ - // The base ground level - double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT - + 20. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+82341, 5, 0.6); - - /*// A bit hillier one - double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+93413, 6, 0.69); - if(base2 > base) - base = base2;*/ -#if 1 - // Higher ground level - double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin( - 0.5+(float)p.X/500., 0.5+(float)p.Y/500., - seed+85039, 5, 0.6); - //higher = 30; // For debugging - - // Limit higher to at least base - if(higher < base) - higher = base; - - // Steepness factor of cliffs - double b = 0.85 + 0.5 * noise2d_perlin( - 0.5+(float)p.X/125., 0.5+(float)p.Y/125., - seed-932, 5, 0.7); - b = rangelim(b, 0.0, 1000.0); - b = b*b*b*b*b*b*b; - b *= 5; - b = rangelim(b, 0.5, 1000.0); - // Values 1.5...100 give quite horrible looking slopes - if(b > 1.5 && b < 100.0){ - if(b < 10.0) - b = 1.5; - else - b = 100.0; - } - //dstream<<"b="<<b<<std::endl; - //double b = 20; - //b = 0.25; - - // Offset to more low - double a_off = -0.20; - // High/low selector - /*double a = 0.5 + b * (a_off + noise2d_perlin( - 0.5+(float)p.X/500., 0.5+(float)p.Y/500., - seed+4213, 6, 0.7));*/ - double a = (double)0.5 + b * (a_off + noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+4213, 5, 0.69)); - // Limit - a = rangelim(a, 0.0, 1.0); - - //dstream<<"a="<<a<<std::endl; - - double h = base*(1.0-a) + higher*a; -#else - double h = base; -#endif - return h; -} - -s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) -{ - return base_rock_level_2d(seed, p2d) + AVERAGE_MUD_AMOUNT; -} - -double get_mud_add_amount(u64 seed, v2s16 p) -{ - return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin( - 0.5+(float)p.X/200, 0.5+(float)p.Y/200, - seed+91013, 3, 0.55)); -} - -bool get_have_beach(u64 seed, v2s16 p2d) -{ - // Determine whether to have sand here - double sandnoise = noise2d_perlin( - 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, - seed+59420, 3, 0.50); - - return (sandnoise > 0.15); -} - -enum BiomeType -{ - BT_NORMAL, - BT_DESERT -}; - -BiomeType get_biome(u64 seed, v2s16 p2d) -{ - // Just do something very simple as for now - double d = noise2d_perlin( - 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250, - seed+9130, 3, 0.50); - if(d > 0.45) - return BT_DESERT; - if(d > 0.35 && (noise2d( p2d.X, p2d.Y, int(seed) ) + 1.0) > ( 0.45 - d ) * 20.0 ) - return BT_DESERT; - return BT_NORMAL; -}; - -u32 get_blockseed(u64 seed, v3s16 p) -{ - s32 x=p.X, y=p.Y, z=p.Z; - return (u32)(seed%0x100000000ULL) + z*38134234 + y*42123 + x*23; -} - -#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1 - -void make_block(BlockMakeData *data) -{ - if(data->no_op) - { - //dstream<<"makeBlock: no-op"<<std::endl; - return; - } - - assert(data->vmanip); - assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); - - INodeDefManager *ndef = data->nodedef; - - // Hack: use minimum block coordinates for old code that assumes - // a single block - v3s16 blockpos = data->blockpos_requested; - - /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<"," - <<blockpos.Z<<")"<<std::endl;*/ - - 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); - - ManualMapVoxelManipulator &vmanip = *(data->vmanip); - // Area of central chunk - v3s16 node_min = blockpos_min*MAP_BLOCKSIZE; - v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1); - // Full allocated area - v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE; - v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1); - - v3s16 central_area_size = node_max - node_min + v3s16(1,1,1); - - const s16 max_spread_amount = MAP_BLOCKSIZE; - - int volume_blocks = (blockpos_max.X - blockpos_min.X + 1) - * (blockpos_max.Y - blockpos_min.Y + 1) - * (blockpos_max.Z - blockpos_max.Z + 1); - - int volume_nodes = volume_blocks * - MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - - // Generated surface area - //double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume; - - // Horribly wrong heuristic, but better than nothing - bool block_is_underground = (WATER_LEVEL > node_max.Y); - - /* - Create a block-specific seed - */ - u32 blockseed = get_blockseed(data->seed, full_node_min); - - /* - Cache some ground type values for speed - */ - -// Creates variables c_name=id and n_name=node -#define CONTENT_VARIABLE(ndef, name)\ - content_t c_##name = ndef->getId("mapgen_" #name);\ - MapNode n_##name(c_##name); -// Default to something else if was CONTENT_IGNORE -#define CONTENT_VARIABLE_FALLBACK(name, dname)\ - if(c_##name == CONTENT_IGNORE){\ - c_##name = c_##dname;\ - n_##name = n_##dname;\ - } - - CONTENT_VARIABLE(ndef, stone); - CONTENT_VARIABLE(ndef, air); - CONTENT_VARIABLE(ndef, water_source); - CONTENT_VARIABLE(ndef, dirt); - CONTENT_VARIABLE(ndef, sand); - CONTENT_VARIABLE(ndef, gravel); - CONTENT_VARIABLE(ndef, clay); - CONTENT_VARIABLE(ndef, lava_source); - CONTENT_VARIABLE(ndef, cobble); - CONTENT_VARIABLE(ndef, mossycobble); - CONTENT_VARIABLE(ndef, dirt_with_grass); - CONTENT_VARIABLE(ndef, junglegrass); - CONTENT_VARIABLE(ndef, stone_with_coal); - CONTENT_VARIABLE(ndef, stone_with_iron); - CONTENT_VARIABLE(ndef, mese); - CONTENT_VARIABLE(ndef, desert_sand); - CONTENT_VARIABLE_FALLBACK(desert_sand, sand); - CONTENT_VARIABLE(ndef, desert_stone); - CONTENT_VARIABLE_FALLBACK(desert_stone, stone); - - // Maximum height of the stone surface and obstacles. - // This is used to guide the cave generation - s16 stone_surface_max_y = 0; - - /* - Generate general ground level to full area - */ - { -#if 1 - TimeTaker timer1("Generating ground level"); - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d = v2s16(x,z); - - /* - Skip of already generated - */ - /*{ - v3s16 p(p2d.X, node_min.Y, p2d.Y); - if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR) - continue; - }*/ - - // Ground height at this point - float surface_y_f = 0.0; - - // Use perlin noise for ground height - surface_y_f = base_rock_level_2d(data->seed, p2d); - - /*// Experimental stuff - { - float a = highlands_level_2d(data->seed, p2d); - if(a > surface_y_f) - surface_y_f = a; - }*/ - - // Convert to integer - s16 surface_y = (s16)surface_y_f; - - // Log it - if(surface_y > stone_surface_max_y) - stone_surface_max_y = surface_y; - - BiomeType bt = get_biome(data->seed, p2d); - /* - Fill ground with stone - */ - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) - { - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){ - if(y <= surface_y){ - if(y > WATER_LEVEL && bt == BT_DESERT) - vmanip.m_data[i] = n_desert_stone; - else - vmanip.m_data[i] = n_stone; - } else if(y <= WATER_LEVEL){ - vmanip.m_data[i] = MapNode(c_water_source); - } else { - vmanip.m_data[i] = MapNode(c_air); - } - } - vmanip.m_area.add_y(em, i, 1); - } - } - } -#endif - - }//timer1 - - // Limit dirt flow area by 1 because mud is flown into neighbors. - assert(central_area_size.X == central_area_size.Z); - s16 mudflow_minpos = 0-max_spread_amount+1; - s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2; - - /* - Loop this part, it will make stuff look older and newer nicely - */ - - const u32 age_loops = 2; - for(u32 i_age=0; i_age<age_loops; i_age++) - { // Aging loop - /****************************** - BEGINNING OF AGING LOOP - ******************************/ - -#if 1 - { - // 24ms @cs=8 - //TimeTaker timer1("caves"); - - /* - Make caves (this code is relatively horrible) - */ - double cave_amount = 6.0 + 6.0 * noise2d_perlin( - 0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250, - data->seed+34329, 3, 0.50); - cave_amount = MYMAX(0.0, cave_amount); - u32 caves_count = cave_amount * volume_nodes / 50000; - u32 bruises_count = 1; - PseudoRandom ps(blockseed+21343); - PseudoRandom ps2(blockseed+1032); - if(ps.range(1, 6) == 1) - bruises_count = ps.range(0, ps.range(0, 2)); - if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_DESERT){ - caves_count /= 3; - bruises_count /= 3; - } - for(u32 jj=0; jj<caves_count+bruises_count; jj++) - { - bool large_cave = (jj >= caves_count); - s16 min_tunnel_diameter = 2; - s16 max_tunnel_diameter = ps.range(2,6); - int dswitchint = ps.range(1,14); - u16 tunnel_routepoints = 0; - int part_max_length_rs = 0; - if(large_cave){ - part_max_length_rs = ps.range(2,4); - tunnel_routepoints = ps.range(5, ps.range(15,30)); - min_tunnel_diameter = 5; - max_tunnel_diameter = ps.range(7, ps.range(8,24)); - } else { - part_max_length_rs = ps.range(2,9); - tunnel_routepoints = ps.range(10, ps.range(15,30)); - } - bool large_cave_is_flat = (ps.range(0,1) == 0); - - v3f main_direction(0,0,0); - - // Allowed route area size in nodes - v3s16 ar = central_area_size; - - // Area starting point in nodes - v3s16 of = node_min; - - // Allow a bit more - //(this should be more than the maximum radius of the tunnel) - //s16 insure = 5; // Didn't work with max_d = 20 - s16 insure = 10; - s16 more = max_spread_amount - max_tunnel_diameter/2 - insure; - ar += v3s16(1,0,1) * more * 2; - of -= v3s16(1,0,1) * more; - - s16 route_y_min = 0; - // Allow half a diameter + 7 over stone surface - s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7; - - /*// If caves, don't go through surface too often - if(large_cave == false) - route_y_max -= ps.range(0, max_tunnel_diameter*2);*/ - - // Limit maximum to area - route_y_max = rangelim(route_y_max, 0, ar.Y-1); - - if(large_cave) - { - /*// Minimum is at y=0 - route_y_min = -of.Y - 0;*/ - // Minimum is at y=max_tunnel_diameter/4 - //route_y_min = -of.Y + max_tunnel_diameter/4; - //s16 min = -of.Y + max_tunnel_diameter/4; - //s16 min = -of.Y + 0; - s16 min = 0; - if(node_min.Y < WATER_LEVEL && node_max.Y > WATER_LEVEL) - { - min = WATER_LEVEL - max_tunnel_diameter/3 - of.Y; - route_y_max = WATER_LEVEL + max_tunnel_diameter/3 - of.Y; - } - route_y_min = ps.range(min, min + max_tunnel_diameter); - route_y_min = rangelim(route_y_min, 0, route_y_max); - } - - /*dstream<<"route_y_min = "<<route_y_min - <<", route_y_max = "<<route_y_max<<std::endl;*/ - - s16 route_start_y_min = route_y_min; - s16 route_start_y_max = route_y_max; - - // Start every 4th cave from surface when applicable - /*bool coming_from_surface = false; - if(node_min.Y <= 0 && node_max.Y >= 0){ - coming_from_surface = (jj % 4 == 0 && large_cave == false); - if(coming_from_surface) - route_start_y_min = -of.Y + stone_surface_max_y + 10; - }*/ - - route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1); - route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1); - - // Randomize starting position - v3f orp( - (float)(ps.next()%ar.X)+0.5, - (float)(ps.range(route_start_y_min, route_start_y_max))+0.5, - (float)(ps.next()%ar.Z)+0.5 - ); - - v3s16 startp(orp.X, orp.Y, orp.Z); - startp += of; - - MapNode airnode(CONTENT_AIR); - MapNode waternode(c_water_source); - MapNode lavanode(c_lava_source); - - /* - Generate some tunnel starting from orp - */ - - for(u16 j=0; j<tunnel_routepoints; j++) - { - if(j%dswitchint==0 && large_cave == false) - { - main_direction = v3f( - ((float)(ps.next()%20)-(float)10)/10, - ((float)(ps.next()%20)-(float)10)/30, - ((float)(ps.next()%20)-(float)10)/10 - ); - main_direction *= (float)ps.range(0, 10)/10; - } - - // Randomize size - s16 min_d = min_tunnel_diameter; - s16 max_d = max_tunnel_diameter; - s16 rs = ps.range(min_d, max_d); - - // Every second section is rough - bool randomize_xz = (ps2.range(1,2) == 1); - - v3s16 maxlen; - if(large_cave) - { - maxlen = v3s16( - rs*part_max_length_rs, - rs*part_max_length_rs/2, - rs*part_max_length_rs - ); - } - else - { - maxlen = v3s16( - rs*part_max_length_rs, - ps.range(1, rs*part_max_length_rs), - rs*part_max_length_rs - ); - } - - v3f vec; - - vec = v3f( - (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2, - (float)(ps.next()%(maxlen.Y*1))-(float)maxlen.Y/2, - (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2 - ); - - // Jump downward sometimes - if(!large_cave && ps.range(0,12) == 0) - { - vec = v3f( - (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2, - (float)(ps.next()%(maxlen.Y*2))-(float)maxlen.Y*2/2, - (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2 - ); - } - - /*if(large_cave){ - v3f p = orp + vec; - s16 h = find_ground_level_clever(vmanip, - v2s16(p.X, p.Z), ndef); - route_y_min = h - rs/3; - route_y_max = h + rs; - }*/ - - vec += main_direction; - - v3f rp = orp + vec; - if(rp.X < 0) - rp.X = 0; - else if(rp.X >= ar.X) - rp.X = ar.X-1; - if(rp.Y < route_y_min) - rp.Y = route_y_min; - else if(rp.Y >= route_y_max) - rp.Y = route_y_max-1; - if(rp.Z < 0) - rp.Z = 0; - else if(rp.Z >= ar.Z) - rp.Z = ar.Z-1; - vec = rp - orp; - - for(float f=0; f<1.0; f+=1.0/vec.getLength()) - { - v3f fp = orp + vec * f; - fp.X += 0.1*ps.range(-10,10); - fp.Z += 0.1*ps.range(-10,10); - v3s16 cp(fp.X, fp.Y, fp.Z); - - s16 d0 = -rs/2; - s16 d1 = d0 + rs; - if(randomize_xz){ - d0 += ps.range(-1,1); - d1 += ps.range(-1,1); - } - for(s16 z0=d0; z0<=d1; z0++) - { - s16 si = rs/2 - MYMAX(0, abs(z0)-rs/7-1); - for(s16 x0=-si-ps.range(0,1); x0<=si-1+ps.range(0,1); x0++) - { - s16 maxabsxz = MYMAX(abs(x0), abs(z0)); - s16 si2 = rs/2 - MYMAX(0, maxabsxz-rs/7-1); - for(s16 y0=-si2; y0<=si2; y0++) - { - /*// Make better floors in small caves - if(y0 <= -rs/2 && rs<=7) - continue;*/ - if(large_cave_is_flat){ - // Make large caves not so tall - if(rs > 7 && abs(y0) >= rs/3) - continue; - } - - s16 z = cp.Z + z0; - s16 y = cp.Y + y0; - s16 x = cp.X + x0; - v3s16 p(x,y,z); - p += of; - - if(vmanip.m_area.contains(p) == false) - continue; - - u32 i = vmanip.m_area.index(p); - - if(large_cave) - { - if(full_node_min.Y < WATER_LEVEL && - full_node_max.Y > WATER_LEVEL){ - if(p.Y <= WATER_LEVEL) - vmanip.m_data[i] = waternode; - else - vmanip.m_data[i] = airnode; - } else if(full_node_max.Y < WATER_LEVEL){ - if(p.Y < startp.Y - 2) - vmanip.m_data[i] = lavanode; - else - vmanip.m_data[i] = airnode; - } else { - vmanip.m_data[i] = airnode; - } - } else { - // Don't replace air or water or lava or ignore - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE || - vmanip.m_data[i].getContent() == CONTENT_AIR || - vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source) - continue; - - vmanip.m_data[i] = airnode; - - // Set tunnel flag - vmanip.m_flags[i] |= VMANIP_FLAG_CAVE; - } - } - } - } - } - - orp = rp; - } - - } - - }//timer1 -#endif - -#if 1 - { - // 15ms @cs=8 - TimeTaker timer1("add mud"); - - /* - Add mud to the central chunk - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position in 2d - v2s16 p2d = v2s16(x,z); - - // Randomize mud amount - s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5; - - // Find ground level - s16 surface_y = find_stone_level(vmanip, p2d, ndef); - // Handle area not found - if(surface_y == vmanip.m_area.MinEdge.Y - 1) - continue; - - MapNode addnode(c_dirt); - BiomeType bt = get_biome(data->seed, p2d); - - if(bt == BT_DESERT) - addnode = MapNode(c_desert_sand); - - if(bt == BT_DESERT && surface_y + mud_add_amount <= WATER_LEVEL+1){ - addnode = MapNode(c_sand); - } else if(mud_add_amount <= 0){ - mud_add_amount = 1 - mud_add_amount; - addnode = MapNode(c_gravel); - } else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) && - surface_y + mud_add_amount <= WATER_LEVEL+2){ - addnode = MapNode(c_sand); - } - - if(bt == BT_DESERT){ - if(surface_y > 20){ - mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5); - } - } - - /* - If topmost node is grass, change it to mud. - It might be if it was flown to there from a neighboring - chunk and then converted. - */ - { - u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y)); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() == c_dirt_with_grass) - *n = MapNode(c_dirt); - } - - /* - Add mud on ground - */ - { - s16 mudcount = 0; - v3s16 em = vmanip.m_area.getExtent(); - s16 y_start = surface_y+1; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); - for(s16 y=y_start; y<=node_max.Y; y++) - { - if(mudcount >= mud_add_amount) - break; - - MapNode &n = vmanip.m_data[i]; - n = addnode; - mudcount++; - - vmanip.m_area.add_y(em, i, 1); - } - } - - } - - }//timer1 -#endif - - /* - Add blobs of dirt and gravel underground - */ - if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_NORMAL) - { - PseudoRandom pr(blockseed+983); - for(int i=0; i<volume_nodes/10/10/10; i++) - { - bool only_fill_cave = (myrand_range(0,1) != 0); - v3s16 size( - pr.range(1, 8), - pr.range(1, 8), - pr.range(1, 8) - ); - v3s16 p0( - pr.range(node_min.X, node_max.X)-size.X/2, - pr.range(node_min.Y, node_max.Y)-size.Y/2, - pr.range(node_min.Z, node_max.Z)-size.Z/2 - ); - MapNode n1; - if(p0.Y > -32 && pr.range(0,1) == 0) - n1 = MapNode(c_dirt); - else - n1 = MapNode(c_gravel); - for(int x1=0; x1<size.X; x1++) - for(int y1=0; y1<size.Y; y1++) - for(int z1=0; z1<size.Z; z1++) - { - v3s16 p = p0 + v3s16(x1,y1,z1); - u32 i = vmanip.m_area.index(p); - if(!vmanip.m_area.contains(i)) - continue; - // Cancel if not stone and not cave air - if(vmanip.m_data[i].getContent() != c_stone && - !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE)) - continue; - if(only_fill_cave && !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE)) - continue; - vmanip.m_data[i] = n1; - } - } - } - -#if 1 - { - // 340ms @cs=8 - TimeTaker timer1("flow mud"); - - /* - Flow mud away from steep edges - */ - - // Iterate a few times - for(s16 k=0; k<3; k++) - { - - for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++) - for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++) - { - // Invert coordinates every 2nd iteration - if(k%2 == 0) - { - x = mudflow_maxpos - (x-mudflow_minpos); - z = mudflow_maxpos - (z-mudflow_minpos); - } - - // Node position in 2d - v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z); - - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - s16 y=node_max.Y; - - while(y >= node_min.Y) - { - - for(;; y--) - { - MapNode *n = NULL; - // Find mud - for(; y>=node_min.Y; y--) - { - n = &vmanip.m_data[i]; - //if(content_walkable(n->d)) - // break; - if(n->getContent() == c_dirt || - n->getContent() == c_dirt_with_grass || - n->getContent() == c_gravel) - break; - - vmanip.m_area.add_y(em, i, -1); - } - - // Stop if out of area - //if(vmanip.m_area.contains(i) == false) - if(y < node_min.Y) - break; - - /*// If not mud, do nothing to it - MapNode *n = &vmanip.m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) - continue;*/ - - if(n->getContent() == c_dirt || - n->getContent() == c_dirt_with_grass) - { - // Make it exactly mud - n->setContent(c_dirt); - - /* - Don't flow it if the stuff under it is not mud - */ - { - u32 i2 = i; - vmanip.m_area.add_y(em, i2, -1); - // Cancel if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - MapNode *n2 = &vmanip.m_data[i2]; - if(n2->getContent() != c_dirt && - n2->getContent() != c_dirt_with_grass) - continue; - } - } - - /*s16 recurse_count = 0; - mudflow_recurse:*/ - - v3s16 dirs4[4] = { - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - - // Theck that upper is air or doesn't exist. - // Cancel dropping if upper keeps it in place - u32 i3 = i; - vmanip.m_area.add_y(em, i3, 1); - if(vmanip.m_area.contains(i3) == true - && ndef->get(vmanip.m_data[i3]).walkable) - { - continue; - } - - // Drop mud on side - - for(u32 di=0; di<4; di++) - { - v3s16 dirp = dirs4[di]; - u32 i2 = i; - // Move to side - vmanip.m_area.add_p(em, i2, dirp); - // Fail if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - // Check that side is air - MapNode *n2 = &vmanip.m_data[i2]; - if(ndef->get(*n2).walkable) - continue; - // Check that under side is air - vmanip.m_area.add_y(em, i2, -1); - if(vmanip.m_area.contains(i2) == false) - continue; - n2 = &vmanip.m_data[i2]; - if(ndef->get(*n2).walkable) - continue; - /*// Check that under that is air (need a drop of 2) - vmanip.m_area.add_y(em, i2, -1); - if(vmanip.m_area.contains(i2) == false) - continue; - n2 = &vmanip.m_data[i2]; - if(content_walkable(n2->d)) - continue;*/ - // Loop further down until not air - bool dropped_to_unknown = false; - do{ - vmanip.m_area.add_y(em, i2, -1); - n2 = &vmanip.m_data[i2]; - // if out of known area - if(vmanip.m_area.contains(i2) == false - || n2->getContent() == CONTENT_IGNORE){ - dropped_to_unknown = true; - break; - } - }while(ndef->get(*n2).walkable == false); - // Loop one up so that we're in air - vmanip.m_area.add_y(em, i2, 1); - n2 = &vmanip.m_data[i2]; - - bool old_is_water = (n->getContent() == c_water_source); - // Move mud to new place - if(!dropped_to_unknown) { - *n2 = *n; - // Set old place to be air (or water) - if(old_is_water) - *n = MapNode(c_water_source); - else - *n = MapNode(CONTENT_AIR); - } - - // Done - break; - } - } - } - } - - } - - }//timer1 -#endif - - } // Aging loop - /*********************** - END OF AGING LOOP - ************************/ - - /* - Add top and bottom side of water to transforming_liquid queue - */ - - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool water_found = false; - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - if(y == full_node_max.Y){ - water_found = - (vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source); - } - else if(water_found == false) - { - if(vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source) - { - v3s16 p = v3s16(p2d.X, y, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = true; - } - } - else - { - // This can be done because water_found can only - // turn to true and end up here after going through - // a single block. - if(vmanip.m_data[i+1].getContent() != c_water_source || - vmanip.m_data[i+1].getContent() != c_lava_source) - { - v3s16 p = v3s16(p2d.X, y+1, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = false; - } - } - - vmanip.m_area.add_y(em, i, -1); - } - } - } - - /* - Grow grass - */ - - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position in 2d - v2s16 p2d = v2s16(x,z); - - /* - Find the lowest surface to which enough light ends up - to make grass grow. - - Basically just wait until not air and not leaves. - */ - s16 surface_y = 0; - { - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - s16 y; - // Go to ground level - for(y=node_max.Y; y>=full_node_min.Y; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).param_type != CPT_LIGHT - || ndef->get(n).liquid_type != LIQUID_NONE) - break; - vmanip.m_area.add_y(em, i, -1); - } - if(y >= full_node_min.Y) - surface_y = y; - else - surface_y = full_node_min.Y; - } - - u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() == c_dirt){ - // Well yeah, this can't be overground... - if(surface_y < WATER_LEVEL - 20) - continue; - n->setContent(c_dirt_with_grass); - } - } - - /* - Generate some trees - */ - assert(central_area_size.X == central_area_size.Z); - { - PseudoRandom ps (blockseed); - // Divide area into parts - s16 div = 8; - s16 sidelen = central_area_size.X / div; - double area = sidelen * sidelen; - for(s16 x0=0; x0<div; x0++) - for(s16 z0=0; z0<div; z0++) - { - // Center position of part of division - v2s16 p2d_center( - node_min.X + sidelen/2 + sidelen*x0, - node_min.Z + sidelen/2 + sidelen*z0 - ); - // Minimum edge of part of division - v2s16 p2d_min( - node_min.X + sidelen*x0, - node_min.Z + sidelen*z0 - ); - // Maximum edge of part of division - v2s16 p2d_max( - node_min.X + sidelen + sidelen*x0 - 1, - node_min.Z + sidelen + sidelen*z0 - 1 - ); - // Amount of trees - u32 tree_count = area * tree_amount_2d(data->seed, p2d_center); - // Put trees in random places on part of division - for(u32 i=0; i<tree_count; i++) - { - s16 x = ps.range(p2d_min.X, p2d_max.X); - s16 z = ps.range(p2d_min.Y, p2d_max.Y); - s16 y = find_ground_level(vmanip, v2s16(x,z), ndef); - // Don't make a tree under water level - if(y < WATER_LEVEL) - continue; - // Don't make a tree so high that it doesn't fit - if(y > node_max.Y - 6) - continue; - v3s16 p(x,y,z); - /* - Trees grow only on mud and grass - */ - { - u32 i = vmanip.m_area.index(v3s16(p)); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() != c_dirt - && n->getContent() != c_dirt_with_grass) - continue; - } - p.Y++; - // Make a tree - treegen::make_tree(vmanip, p, false, ndef, ps.next()); - } - } - } - -#if 0 - /* - Make base ground level - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) - { - // Only modify places that have no content - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE) - { - // First priority: make air and water. - // This avoids caves inside water. - if(all_is_ground_except_caves == false - && val_is_ground(noisebuf_ground.get(x,y,z), - v3s16(x,y,z), data->seed) == false) - { - if(y <= WATER_LEVEL) - vmanip.m_data[i] = n_water_source; - else - vmanip.m_data[i] = n_air; - } - else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD) - vmanip.m_data[i] = n_air; - else - vmanip.m_data[i] = n_stone; - } - - vmanip->m_area.add_y(em, i, 1); - } - } - } - - /* - Add mud and sand and others underground (in place of stone) - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=node_min.Y; y--) - { - if(vmanip.m_data[i].getContent() == c_stone) - { - if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3) - { - if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = n_dirt; - else - vmanip.m_data[i] = n_sand; - } - else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7) - { - if(noisebuf_ground_wetness.get(x,y,z) < -0.6) - vmanip.m_data[i] = n_gravel; - } - else if(noisebuf_ground_crumbleness.get(x,y,z) < - -3.0 + MYMIN(0.1 * sqrt((float)MYMAX(0, -y)), 1.5)) - { - vmanip.m_data[i] = n_lava_source; - for(s16 x1=-1; x1<=1; x1++) - for(s16 y1=-1; y1<=1; y1++) - for(s16 z1=-1; z1<=1; z1++) - data->transforming_liquid.push_back( - v3s16(p2d.X+x1, y+y1, p2d.Y+z1)); - } - } - - vmanip->m_area.add_y(em, i, -1); - } - } - } - - /* - Add dungeons - */ - - //if(node_min.Y < approx_groundlevel) - //if(myrand() % 3 == 0) - //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel) - //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel) - //float dungeon_rarity = g_settings.getFloat("dungeon_rarity"); - float dungeon_rarity = 0.02; - if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0) - < dungeon_rarity - && node_min.Y < approx_groundlevel) - { - // Dungeon generator doesn't modify places which have this set - vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE - | VMANIP_FLAG_DUNGEON_PRESERVE); - - // Set all air and water to be untouchable to make dungeons open - // to caves and open air - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - if(vmanip.m_data[i].getContent() == CONTENT_AIR) - vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - else if(vmanip.m_data[i].getContent() == c_water_source) - vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - vmanip->m_area.add_y(em, i, -1); - } - } - } - - PseudoRandom random(blockseed+2); - - // Add it - make_dungeon1(vmanip, random, ndef); - - // Convert some cobble to mossy cobble - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - // (noisebuf not used because it doesn't contain the - // full area) - double wetness = noise3d_param( - get_ground_wetness_params(data->seed), x,y,z); - double d = noise3d_perlin((float)x/2.5, - (float)y/2.5,(float)z/2.5, - blockseed, 2, 1.4); - if(vmanip.m_data[i].getContent() == c_cobble) - { - if(d < wetness/3.0) - { - vmanip.m_data[i].setContent(c_mossycobble); - } - } - /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE) - { - if(wetness > 1.2) - vmanip.m_data[i].setContent(c_dirt); - }*/ - vmanip->m_area.add_y(em, i, -1); - } - } - } - } - - /* - Add NC - */ - { - PseudoRandom ncrandom(blockseed+9324342); - if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3) - { - make_nc(vmanip, ncrandom, ndef); - } - } - - /* - Add top and bottom side of water to transforming_liquid queue - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool water_found = false; - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=node_min.Y; y--) - { - if(water_found == false) - { - if(vmanip.m_data[i].getContent() == c_water_source) - { - v3s16 p = v3s16(p2d.X, y, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = true; - } - } - else - { - // This can be done because water_found can only - // turn to true and end up here after going through - // a single block. - if(vmanip.m_data[i+1].getContent() != c_water_source) - { - v3s16 p = v3s16(p2d.X, y+1, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = false; - } - } - - vmanip->m_area.add_y(em, i, -1); - } - } - } - - /* - If close to ground level - */ - - //if(abs(approx_ground_depth) < 30) - if(minimum_ground_depth < 5 && maximum_ground_depth > -5) - { - /* - Add grass and mud - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool possibly_have_sand = get_have_beach(data->seed, p2d); - bool have_sand = false; - u32 current_depth = 0; - bool air_detected = false; - bool water_detected = false; - bool have_clay = false; - - // Use fast index incrementing - s16 start_y = node_max.Y+2; - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y)); - for(s16 y=start_y; y>=node_min.Y-3; y--) - { - if(vmanip.m_data[i].getContent() == c_water_source) - water_detected = true; - if(vmanip.m_data[i].getContent() == CONTENT_AIR) - air_detected = true; - - if((vmanip.m_data[i].getContent() == c_stone - || vmanip.m_data[i].getContent() == c_dirt_with_grass - || vmanip.m_data[i].getContent() == c_dirt - || vmanip.m_data[i].getContent() == c_sand - || vmanip.m_data[i].getContent() == c_gravel - ) && (air_detected || water_detected)) - { - if(current_depth == 0 && y <= WATER_LEVEL+2 - && possibly_have_sand) - have_sand = true; - - if(current_depth < 4) - { - if(have_sand) - { - vmanip.m_data[i] = MapNode(c_sand); - } - #if 1 - else if(current_depth==0 && !water_detected - && y >= WATER_LEVEL && air_detected) - vmanip.m_data[i] = MapNode(c_dirt_with_grass); - #endif - else - vmanip.m_data[i] = MapNode(c_dirt); - } - else - { - if(vmanip.m_data[i].getContent() == c_dirt - || vmanip.m_data[i].getContent() == c_dirt_with_grass) - vmanip.m_data[i] = MapNode(c_stone); - } - - current_depth++; - - if(current_depth >= 8) - break; - } - else if(current_depth != 0) - break; - - vmanip->m_area.add_y(em, i, -1); - } - } - } - - /* - Calculate some stuff - */ - - float surface_humidity = surface_humidity_2d(data->seed, p2d_center); - bool is_jungle = surface_humidity > 0.75; - // Amount of trees - u32 tree_count = gen_area_nodes * tree_amount_2d(data->seed, p2d_center); - if(is_jungle) - tree_count *= 5; - - /* - Add trees - */ - PseudoRandom treerandom(blockseed); - // Put trees in random places on part of division - for(u32 i=0; i<tree_count; i++) - { - s16 x = treerandom.range(node_min.X, node_max.X); - s16 z = treerandom.range(node_min.Z, node_max.Z); - //s16 y = find_ground_level(vmanip, v2s16(x,z)); - s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4); - // Don't make a tree under water level - if(y < WATER_LEVEL) - continue; - // Make sure tree fits (only trees whose starting point is - // at this block are added) - if(y < node_min.Y || y > node_max.Y) - continue; - /* - Find exact ground level - */ - v3s16 p(x,y+6,z); - bool found = false; - for(; p.Y >= y-6; p.Y--) - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; - if(n->getContent() != CONTENT_AIR && n->getContent() != c_water_source && n->getContent() != CONTENT_IGNORE) - { - found = true; - break; - } - } - // If not found, handle next one - if(found == false) - continue; - - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; - - if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass && n->getContent() != c_sand) - continue; - - // Papyrus grows only on mud and in water - if(n->getContent() == c_dirt && y <= WATER_LEVEL) - { - p.Y++; - make_papyrus(vmanip, p, ndef); - } - // Trees grow only on mud and grass, on land - else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2) - { - p.Y++; - //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5) - if(is_jungle == false) - { - bool is_apple_tree; - if(myrand_range(0,4) != 0) - is_apple_tree = false; - else - is_apple_tree = noise2d_perlin( - 0.5+(float)p.X/100, 0.5+(float)p.Z/100, - data->seed+342902, 3, 0.45) > 0.2; - make_tree(vmanip, p, is_apple_tree, ndef); - } - else - make_jungletree(vmanip, p, ndef); - } - // Cactii grow only on sand, on land - else if(n->getContent() == c_sand && y > WATER_LEVEL + 2) - { - p.Y++; - make_cactus(vmanip, p, ndef); - } - } - } - - /* - Add jungle grass - */ - if(is_jungle) - { - PseudoRandom grassrandom(blockseed); - for(u32 i=0; i<surface_humidity*5*tree_count; i++) - { - s16 x = grassrandom.range(node_min.X, node_max.X); - s16 z = grassrandom.range(node_min.Z, node_max.Z); - s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 4); - if(y < WATER_LEVEL) - continue; - if(y < node_min.Y || y > node_max.Y) - continue; - /* - Find exact ground level - */ - v3s16 p(x,y+6,z); - bool found = false; - for(; p.Y >= y-6; p.Y--) - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; - if(data->nodedef->get(*n).is_ground_content) - { - found = true; - break; - } - } - // If not found, handle next one - if(found == false) - continue; - p.Y++; - if(vmanip.m_area.contains(p) == false) - continue; - if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR) - continue; - /*p.Y--; - if(vmanip.m_area.contains(p)) - vmanip.m_data[vmanip.m_area.index(p)] = c_dirt; - p.Y++;*/ - if(vmanip.m_area.contains(p)) - vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass; - } - } - -#if 0 - /* - Add some kind of random stones - */ - - u32 random_stone_count = gen_area_nodes * - randomstone_amount_2d(data->seed, p2d_center); - // Put in random places on part of division - for(u32 i=0; i<random_stone_count; i++) - { - s16 x = myrand_range(node_min.X, node_max.X); - s16 z = myrand_range(node_min.Z, node_max.Z); - s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1); - // Don't add under water level - /*if(y < WATER_LEVEL) - continue;*/ - // Don't add if doesn't belong to this block - if(y < node_min.Y || y > node_max.Y) - continue; - v3s16 p(x,y,z); - // Filter placement - /*{ - u32 i = vmanip->m_area.index(v3s16(p)); - MapNode *n = &vmanip->m_data[i]; - if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass) - continue; - }*/ - // Will be placed one higher - p.Y++; - // Add it - make_randomstone(vmanip, p); - } -#endif - -#if 0 - /* - Add larger stones - */ - - u32 large_stone_count = gen_area_nodes * - largestone_amount_2d(data->seed, p2d_center); - //u32 large_stone_count = 1; - // Put in random places on part of division - for(u32 i=0; i<large_stone_count; i++) - { - s16 x = myrand_range(node_min.X, node_max.X); - s16 z = myrand_range(node_min.Z, node_max.Z); - s16 y = find_ground_level_from_noise(data->seed, v2s16(x,z), 1); - // Don't add under water level - /*if(y < WATER_LEVEL) - continue;*/ - // Don't add if doesn't belong to this block - if(y < node_min.Y || y > node_max.Y) - continue; - v3s16 p(x,y,z); - // Filter placement - /*{ - u32 i = vmanip->m_area.index(v3s16(p)); - MapNode *n = &vmanip->m_data[i]; - if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass) - continue; - }*/ - // Will be placed one lower - p.Y--; - // Add it - make_largestone(vmanip, p); - } -#endif - } - - /* - Add minerals - */ - - { - PseudoRandom mineralrandom(blockseed); - - /* - Add meseblocks - */ - for(s16 i=0; i<approx_ground_depth/4; i++) - { - if(mineralrandom.next()%50 == 0) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == c_stone) - if(mineralrandom.next()%8 == 0) - vmanip.m_data[vi] = MapNode(c_mese); - } - - } - } - /* - Add others - */ - { - u16 a = mineralrandom.range(0,15); - a = a*a*a; - u16 amount = 20 * a/1000; - for(s16 i=0; i<amount; i++) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - - u8 base_content = c_stone; - MapNode new_content(CONTENT_IGNORE); - u32 sparseness = 6; - - if(noisebuf_ground_crumbleness.get(x,y+5,z) < -0.1) - { - new_content = MapNode(c_stone_with_coal); - } - else - { - if(noisebuf_ground_wetness.get(x,y+5,z) > 0.0) - new_content = MapNode(c_stone_with_iron); - /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = MapNode(c_dirt); - else - vmanip.m_data[i] = MapNode(c_sand);*/ - } - /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1) - { - }*/ - - if(new_content.getContent() != CONTENT_IGNORE) - { - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == base_content) - { - if(mineralrandom.next()%sparseness == 0) - vmanip.m_data[vi] = new_content; - } - } - } - } - } - /* - Add coal - */ - //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++) - //for(s16 i=0; i<50; i++) - u16 coal_amount = 30; - u16 coal_rareness = 60 / coal_amount; - if(coal_rareness == 0) - coal_rareness = 1; - if(mineralrandom.next()%coal_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = coal_amount * a*a*a / 1000; - for(s16 i=0; i<amount; i++) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == c_stone) - if(mineralrandom.next()%8 == 0) - vmanip.m_data[vi] = MapNode(c_stone_with_coal); - } - } - } - /* - Add iron - */ - u16 iron_amount = 8; - u16 iron_rareness = 60 / iron_amount; - if(iron_rareness == 0) - iron_rareness = 1; - if(mineralrandom.next()%iron_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = iron_amount * a*a*a / 1000; - for(s16 i=0; i<amount; i++) - { - s16 x = mineralrandom.range(node_min.X+1, node_max.X-1); - s16 y = mineralrandom.range(node_min.Y+1, node_max.Y-1); - s16 z = mineralrandom.range(node_min.Z+1, node_max.Z-1); - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == c_stone) - if(mineralrandom.next()%8 == 0) - vmanip.m_data[vi] = MapNode(c_stone_with_iron); - } - } - } - } -#endif - - /* - Calculate lighting - */ - { - ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", - SPT_AVG); - //VoxelArea a(node_min, node_max); - VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE, - node_max+v3s16(1,0,1)*MAP_BLOCKSIZE); - /*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2, - node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/ - enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; - for(int i=0; i<2; i++) - { - enum LightBank bank = banks[i]; - - core::map<v3s16, bool> light_sources; - core::map<v3s16, u8> unlight_from; - - voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef, - light_sources, unlight_from); - - bool inexistent_top_provides_sunlight = !block_is_underground; - voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( - vmanip, a, inexistent_top_provides_sunlight, - light_sources, ndef); - // TODO: Do stuff according to bottom_sunlight_valid - - vmanip.unspreadLight(bank, unlight_from, light_sources, ndef); - - vmanip.spreadLight(bank, light_sources, ndef); - } - } -} - -#endif ///BIG COMMENT - diff --git a/src/mapgen.h b/src/mapgen.h index 911e87537..a900985da 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MG_TREES 0x01 #define MG_CAVES 0x02 #define MG_DUNGEONS 0x04 -#define MGV6_FORESTS 0x08 +#define MGV6_JUNGLES 0x08 #define MGV6_BIOME_BLEND 0x10 #define MG_FLAT 0x20 @@ -45,7 +45,8 @@ class MapBlock; class ManualMapVoxelManipulator; class VoxelManipulator; class INodeDefManager; -class BlockMakeData; +struct BlockMakeData; +class VoxelArea; struct MapgenParams { std::string mg_name; @@ -72,6 +73,14 @@ public: int water_level; bool generating; int id; + ManualMapVoxelManipulator *vm; + INodeDefManager *ndef; + + void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax); + void setLighting(v3s16 nmin, v3s16 nmax, u8 light); + void lightSpread(VoxelArea &a, v3s16 p, u8 light); + void calcLighting(v3s16 nmin, v3s16 nmax); + void calcLightingOld(v3s16 nmin, v3s16 nmax); virtual void makeChunk(BlockMakeData *data) {}; virtual int getGroundLevelAtPoint(v2s16 p) = 0; @@ -88,5 +97,48 @@ struct MapgenFactory { virtual MapgenParams *createMapgenParams() = 0; }; +enum OreType { + ORE_SCATTER, + ORE_SHEET, + ORE_CLAYLIKE +}; + +class Ore { +public: + std::string ore_name; + std::string wherein_name; + + content_t ore; + content_t wherein; // the node to be replaced + s16 clust_scarcity; // + s16 clust_num_ores; // how many ore nodes are in a chunk + s16 clust_size; // how large (in nodes) a chunk of ore is + s16 height_min; + s16 height_max; + float nthresh; // threshhold for noise at which an ore is placed + NoiseParams *np; // noise for distribution of clusters (NULL for uniform scattering) + Noise *noise; + + Ore() { + ore = CONTENT_IGNORE; + wherein = CONTENT_IGNORE; + np = NULL; + noise = NULL; + } + + void resolveNodeNames(INodeDefManager *ndef); + virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) = 0; +}; + +class OreScatter : public Ore { + void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); +}; + +class OreSheet : public Ore { + void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); +}; + +Ore *createOre(OreType type); + #endif diff --git a/src/mapgen_indev.cpp b/src/mapgen_indev.cpp new file mode 100644 index 000000000..5d455827a --- /dev/null +++ b/src/mapgen_indev.cpp @@ -0,0 +1,371 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "mapgen_indev.h" +#include "constants.h" +#include "map.h" +#include "main.h" +#include "log.h" + +/////////////////// Mapgen Indev perlin noise (default values - not used, from config or defaultsettings) + +NoiseIndevParams nparams_indev_def; + +/* +NoiseIndevParams nparams_indev_def_terrain_base; +// (-AVERAGE_MUD_AMOUNT, 20.0, v3f(250.0, 250.0, 250.0), 82341, 5, 0.6, 1); +NoiseIndevParams nparams_indev_def_terrain_higher; +// (20.0, 16.0, v3f(500.0, 500.0, 500.0), 85039, 5, 0.6, 1); +NoiseIndevParams nparams_indev_def_steepness; +// (0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7, 1); +NoiseIndevParams nparams_indev_def_mud; +// (AVERAGE_MUD_AMOUNT, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55, 1); +NoiseIndevParams nparams_indev_def_float_islands; +// (1, 10.0, v3f(500.0, 500.0, 500.0), 32451, 5, 0.6, 1); +NoiseIndevParams nparams_indev_def_biome; +*/ + +/////////////////////////////////////////////////////////////////////////////// + +void NoiseIndev::init(NoiseIndevParams *np, int seed, int sx, int sy, int sz) { + Noise::init((NoiseParams*)np, seed, sx, sy, sz); + this->npindev = np; +} + +NoiseIndev::NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy) : Noise(np, seed, sx, sy) { + init(np, seed, sx, sy, 1); +} + +NoiseIndev::NoiseIndev(NoiseIndevParams *np, int seed, int sx, int sy, int sz) : Noise(np, seed, sx, sy, sz) { + init(np, seed, sx, sy, sz); +} + +float farscale(float scale, float z) { + return ( 1 + ( 1 - (MAP_GENERATION_LIMIT * 1 - (fabs(z)) ) / (MAP_GENERATION_LIMIT * 1) ) * (scale - 1) ); +} + +float farscale(float scale, float x, float z) { + return ( 1 + ( 1 - (MAP_GENERATION_LIMIT * 2 - (fabs(x) + fabs(z)) ) / (MAP_GENERATION_LIMIT * 2) ) * (scale - 1) ); +} + +float farscale(float scale, float x, float y, float z) { + return ( 1 + ( 1 - (MAP_GENERATION_LIMIT * 3 - (fabs(x) + fabs(y) + fabs(z)) ) / (MAP_GENERATION_LIMIT * 3) ) * (scale - 1) ); +} + +void NoiseIndev::transformNoiseMapFarScale(float xx, float yy, float zz) { + int i = 0; + for (int z = 0; z != sz; z++) { + for (int y = 0; y != sy; y++) { + for (int x = 0; x != sx; x++) { + result[i] = result[i] * npindev->scale * farscale(npindev->farscale, xx, yy, zz) + npindev->offset; + i++; + } + } + } +} + +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); +} + +MapgenIndev::~MapgenIndev() { + delete noiseindev_terrain_base; + delete noiseindev_terrain_higher; + delete noiseindev_steepness; + //delete noise_height_select; + //delete noise_trees; + delete noiseindev_mud; + //delete noise_beach; + delete noiseindev_float_islands1; + delete noiseindev_float_islands2; + delete noiseindev_float_islands3; + delete noiseindev_biome; +} + +void MapgenIndev::calculateNoise() { + int x = node_min.X; + int y = node_min.Y; + int z = node_min.Z; + // Need to adjust for the original implementation's +.5 offset... + if (!(flags & MG_FLAT)) { + noiseindev_terrain_base->perlinMap2D( + x + 0.5 * noiseindev_terrain_base->npindev->spread.X * farscale(noiseindev_terrain_base->npindev->farspread, x, z), + z + 0.5 * noiseindev_terrain_base->npindev->spread.Z * farscale(noiseindev_terrain_base->npindev->farspread, x, z)); + noiseindev_terrain_base->transformNoiseMapFarScale(x, y, z); + + noiseindev_terrain_higher->perlinMap2D( + x + 0.5 * noiseindev_terrain_higher->npindev->spread.X * farscale(noiseindev_terrain_higher->npindev->farspread, x, z), + z + 0.5 * noiseindev_terrain_higher->npindev->spread.Z * farscale(noiseindev_terrain_higher->npindev->farspread, x, z)); + noiseindev_terrain_higher->transformNoiseMapFarScale(x, y, z); + + noiseindev_steepness->perlinMap2D( + x + 0.5 * noiseindev_steepness->npindev->spread.X * farscale(noiseindev_steepness->npindev->farspread, x, z), + z + 0.5 * noiseindev_steepness->npindev->spread.Z * farscale(noiseindev_steepness->npindev->farspread, x, z)); + noiseindev_steepness->transformNoiseMapFarScale(x, y, z); + + noise_height_select->perlinMap2D( + x + 0.5 * noise_height_select->np->spread.X, + z + 0.5 * noise_height_select->np->spread.Z); + + noiseindev_float_islands1->perlinMap3D( + x + 0.33 * noiseindev_float_islands1->npindev->spread.X * farscale(noiseindev_float_islands1->npindev->farspread, x, y, z), + y + 0.33 * noiseindev_float_islands1->npindev->spread.Y * farscale(noiseindev_float_islands1->npindev->farspread, x, y, z), + z + 0.33 * noiseindev_float_islands1->npindev->spread.Z * farscale(noiseindev_float_islands1->npindev->farspread, x, y, z) + ); + noiseindev_float_islands1->transformNoiseMapFarScale(x, y, z); + + noiseindev_float_islands2->perlinMap3D( + x + 0.33 * noiseindev_float_islands2->npindev->spread.X * farscale(noiseindev_float_islands2->npindev->farspread, x, y, z), + y + 0.33 * noiseindev_float_islands2->npindev->spread.Y * farscale(noiseindev_float_islands2->npindev->farspread, x, y, z), + z + 0.33 * noiseindev_float_islands2->npindev->spread.Z * farscale(noiseindev_float_islands2->npindev->farspread, x, y, z) + ); + noiseindev_float_islands2->transformNoiseMapFarScale(x, y, z); + + noiseindev_float_islands3->perlinMap2D( + x + 0.5 * noiseindev_float_islands3->npindev->spread.X * farscale(noiseindev_float_islands3->npindev->farspread, x, z), + z + 0.5 * noiseindev_float_islands3->npindev->spread.Z * farscale(noiseindev_float_islands3->npindev->farspread, x, z)); + noiseindev_float_islands3->transformNoiseMapFarScale(x, y, z); + + } + + if (!(flags & MG_FLAT)) { + noiseindev_mud->perlinMap2D( + x + 0.5 * noiseindev_mud->npindev->spread.X * farscale(noiseindev_mud->npindev->farspread, x, y, z), + z + 0.5 * noiseindev_mud->npindev->spread.Z * farscale(noiseindev_mud->npindev->farspread, x, y, z)); + noiseindev_mud->transformNoiseMapFarScale(x, y, z); + } + noise_beach->perlinMap2D( + x + 0.2 * noise_beach->np->spread.X, + z + 0.7 * noise_beach->np->spread.Z); + + noise_biome->perlinMap2D( + x + 0.6 * noiseindev_biome->npindev->spread.X * farscale(noiseindev_biome->npindev->farspread, x, z), + z + 0.2 * noiseindev_biome->npindev->spread.Z * farscale(noiseindev_biome->npindev->farspread, x, z)); +} + +bool MapgenIndevParams::readParams(Settings *settings) { + 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; +} + +void MapgenIndevParams::writeParams(Settings *settings) { + 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); +} + + +float MapgenIndev::baseTerrainLevelFromNoise(v2s16 p) { + if (flags & MG_FLAT) + return water_level; + + float terrain_base = NoisePerlin2DPosOffset(noiseindev_terrain_base->npindev, + p.X, 0.5, p.Y, 0.5, seed); + float terrain_higher = NoisePerlin2DPosOffset(noiseindev_terrain_higher->npindev, + p.X, 0.5, p.Y, 0.5, seed); + float steepness = NoisePerlin2DPosOffset(noiseindev_steepness->npindev, + p.X, 0.5, p.Y, 0.5, seed); + float height_select = NoisePerlin2DNoTxfmPosOffset(noise_height_select->np, + p.X, 0.5, p.Y, 0.5, seed); + + return baseTerrainLevel(terrain_base, terrain_higher, + steepness, height_select); +} + +float MapgenIndev::baseTerrainLevelFromMap(int index) { + if (flags & MG_FLAT) + return water_level; + + float terrain_base = noiseindev_terrain_base->result[index]; + float terrain_higher = noiseindev_terrain_higher->result[index]; + float steepness = noiseindev_steepness->result[index]; + float height_select = noise_height_select->result[index]; + + return baseTerrainLevel(terrain_base, terrain_higher, + steepness, height_select); +} + +float MapgenIndev::getMudAmount(int index) { + if (flags & MG_FLAT) + return AVERAGE_MUD_AMOUNT; + + /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin( + 0.5+(float)p.X/200, 0.5+(float)p.Y/200, + seed+91013, 3, 0.55));*/ + + return noiseindev_mud->result[index]; +} + +void MapgenIndev::defineCave(Cave & cave, PseudoRandom ps, v3s16 node_min, bool large_cave) { + cave.min_tunnel_diameter = 2; + cave.max_tunnel_diameter = ps.range(2,6); + cave.dswitchint = ps.range(1,14); + cave.flooded = large_cave && ps.range(0,4); + if(large_cave){ + cave.part_max_length_rs = ps.range(2,4); + if (node_min.Y < -100 && !ps.range(0, farscale(0.2, node_min.X,node_min.Y,node_min.Z)*30)) { //huge + cave.flooded = !ps.range(0, 3); + cave.tunnel_routepoints = ps.range(5, 20); + cave.min_tunnel_diameter = 30; + cave.max_tunnel_diameter = ps.range(40, ps.range(80,120)); + } else { + cave.tunnel_routepoints = ps.range(5, ps.range(15,30)); + cave.min_tunnel_diameter = 5; + cave.max_tunnel_diameter = ps.range(7, ps.range(8,24)); + } + } else { + cave.part_max_length_rs = ps.range(2,9); + cave.tunnel_routepoints = ps.range(10, ps.range(15,30)); + } + cave.large_cave_is_flat = (ps.range(0,1) == 0); +} + +/* +// version with one perlin3d. use with good params like +settings->setDefault("mgindev_np_float_islands1", "-9.5, 10, (20, 50, 50 ), 45123, 5, 0.6, 1.5, 5"); +void MapgenIndev::generateFloatIslands(int min_y) { + if (node_min.Y < min_y) return; + v3s16 p0(node_min.X, node_min.Y, node_min.Z); + MapNode n1(c_stone), n2(c_desert_stone); + int xl = node_max.X - node_min.X; + int yl = node_max.Y - node_min.Y; + int zl = node_max.Z - node_min.Z; + u32 index = 0; + for (int x1 = 0; x1 <= xl; x1++) + { + //int x = x1 + node_min.Y; + for (int z1 = 0; z1 <= zl; z1++) + { + //int z = z1 + node_min.Z; + for (int y1 = 0; y1 <= yl; y1++, index++) + { + //int y = y1 + node_min.Y; + float noise = noiseindev_float_islands1->result[index]; + //dstream << " y1="<<y1<< " x1="<<x1<<" z1="<<z1<< " noise="<<noise << std::endl; + if (noise > 0 ) { + v3s16 p = p0 + v3s16(x1, y1, z1); + u32 i = vm->m_area.index(p); + if (!vm->m_area.contains(i)) + continue; + // Cancel if not air + if (vm->m_data[i].getContent() != CONTENT_AIR) + continue; + vm->m_data[i] = noise > 1 ? n1 : n2; + } + } + } + } +} +*/ + +void MapgenIndev::generateFloatIslands(int min_y) { + if (node_min.Y < min_y) return; + PseudoRandom pr(blockseed + 985); + // originally from http://forum.minetest.net/viewtopic.php?id=4776 + float RAR = 0.8 * farscale(0.4, node_min.Y); // 0.4; // Island rarity in chunk layer. -0.4 = thick layer with holes, 0 = 50%, 0.4 = desert rarity, 0.7 = very rare. + float AMPY = 24; // 24; // Amplitude of island centre y variation. + float TGRAD = 24; // 24; // Noise gradient to create top surface. Tallness of island top. + float BGRAD = 24; // 24; // Noise gradient to create bottom surface. Tallness of island bottom. + + v3s16 p0(node_min.X, node_min.Y, node_min.Z); + MapNode n1(c_stone); + + float xl = node_max.X - node_min.X; + float yl = node_max.Y - node_min.Y; + float zl = node_max.Z - node_min.Z; + float midy = node_min.Y + yl * 0.5; + u32 index = 0, index2d = 0; + for (int x1 = 0; x1 <= xl; ++x1) + { + for (int z1 = 0; z1 <= zl; ++z1, ++index2d) + { + float noise3 = noiseindev_float_islands3->result[index2d]; + float pmidy = midy + noise3 / 1.5 * AMPY; + for (int y1 = 0; y1 <= yl; ++y1, ++index) + { + int y = y1 + node_min.Y; + float noise1 = noiseindev_float_islands1->result[index]; + float offset = y > pmidy ? (y - pmidy) / TGRAD : (pmidy - y) / BGRAD; + float noise1off = noise1 - offset - RAR; + if (noise1off > 0 && noise1off < 0.7) { + float noise2 = noiseindev_float_islands2->result[index]; + if (noise2 - noise1off > -0.7){ + v3s16 p = p0 + v3s16(x1, y1, z1); + u32 i = vm->m_area.index(p); + if (!vm->m_area.contains(i)) + continue; + // Cancel if not air + if (vm->m_data[i].getContent() != CONTENT_AIR) + continue; + vm->m_data[i] = n1; + } + } + } + } + } +} + +void MapgenIndev::generateSomething() { + int float_islands = g_settings->getS16("mgindev_float_islands"); + if(float_islands) generateFloatIslands(float_islands); +} diff --git a/src/mapgen_indev.h b/src/mapgen_indev.h new file mode 100644 index 000000000..fdac1ba20 --- /dev/null +++ b/src/mapgen_indev.h @@ -0,0 +1,152 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MAPGENINDEV_HEADER +#define MAPGENINDEV_HEADER + +#include "mapgen.h" +#include "mapgen_v6.h" + +float farscale(float scale, float z); +float farscale(float scale, float x, float z); +float farscale(float scale, float x, float y, float z); + +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) + { + offset = offset_; + scale = scale_; + spread = spread_; + seed = seed_; + octaves = octaves_; + persist = persist_; + + farscale = farscale_; + farspread = farspread_; + } + +}; + +#define getNoiseIndevParams(x) getStruct<NoiseIndevParams>((x), "f,f,v3,s32,s32,f,f,f") +#define setNoiseIndevParams(x, y) setStruct((x), "f,f,v3,s32,s32,f,f,f", (y)) + +class NoiseIndev : public Noise { + public: + NoiseIndevParams *npindev; + + //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); + void transformNoiseMapFarScale(float xx = 0, float yy = 0, float zz = 0); +}; + +extern NoiseIndevParams nparams_indev_def; +/* +extern NoiseIndevParams nparams_indev_def_terrain_base; +extern NoiseIndevParams nparams_indev_def_terrain_higher; +extern NoiseIndevParams nparams_indev_def_steepness; +//extern NoiseIndevParams nparams_indev_def_height_select; +//extern NoiseIndevParams nparams_indev_def_trees; +extern NoiseIndevParams nparams_indev_def_mud; +//extern NoiseIndevParams nparams_indev_def_beach; +extern NoiseIndevParams nparams_indev_def_biome; +//extern NoiseIndevParams nparams_indev_def_cave; +extern NoiseIndevParams nparams_indev_def_float_islands; +*/ + +struct MapgenIndevParams : public MapgenV6Params { + NoiseIndevParams *npindev_terrain_base; + NoiseIndevParams *npindev_terrain_higher; + NoiseIndevParams *npindev_steepness; + //NoiseParams *np_height_select; + //NoiseParams *np_trees; + NoiseIndevParams *npindev_mud; + //NoiseParams *np_beach; + NoiseIndevParams *npindev_biome; + //NoiseParams *np_cave; + 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; + //np_height_select = &nparams_v6_def_height_select; + //np_trees = &nparams_v6_def_trees; + npindev_mud = &nparams_indev_def; //&nparams_indev_def_mud; + //np_beach = &nparams_v6_def_beach; + 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; + + } + + bool readParams(Settings *settings); + void writeParams(Settings *settings); +}; + +class MapgenIndev : public MapgenV6 { + public: + NoiseIndev *noiseindev_terrain_base; + NoiseIndev *noiseindev_terrain_higher; + NoiseIndev *noiseindev_steepness; + //NoiseIndev *noise_height_select; + //NoiseIndev *noise_trees; + NoiseIndev *noiseindev_mud; + //NoiseIndev *noise_beach; + NoiseIndev *noiseindev_biome; + //NoiseIndevParams *np_cave; + NoiseIndev *noiseindev_float_islands1; + NoiseIndev *noiseindev_float_islands2; + NoiseIndev *noiseindev_float_islands3; + + MapgenIndev(int mapgenid, MapgenIndevParams *params, EmergeManager *emerge); + ~MapgenIndev(); + void calculateNoise(); + + float baseTerrainLevelFromNoise(v2s16 p); + float baseTerrainLevelFromMap(int index); + float getMudAmount(int index); + void defineCave(Cave & cave, PseudoRandom ps, v3s16 node_min, bool large_cave); + void generateSomething(); + + void generateFloatIslands(int min_y); +}; + +struct MapgenFactoryIndev : public MapgenFactoryV6 { + Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) { + return new MapgenIndev(mgid, (MapgenIndevParams *)params, emerge); + }; + + MapgenParams *createMapgenParams() { + return new MapgenIndevParams(); + }; +}; + +#endif diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp new file mode 100644 index 000000000..22b756abb --- /dev/null +++ b/src/mapgen_singlenode.cpp @@ -0,0 +1,102 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "mapgen_singlenode.h" +#include "voxel.h" +#include "mapblock.h" +#include "mapnode.h" +#include "map.h" +#include "nodedef.h" +#include "voxelalgorithms.h" +#include "profiler.h" +#include "settings.h" // For g_settings +#include "main.h" // For g_profiler +#include "emerge.h" + +//////////////////////// Mapgen Singlenode parameter read/write + +bool MapgenSinglenodeParams::readParams(Settings *settings) { + return true; +} + + +void MapgenSinglenodeParams::writeParams(Settings *settings) { +} + +/////////////////////////////////////////////////////////////////////////////// + +MapgenSinglenode::MapgenSinglenode(int mapgenid, MapgenSinglenodeParams *params) { +} + + +MapgenSinglenode::~MapgenSinglenode() { +} + +//////////////////////// Map generator + +void MapgenSinglenode::makeChunk(BlockMakeData *data) { + assert(data->vmanip); + assert(data->nodedef); + assert(data->blockpos_requested.X >= data->blockpos_min.X && + data->blockpos_requested.Y >= data->blockpos_min.Y && + data->blockpos_requested.Z >= data->blockpos_min.Z); + assert(data->blockpos_requested.X <= data->blockpos_max.X && + data->blockpos_requested.Y <= data->blockpos_max.Y && + data->blockpos_requested.Z <= data->blockpos_max.Z); + + this->generating = true; + this->vm = data->vmanip; + this->ndef = data->nodedef; + + v3s16 blockpos_min = data->blockpos_min; + v3s16 blockpos_max = data->blockpos_max; + + // Area of central chunk + v3s16 node_min = blockpos_min*MAP_BLOCKSIZE; + v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1); + + content_t c_node = ndef->getId("mapgen_singlenode"); + if (c_node == CONTENT_IGNORE) + c_node = CONTENT_AIR; + + MapNode n_node(c_node); + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 y = node_min.Y; y <= node_max.Y; y++) { + u32 i = vm->m_area.index(node_min.X, y, z); + for (s16 x = node_min.X; x <= node_max.X; x++) { + if (vm->m_data[i].getContent() == CONTENT_IGNORE) + vm->m_data[i] = n_node; + i++; + } + } + + // Add top and bottom side of water to transforming_liquid queue + updateLiquid(&data->transforming_liquid, node_min, node_max); + + // Calculate lighting + calcLighting(node_min, node_max); + + this->generating = false; +} + +int MapgenSinglenode::getGroundLevelAtPoint(v2s16 p) { + return 0; +} + diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h new file mode 100644 index 000000000..b86c9a77f --- /dev/null +++ b/src/mapgen_singlenode.h @@ -0,0 +1,53 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MAPGEN_SINGLENODE_HEADER +#define MAPGEN_SINGLENODE_HEADER + +#include "mapgen.h" + +struct MapgenSinglenodeParams : public MapgenParams { + + MapgenSinglenodeParams() { + } + + bool readParams(Settings *settings); + void writeParams(Settings *settings); +}; + +class MapgenSinglenode : public Mapgen { +public: + MapgenSinglenode(int mapgenid, MapgenSinglenodeParams *params); + ~MapgenSinglenode(); + + void makeChunk(BlockMakeData *data); + int getGroundLevelAtPoint(v2s16 p); +}; + +struct MapgenFactorySinglenode : public MapgenFactory { + Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) { + return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params); + }; + + MapgenParams *createMapgenParams() { + return new MapgenSinglenodeParams(); + }; +}; + +#endif diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index d5405876e..0b419617d 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" // For g_settings #include "main.h" // For g_profiler #include "emerge.h" +#include "dungeongen.h" +#include "treegen.h" #include "mapgen_v6.h" /////////////////// Mapgen V6 perlin noise default values @@ -43,8 +45,6 @@ NoiseParams nparams_v6_def_steepness = {0.85, 0.5, v3f(125.0, 125.0, 125.0), -932, 5, 0.7}; NoiseParams nparams_v6_def_height_select = {0.5, 1.0, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69}; -NoiseParams nparams_v6_def_trees = - {0.0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66}; NoiseParams nparams_v6_def_mud = {AVERAGE_MUD_AMOUNT, 2.0, v3f(200.0, 200.0, 200.0), 91013, 3, 0.55}; NoiseParams nparams_v6_def_beach = @@ -53,14 +53,21 @@ NoiseParams nparams_v6_def_biome = {0.0, 1.0, v3f(250.0, 250.0, 250.0), 9130, 3, 0.50}; NoiseParams nparams_v6_def_cave = {6.0, 6.0, v3f(250.0, 250.0, 250.0), 34329, 3, 0.50}; +NoiseParams nparams_v6_def_humidity = + {0.5, 0.5, v3f(500.0, 500.0, 500.0), 72384, 4, 0.66}; +NoiseParams nparams_v6_def_trees = + {0.0, 1.0, v3f(125.0, 125.0, 125.0), 2, 4, 0.66}; +NoiseParams nparams_v6_def_apple_trees = + {0.0, 1.0, v3f(100.0, 100.0, 100.0), 342902, 3, 0.45}; /////////////////////////////////////////////////////////////////////////////// -MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params) { +MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) { this->generating = false; this->id = mapgenid; + this->emerge = emerge; this->seed = (int)params->seed; this->water_level = params->water_level; @@ -72,25 +79,18 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params) { this->ystride = csize.X; //////fix this - np_cave = params->np_cave; + np_cave = params->np_cave; + np_humidity = params->np_humidity; + np_trees = params->np_trees; + np_apple_trees = params->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_trees = new Noise(params->np_trees, 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); - - map_terrain_base = noise_terrain_base->result; - map_terrain_higher = noise_terrain_higher->result; - map_steepness = noise_steepness->result; - map_height_select = noise_height_select->result; - map_trees = noise_trees->result; - map_mud = noise_mud->result; - map_beach = noise_beach->result; - map_biome = noise_biome->result; } @@ -99,169 +99,53 @@ MapgenV6::~MapgenV6() { delete noise_terrain_higher; delete noise_steepness; delete noise_height_select; - delete noise_trees; delete noise_mud; delete noise_beach; delete noise_biome; } -/* - Some helper functions for the map generator -*/ +//////////////////////// Some helper functions for the map generator -#if 1 // Returns Y one under area minimum if not found -s16 MapgenV6::find_ground_level(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); +s16 MapgenV6::find_ground_level(v2s16 p2d) { + v3s16 em = vm->m_area.getExtent(); + s16 y_nodes_max = vm->m_area.MaxEdge.Y; + s16 y_nodes_min = vm->m_area.MinEdge.Y; + u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); s16 y; - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; + + for (y = y_nodes_max; y >= y_nodes_min; y--) { + MapNode &n = vm->m_data[i]; if(ndef->get(n).walkable) break; - vmanip.m_area.add_y(em, i, -1); + vm->m_area.add_y(em, i, -1); } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; + return (y >= y_nodes_min) ? y : y_nodes_min - 1; } // Returns Y one under area minimum if not found -s16 MapgenV6::find_stone_level(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); +s16 MapgenV6::find_stone_level(v2s16 p2d) { + v3s16 em = vm->m_area.getExtent(); + s16 y_nodes_max = vm->m_area.MaxEdge.Y; + s16 y_nodes_min = vm->m_area.MinEdge.Y; + u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); s16 y; - content_t c_stone = ndef->getId("mapgen_stone"); - content_t c_desert_stone = ndef->getId("mapgen_desert_stone"); - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; + + for (y = y_nodes_max; y >= y_nodes_min; y--) { + MapNode &n = vm->m_data[i]; content_t c = n.getContent(); - if(c != CONTENT_IGNORE && ( - c == c_stone || c == c_desert_stone)) + if (c != CONTENT_IGNORE && ( + c == c_stone || c == c_desert_stone)) break; - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; -} -#endif - -void MapgenV6::make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef) -{ - MapNode treenode(ndef->getId("mapgen_tree")); - MapNode leavesnode(ndef->getId("mapgen_leaves")); - MapNode applenode(ndef->getId("mapgen_apple")); - - s16 trunk_h = myrand_range(4, 5); - v3s16 p1 = p0; - for(s16 ii=0; ii<trunk_h; ii++) - { - if(vmanip.m_area.contains(p1)) - vmanip.m_data[vmanip.m_area.index(p1)] = treenode; - p1.Y++; - } - - // p1 is now the last piece of the trunk - p1.Y -= 1; - - VoxelArea leaves_a(v3s16(-2,-1,-2), v3s16(2,2,2)); - //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]); - Buffer<u8> leaves_d(leaves_a.getVolume()); - for(s32 i=0; i<leaves_a.getVolume(); i++) - leaves_d[i] = 0; - - // Force leaves at near the end of the trunk - { - s16 d = 1; - for(s16 z=-d; z<=d; z++) - for(s16 y=-d; y<=d; y++) - for(s16 x=-d; x<=d; x++) - { - leaves_d[leaves_a.index(v3s16(x,y,z))] = 1; - } - } - - // Add leaves randomly - for(u32 iii=0; iii<7; iii++) - { - s16 d = 1; - - v3s16 p( - myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), - myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), - myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) - ); - - for(s16 z=0; z<=d; z++) - for(s16 y=0; y<=d; y++) - for(s16 x=0; x<=d; x++) - { - leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1; - } - } - - // Blit leaves to vmanip - for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++) - for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++) - for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++) - { - v3s16 p(x,y,z); - p += p1; - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() != CONTENT_AIR - && vmanip.m_data[vi].getContent() != CONTENT_IGNORE) - continue; - u32 i = leaves_a.index(x,y,z); - if(leaves_d[i] == 1) { - bool is_apple = myrand_range(0,99) < 10; - if(is_apple_tree && is_apple) { - vmanip.m_data[vi] = applenode; - } else { - vmanip.m_data[vi] = leavesnode; - } - } + vm->m_area.add_y(em, i, -1); } + return (y >= y_nodes_min) ? y : y_nodes_min - 1; } -/* - Noise functions. Make sure seed is mangled differently in each one. -*/ - - -// Amount of trees per area in nodes -double MapgenV6::tree_amount_2d(u64 seed, v2s16 p) -{ - /*double noise = noise2d_perlin( - 0.5+(float)p.X/125, 0.5+(float)p.Y/125, - seed+2, 4, 0.66);*/ - double noise = map_trees[(p.Y - node_min.Z) * ystride + (p.X - node_min.X)]; - double zeroval = -0.39; - if(noise < zeroval) - return 0; - else - return 0.04 * (noise-zeroval) / (1.0-zeroval); -} - // Required by mapgen.h bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos) { @@ -277,105 +161,148 @@ bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos) } -double MapgenV6::base_rock_level_2d(u64 seed, v2s16 p) -{ - if (flags & MG_FLAT) - return water_level; - - int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); - - // The base ground level - /*double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT - + 20. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+82341, 5, 0.6);*/ - double base = water_level + map_terrain_base[index]; +//////////////////////// Base terrain height functions - // Higher ground level - /*double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin( - 0.5+(float)p.X/500., 0.5+(float)p.Y/500., - seed+85039, 5, 0.6);*/ - double higher = water_level + map_terrain_higher[index]; +float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher, + float steepness, float height_select) { + float base = water_level + terrain_base; + float higher = water_level + terrain_higher; - // Limit higher to at least base + // Limit higher ground level to at least base if(higher < base) higher = base; // Steepness factor of cliffs - /*double b = 0.85 + 0.5 * noise2d_perlin( - 0.5+(float)p.X/125., 0.5+(float)p.Y/125., - seed-932, 5, 0.7);*/ - double b = map_steepness[index]; + float b = steepness; b = rangelim(b, 0.0, 1000.0); - b = pow(b, 7); - b *= 5; + b = 5 * b * b * b * b * b * b * b; b = rangelim(b, 0.5, 1000.0); // Values 1.5...100 give quite horrible looking slopes - if(b > 1.5 && b < 100.0){ - if(b < 10.0) - b = 1.5; - else - b = 100.0; - } + if (b > 1.5 && b < 100.0) + b = (b < 10.0) ? 1.5 : 100.0; - // Offset to more low - double a_off = -0.20; + float a_off = -0.20; // Offset to more low + float a = 0.5 + b * (a_off + height_select); + a = rangelim(a, 0.0, 1.0); // Limit + + return base * (1.0 - a) + higher * a; +} - // High/low selector - /*double a = (double)0.5 + b * (a_off + noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+4213, 5, 0.69));*/ - double a = 0.5 + b * (a_off + map_height_select[index]); - // Limit - a = rangelim(a, 0.0, 1.0); +float MapgenV6::baseTerrainLevelFromNoise(v2s16 p) { + if (flags & MG_FLAT) + return water_level; + + float terrain_base = NoisePerlin2DPosOffset(noise_terrain_base->np, + p.X, 0.5, p.Y, 0.5, seed); + float terrain_higher = NoisePerlin2DPosOffset(noise_terrain_higher->np, + p.X, 0.5, p.Y, 0.5, seed); + float steepness = NoisePerlin2DPosOffset(noise_steepness->np, + p.X, 0.5, p.Y, 0.5, seed); + float height_select = NoisePerlin2DNoTxfmPosOffset(noise_height_select->np, + p.X, 0.5, p.Y, 0.5, seed); + + return baseTerrainLevel(terrain_base, terrain_higher, + steepness, height_select); +} - double h = base*(1.0-a) + higher*a; - return h; +float MapgenV6::baseTerrainLevelFromMap(v2s16 p) { + int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); + return baseTerrainLevelFromMap(index); } -double MapgenV6::baseRockLevelFromNoise(v2s16 p) { + +float MapgenV6::baseTerrainLevelFromMap(int index) { if (flags & MG_FLAT) return water_level; - double base = water_level + - NoisePerlin2DPosOffset(noise_terrain_base->np, p.X, 0.5, p.Y, 0.5, seed); - double higher = water_level + - NoisePerlin2DPosOffset(noise_terrain_higher->np, p.X, 0.5, p.Y, 0.5, seed); + float terrain_base = noise_terrain_base->result[index]; + float terrain_higher = noise_terrain_higher->result[index]; + float steepness = noise_steepness->result[index]; + float height_select = noise_height_select->result[index]; + + return baseTerrainLevel(terrain_base, terrain_higher, + steepness, height_select); +} - if (higher < base) - higher = base; - double b = NoisePerlin2DPosOffset(noise_steepness->np, p.X, 0.5, p.Y, 0.5, seed); - b = rangelim(b, 0.0, 1000.0); - b = b*b*b*b*b*b*b; - b *= 5; - b = rangelim(b, 0.5, 1000.0); +s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { + return baseTerrainLevelFromNoise(p2d) + AVERAGE_MUD_AMOUNT; +} - if(b > 1.5 && b < 100.0){ - if(b < 10.0) - b = 1.5; - else - b = 100.0; - } - - double a_off = -0.20; - double a = 0.5 + b * (a_off + NoisePerlin2DNoTxfmPosOffset( - noise_height_select->np, p.X, 0.5, p.Y, 0.5, seed)); - a = rangelim(a, 0.0, 1.0); - return base * (1.0 - a) + higher * a; +int MapgenV6::getGroundLevelAtPoint(v2s16 p) { + return baseTerrainLevelFromNoise(p) + AVERAGE_MUD_AMOUNT; } -s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) +//////////////////////// Noise functions + +float MapgenV6::getMudAmount(v2s16 p) { + int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); + return getMudAmount(index); +} + + +bool MapgenV6::getHaveBeach(v2s16 p) { + int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); + return getHaveBeach(index); +} + + +BiomeType MapgenV6::getBiome(v2s16 p) { + int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); + return getBiome(index, p); +} + + +float MapgenV6::getHumidity(v2s16 p) { - return baseRockLevelFromNoise(p2d) + AVERAGE_MUD_AMOUNT; + /*double noise = noise2d_perlin( + 0.5+(float)p.X/500, 0.5+(float)p.Y/500, + seed+72384, 4, 0.66); + noise = (noise + 1.0)/2.0;*/ + + float noise = NoisePerlin2D(np_humidity, p.X, p.Y, seed); + + if (noise < 0.0) + noise = 0.0; + if (noise > 1.0) + noise = 1.0; + return noise; } -double MapgenV6::get_mud_add_amount(u64 seed, v2s16 p) + +float MapgenV6::getTreeAmount(v2s16 p) +{ + /*double noise = noise2d_perlin( + 0.5+(float)p.X/125, 0.5+(float)p.Y/125, + seed+2, 4, 0.66);*/ + + float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed); + float zeroval = -0.39; + if (noise < zeroval) + return 0; + else + return 0.04 * (noise-zeroval) / (1.0-zeroval); +} + + +bool MapgenV6::getHaveAppleTree(v2s16 p) +{ + /*is_apple_tree = noise2d_perlin( + 0.5+(float)p.X/100, 0.5+(float)p.Z/100, + data->seed+342902, 3, 0.45) > 0.2;*/ + + float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed); + + return noise > 0.2; +} + + +float MapgenV6::getMudAmount(int index) { if (flags & MG_FLAT) return AVERAGE_MUD_AMOUNT; @@ -383,39 +310,42 @@ double MapgenV6::get_mud_add_amount(u64 seed, v2s16 p) /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin( 0.5+(float)p.X/200, 0.5+(float)p.Y/200, seed+91013, 3, 0.55));*/ - int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X); - return map_mud[index]; + + return noise_mud->result[index]; } -bool MapgenV6::get_have_beach(u64 seed, v2s16 p2d) + +bool MapgenV6::getHaveBeach(int index) { // Determine whether to have sand here /*double sandnoise = noise2d_perlin( 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, seed+59420, 3, 0.50);*/ - int index = (p2d.Y - node_min.Z) * ystride + (p2d.X - node_min.X); - double sandnoise = map_beach[index]; - + + float sandnoise = noise_beach->result[index]; return (sandnoise > freq_beach); } -BiomeType MapgenV6::get_biome(u64 seed, v2s16 p2d) + +BiomeType MapgenV6::getBiome(int index, v2s16 p) { // Just do something very simple as for now /*double d = noise2d_perlin( 0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250, seed+9130, 3, 0.50);*/ - int index = (p2d.Y - node_min.Z) * ystride + (p2d.X - node_min.X); - double d = map_biome[index]; - if(d > freq_desert) + + float d = noise_biome->result[index]; + if (d > freq_desert) return BT_DESERT; - if (flags & MGV6_BIOME_BLEND) { - if(d > freq_desert - 0.10 && - (noise2d(p2d.X, p2d.Y, seed) + 1.0) > (freq_desert - d) * 20.0) - return BT_DESERT; - } + + if ((flags & MGV6_BIOME_BLEND) && + (d > freq_desert - 0.10) && + ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0)) + return BT_DESERT; + return BT_NORMAL; -}; +} + u32 MapgenV6::get_blockseed(u64 seed, v3s16 p) { @@ -424,275 +354,622 @@ u32 MapgenV6::get_blockseed(u64 seed, v3s16 p) } -int MapgenV6::getGroundLevelAtPoint(v2s16 p) { - return baseRockLevelFromNoise(p) + AVERAGE_MUD_AMOUNT; -} - -#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1 - -void MapgenV6::makeChunk(BlockMakeData *data) -{ - this->generating = true; +//////////////////////// Map generator +void MapgenV6::makeChunk(BlockMakeData *data) { assert(data->vmanip); assert(data->nodedef); assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); + data->blockpos_requested.Y >= data->blockpos_min.Y && + data->blockpos_requested.Z >= data->blockpos_min.Z); assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); - - INodeDefManager *ndef = data->nodedef; - - // Hack: use minimum block coordinates for old code that assumes - // a single block + data->blockpos_requested.Y <= data->blockpos_max.Y && + data->blockpos_requested.Z <= data->blockpos_max.Z); + + this->generating = true; + this->vm = data->vmanip; + this->ndef = data->nodedef; + + // Hack: use minimum block coords for old code that assumes a single block v3s16 blockpos = data->blockpos_requested; - - /*dstream<<"makeBlock(): ("<<blockpos.X<<","<<blockpos.Y<<"," - <<blockpos.Z<<")"<<std::endl;*/ - 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); - ManualMapVoxelManipulator &vmanip = *(data->vmanip); // Area of central chunk node_min = blockpos_min*MAP_BLOCKSIZE; node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1); + // Full allocated area - v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE; - v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1); + full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE; + full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1); - v3s16 central_area_size = node_max - node_min + v3s16(1,1,1); + central_area_size = node_max - node_min + v3s16(1,1,1); + assert(central_area_size.X == central_area_size.Z); + + int volume_blocks = (blockpos_max.X - blockpos_min.X + 1) + * (blockpos_max.Y - blockpos_min.Y + 1) + * (blockpos_max.Z - blockpos_max.Z + 1); + + volume_nodes = volume_blocks * + MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE; + + // Create a block-specific seed + blockseed = get_blockseed(data->seed, full_node_min); + + // Make some noise + calculateNoise(); + + c_stone = ndef->getId("mapgen_stone"); + c_dirt = ndef->getId("mapgen_dirt"); + c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass"); + c_sand = ndef->getId("mapgen_sand"); + c_water_source = ndef->getId("mapgen_water_source"); + c_lava_source = ndef->getId("mapgen_lava_source"); + c_gravel = ndef->getId("mapgen_gravel"); + c_cobble = ndef->getId("mapgen_cobble"); + c_desert_sand = ndef->getId("mapgen_desert_sand"); + c_desert_stone = ndef->getId("mapgen_desert_stone"); + if (c_desert_sand == CONTENT_IGNORE) + c_desert_sand = c_sand; + if (c_desert_stone == CONTENT_IGNORE) + c_desert_stone = c_stone; + + // Maximum height of the stone surface and obstacles. + // This is used to guide the cave generation + s16 stone_surface_max_y; + + // Generate general ground level to full area + stone_surface_max_y = generateGround(); + + generateSomething(); const s16 max_spread_amount = MAP_BLOCKSIZE; + // Limit dirt flow area by 1 because mud is flown into neighbors. + s16 mudflow_minpos = -max_spread_amount + 1; + s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2; - int volume_blocks = (blockpos_max.X - blockpos_min.X + 1) - * (blockpos_max.Y - blockpos_min.Y + 1) - * (blockpos_max.Z - blockpos_max.Z + 1); - - int volume_nodes = volume_blocks * - MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - - // Generated surface area - //double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume; - - // Horribly wrong heuristic, but better than nothing - bool block_is_underground = (water_level > node_max.Y); - - /* - Create a block-specific seed - */ - u32 blockseed = get_blockseed(data->seed, full_node_min); - - /* - Make some noise - */ - { - int x = node_min.X; - int z = node_min.Z; - - // Need to adjust for the original implementation's +.5 offset... - if (!(flags & MG_FLAT)) { - noise_terrain_base->perlinMap2D( - x + 0.5 * noise_terrain_base->np->spread.X, - z + 0.5 * noise_terrain_base->np->spread.Z); - noise_terrain_base->transformNoiseMap(); - - noise_terrain_higher->perlinMap2D( - x + 0.5 * noise_terrain_higher->np->spread.X, - z + 0.5 * noise_terrain_higher->np->spread.Z); - noise_terrain_higher->transformNoiseMap(); - - noise_steepness->perlinMap2D( - x + 0.5 * noise_steepness->np->spread.X, - z + 0.5 * noise_steepness->np->spread.Z); - noise_steepness->transformNoiseMap(); - - noise_height_select->perlinMap2D( - x + 0.5 * noise_height_select->np->spread.X, - z + 0.5 * noise_height_select->np->spread.Z); - } - - noise_trees->perlinMap2D( - x + 0.5 * noise_trees->np->spread.X, - z + 0.5 * noise_trees->np->spread.Z); - - if (!(flags & MG_FLAT)) { - noise_mud->perlinMap2D( - x + 0.5 * noise_mud->np->spread.X, - z + 0.5 * noise_mud->np->spread.Z); - noise_mud->transformNoiseMap(); - } - noise_beach->perlinMap2D( - x + 0.2 * noise_beach->np->spread.X, - z + 0.7 * noise_beach->np->spread.Z); + // Loop this part, it will make stuff look older and newer nicely + const u32 age_loops = 2; + for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop + // Make caves (this code is relatively horrible) + if (flags & MG_CAVES) + generateCaves(stone_surface_max_y); + + // Add mud to the central chunk + addMud(); - noise_biome->perlinMap2D( - x + 0.6 * noise_biome->np->spread.X, - z + 0.2 * noise_biome->np->spread.Z); + // Add blobs of dirt and gravel underground + addDirtGravelBlobs(); + + // Flow mud away from steep edges + flowMud(mudflow_minpos, mudflow_maxpos); + + } + + // Add dungeons + if (flags & MG_DUNGEONS) { + DungeonGen dgen(ndef, data->seed, water_level); + dgen.generate(vm, blockseed, full_node_min, full_node_max); } + + // Add top and bottom side of water to transforming_liquid queue + updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); + // Grow grass + growGrass(); - /* - Cache some ground type values for speed - */ + // Generate some trees, and add grass, if a jungle + if (flags & MG_TREES) + placeTreesAndJungleGrass(); -// Creates variables c_name=id and n_name=node -#define CONTENT_VARIABLE(ndef, name)\ - content_t c_##name = ndef->getId("mapgen_" #name);\ - MapNode n_##name(c_##name); -// Default to something else if was CONTENT_IGNORE -#define CONTENT_VARIABLE_FALLBACK(name, dname)\ - if(c_##name == CONTENT_IGNORE){\ - c_##name = c_##dname;\ - n_##name = n_##dname;\ + // Generate the registered ores + for (unsigned int i = 0; i != emerge->ores.size(); i++) { + Ore *ore = emerge->ores[i]; + ore->generate(this, blockseed + i, node_min, node_max); } - CONTENT_VARIABLE(ndef, stone); - CONTENT_VARIABLE(ndef, air); - CONTENT_VARIABLE(ndef, water_source); - CONTENT_VARIABLE(ndef, dirt); - CONTENT_VARIABLE(ndef, sand); - CONTENT_VARIABLE(ndef, gravel); - CONTENT_VARIABLE(ndef, clay); - CONTENT_VARIABLE(ndef, lava_source); - CONTENT_VARIABLE(ndef, cobble); - CONTENT_VARIABLE(ndef, mossycobble); - CONTENT_VARIABLE(ndef, dirt_with_grass); - CONTENT_VARIABLE(ndef, junglegrass); - CONTENT_VARIABLE(ndef, stone_with_coal); - CONTENT_VARIABLE(ndef, stone_with_iron); - CONTENT_VARIABLE(ndef, mese); - CONTENT_VARIABLE(ndef, desert_sand); - CONTENT_VARIABLE_FALLBACK(desert_sand, sand); - CONTENT_VARIABLE(ndef, desert_stone); - CONTENT_VARIABLE_FALLBACK(desert_stone, stone); + // Calculate lighting + calcLighting(node_min, node_max); + + this->generating = false; +} - // Maximum height of the stone surface and obstacles. - // This is used to guide the cave generation - s16 stone_surface_max_y = 0; - /* - Generate general ground level to full area - */ - { -#if 1 - TimeTaker timer1("Generating ground level"); +void MapgenV6::calculateNoise() { + int x = node_min.X; + int z = node_min.Z; - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d = v2s16(x,z); + // Need to adjust for the original implementation's +.5 offset... + if (!(flags & MG_FLAT)) { + noise_terrain_base->perlinMap2D( + x + 0.5 * noise_terrain_base->np->spread.X, + z + 0.5 * noise_terrain_base->np->spread.Z); + noise_terrain_base->transformNoiseMap(); - /* - Skip of already generated - */ - /*{ - v3s16 p(p2d.X, node_min.Y, p2d.Y); - if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR) - continue; - }*/ + noise_terrain_higher->perlinMap2D( + x + 0.5 * noise_terrain_higher->np->spread.X, + z + 0.5 * noise_terrain_higher->np->spread.Z); + noise_terrain_higher->transformNoiseMap(); - // Ground height at this point - float surface_y_f = 0.0; + noise_steepness->perlinMap2D( + x + 0.5 * noise_steepness->np->spread.X, + z + 0.5 * noise_steepness->np->spread.Z); + noise_steepness->transformNoiseMap(); - // Use perlin noise for ground height - surface_y_f = base_rock_level_2d(data->seed, p2d); + noise_height_select->perlinMap2D( + x + 0.5 * noise_height_select->np->spread.X, + z + 0.5 * noise_height_select->np->spread.Z); - /*// Experimental stuff - { - float a = highlands_level_2d(data->seed, p2d); - if(a > surface_y_f) - surface_y_f = a; - }*/ + noise_mud->perlinMap2D( + x + 0.5 * noise_mud->np->spread.X, + z + 0.5 * noise_mud->np->spread.Z); + noise_mud->transformNoiseMap(); + } - // Convert to integer - s16 surface_y = (s16)surface_y_f; + noise_beach->perlinMap2D( + x + 0.2 * noise_beach->np->spread.X, + z + 0.7 * noise_beach->np->spread.Z); + noise_biome->perlinMap2D( + x + 0.6 * noise_biome->np->spread.X, + z + 0.2 * noise_biome->np->spread.Z); +} + + +int MapgenV6::generateGround() { + //TimeTaker timer1("Generating ground level"); + MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); + MapNode n_stone(c_stone), n_desert_stone(c_desert_stone); + int stone_surface_max_y = -MAP_GENERATION_LIMIT; + u32 index = 0; + + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + // Surface height + s16 surface_y = (s16)baseTerrainLevelFromMap(index); + // Log it - if(surface_y > stone_surface_max_y) + if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; - BiomeType bt = get_biome(data->seed, p2d); - /* - Fill ground with stone - */ - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) + BiomeType bt = getBiome(index, v2s16(x, z)); + + // Fill ground with stone + v3s16 em = vm->m_area.getExtent(); + u32 i = vm->m_area.index(x, node_min.Y, z); + for (s16 y = node_min.Y; y <= node_max.Y; y++) { + if (vm->m_data[i].getContent() == CONTENT_IGNORE) { + if (y <= surface_y) { + vm->m_data[i] = (y > water_level && bt == BT_DESERT) ? + n_desert_stone : n_stone; + } else if (y <= water_level) { + vm->m_data[i] = n_water_source; + } else { + vm->m_data[i] = n_air; + } + } + vm->m_area.add_y(em, i, 1); + } + } + + return stone_surface_max_y; +} + + +void MapgenV6::addMud() { + // 15ms @cs=8 + //TimeTaker timer1("add mud"); + MapNode n_dirt(c_dirt), n_gravel(c_gravel); + MapNode n_sand(c_sand), n_desert_sand(c_desert_sand); + MapNode addnode; + + u32 index = 0; + for (s16 z = node_min.Z; z <= node_max.Z; z++) + for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + // Randomize mud amount + s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5; + + // Find ground level + s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this! + + // Handle area not found + if (surface_y == vm->m_area.MinEdge.Y - 1) + continue; + + BiomeType bt = getBiome(index, v2s16(x, z)); + addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt; + + if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) { + addnode = n_sand; + } else if (mud_add_amount <= 0) { + mud_add_amount = 1 - mud_add_amount; + addnode = n_gravel; + } else if (bt == BT_NORMAL && getHaveBeach(index) && + surface_y + mud_add_amount <= water_level + 2) { + addnode = n_sand; + } + + if (bt == BT_DESERT && surface_y > 20) + mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5); + + // If topmost node is grass, change it to mud. It might be if it was + // flown to there from a neighboring chunk and then converted. + u32 i = vm->m_area.index(x, surface_y, z); + if (vm->m_data[i].getContent() == c_dirt_with_grass) + vm->m_data[i] = n_dirt; + + // Add mud on ground + s16 mudcount = 0; + v3s16 em = vm->m_area.getExtent(); + s16 y_start = surface_y + 1; + i = vm->m_area.index(x, y_start, z); + for (s16 y = y_start; y <= node_max.Y; y++) { + if (mudcount >= mud_add_amount) + break; + + vm->m_data[i] = addnode; + mudcount++; + + vm->m_area.add_y(em, i, 1); + } + } +} + + +void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos) { + // 340ms @cs=8 + TimeTaker timer1("flow mud"); + + // Iterate a few times + for(s16 k = 0; k < 3; k++) { + for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++) + for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) { + // Invert coordinates every 2nd iteration + if (k % 2 == 0) { + x = mudflow_maxpos - (x - mudflow_minpos); + z = mudflow_maxpos - (z - mudflow_minpos); + } + + // Node position in 2d + v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z); + + v3s16 em = vm->m_area.getExtent(); + u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y); + s16 y = node_max.Y; + + while(y >= node_min.Y) + { + + for(;; y--) { - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){ - if(y <= surface_y){ - if(y > water_level && bt == BT_DESERT) - vmanip.m_data[i] = n_desert_stone; + MapNode *n = NULL; + // Find mud + for(; y >= node_min.Y; y--) { + n = &vm->m_data[i]; + if (n->getContent() == c_dirt || + n->getContent() == c_dirt_with_grass || + n->getContent() == c_gravel) + break; + + vm->m_area.add_y(em, i, -1); + } + + // Stop if out of area + //if(vmanip.m_area.contains(i) == false) + if (y < node_min.Y) + break; + + if (n->getContent() == c_dirt || + n->getContent() == c_dirt_with_grass) + { + // Make it exactly mud + n->setContent(c_dirt); + + // Don't flow it if the stuff under it is not mud + { + u32 i2 = i; + vm->m_area.add_y(em, i2, -1); + // Cancel if out of area + if(vm->m_area.contains(i2) == false) + continue; + MapNode *n2 = &vm->m_data[i2]; + if (n2->getContent() != c_dirt && + n2->getContent() != c_dirt_with_grass) + continue; + } + } + + v3s16 dirs4[4] = { + v3s16(0,0,1), // back + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(-1,0,0), // left + }; + + // Check that upper is air or doesn't exist. + // Cancel dropping if upper keeps it in place + u32 i3 = i; + vm->m_area.add_y(em, i3, 1); + if (vm->m_area.contains(i3) == true && + ndef->get(vm->m_data[i3]).walkable) + continue; + + // Drop mud on side + for(u32 di=0; di<4; di++) { + v3s16 dirp = dirs4[di]; + u32 i2 = i; + // Move to side + vm->m_area.add_p(em, i2, dirp); + // Fail if out of area + if (vm->m_area.contains(i2) == false) + continue; + // Check that side is air + MapNode *n2 = &vm->m_data[i2]; + if (ndef->get(*n2).walkable) + continue; + // Check that under side is air + vm->m_area.add_y(em, i2, -1); + if (vm->m_area.contains(i2) == false) + continue; + n2 = &vm->m_data[i2]; + if (ndef->get(*n2).walkable) + continue; + // Loop further down until not air + bool dropped_to_unknown = false; + do { + vm->m_area.add_y(em, i2, -1); + n2 = &vm->m_data[i2]; + // if out of known area + if(vm->m_area.contains(i2) == false || + n2->getContent() == CONTENT_IGNORE) { + dropped_to_unknown = true; + break; + } + } while (ndef->get(*n2).walkable == false); + // Loop one up so that we're in air + vm->m_area.add_y(em, i2, 1); + n2 = &vm->m_data[i2]; + + bool old_is_water = (n->getContent() == c_water_source); + // Move mud to new place + if (!dropped_to_unknown) { + *n2 = *n; + // Set old place to be air (or water) + if(old_is_water) + *n = MapNode(c_water_source); else - vmanip.m_data[i] = n_stone; - } else if(y <= water_level){ - vmanip.m_data[i] = MapNode(c_water_source); - } else { - vmanip.m_data[i] = MapNode(c_air); + *n = MapNode(CONTENT_AIR); } + + // Done + break; } - vmanip.m_area.add_y(em, i, 1); + } } } } -#endif +} - }//timer1 - // Limit dirt flow area by 1 because mud is flown into neighbors. - assert(central_area_size.X == central_area_size.Z); - s16 mudflow_minpos = 0-max_spread_amount+1; - s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2; +void MapgenV6::addDirtGravelBlobs() { + if (getBiome(v2s16(node_min.X, node_min.Z)) != BT_NORMAL) + return; + + PseudoRandom pr(blockseed + 983); + for (int i = 0; i < volume_nodes/10/10/10; i++) { + bool only_fill_cave = (myrand_range(0,1) != 0); + v3s16 size( + pr.range(1, 8), + pr.range(1, 8), + pr.range(1, 8) + ); + v3s16 p0( + pr.range(node_min.X, node_max.X) - size.X / 2, + pr.range(node_min.Y, node_max.Y) - size.Y / 2, + pr.range(node_min.Z, node_max.Z) - size.Z / 2 + ); + + MapNode n1((p0.Y > -32 && !pr.range(0, 1)) ? c_dirt : c_gravel); + for (int z1 = 0; z1 < size.Z; z1++) + for (int y1 = 0; y1 < size.Y; y1++) + for (int x1 = 0; x1 < size.X; x1++) { + v3s16 p = p0 + v3s16(x1, y1, z1); + u32 i = vm->m_area.index(p); + if (!vm->m_area.contains(i)) + continue; + // Cancel if not stone and not cave air + if (vm->m_data[i].getContent() != c_stone && + !(vm->m_flags[i] & VMANIP_FLAG_CAVE)) + continue; + if (only_fill_cave && !(vm->m_flags[i] & VMANIP_FLAG_CAVE)) + continue; + vm->m_data[i] = n1; + } + } +} - /* - Loop this part, it will make stuff look older and newer nicely - */ - /*double cave_amount = 6.0 + 6.0 * noise2d_perlin( - 0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250, - data->seed+34329, 3, 0.50);*/ +void MapgenV6::placeTreesAndJungleGrass() { + //TimeTaker t("placeTrees"); + if (node_max.Y < water_level) + return; + + PseudoRandom grassrandom(blockseed + 53); + content_t c_junglegrass = ndef->getId("mapgen_junglegrass"); + // if we don't have junglegrass, don't place cignore... that's bad + if (c_junglegrass == CONTENT_IGNORE) + c_junglegrass = CONTENT_AIR; + MapNode n_junglegrass(c_junglegrass); + v3s16 em = vm->m_area.getExtent(); + + // Divide area into parts + s16 div = 8; + s16 sidelen = central_area_size.X / div; + double area = sidelen * sidelen; + + // N.B. We must add jungle grass first, since tree leaves will + // obstruct the ground, giving us a false ground level + for (s16 z0 = 0; z0 < div; z0++) + for (s16 x0 = 0; x0 < div; x0++) { + // Center position of part of division + v2s16 p2d_center( + node_min.X + sidelen / 2 + sidelen * x0, + node_min.Z + sidelen / 2 + sidelen * z0 + ); + // Minimum edge of part of division + v2s16 p2d_min( + node_min.X + sidelen * x0, + node_min.Z + sidelen * z0 + ); + // Maximum edge of part of division + v2s16 p2d_max( + node_min.X + sidelen + sidelen * x0 - 1, + node_min.Z + sidelen + sidelen * z0 - 1 + ); + + // Amount of trees, jungle area + u32 tree_count = area * getTreeAmount(p2d_center); + + float humidity; + bool is_jungle = false; + if (flags & MGV6_JUNGLES) { + humidity = getHumidity(p2d_center); + if (humidity > 0.75) { + is_jungle = true; + tree_count *= 4; + } + } + + // Add jungle grass + if (is_jungle) { + u32 grass_count = 5 * humidity * tree_count; + for (u32 i = 0; i < grass_count; i++) { + s16 x = grassrandom.range(p2d_min.X, p2d_max.X); + s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y); + + s16 y = find_ground_level(v2s16(x, z)); ////////////////optimize this! + if (y < water_level || y < node_min.Y || y > node_max.Y) + continue; + + u32 vi = vm->m_area.index(x, y, z); + // place on dirt_with_grass, since we know it is exposed to sunlight + if (vm->m_data[vi].getContent() == c_dirt_with_grass) { + vm->m_area.add_y(em, vi, 1); + vm->m_data[vi] = n_junglegrass; + } + } + } + + // Put trees in random places on part of division + for (u32 i = 0; i < tree_count; i++) { + s16 x = myrand_range(p2d_min.X, p2d_max.X); + s16 z = myrand_range(p2d_min.Y, p2d_max.Y); + s16 y = find_ground_level(v2s16(x, z)); ////////////////////optimize this! + // Don't make a tree under water level + // Don't make a tree so high that it doesn't fit + if(y < water_level || y > node_max.Y - 6) + continue; + + v3s16 p(x,y,z); + // Trees grow only on mud and grass + { + u32 i = vm->m_area.index(p); + MapNode *n = &vm->m_data[i]; + if (n->getContent() != c_dirt && + n->getContent() != c_dirt_with_grass) + continue; + } + p.Y++; + + // Make a tree + if (is_jungle) { + treegen::make_jungletree(*vm, p, ndef, myrand()); + } else { + bool is_apple_tree = (myrand_range(0, 3) == 0) && + getHaveAppleTree(v2s16(x, z)); + treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand()); + } + } + } + //printf("placeTreesAndJungleGrass: %dms\n", t.stop()); +} - double cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, data->seed); - const u32 age_loops = 2; - for(u32 i_age=0; i_age<age_loops; i_age++) - { // Aging loop - /****************************** - BEGINNING OF AGING LOOP - ******************************/ - -#if 1 - { +void MapgenV6::growGrass() { + for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++) + for (s16 x = full_node_min.X; x <= full_node_max.X; x++) { + // Find the lowest surface to which enough light ends up to make + // grass grow. Basically just wait until not air and not leaves. + s16 surface_y = 0; + { + v3s16 em = vm->m_area.getExtent(); + u32 i = vm->m_area.index(x, node_max.Y, z); + s16 y; + // Go to ground level + for (y = node_max.Y; y >= full_node_min.Y; y--) { + MapNode &n = vm->m_data[i]; + if (ndef->get(n).param_type != CPT_LIGHT || + ndef->get(n).liquid_type != LIQUID_NONE) + break; + vm->m_area.add_y(em, i, -1); + } + surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y; + } + + u32 i = vm->m_area.index(x, surface_y, z); + MapNode *n = &vm->m_data[i]; + if (n->getContent() == c_dirt && surface_y >= water_level - 20) + n->setContent(c_dirt_with_grass); + } +} + + +void MapgenV6::defineCave(Cave &cave, PseudoRandom ps, + v3s16 node_min, bool large_cave) { + cave.min_tunnel_diameter = 2; + cave.max_tunnel_diameter = ps.range(2,6); + cave.dswitchint = ps.range(1,14); + cave.flooded = true; //large_cave && ps.range(0,4); + if(large_cave){ + cave.part_max_length_rs = ps.range(2,4); + cave.tunnel_routepoints = ps.range(5, ps.range(15,30)); + cave.min_tunnel_diameter = 5; + cave.max_tunnel_diameter = ps.range(7, ps.range(8,24)); + } else { + cave.part_max_length_rs = ps.range(2,9); + cave.tunnel_routepoints = ps.range(10, ps.range(15,30)); + } + cave.large_cave_is_flat = (ps.range(0,1) == 0); +} + + +void MapgenV6::generateCaves(int max_stone_y) { // 24ms @cs=8 //TimeTaker timer1("caves"); + + /*double cave_amount = 6.0 + 6.0 * noise2d_perlin( + 0.5+(double)node_min.X/250, 0.5+(double)node_min.Y/250, + data->seed+34329, 3, 0.50);*/ + const s16 max_spread_amount = MAP_BLOCKSIZE; + float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed); - /* - Make caves (this code is relatively horrible) - */ cave_amount = MYMAX(0.0, cave_amount); u32 caves_count = cave_amount * volume_nodes / 50000; u32 bruises_count = 1; - PseudoRandom ps(blockseed+21343); - PseudoRandom ps2(blockseed+1032); - if(ps.range(1, 6) == 1) + PseudoRandom ps(blockseed + 21343); + PseudoRandom ps2(blockseed + 1032); + + if (ps.range(1, 6) == 1) bruises_count = ps.range(0, ps.range(0, 2)); - if(get_biome(data->seed, v2s16(node_min.X, node_min.Z)) == BT_DESERT){ - caves_count /= 3; + + if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) { + caves_count /= 3; bruises_count /= 3; } - for(u32 jj=0; jj<caves_count+bruises_count; jj++) - { - if (!(flags & MG_CAVES)) - continue; - + + for(u32 jj = 0; jj < caves_count + bruises_count; jj++) { /*int avg_height = (int) ((base_rock_level_2d(data->seed, v2s16(node_min.X, node_min.Z)) + base_rock_level_2d(data->seed, v2s16(node_max.X, node_max.Z))) / 2); @@ -700,21 +977,9 @@ void MapgenV6::makeChunk(BlockMakeData *data) break;*/ bool large_cave = (jj >= caves_count); - s16 min_tunnel_diameter = 2; - s16 max_tunnel_diameter = ps.range(2,6); - int dswitchint = ps.range(1,14); - u16 tunnel_routepoints = 0; - int part_max_length_rs = 0; - if(large_cave){ - part_max_length_rs = ps.range(2,4); - tunnel_routepoints = ps.range(5, ps.range(15,30)); - min_tunnel_diameter = 5; - max_tunnel_diameter = ps.range(7, ps.range(8,24)); - } else { - part_max_length_rs = ps.range(2,9); - tunnel_routepoints = ps.range(10, ps.range(15,30)); - } - bool large_cave_is_flat = (ps.range(0,1) == 0); + + Cave cave; + defineCave(cave, ps, node_min, large_cave); v3f main_direction(0,0,0); @@ -727,13 +992,13 @@ void MapgenV6::makeChunk(BlockMakeData *data) // Allow a bit more //(this should be more than the maximum radius of the tunnel) s16 insure = 10; - s16 more = max_spread_amount - max_tunnel_diameter/2 - insure; + s16 more = max_spread_amount - cave.max_tunnel_diameter / 2 - insure; ar += v3s16(1,0,1) * more * 2; of -= v3s16(1,0,1) * more; s16 route_y_min = 0; // Allow half a diameter + 7 over stone surface - s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7; + s16 route_y_max = -of.Y + max_stone_y + cave.max_tunnel_diameter/2 + 7; // Limit maximum to area route_y_max = rangelim(route_y_max, 0, ar.Y-1); @@ -743,10 +1008,10 @@ void MapgenV6::makeChunk(BlockMakeData *data) s16 min = 0; if(node_min.Y < water_level && node_max.Y > water_level) { - min = water_level - max_tunnel_diameter/3 - of.Y; - route_y_max = water_level + max_tunnel_diameter/3 - of.Y; + min = water_level - cave.max_tunnel_diameter/3 - of.Y; + route_y_max = water_level + cave.max_tunnel_diameter/3 - of.Y; } - route_y_min = ps.range(min, min + max_tunnel_diameter); + route_y_min = ps.range(min, min + cave.max_tunnel_diameter); route_y_min = rangelim(route_y_min, 0, route_y_max); } @@ -774,9 +1039,9 @@ void MapgenV6::makeChunk(BlockMakeData *data) Generate some tunnel starting from orp */ - for(u16 j=0; j<tunnel_routepoints; j++) + for(u16 j=0; j<cave.tunnel_routepoints; j++) { - if(j%dswitchint==0 && large_cave == false) + if(j%cave.dswitchint==0 && large_cave == false) { main_direction = v3f( ((float)(ps.next()%20)-(float)10)/10, @@ -787,8 +1052,8 @@ void MapgenV6::makeChunk(BlockMakeData *data) } // Randomize size - s16 min_d = min_tunnel_diameter; - s16 max_d = max_tunnel_diameter; + s16 min_d = cave.min_tunnel_diameter; + s16 max_d = cave.max_tunnel_diameter; s16 rs = ps.range(min_d, max_d); // Every second section is rough @@ -798,17 +1063,17 @@ void MapgenV6::makeChunk(BlockMakeData *data) if(large_cave) { maxlen = v3s16( - rs*part_max_length_rs, - rs*part_max_length_rs/2, - rs*part_max_length_rs + rs*cave.part_max_length_rs, + rs*cave.part_max_length_rs/2, + rs*cave.part_max_length_rs ); } else { maxlen = v3s16( - rs*part_max_length_rs, - ps.range(1, rs*part_max_length_rs), - rs*part_max_length_rs + rs*cave.part_max_length_rs, + ps.range(1, rs*cave.part_max_length_rs), + rs*cave.part_max_length_rs ); } @@ -880,9 +1145,9 @@ void MapgenV6::makeChunk(BlockMakeData *data) /*// Make better floors in small caves if(y0 <= -rs/2 && rs<=7) continue;*/ - if(large_cave_is_flat){ + if (cave.large_cave_is_flat) { // Make large caves not so tall - if(rs > 7 && abs(y0) >= rs/3) + if (rs > 7 && abs(y0) >= rs/3) continue; } @@ -892,543 +1157,44 @@ void MapgenV6::makeChunk(BlockMakeData *data) v3s16 p(x,y,z); p += of; - if(vmanip.m_area.contains(p) == false) + if(vm->m_area.contains(p) == false) continue; - u32 i = vmanip.m_area.index(p); + u32 i = vm->m_area.index(p); - if(large_cave) - { - if(full_node_min.Y < water_level && - full_node_max.Y > water_level){ - if(p.Y <= water_level) - vmanip.m_data[i] = waternode; + if(large_cave) { + if (cave.flooded && full_node_min.Y < water_level && + full_node_max.Y > water_level) { + if (p.Y <= water_level) + vm->m_data[i] = waternode; else - vmanip.m_data[i] = airnode; - } else if(full_node_max.Y < water_level){ - if(p.Y < startp.Y - 2) - vmanip.m_data[i] = lavanode; + vm->m_data[i] = airnode; + } else if (cave.flooded && full_node_max.Y < water_level) { + if (p.Y < startp.Y - 2) + vm->m_data[i] = lavanode; else - vmanip.m_data[i] = airnode; + vm->m_data[i] = airnode; } else { - vmanip.m_data[i] = airnode; + vm->m_data[i] = airnode; } } else { // Don't replace air or water or lava or ignore - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE || - vmanip.m_data[i].getContent() == CONTENT_AIR || - vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source) + if (vm->m_data[i].getContent() == CONTENT_IGNORE || + vm->m_data[i].getContent() == CONTENT_AIR || + vm->m_data[i].getContent() == c_water_source || + vm->m_data[i].getContent() == c_lava_source) continue; - vmanip.m_data[i] = airnode; + vm->m_data[i] = airnode; // Set tunnel flag - vmanip.m_flags[i] |= VMANIP_FLAG_CAVE; + vm->m_flags[i] |= VMANIP_FLAG_CAVE; } } } } } - orp = rp; } - } - - }//timer1 -#endif - -#if 1 - { - // 15ms @cs=8 - TimeTaker timer1("add mud"); - - /* - Add mud to the central chunk - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position in 2d - v2s16 p2d = v2s16(x,z); - - // Randomize mud amount - s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5; - - // Find ground level - s16 surface_y = find_stone_level(vmanip, p2d, ndef); - // Handle area not found - if(surface_y == vmanip.m_area.MinEdge.Y - 1) - continue; - - MapNode addnode(c_dirt); - BiomeType bt = get_biome(data->seed, p2d); - - if(bt == BT_DESERT) - addnode = MapNode(c_desert_sand); - - if(bt == BT_DESERT && surface_y + mud_add_amount <= water_level+1){ - addnode = MapNode(c_sand); - } else if(mud_add_amount <= 0){ - mud_add_amount = 1 - mud_add_amount; - addnode = MapNode(c_gravel); - } else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) && - surface_y + mud_add_amount <= water_level+2){ - addnode = MapNode(c_sand); - } - - if(bt == BT_DESERT){ - if(surface_y > 20){ - mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5); - } - } - - /* - If topmost node is grass, change it to mud. - It might be if it was flown to there from a neighboring - chunk and then converted. - */ - { - u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y)); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() == c_dirt_with_grass) - *n = MapNode(c_dirt); - } - - /* - Add mud on ground - */ - { - s16 mudcount = 0; - v3s16 em = vmanip.m_area.getExtent(); - s16 y_start = surface_y+1; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); - for(s16 y=y_start; y<=node_max.Y; y++) - { - if(mudcount >= mud_add_amount) - break; - - MapNode &n = vmanip.m_data[i]; - n = addnode; - mudcount++; - - vmanip.m_area.add_y(em, i, 1); - } - } - - } - - }//timer1 -#endif - - /* - Add blobs of dirt and gravel underground - */ - if(get_biome(data->seed, v2s16(node_min.X, node_min.Z)) == BT_NORMAL) - { - PseudoRandom pr(blockseed+983); - for(int i=0; i<volume_nodes/10/10/10; i++) - { - bool only_fill_cave = (myrand_range(0,1) != 0); - v3s16 size( - pr.range(1, 8), - pr.range(1, 8), - pr.range(1, 8) - ); - v3s16 p0( - pr.range(node_min.X, node_max.X)-size.X/2, - pr.range(node_min.Y, node_max.Y)-size.Y/2, - pr.range(node_min.Z, node_max.Z)-size.Z/2 - ); - MapNode n1; - if(p0.Y > -32 && pr.range(0,1) == 0) - n1 = MapNode(c_dirt); - else - n1 = MapNode(c_gravel); - for(int x1=0; x1<size.X; x1++) - for(int y1=0; y1<size.Y; y1++) - for(int z1=0; z1<size.Z; z1++) - { - v3s16 p = p0 + v3s16(x1,y1,z1); - u32 i = vmanip.m_area.index(p); - if(!vmanip.m_area.contains(i)) - continue; - // Cancel if not stone and not cave air - if(vmanip.m_data[i].getContent() != c_stone && - !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE)) - continue; - if(only_fill_cave && !(vmanip.m_flags[i] & VMANIP_FLAG_CAVE)) - continue; - vmanip.m_data[i] = n1; - } - } - } - -#if 1 - { - // 340ms @cs=8 - TimeTaker timer1("flow mud"); - - /* - Flow mud away from steep edges - */ - - // Iterate a few times - for(s16 k=0; k<3; k++) - { - - for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++) - for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++) - { - // Invert coordinates every 2nd iteration - if(k%2 == 0) - { - x = mudflow_maxpos - (x-mudflow_minpos); - z = mudflow_maxpos - (z-mudflow_minpos); - } - - // Node position in 2d - v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z); - - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - s16 y=node_max.Y; - - while(y >= node_min.Y) - { - - for(;; y--) - { - MapNode *n = NULL; - // Find mud - for(; y>=node_min.Y; y--) - { - n = &vmanip.m_data[i]; - //if(content_walkable(n->d)) - // break; - if(n->getContent() == c_dirt || - n->getContent() == c_dirt_with_grass || - n->getContent() == c_gravel) - break; - - vmanip.m_area.add_y(em, i, -1); - } - - // Stop if out of area - //if(vmanip.m_area.contains(i) == false) - if(y < node_min.Y) - break; - - /*// If not mud, do nothing to it - MapNode *n = &vmanip.m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) - continue;*/ - - if(n->getContent() == c_dirt || - n->getContent() == c_dirt_with_grass) - { - // Make it exactly mud - n->setContent(c_dirt); - - /* - Don't flow it if the stuff under it is not mud - */ - { - u32 i2 = i; - vmanip.m_area.add_y(em, i2, -1); - // Cancel if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - MapNode *n2 = &vmanip.m_data[i2]; - if(n2->getContent() != c_dirt && - n2->getContent() != c_dirt_with_grass) - continue; - } - } - - /*s16 recurse_count = 0; - mudflow_recurse:*/ - - v3s16 dirs4[4] = { - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - - // Theck that upper is air or doesn't exist. - // Cancel dropping if upper keeps it in place - u32 i3 = i; - vmanip.m_area.add_y(em, i3, 1); - if(vmanip.m_area.contains(i3) == true - && ndef->get(vmanip.m_data[i3]).walkable) - { - continue; - } - - // Drop mud on side - - for(u32 di=0; di<4; di++) - { - v3s16 dirp = dirs4[di]; - u32 i2 = i; - // Move to side - vmanip.m_area.add_p(em, i2, dirp); - // Fail if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - // Check that side is air - MapNode *n2 = &vmanip.m_data[i2]; - if(ndef->get(*n2).walkable) - continue; - // Check that under side is air - vmanip.m_area.add_y(em, i2, -1); - if(vmanip.m_area.contains(i2) == false) - continue; - n2 = &vmanip.m_data[i2]; - if(ndef->get(*n2).walkable) - continue; - /*// Check that under that is air (need a drop of 2) - vmanip.m_area.add_y(em, i2, -1); - if(vmanip.m_area.contains(i2) == false) - continue; - n2 = &vmanip.m_data[i2]; - if(content_walkable(n2->d)) - continue;*/ - // Loop further down until not air - bool dropped_to_unknown = false; - do{ - vmanip.m_area.add_y(em, i2, -1); - n2 = &vmanip.m_data[i2]; - // if out of known area - if(vmanip.m_area.contains(i2) == false - || n2->getContent() == CONTENT_IGNORE){ - dropped_to_unknown = true; - break; - } - }while(ndef->get(*n2).walkable == false); - // Loop one up so that we're in air - vmanip.m_area.add_y(em, i2, 1); - n2 = &vmanip.m_data[i2]; - - bool old_is_water = (n->getContent() == c_water_source); - // Move mud to new place - if(!dropped_to_unknown) { - *n2 = *n; - // Set old place to be air (or water) - if(old_is_water) - *n = MapNode(c_water_source); - else - *n = MapNode(CONTENT_AIR); - } - - // Done - break; - } - } - } - } - - } - - }//timer1 -#endif - - } // Aging loop - /*********************** - END OF AGING LOOP - ************************/ - - /* - Add top and bottom side of water to transforming_liquid queue - */ - - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool water_found = false; - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - if(y == full_node_max.Y){ - water_found = - (vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source); - } - else if(water_found == false) - { - if(vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source) - { - v3s16 p = v3s16(p2d.X, y, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = true; - } - } - else - { - // This can be done because water_found can only - // turn to true and end up here after going through - // a single block. - if(vmanip.m_data[i+1].getContent() != c_water_source || - vmanip.m_data[i+1].getContent() != c_lava_source) - { - v3s16 p = v3s16(p2d.X, y+1, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = false; - } - } - - vmanip.m_area.add_y(em, i, -1); - } - } - } - - /* - Grow grass - */ - - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position in 2d - v2s16 p2d = v2s16(x,z); - - /* - Find the lowest surface to which enough light ends up - to make grass grow. - - Basically just wait until not air and not leaves. - */ - s16 surface_y = 0; - { - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - s16 y; - // Go to ground level - for(y=node_max.Y; y>=full_node_min.Y; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).param_type != CPT_LIGHT - || ndef->get(n).liquid_type != LIQUID_NONE) - break; - vmanip.m_area.add_y(em, i, -1); - } - if(y >= full_node_min.Y) - surface_y = y; - else - surface_y = full_node_min.Y; - } - - u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() == c_dirt){ - // Well yeah, this can't be overground... - if(surface_y < water_level - 20) - continue; - n->setContent(c_dirt_with_grass); - } - } - - /* - Generate some trees - */ - assert(central_area_size.X == central_area_size.Z); - if (flags & MG_TREES) { - // Divide area into parts - s16 div = 8; - s16 sidelen = central_area_size.X / div; - double area = sidelen * sidelen; - for(s16 x0=0; x0<div; x0++) - for(s16 z0=0; z0<div; z0++) - { - // Center position of part of division - v2s16 p2d_center( - node_min.X + sidelen/2 + sidelen*x0, - node_min.Z + sidelen/2 + sidelen*z0 - ); - // Minimum edge of part of division - v2s16 p2d_min( - node_min.X + sidelen*x0, - node_min.Z + sidelen*z0 - ); - // Maximum edge of part of division - v2s16 p2d_max( - node_min.X + sidelen + sidelen*x0 - 1, - node_min.Z + sidelen + sidelen*z0 - 1 - ); - // Amount of trees - u32 tree_count = area * tree_amount_2d(data->seed, p2d_center); - // Put trees in random places on part of division - for(u32 i=0; i<tree_count; i++) - { - s16 x = myrand_range(p2d_min.X, p2d_max.X); - s16 z = myrand_range(p2d_min.Y, p2d_max.Y); - s16 y = find_ground_level(vmanip, v2s16(x,z), ndef); - // Don't make a tree under water level - if(y < water_level) - continue; - // Don't make a tree so high that it doesn't fit - if(y > node_max.Y - 6) - continue; - v3s16 p(x,y,z); - /* - Trees grow only on mud and grass - */ - { - u32 i = vmanip.m_area.index(v3s16(p)); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() != c_dirt - && n->getContent() != c_dirt_with_grass) - continue; - } - p.Y++; - // Make a tree - make_tree(vmanip, p, false, ndef); - } - } - } - - - /* - Calculate lighting - */ - { - ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", - SPT_AVG); - //VoxelArea a(node_min, node_max); - VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE, - node_max+v3s16(1,0,1)*MAP_BLOCKSIZE); - /*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2, - node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/ - enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; - for(int i=0; i<2; i++) - { - enum LightBank bank = banks[i]; - - core::map<v3s16, bool> light_sources; - core::map<v3s16, u8> unlight_from; - - voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef, - light_sources, unlight_from); - - bool inexistent_top_provides_sunlight = !block_is_underground; - voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( - vmanip, a, inexistent_top_provides_sunlight, - light_sources, ndef); - // TODO: Do stuff according to bottom_sunlight_valid - - vmanip.unspreadLight(bank, unlight_from, light_sources, ndef); - - vmanip.spreadLight(bank, light_sources, ndef); - } - } - this->generating = false; } diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index d2f05252b..d37e406cb 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" #define AVERAGE_MUD_AMOUNT 4 +#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1 enum BiomeType { @@ -34,11 +35,23 @@ extern NoiseParams nparams_v6_def_terrain_base; extern NoiseParams nparams_v6_def_terrain_higher; extern NoiseParams nparams_v6_def_steepness; extern NoiseParams nparams_v6_def_height_select; -extern NoiseParams nparams_v6_def_trees; extern NoiseParams nparams_v6_def_mud; extern NoiseParams nparams_v6_def_beach; extern NoiseParams nparams_v6_def_biome; extern NoiseParams nparams_v6_def_cave; +extern NoiseParams nparams_v6_def_humidity; +extern NoiseParams nparams_v6_def_trees; +extern NoiseParams nparams_v6_def_apple_trees; + +struct Cave { + s16 min_tunnel_diameter; + s16 max_tunnel_diameter; + int dswitchint; + u16 tunnel_routepoints; + int part_max_length_rs; + bool large_cave_is_flat; + bool flooded; +}; struct MapgenV6Params : public MapgenParams { float freq_desert; @@ -47,12 +60,14 @@ struct MapgenV6Params : public MapgenParams { NoiseParams *np_terrain_higher; NoiseParams *np_steepness; NoiseParams *np_height_select; - NoiseParams *np_trees; 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; @@ -60,11 +75,14 @@ struct MapgenV6Params : public MapgenParams { np_terrain_higher = &nparams_v6_def_terrain_higher; np_steepness = &nparams_v6_def_steepness; np_height_select = &nparams_v6_def_height_select; - np_trees = &nparams_v6_def_trees; 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; + } bool readParams(Settings *settings); @@ -73,64 +91,90 @@ struct MapgenV6Params : public MapgenParams { class MapgenV6 : public Mapgen { public: - //ManualMapVoxelManipulator &vmanip; + EmergeManager *emerge; int ystride; v3s16 csize; + u32 flags; + u32 blockseed; v3s16 node_min; v3s16 node_max; + v3s16 full_node_min; + v3s16 full_node_max; + v3s16 central_area_size; + int volume_nodes; Noise *noise_terrain_base; Noise *noise_terrain_higher; Noise *noise_steepness; Noise *noise_height_select; - Noise *noise_trees; Noise *noise_mud; Noise *noise_beach; Noise *noise_biome; - - float *map_terrain_base; - float *map_terrain_higher; - float *map_steepness; - float *map_height_select; - float *map_trees; - float *map_mud; - float *map_beach; - float *map_biome; - NoiseParams *np_cave; - - u32 flags; + NoiseParams *np_humidity; + NoiseParams *np_trees; + NoiseParams *np_apple_trees; float freq_desert; float freq_beach; - - MapgenV6(int mapgenid, MapgenV6Params *params); + + content_t c_stone; + content_t c_dirt; + content_t c_dirt_with_grass; + content_t c_sand; + content_t c_water_source; + content_t c_lava_source; + content_t c_gravel; + content_t c_cobble; + content_t c_desert_sand; + content_t c_desert_stone; + + MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge); ~MapgenV6(); void makeChunk(BlockMakeData *data); int getGroundLevelAtPoint(v2s16 p); - double baseRockLevelFromNoise(v2s16 p); - static s16 find_ground_level(VoxelManipulator &vmanip, - v2s16 p2d, INodeDefManager *ndef); - static s16 find_stone_level(VoxelManipulator &vmanip, - v2s16 p2d, INodeDefManager *ndef); - void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef); - double tree_amount_2d(u64 seed, v2s16 p); + float baseTerrainLevel(float terrain_base, float terrain_higher, + float steepness, float height_select); + virtual float baseTerrainLevelFromNoise(v2s16 p); + virtual float baseTerrainLevelFromMap(v2s16 p); + virtual float baseTerrainLevelFromMap(int index); + + s16 find_ground_level(v2s16 p2d); + s16 find_stone_level(v2s16 p2d); bool block_is_underground(u64 seed, v3s16 blockpos); - double base_rock_level_2d(u64 seed, v2s16 p); s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision); - double get_mud_add_amount(u64 seed, v2s16 p); - bool get_have_beach(u64 seed, v2s16 p2d); - BiomeType get_biome(u64 seed, v2s16 p2d); + + float getHumidity(v2s16 p); + float getTreeAmount(v2s16 p); + bool getHaveAppleTree(v2s16 p); + float getMudAmount(v2s16 p); + virtual float getMudAmount(int index); + bool getHaveBeach(v2s16 p); + bool getHaveBeach(int index); + BiomeType getBiome(v2s16 p); + BiomeType getBiome(int index, v2s16 p); + u32 get_blockseed(u64 seed, v3s16 p); + + virtual void calculateNoise(); + int generateGround(); + void addMud(); + void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos); + void addDirtGravelBlobs(); + void growGrass(); + void placeTreesAndJungleGrass(); + virtual void defineCave(Cave &cave, PseudoRandom ps, + v3s16 node_min, bool large_cave); + void generateCaves(int max_stone_y); + virtual void generateSomething() {}; //for next mapgen }; struct MapgenFactoryV6 : public MapgenFactory { Mapgen *createMapgen(int mgid, MapgenParams *params, EmergeManager *emerge) { - return new MapgenV6(mgid, (MapgenV6Params *)params); + return new MapgenV6(mgid, (MapgenV6Params *)params, emerge); }; MapgenParams *createMapgenParams() { diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 0513e688c..bba845fcc 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -107,7 +107,7 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const { const ContentFeatures &f = nodemgr->get(*this); if(f.param_type_2 == CPT2_FACEDIR) - return getParam2() & 0x03; + return getParam2() & 0x1F; return 0; } @@ -140,29 +140,131 @@ static std::vector<aabb3f> transformNodeBox(const MapNode &n, { const std::vector<aabb3f> &fixed = nodebox.fixed; int facedir = n.getFaceDir(nodemgr); + u8 axisdir = facedir>>2; + facedir&=0x03; for(std::vector<aabb3f>::const_iterator i = fixed.begin(); i != fixed.end(); i++) { aabb3f box = *i; - if(facedir == 1) + switch (axisdir) { - box.MinEdge.rotateXZBy(-90); - box.MaxEdge.rotateXZBy(-90); - box.repair(); - } - else if(facedir == 2) - { - box.MinEdge.rotateXZBy(180); - box.MaxEdge.rotateXZBy(180); - box.repair(); - } - else if(facedir == 3) - { - box.MinEdge.rotateXZBy(90); - box.MaxEdge.rotateXZBy(90); - box.repair(); + case 0: + if(facedir == 1) + { + box.MinEdge.rotateXZBy(-90); + box.MaxEdge.rotateXZBy(-90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXZBy(180); + box.MaxEdge.rotateXZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXZBy(90); + box.MaxEdge.rotateXZBy(90); + } + break; + case 1: // z+ + box.MinEdge.rotateYZBy(90); + box.MaxEdge.rotateYZBy(90); + if(facedir == 1) + { + box.MinEdge.rotateXYBy(90); + box.MaxEdge.rotateXYBy(90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXYBy(180); + box.MaxEdge.rotateXYBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXYBy(-90); + box.MaxEdge.rotateXYBy(-90); + } + break; + case 2: //z- + box.MinEdge.rotateYZBy(-90); + box.MaxEdge.rotateYZBy(-90); + if(facedir == 1) + { + box.MinEdge.rotateXYBy(-90); + box.MaxEdge.rotateXYBy(-90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXYBy(180); + box.MaxEdge.rotateXYBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXYBy(90); + box.MaxEdge.rotateXYBy(90); + } + break; + case 3: //x+ + box.MinEdge.rotateXYBy(-90); + box.MaxEdge.rotateXYBy(-90); + if(facedir == 1) + { + box.MinEdge.rotateYZBy(90); + box.MaxEdge.rotateYZBy(90); + } + else if(facedir == 2) + { + box.MinEdge.rotateYZBy(180); + box.MaxEdge.rotateYZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateYZBy(-90); + box.MaxEdge.rotateYZBy(-90); + } + break; + case 4: //x- + box.MinEdge.rotateXYBy(90); + box.MaxEdge.rotateXYBy(90); + if(facedir == 1) + { + box.MinEdge.rotateYZBy(-90); + box.MaxEdge.rotateYZBy(-90); + } + else if(facedir == 2) + { + box.MinEdge.rotateYZBy(180); + box.MaxEdge.rotateYZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateYZBy(90); + box.MaxEdge.rotateYZBy(90); + } + break; + case 5: + box.MinEdge.rotateXYBy(-180); + box.MaxEdge.rotateXYBy(-180); + if(facedir == 1) + { + box.MinEdge.rotateXZBy(90); + box.MaxEdge.rotateXZBy(90); + } + else if(facedir == 2) + { + box.MinEdge.rotateXZBy(180); + box.MaxEdge.rotateXZBy(180); + } + else if(facedir == 3) + { + box.MinEdge.rotateXZBy(-90); + box.MaxEdge.rotateXZBy(-90); + } + break; + default: + break; } + box.repair(); boxes.push_back(box); } } diff --git a/src/mapsector.cpp b/src/mapsector.cpp index 108effa79..ebb050ec3 100644 --- a/src/mapsector.cpp +++ b/src/mapsector.cpp @@ -45,10 +45,10 @@ void MapSector::deleteBlocks() m_block_cache = NULL; // Delete all - core::map<s16, MapBlock*>::Iterator i = m_blocks.getIterator(); - for(; i.atEnd() == false; i++) + for(std::map<s16, MapBlock*>::iterator i = m_blocks.begin(); + i != m_blocks.end(); ++i) { - delete i.getNode()->getValue(); + delete i->second; } // Clear container @@ -64,14 +64,14 @@ MapBlock * MapSector::getBlockBuffered(s16 y) } // If block doesn't exist, return NULL - core::map<s16, MapBlock*>::Node *n = m_blocks.find(y); - if(n == NULL) + std::map<s16, MapBlock*>::iterator n = m_blocks.find(y); + if(n == m_blocks.end()) { block = NULL; } // If block exists, return it else{ - block = n->getValue(); + block = n->second; } // Cache the last result @@ -101,7 +101,7 @@ MapBlock * MapSector::createBlankBlock(s16 y) { MapBlock *block = createBlankBlockNoInsert(y); - m_blocks.insert(y, block); + m_blocks[y] = block; return block; } @@ -119,7 +119,7 @@ void MapSector::insertBlock(MapBlock *block) assert(p2d == m_pos); // Insert into container - m_blocks.insert(block_y, block); + m_blocks[block_y] = block; } void MapSector::deleteBlock(MapBlock *block) @@ -130,23 +130,18 @@ void MapSector::deleteBlock(MapBlock *block) m_block_cache = NULL; // Remove from container - m_blocks.remove(block_y); + m_blocks.erase(block_y); // Delete delete block; } -void MapSector::getBlocks(core::list<MapBlock*> &dest) +void MapSector::getBlocks(std::list<MapBlock*> &dest) { - core::list<MapBlock*> ref_list; - - core::map<s16, MapBlock*>::Iterator bi; - - bi = m_blocks.getIterator(); - for(; bi.atEnd() == false; bi++) + for(std::map<s16, MapBlock*>::iterator bi = m_blocks.begin(); + bi != m_blocks.end(); ++bi) { - MapBlock *b = bi.getNode()->getValue(); - dest.push_back(b); + dest.push_back(bi->second); } } @@ -189,7 +184,7 @@ ServerMapSector* ServerMapSector::deSerialize( std::istream &is, Map *parent, v2s16 p2d, - core::map<v2s16, MapSector*> & sectors, + std::map<v2s16, MapSector*> & sectors, IGameDef *gamedef ) { @@ -219,22 +214,22 @@ ServerMapSector* ServerMapSector::deSerialize( ServerMapSector *sector = NULL; - core::map<v2s16, MapSector*>::Node *n = sectors.find(p2d); + std::map<v2s16, MapSector*>::iterator n = sectors.find(p2d); - if(n != NULL) + if(n != sectors.end()) { dstream<<"WARNING: deSerializing existent sectors not supported " "at the moment, because code hasn't been tested." <<std::endl; - MapSector *sector = n->getValue(); + MapSector *sector = n->second; assert(sector->getId() == MAPSECTOR_SERVER); return (ServerMapSector*)sector; } else { sector = new ServerMapSector(parent, p2d, gamedef); - sectors.insert(p2d, sector); + sectors[p2d] = sector; } /* diff --git a/src/mapsector.h b/src/mapsector.h index 88fc76b57..4f2b3f31f 100644 --- a/src/mapsector.h +++ b/src/mapsector.h @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "exceptions.h" #include <ostream> +#include <map> +#include <list> class MapBlock; class Map; @@ -60,7 +62,7 @@ public: void deleteBlock(MapBlock *block); - void getBlocks(core::list<MapBlock*> &dest); + void getBlocks(std::list<MapBlock*> &dest); // Always false at the moment, because sector contains no metadata. bool differs_from_disk; @@ -68,7 +70,7 @@ public: protected: // The pile of MapBlocks - core::map<s16, MapBlock*> m_blocks; + std::map<s16, MapBlock*> m_blocks; Map *m_parent; // Position on parent (in MapBlock widths) @@ -110,7 +112,7 @@ public: std::istream &is, Map *parent, v2s16 p2d, - core::map<v2s16, MapSector*> & sectors, + std::map<v2s16, MapSector*> & sectors, IGameDef *gamedef ); diff --git a/src/mods.cpp b/src/mods.cpp index ac2d9b17d..6a7ab79aa 100644 --- a/src/mods.cpp +++ b/src/mods.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "subgame.h" #include "settings.h" +#include "strfnd.h" std::map<std::string, ModSpec> getModsInPath(std::string path) { @@ -188,11 +189,58 @@ void ModConfiguration::addMods(std::vector<ModSpec> new_mods) } } +// If failed, returned modspec has name=="" +static ModSpec findCommonMod(const std::string &modname) +{ + // Try to find in {$user,$share}/games/common/$modname + std::vector<std::string> find_paths; + find_paths.push_back(porting::path_user + DIR_DELIM + "games" + + DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname); + find_paths.push_back(porting::path_share + DIR_DELIM + "games" + + DIR_DELIM + "common" + DIR_DELIM + "mods" + DIR_DELIM + modname); + for(u32 i=0; i<find_paths.size(); i++){ + const std::string &try_path = find_paths[i]; + if(fs::PathExists(try_path)) + return ModSpec(modname, try_path); + } + // Failed to find mod + return ModSpec(); +} + ModConfiguration::ModConfiguration(std::string worldpath) { + SubgameSpec gamespec = findWorldSubgame(worldpath); + + // Add common mods without dependency handling + std::vector<std::string> inexistent_common_mods; + Settings gameconf; + if(getGameConfig(gamespec.path, gameconf)){ + if(gameconf.exists("common_mods")){ + Strfnd f(gameconf.get("common_mods")); + while(!f.atend()){ + std::string modname = trim(f.next(",")); + if(modname.empty()) + continue; + ModSpec spec = findCommonMod(modname); + if(spec.name.empty()) + inexistent_common_mods.push_back(modname); + else + m_sorted_mods.push_back(spec); + } + } + } + if(!inexistent_common_mods.empty()){ + std::string s = "Required common mods "; + for(u32 i=0; i<inexistent_common_mods.size(); i++){ + if(i != 0) s += ", "; + s += std::string("\"") + inexistent_common_mods[i] + "\""; + } + s += " could not be found."; + throw ModError(s); + } + // Add all world mods and all game mods addModsInPath(worldpath + DIR_DELIM + "worldmods"); - SubgameSpec gamespec = findWorldSubgame(worldpath); addModsInPath(gamespec.gamemods_path); // check world.mt file for mods explicitely declared to be @@ -217,6 +265,6 @@ ModConfiguration::ModConfiguration(std::string worldpath) } for(std::set<std::string>::const_iterator i = gamespec.addon_mods_paths.begin(); - i != gamespec.addon_mods_paths.end(); ++i) + i != gamespec.addon_mods_paths.end(); ++i) addModsInPathFiltered((*i),exclude_mod_names); } diff --git a/src/mods.h b/src/mods.h index 9761a9103..32bcfb471 100644 --- a/src/mods.h +++ b/src/mods.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <string> #include <map> #include <exception> +#include <list> class ModError : public std::exception { @@ -68,7 +69,6 @@ struct ModSpec {} }; - std::map<std::string,ModSpec> getModsInPath(std::string path); // expands modpack contents, but does not replace them. @@ -140,6 +140,4 @@ private: }; - #endif - diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 9a1145a8e..d41df5c3b 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -107,26 +107,31 @@ void NodeBox::deSerialize(std::istream &is) TileDef */ -void TileDef::serialize(std::ostream &os) const +void TileDef::serialize(std::ostream &os, u16 protocol_version) const { - writeU8(os, 0); // version + if(protocol_version >= 17) + writeU8(os, 1); + else + writeU8(os, 0); os<<serializeString(name); writeU8(os, animation.type); writeU16(os, animation.aspect_w); writeU16(os, animation.aspect_h); writeF1000(os, animation.length); + if(protocol_version >= 17) + writeU8(os, backface_culling); } void TileDef::deSerialize(std::istream &is) { int version = readU8(is); - if(version != 0) - throw SerializationError("unsupported TileDef version"); name = deSerializeString(is); animation.type = (TileAnimationType)readU8(is); animation.aspect_w = readU16(is); animation.aspect_h = readU16(is); animation.length = readF1000(is); + if(version >= 1) + backface_culling = readU8(is); } /* @@ -235,10 +240,10 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) writeF1000(os, visual_scale); writeU8(os, 6); for(u32 i=0; i<6; i++) - tiledef[i].serialize(os); + tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); for(u32 i=0; i<CF_SPECIAL_COUNT; i++){ - tiledef_special[i].serialize(os); + tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); writeU8(os, post_effect_color.getAlpha()); @@ -270,9 +275,9 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) serializeSimpleSoundSpec(sound_footstep, os); serializeSimpleSoundSpec(sound_dig, os); serializeSimpleSoundSpec(sound_dug, os); + writeU8(os, rightclickable); // Stuff below should be moved to correct place in a version that otherwise changes // the protocol version - writeU8(os, rightclickable); } void ContentFeatures::deSerialize(std::istream &is) @@ -331,12 +336,12 @@ void ContentFeatures::deSerialize(std::istream &is) deSerializeSimpleSoundSpec(sound_footstep, is); deSerializeSimpleSoundSpec(sound_dig, is); deSerializeSimpleSoundSpec(sound_dug, is); + rightclickable = readU8(is); // If you add anything here, insert it primarily inside the try-catch // block to not need to increase the version. try{ // Stuff below should be moved to correct place in a version that // otherwise changes the protocol version - rightclickable = readU8(is); }catch(SerializationError &e) {}; } @@ -809,10 +814,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) writeF1000(os, visual_scale); writeU8(os, 6); for(u32 i=0; i<6; i++) - tiledef[i].serialize(os); + tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); for(u32 i=0; i<CF_SPECIAL_COUNT; i++){ - tiledef_special[i].serialize(os); + tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); writeU8(os, post_effect_color.getAlpha()); diff --git a/src/nodedef.h b/src/nodedef.h index b3f972b9b..4f07565d1 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -119,7 +119,7 @@ struct TileDef animation.length = 1.0; } - void serialize(std::ostream &os) const; + void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is); }; diff --git a/src/noise.h b/src/noise.h index c2a85771c..34b4d0b41 100644 --- a/src/noise.h +++ b/src/noise.h @@ -89,7 +89,7 @@ public: Noise(NoiseParams *np, int seed, int sx, int sy, int sz); ~Noise(); - void init(NoiseParams *np, int seed, int sx, int sy, int sz); + virtual void init(NoiseParams *np, int seed, int sx, int sy, int sz); void setSize(int sx, int sy); void setSize(int sx, int sy, int sz); void setSpreadFactor(v3f spread); @@ -157,7 +157,7 @@ inline float easeCurve(float t) { (s) + (np)->seed, (np)->octaves, (np)->persist)) #define NoisePerlin3D(np, x, y, z, s) ((np)->offset + (np)->scale * \ - noise2d_perlin((float)(x) / (np)->spread.X, (float)(y) / (np)->spread.Y, \ + noise3d_perlin((float)(x) / (np)->spread.X, (float)(y) / (np)->spread.Y, \ (float)(z) / (np)->spread.Z, (s) + (np)->seed, (np)->octaves, (np)->persist)) #endif diff --git a/src/object_properties.h b/src/object_properties.h index bde38bd66..eeb397efa 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include <iostream> #include <map> +#include <vector> struct ObjectProperties { @@ -35,8 +36,8 @@ struct ObjectProperties std::string visual; std::string mesh; v2f visual_size; - core::array<std::string> textures; - core::array<video::SColor> colors; + std::vector<std::string> textures; + std::vector<video::SColor> colors; v2s16 spritediv; v2s16 initial_sprite_basepos; bool is_visible; diff --git a/src/particles.cpp b/src/particles.cpp index fef21d91b..1d814f619 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -32,19 +32,34 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientmap.h" #include "mapnode.h" +/* + Utility +*/ + +v3f random_v3f(v3f min, v3f max) +{ + return v3f( rand()/(float)RAND_MAX*(max.X-min.X)+min.X, + rand()/(float)RAND_MAX*(max.Y-min.Y)+min.Y, + rand()/(float)RAND_MAX*(max.Z-min.Z)+min.Z); +} + +std::vector<Particle*> all_particles; +std::map<u32, ParticleSpawner*> all_particlespawners; + Particle::Particle( IGameDef *gamedef, scene::ISceneManager* smgr, LocalPlayer *player, - s32 id, + ClientEnvironment &env, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, + bool collisiondetection, AtlasPointer ap ): - scene::ISceneNode(smgr->getRootSceneNode(), smgr, id) + scene::ISceneNode(smgr->getRootSceneNode(), smgr) { // Misc m_gamedef = gamedef; @@ -57,7 +72,6 @@ Particle::Particle( m_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; m_material.setTexture(0, ap.atlas); m_ap = ap; - m_light = 0; // Particle related @@ -68,10 +82,20 @@ Particle::Particle( m_time = 0; m_player = player; m_size = size; + m_collisiondetection = collisiondetection; - // Irrlicht stuff (TODO) - m_collisionbox = core::aabbox3d<f32>(-size/2,-size/2,-size/2,size/2,size/2,size/2); + // Irrlicht stuff + m_collisionbox = core::aabbox3d<f32> + (-size/2,-size/2,-size/2,size/2,size/2,size/2); this->setAutomaticCulling(scene::EAC_OFF); + + // Init lighting + updateLight(env); + + // Init model + updateVertices(); + + all_particles.push_back(this); } Particle::~Particle() @@ -82,8 +106,10 @@ void Particle::OnRegisterSceneNode() { if (IsVisible) { - SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); - SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID); + SceneManager->registerNodeForRendering + (this, scene::ESNRP_TRANSPARENT); + SceneManager->registerNodeForRendering + (this, scene::ESNRP_SOLID); } ISceneNode::OnRegisterSceneNode(); @@ -96,45 +122,45 @@ void Particle::render() video::IVideoDriver* driver = SceneManager->getVideoDriver(); driver->setMaterial(m_material); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); - video::SColor c(255, m_light, m_light, m_light); - - video::S3DVertex vertices[4] = - { - video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x0(), m_ap.y1()), - video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y1()), - video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, c, m_ap.x1(), m_ap.y0()), - video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, c ,m_ap.x0(), m_ap.y0()), - }; - - for(u16 i=0; i<4; i++) - { - vertices[i].Pos.rotateYZBy(m_player->getPitch()); - vertices[i].Pos.rotateXZBy(m_player->getYaw()); - m_box.addInternalPoint(vertices[i].Pos); - vertices[i].Pos += m_pos*BS; - } u16 indices[] = {0,1,2, 2,3,0}; - driver->drawVertexPrimitiveList(vertices, 4, indices, 2, - video::EVT_STANDARD, scene::EPT_TRIANGLES, video::EIT_16BIT); + driver->drawVertexPrimitiveList(m_vertices, 4, + indices, 2, video::EVT_STANDARD, + scene::EPT_TRIANGLES, video::EIT_16BIT); } void Particle::step(float dtime, ClientEnvironment &env) { - core::aabbox3d<f32> box = m_collisionbox; - v3f p_pos = m_pos*BS; - v3f p_velocity = m_velocity*BS; - v3f p_acceleration = m_acceleration*BS; - collisionMoveSimple(&env.getClientMap(), m_gamedef, - BS*0.5, box, - 0, dtime, - p_pos, p_velocity, p_acceleration); - m_pos = p_pos/BS; - m_velocity = p_velocity/BS; - m_acceleration = p_acceleration/BS; m_time += dtime; + if (m_collisiondetection) + { + core::aabbox3d<f32> box = m_collisionbox; + v3f p_pos = m_pos*BS; + v3f p_velocity = m_velocity*BS; + v3f p_acceleration = m_acceleration*BS; + collisionMoveSimple(&env, m_gamedef, + BS*0.5, box, + 0, dtime, + p_pos, p_velocity, p_acceleration); + m_pos = p_pos/BS; + m_velocity = p_velocity/BS; + m_acceleration = p_acceleration/BS; + } + else + { + m_velocity += m_acceleration * dtime; + m_pos += m_velocity * dtime; + } // Update lighting + updateLight(env); + + // Update model + updateVertices(); +} + +void Particle::updateLight(ClientEnvironment &env) +{ u8 light = 0; try{ v3s16 p = v3s16( @@ -151,11 +177,37 @@ void Particle::step(float dtime, ClientEnvironment &env) m_light = decode_light(light); } -std::vector<Particle*> all_particles; +void Particle::updateVertices() +{ + video::SColor c(255, m_light, m_light, m_light); + m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, + c, m_ap.x0(), m_ap.y1()); + m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, + c, m_ap.x1(), m_ap.y1()); + m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, + c, m_ap.x1(), m_ap.y0()); + m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, + c ,m_ap.x0(), m_ap.y0()); + + for(u16 i=0; i<4; i++) + { + m_vertices[i].Pos.rotateYZBy(m_player->getPitch()); + m_vertices[i].Pos.rotateXZBy(m_player->getYaw()); + m_box.addInternalPoint(m_vertices[i].Pos); + m_vertices[i].Pos += m_pos*BS; + } +} + + +/* + Helpers +*/ + void allparticles_step (float dtime, ClientEnvironment &env) { - for(std::vector<Particle*>::iterator i = all_particles.begin(); i != all_particles.end();) + for(std::vector<Particle*>::iterator i = all_particles.begin(); + i != all_particles.end();) { if ((*i)->get_expired()) { @@ -171,22 +223,28 @@ void allparticles_step (float dtime, ClientEnvironment &env) } } -void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]) { for (u16 j = 0; j < 32; j++) // set the amount of particles here { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, env, pos, tiles); } } -void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, + v3s16 pos, const TileSpec tiles[]) { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, env, pos, tiles); } // add a particle of a node // used by digging and punching particles -void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]) { // Texture u8 texid = myrand_range(0,5); @@ -205,7 +263,10 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer ap.pos.Y = ap.y0() + (y1 - ap.y0()) * ((rand()%64)/64.-texsize); // Physics - v3f velocity((rand()%100/50.-1)/1.5, rand()%100/35., (rand()%100/50.-1)/1.5); + v3f velocity( (rand()%100/50.-1)/1.5, + rand()%100/35., + (rand()%100/50.-1)/1.5); + v3f acceleration(0,-9,0); v3f particlepos = v3f( (f32)pos.X+rand()%100/200.-0.25, @@ -213,17 +274,180 @@ void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer (f32)pos.Z+rand()%100/200.-0.25 ); - Particle *particle = new Particle( + new Particle( gamedef, smgr, player, - 0, + env, particlepos, velocity, acceleration, rand()%100/100., // expiration time visual_size, + true, ap); +} - all_particles.push_back(particle); +/* + ParticleSpawner +*/ + +ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, LocalPlayer *player, + u16 amount, float time, + v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, + float minexptime, float maxexptime, float minsize, float maxsize, + bool collisiondetection, AtlasPointer ap, u32 id) +{ + m_gamedef = gamedef; + m_smgr = smgr; + m_player = player; + m_amount = amount; + m_spawntime = time; + m_minpos = minpos; + m_maxpos = maxpos; + m_minvel = minvel; + m_maxvel = maxvel; + m_minacc = minacc; + m_maxacc = maxacc; + m_minexptime = minexptime; + m_maxexptime = maxexptime; + m_minsize = minsize; + m_maxsize = maxsize; + m_collisiondetection = collisiondetection; + m_ap = ap; + m_time = 0; + + for (u16 i = 0; i<=m_amount; i++) + { + float spawntime = (float)rand()/(float)RAND_MAX*m_spawntime; + m_spawntimes.push_back(spawntime); + } + + all_particlespawners.insert(std::pair<u32, ParticleSpawner*>(id, this)); +} + +ParticleSpawner::~ParticleSpawner() {} + +void ParticleSpawner::step(float dtime, ClientEnvironment &env) +{ + m_time += dtime; + + if (m_spawntime != 0) // Spawner exists for a predefined timespan + { + for(std::vector<float>::iterator i = m_spawntimes.begin(); + i != m_spawntimes.end();) + { + if ((*i) <= m_time && m_amount > 0) + { + m_amount--; + + v3f pos = random_v3f(m_minpos, m_maxpos); + v3f vel = random_v3f(m_minvel, m_maxvel); + v3f acc = random_v3f(m_minacc, m_maxacc); + float exptime = rand()/(float)RAND_MAX + *(m_maxexptime-m_minexptime) + +m_minexptime; + float size = rand()/(float)RAND_MAX + *(m_maxsize-m_minsize) + +m_minsize; + + new Particle( + m_gamedef, + m_smgr, + m_player, + env, + pos, + vel, + acc, + exptime, + size, + m_collisiondetection, + m_ap); + m_spawntimes.erase(i); + } + else + { + i++; + } + } + } + else // Spawner exists for an infinity timespan, spawn on a per-second base + { + for (int i = 0; i <= m_amount; i++) + { + if (rand()/(float)RAND_MAX < dtime) + { + v3f pos = random_v3f(m_minpos, m_maxpos); + v3f vel = random_v3f(m_minvel, m_maxvel); + v3f acc = random_v3f(m_minacc, m_maxacc); + float exptime = rand()/(float)RAND_MAX + *(m_maxexptime-m_minexptime) + +m_minexptime; + float size = rand()/(float)RAND_MAX + *(m_maxsize-m_minsize) + +m_minsize; + + new Particle( + m_gamedef, + m_smgr, + m_player, + env, + pos, + vel, + acc, + exptime, + size, + m_collisiondetection, + m_ap); + } + } + } +} + +void allparticlespawners_step (float dtime, ClientEnvironment &env) +{ + for(std::map<u32, ParticleSpawner*>::iterator i = + all_particlespawners.begin(); + i != all_particlespawners.end();) + { + if (i->second->get_expired()) + { + delete i->second; + all_particlespawners.erase(i++); + } + else + { + i->second->step(dtime, env); + i++; + } + } +} + +void delete_particlespawner (u32 id) +{ + if (all_particlespawners.find(id) != all_particlespawners.end()) + { + delete all_particlespawners.find(id)->second; + all_particlespawners.erase(id); + } +} + +void clear_particles () +{ + for(std::map<u32, ParticleSpawner*>::iterator i = + all_particlespawners.begin(); + i != all_particlespawners.end();) + { + delete i->second; + all_particlespawners.erase(i++); + } + + for(std::vector<Particle*>::iterator i = + all_particles.begin(); + i != all_particles.end();) + { + (*i)->remove(); + delete *i; + all_particles.erase(i); + } } diff --git a/src/particles.h b/src/particles.h index b317549e7..308da551f 100644 --- a/src/particles.h +++ b/src/particles.h @@ -35,12 +35,13 @@ class Particle : public scene::ISceneNode IGameDef* gamedef, scene::ISceneManager* mgr, LocalPlayer *player, - s32 id, + ClientEnvironment &env, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, + bool collisiondetection, AtlasPointer texture ); ~Particle(); @@ -69,6 +70,10 @@ class Particle : public scene::ISceneNode { return m_expiration < m_time; } private: + void updateLight(ClientEnvironment &env); + void updateVertices(); + + video::S3DVertex m_vertices[4]; float m_time; float m_expiration; @@ -87,12 +92,71 @@ private: float m_size; AtlasPointer m_ap; u8 m_light; + bool m_collisiondetection; +}; + +class ParticleSpawner +{ + public: + ParticleSpawner(IGameDef* gamedef, + scene::ISceneManager *smgr, + LocalPlayer *player, + u16 amount, + float time, + v3f minp, v3f maxp, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, + AtlasPointer ap, + u32 id); + + ~ParticleSpawner(); + + void step(float dtime, ClientEnvironment &env); + + bool get_expired () + { return (m_amount <= 0) && m_spawntime != 0; } + + private: + float m_time; + IGameDef *m_gamedef; + scene::ISceneManager *m_smgr; + LocalPlayer *m_player; + u16 m_amount; + float m_spawntime; + v3f m_minpos; + v3f m_maxpos; + v3f m_minvel; + v3f m_maxvel; + v3f m_minacc; + v3f m_maxacc; + float m_minexptime; + float m_maxexptime; + float m_minsize; + float m_maxsize; + AtlasPointer m_ap; + std::vector<float> m_spawntimes; + bool m_collisiondetection; }; void allparticles_step (float dtime, ClientEnvironment &env); +void allparticlespawners_step (float dtime, ClientEnvironment &env); + +void delete_particlespawner (u32 id); +void clear_particles (); + +void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]); + +void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]); -void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); -void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); -void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); +void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, + LocalPlayer *player, ClientEnvironment &env, v3s16 pos, + const TileSpec tiles[]); #endif diff --git a/src/porting.h b/src/porting.h index d7d107340..bcce96ef7 100644 --- a/src/porting.h +++ b/src/porting.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" // u32 #include "debug.h" #include "constants.h" +#include "gettime.h" #ifdef _MSC_VER #define SWPRINTF_CHARSTRING L"%S" @@ -153,18 +154,65 @@ bool threadSetPriority(threadid_t tid, int prio); */ #ifdef _WIN32 // Windows #include <windows.h> + + inline u32 getTimeS() + { + return GetTickCount() / 1000; + } + inline u32 getTimeMs() { return GetTickCount(); } + + inline u32 getTimeUs() + { + LARGE_INTEGER freq, t; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&t); + return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000.0); + } + + inline u32 getTimeNs() + { + LARGE_INTEGER freq, t; + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&t); + return (double)(t.QuadPart) / ((double)(freq.QuadPart) / 1000000000.0); + } + #else // Posix #include <sys/time.h> + #include <time.h> + + inline u32 getTimeS() + { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec; + } + inline u32 getTimeMs() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } + + inline u32 getTimeUs() + { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; + } + + inline u32 getTimeNs() + { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return ts.tv_sec * 1000000000 + ts.tv_nsec; + } + /*#include <sys/timeb.h> inline u32 getTimeMs() { @@ -174,6 +222,22 @@ bool threadSetPriority(threadid_t tid, int prio); }*/ #endif +inline u32 getTime(TimePrecision prec) +{ + switch (prec) { + case PRECISION_SECONDS: + return getTimeS(); + case PRECISION_MILLI: + return getTimeMs(); + case PRECISION_MICRO: + return getTimeUs(); + case PRECISION_NANO: + return getTimeNs(); + } + return 0; +} + + } // namespace porting #endif // PORTING_HEADER diff --git a/src/profiler.h b/src/profiler.h index b9fa22485..271ad70c1 100644 --- a/src/profiler.h +++ b/src/profiler.h @@ -45,21 +45,21 @@ public: JMutexAutoLock lock(m_mutex); { /* No average shall have been used; mark add used as -2 */ - core::map<std::string, int>::Node *n = m_avgcounts.find(name); - if(n == NULL) + std::map<std::string, int>::iterator n = m_avgcounts.find(name); + if(n == m_avgcounts.end()) m_avgcounts[name] = -2; else{ - if(n->getValue() == -1) - n->setValue(-2); - assert(n->getValue() == -2); + if(n->second == -1) + n->second = -2; + assert(n->second == -2); } } { - core::map<std::string, float>::Node *n = m_data.find(name); - if(n == NULL) + std::map<std::string, float>::iterator n = m_data.find(name); + if(n == m_data.end()) m_data[name] = value; else - n->setValue(n->getValue() + value); + n->second += value; } } @@ -67,35 +67,32 @@ public: { JMutexAutoLock lock(m_mutex); { - core::map<std::string, int>::Node *n = m_avgcounts.find(name); - if(n == NULL) + std::map<std::string, int>::iterator n = m_avgcounts.find(name); + if(n == m_avgcounts.end()) m_avgcounts[name] = 1; else{ /* No add shall have been used */ - assert(n->getValue() != -2); - if(n->getValue() <= 0) - n->setValue(1); - else - n->setValue(n->getValue() + 1); + assert(n->second != -2); + n->second = (std::max)(n->second, 0) + 1; } } { - core::map<std::string, float>::Node *n = m_data.find(name); - if(n == NULL) + std::map<std::string, float>::iterator n = m_data.find(name); + if(n == m_data.end()) m_data[name] = value; else - n->setValue(n->getValue() + value); + n->second += value; } } void clear() { JMutexAutoLock lock(m_mutex); - for(core::map<std::string, float>::Iterator - i = m_data.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, float>::iterator + i = m_data.begin(); + i != m_data.end(); ++i) { - i.getNode()->setValue(0); + i->second = 0; } m_avgcounts.clear(); } @@ -112,9 +109,9 @@ public: u32 minindex, maxindex; paging(m_data.size(), page, pagecount, minindex, maxindex); - for(core::map<std::string, float>::Iterator - i = m_data.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, float>::iterator + i = m_data.begin(); + i != m_data.end(); ++i) { if(maxindex == 0) break; @@ -126,12 +123,12 @@ public: continue; } - std::string name = i.getNode()->getKey(); + std::string name = i->first; int avgcount = 1; - core::map<std::string, int>::Node *n = m_avgcounts.find(name); - if(n){ - if(n->getValue() >= 1) - avgcount = n->getValue(); + std::map<std::string, int>::iterator n = m_avgcounts.find(name); + if(n != m_avgcounts.end()){ + if(n->second >= 1) + avgcount = n->second; } o<<" "<<name<<": "; s32 clampsize = 40; @@ -143,7 +140,7 @@ public: else o<<" "; } - o<<(i.getNode()->getValue() / avgcount); + o<<(i->second / avgcount); o<<std::endl; } } @@ -169,8 +166,8 @@ public: private: JMutex m_mutex; - core::map<std::string, float> m_data; - core::map<std::string, int> m_avgcounts; + std::map<std::string, float> m_data; + std::map<std::string, int> m_avgcounts; std::map<std::string, float> m_graphvalues; }; diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 7aa148fd6..80abffa2b 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -27,89 +27,29 @@ extern "C" { #include <lauxlib.h> } -#include "log.h" -#include "server.h" -#include "porting.h" -#include "filesys.h" -#include "serverobject.h" -#include "script.h" -#include "object_properties.h" -#include "content_sao.h" // For LuaEntitySAO and PlayerSAO -#include "itemdef.h" -#include "nodedef.h" -#include "biome.h" -#include "craftdef.h" -#include "main.h" // For g_settings #include "settings.h" // For accessing g_settings -#include "nodemetadata.h" -#include "mapblock.h" // For getNodeBlockPos -#include "content_nodemeta.h" -#include "tool.h" -#include "daynightratio.h" -#include "noise.h" // PseudoRandom for LuaPseudoRandom -#include "util/pointedthing.h" +#include "main.h" // For g_settings +#include "biome.h" +#include "emerge.h" +#include "script.h" #include "rollback.h" -#include "treegen.h" - -static void stackDump(lua_State *L, std::ostream &o) -{ - int i; - int top = lua_gettop(L); - for (i = 1; i <= top; i++) { /* repeat for each level */ - int t = lua_type(L, i); - switch (t) { - - case LUA_TSTRING: /* strings */ - o<<"\""<<lua_tostring(L, i)<<"\""; - break; - - case LUA_TBOOLEAN: /* booleans */ - o<<(lua_toboolean(L, i) ? "true" : "false"); - break; - - case LUA_TNUMBER: /* numbers */ { - char buf[10]; - snprintf(buf, 10, "%g", lua_tonumber(L, i)); - o<<buf; - break; } - - default: /* other values */ - o<<lua_typename(L, t); - break; - - } - o<<" "; - } - o<<std::endl; -} -static void realitycheck(lua_State *L) -{ - int top = lua_gettop(L); - if(top >= 30){ - dstream<<"Stack is over 30:"<<std::endl; - stackDump(L, dstream); - script_error(L, "Stack is over 30 (reality check)"); - } -} - -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 - } -}; +#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 { @@ -131,596 +71,150 @@ public: } }; -/* - Getters for stuff in main tables -*/ - -static 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; -} - -static 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; -} - -static 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 -} - -static 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 -} - -/* - Table field getters -*/ - -static bool getstringfield(lua_State *L, int table, - const char *fieldname, std::string &result) -{ - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isstring(L, -1)){ - size_t len = 0; - const char *ptr = lua_tolstring(L, -1, &len); - result.assign(ptr, len); - got = true; - } - lua_pop(L, 1); - return got; -} - -static bool getintfield(lua_State *L, int table, - const char *fieldname, int &result) -{ - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); - got = true; - } - lua_pop(L, 1); - return got; -} - -static bool getfloatfield(lua_State *L, int table, - const char *fieldname, float &result) +bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, + const std::string &modname) { - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isnumber(L, -1)){ - result = lua_tonumber(L, -1); - got = true; - } - lua_pop(L, 1); - return got; -} + ModNameStorer modnamestorer(L, modname); -static bool getboolfield(lua_State *L, int table, - const char *fieldname, bool &result) -{ - lua_getfield(L, table, fieldname); - bool got = false; - if(lua_isboolean(L, -1)){ - result = lua_toboolean(L, -1); - got = true; + if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz" + "0123456789_")){ + errorstream<<"Error loading mod \""<<modname + <<"\": modname does not follow naming conventions: " + <<"Only chararacters [a-z0-9_] are allowed."<<std::endl; + return false; } - lua_pop(L, 1); - return got; -} - -static std::string checkstringfield(lua_State *L, int table, - const char *fieldname) -{ - lua_getfield(L, table, fieldname); - std::string s = luaL_checkstring(L, -1); - lua_pop(L, 1); - return s; -} -static std::string getstringfield_default(lua_State *L, int table, - const char *fieldname, const std::string &default_) -{ - std::string result = default_; - getstringfield(L, table, fieldname, result); - return result; -} - -static int getintfield_default(lua_State *L, int table, - const char *fieldname, int default_) -{ - int result = default_; - getintfield(L, table, fieldname, result); - return result; -} - -static float getfloatfield_default(lua_State *L, int table, - const char *fieldname, float default_) -{ - float result = default_; - getfloatfield(L, table, fieldname, result); - return result; -} - -static bool getboolfield_default(lua_State *L, int table, - const char *fieldname, bool default_) -{ - bool result = default_; - getboolfield(L, table, fieldname, result); - return result; -} - -struct EnumString -{ - int num; - const char *str; -}; + bool success = false; -static bool string_to_enum(const EnumString *spec, int &result, - const std::string &str) -{ - const EnumString *esp = spec; - while(esp->str){ - if(str == std::string(esp->str)){ - result = esp->num; - return true; - } - esp++; + try{ + success = script_load(L, scriptpath.c_str()); } - return false; -} - -/*static 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++; + catch(LuaError &e){ + errorstream<<"Error loading mod \""<<modname + <<"\": "<<e.what()<<std::endl; } - return false; -}*/ - -static 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; -} - -static void setintfield(lua_State *L, int table, - const char *fieldname, int value) -{ - lua_pushinteger(L, value); - if(table < 0) - table -= 1; - lua_setfield(L, table, fieldname); -} -static void setfloatfield(lua_State *L, int table, - const char *fieldname, float value) -{ - lua_pushnumber(L, value); - if(table < 0) - table -= 1; - lua_setfield(L, table, fieldname); -} - -static void setboolfield(lua_State *L, int table, - const char *fieldname, bool value) -{ - lua_pushboolean(L, value); - if(table < 0) - table -= 1; - lua_setfield(L, table, fieldname); -} - -static 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<<script_get_backtrace(L)<<std::endl; - infostream<<"WARNING: field \""<<fieldname<<"\": " - <<message<<std::endl; - } - lua_pop(L, 1); + return success; } -/* - EnumString definitions -*/ - -struct EnumString es_ItemType[] = -{ - {ITEM_NONE, "none"}, - {ITEM_NODE, "node"}, - {ITEM_CRAFT, "craft"}, - {ITEM_TOOL, "tool"}, - {0, NULL}, -}; - -struct EnumString es_DrawType[] = -{ - {NDT_NORMAL, "normal"}, - {NDT_AIRLIKE, "airlike"}, - {NDT_LIQUID, "liquid"}, - {NDT_FLOWINGLIQUID, "flowingliquid"}, - {NDT_GLASSLIKE, "glasslike"}, - {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}, -}; - -struct EnumString es_CraftMethod[] = -{ - {CRAFT_METHOD_NORMAL, "normal"}, - {CRAFT_METHOD_COOKING, "cooking"}, - {CRAFT_METHOD_FUEL, "fuel"}, - {0, NULL}, -}; - -struct EnumString es_TileAnimationType[] = -{ - {TAT_NONE, "none"}, - {TAT_VERTICAL_FRAMES, "vertical_frames"}, - {0, NULL}, -}; - -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}, -}; +/*****************************************************************************/ +/* Auth */ +/*****************************************************************************/ /* - C struct <-> Lua table converter functions + Privileges */ - -static void push_v3f(lua_State *L, v3f p) -{ - lua_newtable(L); - lua_pushnumber(L, p.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, p.Z); - lua_setfield(L, -2, "z"); -} - -static v2s16 read_v2s16(lua_State *L, int index) -{ - v2s16 p; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - p.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - p.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - return p; -} - -static v2f read_v2f(lua_State *L, int index) -{ - v2f p; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - p.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - p.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - return p; -} - -static v3f read_v3f(lua_State *L, int index) -{ - v3f pos; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - pos.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - pos.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "z"); - pos.Z = lua_tonumber(L, -1); - lua_pop(L, 1); - return pos; -} - -static v3f check_v3f(lua_State *L, int index) -{ - v3f pos; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - pos.X = luaL_checknumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - pos.Y = luaL_checknumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "z"); - pos.Z = luaL_checknumber(L, -1); - lua_pop(L, 1); - return pos; -} - -static void pushFloatPos(lua_State *L, v3f p) +static void read_privileges(lua_State *L, int index, + std::set<std::string> &result) { - p /= BS; - push_v3f(L, p); + 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 v3f checkFloatPos(lua_State *L, int index) +static void get_auth_handler(lua_State *L) { - return check_v3f(L, index) * BS; + 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"); } -static void push_v3s16(lua_State *L, v3s16 p) +bool scriptapi_get_auth(lua_State *L, const std::string &playername, + std::string *dst_password, std::set<std::string> *dst_privs) { - lua_newtable(L); - lua_pushnumber(L, p.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, p.Z); - lua_setfield(L, -2, "z"); -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -static v3s16 read_v3s16(lua_State *L, int index) -{ - // Correct rounding at <0 - v3f pf = read_v3f(L, index); - return floatToInt(pf, 1.0); -} + 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)); -static v3s16 check_v3s16(lua_State *L, int index) -{ - // Correct rounding at <0 - v3f pf = check_v3f(L, index); - return floatToInt(pf, 1.0); -} + // nil = login not allowed + if(lua_isnil(L, -1)) + return false; + luaL_checktype(L, -1, LUA_TTABLE); -static 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"); -} + 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; -static 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_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 MapNode(ndef, name, param1, param2); -} -static video::SColor readARGB8(lua_State *L, int index) -{ - video::SColor color; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "a"); - if(lua_isnumber(L, -1)) - color.setAlpha(lua_tonumber(L, -1)); - lua_pop(L, 1); - lua_getfield(L, index, "r"); - color.setRed(lua_tonumber(L, -1)); - lua_pop(L, 1); - lua_getfield(L, index, "g"); - color.setGreen(lua_tonumber(L, -1)); - lua_pop(L, 1); - lua_getfield(L, index, "b"); - color.setBlue(lua_tonumber(L, -1)); - lua_pop(L, 1); - return color; + return true; } -static aabb3f read_aabb3f(lua_State *L, int index, f32 scale) +void scriptapi_create_auth(lua_State *L, const std::string &playername, + const std::string &password) { - aabb3f box; - if(lua_istable(L, index)){ - lua_rawgeti(L, index, 1); - box.MinEdge.X = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, index, 2); - box.MinEdge.Y = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, index, 3); - box.MinEdge.Z = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, index, 4); - box.MaxEdge.X = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, index, 5); - box.MaxEdge.Y = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, index, 6); - box.MaxEdge.Z = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - } - return box; -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -static std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale) -{ - std::vector<aabb3f> boxes; - if(lua_istable(L, index)){ - int n = lua_objlen(L, index); - // Check if it's a single box or a list of boxes - bool possibly_single_box = (n == 6); - for(int i = 1; i <= n && possibly_single_box; i++){ - lua_rawgeti(L, index, i); - if(!lua_isnumber(L, -1)) - possibly_single_box = false; - lua_pop(L, 1); - } - if(possibly_single_box){ - // Read a single box - boxes.push_back(read_aabb3f(L, index, scale)); - } else { - // Read a list of boxes - for(int i = 1; i <= n; i++){ - lua_rawgeti(L, index, i); - boxes.push_back(read_aabb3f(L, -1, scale)); - lua_pop(L, 1); - } - } - } - return boxes; + 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)); } -static NodeBox read_nodebox(lua_State *L, int index) +bool scriptapi_set_password(lua_State *L, const std::string &playername, + const std::string &password) { - 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; -} + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -/* - NoiseParams -*/ -static 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); - 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; + 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 */ -static void read_groups(lua_State *L, int index, +void read_groups(lua_State *L, int index, std::map<std::string, int> &result) { if (!lua_istable(L,index)) @@ -739,131 +233,27 @@ static void read_groups(lua_State *L, int index, } } -/* - Privileges -*/ -static void read_privileges(lua_State *L, int index, - std::set<std::string> &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); - } -} - -/* - ToolCapabilities -*/ - -static 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<<script_get_backtrace(L)<<std::endl; - infostream<<"WARNING: field \"maxwear\" is deprecated; " - <<"should replace with uses=1/maxwear"<<std::endl; - } - // Read "times" table - lua_getfield(L, table_groupcap, "times"); - if(lua_istable(L, -1)){ - int table_times = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table_times) != 0){ - // key at index -2 and value at index -1 - int rating = luaL_checkinteger(L, -2); - float time = luaL_checknumber(L, -1); - groupcap.times[rating] = time; - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } - lua_pop(L, 1); - // Insert groupcap into toolcap - toolcap.groupcaps[groupname] = groupcap; - } - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } - lua_pop(L, 1); - return toolcap; -} - -static void set_tool_capabilities(lua_State *L, int table, - const ToolCapabilities &toolcap) +struct EnumString es_BiomeTerrainType[] = { - setfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval); - setintfield(L, table, "max_drop_level", toolcap.max_drop_level); - // Create groupcaps table - lua_newtable(L); - // For each groupcap - for(std::map<std::string, ToolGroupCap>::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<int, float>::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"); -} + {BIOME_TERRAIN_NORMAL, "normal"}, + {BIOME_TERRAIN_LIQUID, "liquid"}, + {BIOME_TERRAIN_NETHER, "nether"}, + {BIOME_TERRAIN_AETHER, "aether"}, + {BIOME_TERRAIN_FLAT, "flat"}, + {0, NULL}, +}; -static void push_tool_capabilities(lua_State *L, - const ToolCapabilities &prop) +struct EnumString es_OreType[] = { - lua_newtable(L); - set_tool_capabilities(L, -1, prop); -} + {ORE_SCATTER, "scatter"}, + {ORE_SHEET, "sheet"}, + {ORE_CLAYLIKE, "claylike"}, + {0, NULL}, +}; +/*****************************************************************************/ +/* Parameters */ +/*****************************************************************************/ /* DigParams */ @@ -902,3692 +292,279 @@ static void push_hit_params(lua_State *L, } /* - PointedThing -*/ - -static void push_pointed_thing(lua_State *L, const PointedThing& pointed) -{ - lua_newtable(L); - if(pointed.type == POINTEDTHING_NODE) - { - lua_pushstring(L, "node"); - lua_setfield(L, -2, "type"); - push_v3s16(L, pointed.node_undersurface); - lua_setfield(L, -2, "under"); - push_v3s16(L, pointed.node_abovesurface); - lua_setfield(L, -2, "above"); - } - else if(pointed.type == POINTEDTHING_OBJECT) - { - lua_pushstring(L, "object"); - lua_setfield(L, -2, "type"); - objectref_get(L, pointed.object_id); - lua_setfield(L, -2, "ref"); - } - else - { - lua_pushstring(L, "nothing"); - lua_setfield(L, -2, "type"); - } -} - -/* - SimpleSoundSpec -*/ - -static 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); - } -} - -/* - ObjectProperties -*/ - -static 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); -} - -/* - ItemDefinition -*/ - -static ItemDefinition read_item_definition(lua_State *L, int index, - ItemDefinition default_def = ItemDefinition()) -{ - 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); - - // 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; -} - -/* - TileDef + ServerSoundParams */ -static TileDef read_tiledef(lua_State *L, int index) +static void read_server_sound_params(lua_State *L, int index, + ServerSoundParams ¶ms) { 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); + // 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); - } - - return tiledef; -} - -/* - ContentFeatures -*/ - -static 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_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); - - f.alpha = getintfield_default(L, index, "alpha", 255); - - /* 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; -} - -/* - Inventory stuff -*/ - -static ItemStack read_item(lua_State *L, int index); -static std::vector<ItemStack> read_items(lua_State *L, int index); -// creates a table of ItemStacks -static void push_items(lua_State *L, const std::vector<ItemStack> &items); - -static void inventory_set_list_from_lua(Inventory *inv, const char *name, - lua_State *L, int tableindex, int forcesize=-1) -{ - 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<ItemStack> items = read_items(L, tableindex); - int listsize = (forcesize != -1) ? forcesize : items.size(); - InventoryList *invlist = inv->addList(name, listsize); - int index = 0; - for(std::vector<ItemStack>::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++; - } -} - -static 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; + params.max_hear_distance = BS*getfloatfield_default(L, index, + "max_hear_distance", params.max_hear_distance/BS); + getboolfield(L, index, "loop", params.loop); } - std::vector<ItemStack> items; - for(u32 i=0; i<invlist->getSize(); i++) - items.push_back(invlist->getItem(i)); - push_items(L, items); } -/* - Helpful macros for userdata classes -*/ - -#define method(class, name) {#name, class::l_##name} - -/* - LuaItemStack -*/ - -class LuaItemStack -{ -private: - ItemStack m_stack; - - static const char className[]; - static const luaL_reg methods[]; - - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) - { - LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - // is_empty(self) -> true/false - static int l_is_empty(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushboolean(L, item.empty()); - return 1; - } - - // get_name(self) -> string - static int l_get_name(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushstring(L, item.name.c_str()); - return 1; - } - - // get_count(self) -> number - static int l_get_count(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushinteger(L, item.count); - return 1; - } - - // get_wear(self) -> number - static int l_get_wear(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushinteger(L, item.wear); - return 1; - } - - // get_metadata(self) -> string - static int l_get_metadata(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); - return 1; - } - - // clear(self) -> true - static int l_clear(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - o->m_stack.clear(); - lua_pushboolean(L, true); - return 1; - } - - // replace(self, itemstack or itemstring or table or nil) -> true - static int l_replace(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - o->m_stack = read_item(L, 2); - lua_pushboolean(L, true); - return 1; - } - - // to_string(self) -> string - static int l_to_string(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - std::string itemstring = o->m_stack.getItemString(); - lua_pushstring(L, itemstring.c_str()); - return 1; - } - - // to_table(self) -> table or nil - static int l_to_table(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - const ItemStack &item = o->m_stack; - if(item.empty()) - { - lua_pushnil(L); - } - else - { - lua_newtable(L); - lua_pushstring(L, item.name.c_str()); - lua_setfield(L, -2, "name"); - lua_pushinteger(L, item.count); - lua_setfield(L, -2, "count"); - lua_pushinteger(L, item.wear); - lua_setfield(L, -2, "wear"); - lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); - lua_setfield(L, -2, "metadata"); - } - return 1; - } - - // get_stack_max(self) -> number - static int l_get_stack_max(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushinteger(L, item.getStackMax(get_server(L)->idef())); - return 1; - } - - // get_free_space(self) -> number - static int l_get_free_space(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - lua_pushinteger(L, item.freeSpace(get_server(L)->idef())); - return 1; - } - - // is_known(self) -> true/false - // Checks if the item is defined. - static int l_is_known(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - bool is_known = item.isKnown(get_server(L)->idef()); - lua_pushboolean(L, is_known); - return 1; - } - - // get_definition(self) -> table - // Returns the item definition table from minetest.registered_items, - // or a fallback one (name="unknown") - static int l_get_definition(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - - // Get minetest.registered_items[name] - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_items"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, item.name.c_str()); - if(lua_isnil(L, -1)) - { - lua_pop(L, 1); - lua_getfield(L, -1, "unknown"); - } - return 1; - } - - // get_tool_capabilities(self) -> table - // Returns the effective tool digging properties. - // Returns those of the hand ("") if this item has none associated. - static int l_get_tool_capabilities(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - const ToolCapabilities &prop = - item.getToolCapabilities(get_server(L)->idef()); - push_tool_capabilities(L, prop); - return 1; - } - - // add_wear(self, amount) -> true/false - // The range for "amount" is [0,65535]. Wear is only added if the item - // is a tool. Adding wear might destroy the item. - // Returns true if the item is (or was) a tool. - static int l_add_wear(lua_State *L) - { - 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()); - lua_pushboolean(L, result); - return 1; - } - - // add_item(self, itemstack or itemstring or table or nil) -> itemstack - // Returns leftover item stack - static int l_add_item(lua_State *L) - { - 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()); - create(L, leftover); - return 1; - } - - // item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack - // First return value is true iff the new item fits fully into the stack - // Second return value is the would-be-left-over item stack - static int l_item_fits(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - ItemStack newitem = read_item(L, 2); - ItemStack restitem; - bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef()); - lua_pushboolean(L, fits); // first return value - create(L, restitem); // second return value - return 2; - } - - // take_item(self, takecount=1) -> itemstack - static int l_take_item(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - u32 takecount = 1; - if(!lua_isnone(L, 2)) - takecount = luaL_checkinteger(L, 2); - ItemStack taken = item.takeItem(takecount); - create(L, taken); - return 1; - } - - // peek_item(self, peekcount=1) -> itemstack - static int l_peek_item(lua_State *L) - { - LuaItemStack *o = checkobject(L, 1); - ItemStack &item = o->m_stack; - u32 peekcount = 1; - if(!lua_isnone(L, 2)) - peekcount = lua_tointeger(L, 2); - ItemStack peekaboo = item.peekItem(peekcount); - create(L, peekaboo); - return 1; - } - -public: - LuaItemStack(const ItemStack &item): - m_stack(item) - { - } - - ~LuaItemStack() - { - } - - const ItemStack& getItem() const - { - return m_stack; - } - ItemStack& getItem() - { - return m_stack; - } - - // LuaItemStack(itemstack or itemstring or table or nil) - // Creates an LuaItemStack and leaves it on top of stack - static int create_object(lua_State *L) - { - ItemStack item = read_item(L, 1); - LuaItemStack *o = new LuaItemStack(item); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - return 1; - } - // Not callable from Lua - static int create(lua_State *L, const ItemStack &item) - { - LuaItemStack *o = new LuaItemStack(item); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - return 1; - } - - static LuaItemStack* 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 *(LuaItemStack**)ud; // unbox pointer - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); +/*****************************************************************************/ +/* callbacks */ +/*****************************************************************************/ - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil)) - lua_register(L, className, create_object); - } -}; -const char LuaItemStack::className[] = "ItemStack"; -const luaL_reg LuaItemStack::methods[] = { - method(LuaItemStack, is_empty), - method(LuaItemStack, get_name), - method(LuaItemStack, get_count), - method(LuaItemStack, get_wear), - method(LuaItemStack, get_metadata), - method(LuaItemStack, clear), - method(LuaItemStack, replace), - method(LuaItemStack, to_string), - method(LuaItemStack, to_table), - method(LuaItemStack, get_stack_max), - method(LuaItemStack, get_free_space), - method(LuaItemStack, is_known), - method(LuaItemStack, get_definition), - method(LuaItemStack, get_tool_capabilities), - method(LuaItemStack, add_wear), - method(LuaItemStack, add_item), - method(LuaItemStack, item_fits), - method(LuaItemStack, take_item), - method(LuaItemStack, peek_item), - {0,0} -}; - -static ItemStack read_item(lua_State *L, int index) +// 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) { - if(index < 0) - index = lua_gettop(L) + 1 + index; + // 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: + // ... <return value = nil> <table> <arg#1> <arg#2> ... <arg#n> - 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" - <<": "<<itemstring<<std::endl; - return ItemStack(); - } - } - else if(lua_istable(L, index)) - { - // Convert from table - IItemDefManager *idef = get_server(L)->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"); - } -} + int rv = lua_gettop(L) - nargs - 1; + int table = rv + 1; + int arg = table + 1; -static std::vector<ItemStack> read_items(lua_State *L, int index) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; + luaL_checktype(L, table, LUA_TTABLE); - std::vector<ItemStack> items; - luaL_checktype(L, index, LUA_TTABLE); + // Foreach lua_pushnil(L); - while(lua_next(L, index) != 0){ + bool first_loop = true; + while(lua_next(L, table) != 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); - } - return items; -} - -// creates a table of ItemStacks -static void push_items(lua_State *L, const std::vector<ItemStack> &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<items.size(); i++){ - ItemStack item = items[i]; - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - LuaItemStack::create(L, item); - if(lua_pcall(L, 2, 0, 0)) + 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)); - } - lua_remove(L, -2); // Remove table - lua_remove(L, -2); // Remove insert -} -/* - InvRef -*/ - -class InvRef -{ -private: - InventoryLocation m_loc; - - static const char className[]; - static const luaL_reg methods[]; - - static InvRef *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 *(InvRef**)ud; // unbox pointer - } - - static Inventory* getinv(lua_State *L, InvRef *ref) - { - return get_server(L)->getInventory(ref->m_loc); - } - - static InventoryList* getlist(lua_State *L, InvRef *ref, - const char *listname) - { - Inventory *inv = getinv(L, ref); - if(!inv) - return NULL; - return inv->getList(listname); - } - - static void reportInventoryChange(lua_State *L, InvRef *ref) - { - // Inform other things that the inventory has changed - get_server(L)->setInventoryModified(ref->m_loc); - } - - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) { - InvRef *o = *(InvRef **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - // is_empty(self, listname) -> true/false - static int l_is_empty(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - InventoryList *list = getlist(L, ref, listname); - if(list && list->getUsedSlots() > 0){ - lua_pushboolean(L, false); - } else { - lua_pushboolean(L, true); - } - return 1; - } - - // get_size(self, listname) - static int l_get_size(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - InventoryList *list = getlist(L, ref, listname); - if(list){ - lua_pushinteger(L, list->getSize()); - } else { - lua_pushinteger(L, 0); - } - return 1; - } - - // get_width(self, listname) - static int l_get_width(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - InventoryList *list = getlist(L, ref, listname); - if(list){ - lua_pushinteger(L, list->getWidth()); + // 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 { - lua_pushinteger(L, 0); + // 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); } - return 1; - } - // set_size(self, listname, size) - static int l_set_size(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - int newsize = luaL_checknumber(L, 3); - Inventory *inv = getinv(L, ref); - if(newsize == 0){ - inv->deleteList(listname); - reportInventoryChange(L, ref); - return 0; - } - InventoryList *list = inv->getList(listname); - if(list){ - list->setSize(newsize); - } else { - list = inv->addList(listname, newsize); - } - reportInventoryChange(L, ref); - return 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; - // set_width(self, listname, size) - static int l_set_width(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - int newwidth = luaL_checknumber(L, 3); - Inventory *inv = getinv(L, ref); - InventoryList *list = inv->getList(listname); - if(list){ - list->setWidth(newwidth); - } else { - return 0; - } - reportInventoryChange(L, ref); - return 0; + // value removed, keep key for next iteration } - // get_stack(self, listname, i) -> itemstack - static int l_get_stack(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - int i = luaL_checknumber(L, 3) - 1; - InventoryList *list = getlist(L, ref, listname); - ItemStack item; - if(list != NULL && i >= 0 && i < (int) list->getSize()) - item = list->getItem(i); - LuaItemStack::create(L, item); - return 1; - } + // Remove stuff from stack, leaving only the return value + lua_settop(L, rv); - // set_stack(self, listname, i, stack) -> true/false - static int l_set_stack(lua_State *L) - { - 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); - InventoryList *list = getlist(L, ref, listname); - if(list != NULL && i >= 0 && i < (int) list->getSize()){ - list->changeItem(i, newitem); - reportInventoryChange(L, ref); + // 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 { - lua_pushboolean(L, false); - } - return 1; - } - - // get_list(self, listname) -> list or nil - static int l_get_list(lua_State *L) - { - 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); - return 1; - } - - // set_list(self, listname, list) - static int l_set_list(lua_State *L) - { - 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()); - else - inventory_set_list_from_lua(inv, listname, L, 3); - reportInventoryChange(L, ref); - return 0; - } - - // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack - // Returns the leftover stack - static int l_add_item(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); - InventoryList *list = getlist(L, ref, listname); - if(list){ - ItemStack leftover = list->addItem(item); - if(leftover.count != item.count) - reportInventoryChange(L, ref); - LuaItemStack::create(L, leftover); - } else { - LuaItemStack::create(L, item); - } - return 1; - } - - // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false - // Returns true if the item completely fits into the list - static int l_room_for_item(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); - InventoryList *list = getlist(L, ref, listname); - if(list){ - lua_pushboolean(L, list->roomForItem(item)); - } else { - lua_pushboolean(L, false); } - return 1; - } - - // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false - // Returns true if the list contains the given count of the given item name - static int l_contains_item(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); - InventoryList *list = getlist(L, ref, listname); - if(list){ - lua_pushboolean(L, list->containsItem(item)); - } else { + else if(mode == RUN_CALLBACKS_MODE_OR || + mode == RUN_CALLBACKS_MODE_OR_SC){ + lua_pop(L, 1); lua_pushboolean(L, false); } - return 1; - } - - // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack - // Returns the items that were actually removed - static int l_remove_item(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const char *listname = luaL_checkstring(L, 2); - ItemStack item = read_item(L, 3); - InventoryList *list = getlist(L, ref, listname); - if(list){ - ItemStack removed = list->removeItem(item); - if(!removed.empty()) - reportInventoryChange(L, ref); - LuaItemStack::create(L, removed); - } else { - LuaItemStack::create(L, ItemStack()); - } - return 1; - } - - // get_location() -> location (like minetest.get_inventory(location)) - static int l_get_location(lua_State *L) - { - InvRef *ref = checkobject(L, 1); - const InventoryLocation &loc = ref->m_loc; - switch(loc.type){ - case InventoryLocation::PLAYER: - lua_newtable(L); - lua_pushstring(L, "player"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, loc.name.c_str()); - lua_setfield(L, -2, "name"); - return 1; - case InventoryLocation::NODEMETA: - lua_newtable(L); - lua_pushstring(L, "nodemeta"); - lua_setfield(L, -2, "type"); - push_v3s16(L, loc.p); - lua_setfield(L, -2, "name"); - return 1; - case InventoryLocation::DETACHED: - lua_newtable(L); - lua_pushstring(L, "detached"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, loc.name.c_str()); - lua_setfield(L, -2, "name"); - return 1; - case InventoryLocation::UNDEFINED: - case InventoryLocation::CURRENT_PLAYER: - break; - } - lua_newtable(L); - lua_pushstring(L, "undefined"); - lua_setfield(L, -2, "type"); - return 1; } +} -public: - InvRef(const InventoryLocation &loc): - m_loc(loc) - { - } - - ~InvRef() - { - } - - // Creates an InvRef 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, const InventoryLocation &loc) - { - InvRef *o = new InvRef(loc); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - } - static void createPlayer(lua_State *L, Player *player) - { - InventoryLocation loc; - loc.setPlayer(player->getName()); - create(L, loc); - } - static void createNodeMeta(lua_State *L, v3s16 p) - { - InventoryLocation loc; - loc.setNodeMeta(p); - create(L, loc); - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Cannot be created from Lua - //lua_register(L, className, create_object); - } -}; -const char InvRef::className[] = "InvRef"; -const luaL_reg InvRef::methods[] = { - method(InvRef, is_empty), - method(InvRef, get_size), - method(InvRef, set_size), - method(InvRef, get_width), - method(InvRef, set_width), - method(InvRef, get_stack), - method(InvRef, set_stack), - method(InvRef, get_list), - method(InvRef, set_list), - method(InvRef, add_item), - method(InvRef, room_for_item), - method(InvRef, contains_item), - method(InvRef, remove_item), - method(InvRef, get_location), - {0,0} -}; - -/* - NodeMetaRef -*/ - -class NodeMetaRef +bool scriptapi_on_chat_message(lua_State *L, const std::string &name, + const std::string &message) { -private: - v3s16 m_p; - ServerEnvironment *m_env; - - static const char className[]; - static const luaL_reg methods[]; - - static NodeMetaRef *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 *(NodeMetaRef**)ud; // unbox pointer - } - - static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create) - { - NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p); - if(meta == NULL && auto_create) - { - meta = new NodeMetadata(ref->m_env->getGameDef()); - ref->m_env->getMap().setNodeMetadata(ref->m_p, meta); - } - return meta; - } - - static void reportMetadataChange(NodeMetaRef *ref) - { - // NOTE: This same code is in rollback_interface.cpp - // Inform other things that the metadata has changed - v3s16 blockpos = getNodeBlockPos(ref->m_p); - MapEditEvent event; - event.type = MEET_BLOCK_NODE_METADATA_CHANGED; - event.p = blockpos; - ref->m_env->getMap().dispatchEvent(&event); - // Set the block to be saved - MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos); - if(block) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - "NodeMetaRef::reportMetadataChange"); - } - - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) { - NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - // get_string(self, name) - static int l_get_string(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - std::string name = luaL_checkstring(L, 2); - - NodeMetadata *meta = getmeta(ref, false); - if(meta == NULL){ - lua_pushlstring(L, "", 0); - return 1; - } - std::string str = meta->getString(name); - lua_pushlstring(L, str.c_str(), str.size()); - return 1; - } - - // set_string(self, name, var) - static int l_set_string(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - std::string name = luaL_checkstring(L, 2); - size_t len = 0; - const char *s = lua_tolstring(L, 3, &len); - std::string str(s, len); - - NodeMetadata *meta = getmeta(ref, !str.empty()); - if(meta == NULL || str == meta->getString(name)) - return 0; - meta->setString(name, str); - reportMetadataChange(ref); - return 0; - } - - // get_int(self, name) - static int l_get_int(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - - NodeMetadata *meta = getmeta(ref, false); - if(meta == NULL){ - lua_pushnumber(L, 0); - return 1; - } - std::string str = meta->getString(name); - lua_pushnumber(L, stoi(str)); - return 1; - } - - // set_int(self, name, var) - static int l_set_int(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - int a = lua_tointeger(L, 3); - std::string str = itos(a); - - NodeMetadata *meta = getmeta(ref, true); - if(meta == NULL || str == meta->getString(name)) - return 0; - meta->setString(name, str); - reportMetadataChange(ref); - return 0; - } - - // get_float(self, name) - static int l_get_float(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - - NodeMetadata *meta = getmeta(ref, false); - if(meta == NULL){ - lua_pushnumber(L, 0); - return 1; - } - std::string str = meta->getString(name); - lua_pushnumber(L, stof(str)); - return 1; - } - - // set_float(self, name, var) - static int l_set_float(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - float a = lua_tonumber(L, 3); - std::string str = ftos(a); - - NodeMetadata *meta = getmeta(ref, true); - if(meta == NULL || str == meta->getString(name)) - return 0; - meta->setString(name, str); - reportMetadataChange(ref); - return 0; - } - - // get_inventory(self) - static int l_get_inventory(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - getmeta(ref, true); // try to ensure the metadata exists - InvRef::createNodeMeta(L, ref->m_p); - return 1; - } - - // to_table(self) - static int l_to_table(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - - NodeMetadata *meta = getmeta(ref, true); - if(meta == NULL){ - lua_pushnil(L); - return 1; - } - lua_newtable(L); - // fields - lua_newtable(L); - { - std::map<std::string, std::string> fields = meta->getStrings(); - for(std::map<std::string, std::string>::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; - lua_pushlstring(L, name.c_str(), name.size()); - lua_pushlstring(L, value.c_str(), value.size()); - lua_settable(L, -3); - } - } - lua_setfield(L, -2, "fields"); - // inventory - lua_newtable(L); - Inventory *inv = meta->getInventory(); - if(inv){ - std::vector<const InventoryList*> lists = inv->getLists(); - for(std::vector<const InventoryList*>::const_iterator - i = lists.begin(); i != lists.end(); i++){ - inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L); - lua_setfield(L, -2, (*i)->getName().c_str()); - } - } - lua_setfield(L, -2, "inventory"); - return 1; - } - - // from_table(self, table) - static int l_from_table(lua_State *L) - { - NodeMetaRef *ref = checkobject(L, 1); - int base = 2; - - if(lua_isnil(L, base)){ - // No metadata - ref->m_env->getMap().removeNodeMetadata(ref->m_p); - lua_pushboolean(L, true); - return 1; - } - - // Has metadata; clear old one first - ref->m_env->getMap().removeNodeMetadata(ref->m_p); - // Create new metadata - NodeMetadata *meta = getmeta(ref, true); - // Set fields - lua_getfield(L, base, "fields"); - int fieldstable = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, fieldstable) != 0){ - // key at index -2 and value at index -1 - std::string name = lua_tostring(L, -2); - size_t cl; - const char *cs = lua_tolstring(L, -1, &cl); - std::string value(cs, cl); - meta->setString(name, value); - lua_pop(L, 1); // removes value, keeps key for next iteration - } - // Set inventory - Inventory *inv = meta->getInventory(); - lua_getfield(L, base, "inventory"); - int inventorytable = lua_gettop(L); - lua_pushnil(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); - lua_pop(L, 1); // removes value, keeps key for next iteration - } - reportMetadataChange(ref); - lua_pushboolean(L, true); - return 1; - } - -public: - NodeMetaRef(v3s16 p, ServerEnvironment *env): - m_p(p), - m_env(env) - { - } - - ~NodeMetaRef() - { - } - - // Creates an NodeMetaRef 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, v3s16 p, ServerEnvironment *env) - { - NodeMetaRef *o = new NodeMetaRef(p, env); - //infostream<<"NodeMetaRef::create: o="<<o<<std::endl; - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Cannot be created from Lua - //lua_register(L, className, create_object); - } -}; -const char NodeMetaRef::className[] = "NodeMetaRef"; -const luaL_reg NodeMetaRef::methods[] = { - method(NodeMetaRef, get_string), - method(NodeMetaRef, set_string), - method(NodeMetaRef, get_int), - method(NodeMetaRef, set_int), - method(NodeMetaRef, get_float), - method(NodeMetaRef, set_float), - method(NodeMetaRef, get_inventory), - method(NodeMetaRef, to_table), - method(NodeMetaRef, from_table), - {0,0} -}; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -/* - ObjectRef -*/ + // 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; +} -class ObjectRef +void scriptapi_on_shutdown(lua_State *L) { -private: - ServerActiveObject *m_object; - - static const char className[]; - static const luaL_reg methods[]; -public: - static ObjectRef *checkobject(lua_State *L, int narg) - { - luaL_checktype(L, narg, LUA_TUSERDATA); - void *ud = luaL_checkudata(L, narg, className); - if(!ud) luaL_typerror(L, narg, className); - return *(ObjectRef**)ud; // unbox pointer - } - - static ServerActiveObject* getobject(ObjectRef *ref) - { - ServerActiveObject *co = ref->m_object; - return co; - } -private: - static LuaEntitySAO* getluaobject(ObjectRef *ref) - { - ServerActiveObject *obj = getobject(ref); - if(obj == NULL) - return NULL; - if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) - return NULL; - return (LuaEntitySAO*)obj; - } - - static PlayerSAO* getplayersao(ObjectRef *ref) - { - ServerActiveObject *obj = getobject(ref); - if(obj == NULL) - return NULL; - if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) - return NULL; - return (PlayerSAO*)obj; - } - - static Player* getplayer(ObjectRef *ref) - { - PlayerSAO *playersao = getplayersao(ref); - if(playersao == NULL) - return NULL; - return playersao->getPlayer(); - } - - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) { - ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1)); - //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl; - delete o; - return 0; - } - - // remove(self) - static int l_remove(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl; - co->m_removed = true; - return 0; - } - - // getpos(self) - // returns: {x=num, y=num, z=num} - static int l_getpos(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - v3f pos = co->getBasePosition() / BS; - lua_newtable(L); - lua_pushnumber(L, pos.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, pos.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, pos.Z); - lua_setfield(L, -2, "z"); - return 1; - } - - // setpos(self, pos) - static int l_setpos(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - //LuaEntitySAO *co = getluaobject(ref); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // Do it - co->setPos(pos); - return 0; - } - - // moveto(self, pos, continuous=false) - static int l_moveto(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - //LuaEntitySAO *co = getluaobject(ref); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // continuous - bool continuous = lua_toboolean(L, 3); - // Do it - co->moveTo(pos, continuous); - return 0; - } - - // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) - static int l_punch(lua_State *L) - { - 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; - 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); - dir.normalize(); - // Do it - co->punch(dir, &toolcap, puncher, time_from_last_punch); - return 0; - } - - // right_click(self, clicker); clicker = an another ObjectRef - static int l_right_click(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ObjectRef *ref2 = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); - ServerActiveObject *co2 = getobject(ref2); - if(co == NULL) return 0; - if(co2 == NULL) return 0; - // Do it - co->rightClick(co2); - return 0; - } - - // set_hp(self, hp) - // hp = number of hitpoints (2 * number of hearts) - // returns: nil - static int l_set_hp(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - luaL_checknumber(L, 2); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - int hp = lua_tonumber(L, 2); - /*infostream<<"ObjectRef::l_set_hp(): id="<<co->getId() - <<" hp="<<hp<<std::endl;*/ - // Do it - co->setHP(hp); - // Return - return 0; - } - - // get_hp(self) - // returns: number of hitpoints (2 * number of hearts) - // 0 if not applicable to this type of object - static int l_get_hp(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL){ - // Default hp is 1 - lua_pushnumber(L, 1); - return 1; - } - int hp = co->getHP(); - /*infostream<<"ObjectRef::l_get_hp(): id="<<co->getId() - <<" hp="<<hp<<std::endl;*/ - // Return - lua_pushnumber(L, hp); - return 1; - } - - // get_inventory(self) - static int l_get_inventory(lua_State *L) - { - 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) - InvRef::create(L, loc); - else - lua_pushnil(L); // An object may have no inventory (nil) - return 1; - } - - // get_wield_list(self) - static int l_get_wield_list(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - lua_pushstring(L, co->getWieldList().c_str()); - return 1; - } - - // get_wield_index(self) - static int l_get_wield_index(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - lua_pushinteger(L, co->getWieldIndex() + 1); - return 1; - } - - // get_wielded_item(self) - static int l_get_wielded_item(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL){ - // Empty ItemStack - LuaItemStack::create(L, ItemStack()); - return 1; - } - // Do it - LuaItemStack::create(L, co->getWieldedItem()); - return 1; - } - - // set_wielded_item(self, itemstack or itemstring or table or nil) - static int l_set_wielded_item(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - ItemStack item = read_item(L, 2); - bool success = co->setWieldedItem(item); - lua_pushboolean(L, success); - return 1; - } - - // set_armor_groups(self, groups) - static int l_set_armor_groups(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - ItemGroupList groups; - read_groups(L, 2, groups); - co->setArmorGroups(groups); - return 0; - } - - // set_animation(self, frame_range, frame_speed, frame_blend) - static int l_set_animation(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - v2f frames = v2f(1, 1); - if(!lua_isnil(L, 2)) - frames = read_v2f(L, 2); - float frame_speed = 15; - if(!lua_isnil(L, 3)) - frame_speed = lua_tonumber(L, 3); - float frame_blend = 0; - if(!lua_isnil(L, 4)) - frame_blend = lua_tonumber(L, 4); - co->setAnimation(frames, frame_speed, frame_blend); - return 0; - } - - // set_bone_position(self, std::string bone, v3f position, v3f rotation) - static int l_set_bone_position(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - std::string bone = ""; - if(!lua_isnil(L, 2)) - bone = lua_tostring(L, 2); - v3f position = v3f(0, 0, 0); - if(!lua_isnil(L, 3)) - position = read_v3f(L, 3); - v3f rotation = v3f(0, 0, 0); - if(!lua_isnil(L, 4)) - rotation = read_v3f(L, 4); - co->setBonePosition(bone, position, rotation); - return 0; - } - - // set_attach(self, parent, bone, position, rotation) - static int l_set_attach(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ObjectRef *parent_ref = checkobject(L, 2); - ServerActiveObject *co = getobject(ref); - ServerActiveObject *parent = getobject(parent_ref); - if(co == NULL) return 0; - if(parent == NULL) return 0; - // Do it - std::string bone = ""; - if(!lua_isnil(L, 3)) - bone = lua_tostring(L, 3); - v3f position = v3f(0, 0, 0); - if(!lua_isnil(L, 4)) - position = read_v3f(L, 4); - v3f rotation = v3f(0, 0, 0); - if(!lua_isnil(L, 5)) - rotation = read_v3f(L, 5); - co->setAttachment(parent->getId(), bone, position, rotation); - return 0; - } - - // set_detach(self) - static int l_set_detach(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - // Do it - co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0)); - return 0; - } - - // set_properties(self, properties) - static int l_set_properties(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerActiveObject *co = getobject(ref); - if(co == NULL) return 0; - ObjectProperties *prop = co->accessObjectProperties(); - if(!prop) - return 0; - read_object_properties(L, 2, prop); - co->notifyObjectPropertiesModified(); - return 0; - } - - /* LuaEntitySAO-only */ - - // setvelocity(self, {x=num, y=num, z=num}) - static int l_setvelocity(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - v3f pos = checkFloatPos(L, 2); - // Do it - co->setVelocity(pos); - return 0; - } - - // getvelocity(self) - static int l_getvelocity(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - v3f v = co->getVelocity(); - pushFloatPos(L, v); - return 1; - } - - // setacceleration(self, {x=num, y=num, z=num}) - static int l_setacceleration(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // pos - v3f pos = checkFloatPos(L, 2); - // Do it - co->setAcceleration(pos); - return 0; - } - - // getacceleration(self) - static int l_getacceleration(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - v3f v = co->getAcceleration(); - pushFloatPos(L, v); - return 1; - } - - // setyaw(self, radians) - static int l_setyaw(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - float yaw = luaL_checknumber(L, 2) * core::RADTODEG; - // Do it - co->setYaw(yaw); - return 0; - } - - // getyaw(self) - static int l_getyaw(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - float yaw = co->getYaw() * core::DEGTORAD; - lua_pushnumber(L, yaw); - return 1; - } - - // settexturemod(self, mod) - static int l_settexturemod(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - std::string mod = luaL_checkstring(L, 2); - co->setTextureMod(mod); - return 0; - } - - // setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, - // select_horiz_by_yawpitch=false) - static int l_setsprite(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - v2s16 p(0,0); - if(!lua_isnil(L, 2)) - p = read_v2s16(L, 2); - int num_frames = 1; - if(!lua_isnil(L, 3)) - num_frames = lua_tonumber(L, 3); - float framelength = 0.2; - if(!lua_isnil(L, 4)) - framelength = lua_tonumber(L, 4); - bool select_horiz_by_yawpitch = false; - if(!lua_isnil(L, 5)) - select_horiz_by_yawpitch = lua_toboolean(L, 5); - co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch); - return 0; - } - - // DEPRECATED - // get_entity_name(self) - static int l_get_entity_name(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - std::string name = co->getName(); - lua_pushstring(L, name.c_str()); - return 1; - } - - // get_luaentity(self) - static int l_get_luaentity(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - luaentity_get(L, co->getId()); - return 1; - } - - /* Player-only */ - - // is_player(self) - static int l_is_player(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - lua_pushboolean(L, (player != NULL)); - return 1; - } - - // get_player_name(self) - static int l_get_player_name(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL){ - lua_pushlstring(L, "", 0); - return 1; - } - // Do it - lua_pushstring(L, player->getName()); - return 1; - } - - // get_look_dir(self) - static int l_get_look_dir(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL) return 0; - // Do it - float pitch = player->getRadPitch(); - float yaw = player->getRadYaw(); - v3f v(cos(pitch)*cos(yaw), sin(pitch), cos(pitch)*sin(yaw)); - push_v3f(L, v); - return 1; - } - - // get_look_pitch(self) - static int l_get_look_pitch(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL) return 0; - // Do it - lua_pushnumber(L, player->getRadPitch()); - return 1; - } - - // get_look_yaw(self) - static int l_get_look_yaw(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL) return 0; - // Do it - lua_pushnumber(L, player->getRadYaw()); - return 1; - } - - // set_inventory_formspec(self, formspec) - static int l_set_inventory_formspec(lua_State *L) - { - 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()); - lua_pushboolean(L, true); - return 1; - } - - // get_inventory_formspec(self) -> formspec - static int l_get_inventory_formspec(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL) return 0; - - std::string formspec = player->inventory_formspec; - lua_pushlstring(L, formspec.c_str(), formspec.size()); - return 1; - } - - // get_player_control(self) - static int l_get_player_control(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL){ - lua_pushlstring(L, "", 0); - return 1; - } - // Do it - PlayerControl control = player->getPlayerControl(); - lua_newtable(L); - lua_pushboolean(L, control.up); - lua_setfield(L, -2, "up"); - lua_pushboolean(L, control.down); - lua_setfield(L, -2, "down"); - lua_pushboolean(L, control.left); - lua_setfield(L, -2, "left"); - lua_pushboolean(L, control.right); - lua_setfield(L, -2, "right"); - lua_pushboolean(L, control.jump); - lua_setfield(L, -2, "jump"); - lua_pushboolean(L, control.aux1); - lua_setfield(L, -2, "aux1"); - lua_pushboolean(L, control.sneak); - lua_setfield(L, -2, "sneak"); - lua_pushboolean(L, control.LMB); - lua_setfield(L, -2, "LMB"); - lua_pushboolean(L, control.RMB); - lua_setfield(L, -2, "RMB"); - return 1; - } - - // get_player_control_bits(self) - static int l_get_player_control_bits(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - Player *player = getplayer(ref); - if(player == NULL){ - lua_pushlstring(L, "", 0); - return 1; - } - // Do it - lua_pushnumber(L, player->keyPressed); - return 1; - } - -public: - ObjectRef(ServerActiveObject *object): - m_object(object) - { - //infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl; - } - - ~ObjectRef() - { - /*if(m_object) - infostream<<"ObjectRef destructing for id=" - <<m_object->getId()<<std::endl; - else - infostream<<"ObjectRef destructing for id=unknown"<<std::endl;*/ - } - - // Creates an ObjectRef 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, ServerActiveObject *object) - { - ObjectRef *o = new ObjectRef(object); - //infostream<<"ObjectRef::create: o="<<o<<std::endl; - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - } - - static void set_null(lua_State *L) - { - ObjectRef *o = checkobject(L, -1); - o->m_object = NULL; - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Cannot be created from Lua - //lua_register(L, className, create_object); - } -}; -const char ObjectRef::className[] = "ObjectRef"; -const luaL_reg ObjectRef::methods[] = { - // ServerActiveObject - method(ObjectRef, remove), - method(ObjectRef, getpos), - method(ObjectRef, setpos), - method(ObjectRef, moveto), - method(ObjectRef, punch), - method(ObjectRef, right_click), - method(ObjectRef, set_hp), - method(ObjectRef, get_hp), - method(ObjectRef, get_inventory), - method(ObjectRef, get_wield_list), - method(ObjectRef, get_wield_index), - method(ObjectRef, get_wielded_item), - method(ObjectRef, set_wielded_item), - method(ObjectRef, set_armor_groups), - method(ObjectRef, set_animation), - method(ObjectRef, set_bone_position), - method(ObjectRef, set_attach), - method(ObjectRef, set_detach), - method(ObjectRef, set_properties), - // LuaEntitySAO-only - method(ObjectRef, setvelocity), - method(ObjectRef, getvelocity), - method(ObjectRef, setacceleration), - method(ObjectRef, getacceleration), - method(ObjectRef, setyaw), - method(ObjectRef, getyaw), - method(ObjectRef, settexturemod), - method(ObjectRef, setsprite), - method(ObjectRef, get_entity_name), - method(ObjectRef, get_luaentity), - // Player-only - method(ObjectRef, is_player), - method(ObjectRef, get_player_name), - method(ObjectRef, get_look_dir), - method(ObjectRef, get_look_pitch), - method(ObjectRef, get_look_yaw), - method(ObjectRef, set_inventory_formspec), - method(ObjectRef, get_inventory_formspec), - method(ObjectRef, get_player_control), - method(ObjectRef, get_player_control_bits), - {0,0} -}; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -// Creates a new anonymous reference if cobj=NULL or id=0 -static 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()); - } + // 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); } -class LuaPerlinNoise +void scriptapi_on_newplayer(lua_State *L, ServerActiveObject *player) { -private: - int seed; - int octaves; - float persistence; - float scale; - static const char className[]; - static const luaL_reg methods[]; - - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) - { - LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - static int l_get2d(lua_State *L) - { - 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); - lua_pushnumber(L, val); - return 1; - } - static int l_get3d(lua_State *L) - { - 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); - lua_pushnumber(L, val); - return 1; - } - -public: - LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence, - float a_scale): - seed(a_seed), - octaves(a_octaves), - persistence(a_persistence), - scale(a_scale) - { - } - - ~LuaPerlinNoise() - { - } - - // LuaPerlinNoise(seed, octaves, persistence, scale) - // Creates an LuaPerlinNoise and leaves it on top of stack - static int create_object(lua_State *L) - { - int seed = luaL_checkint(L, 1); - int octaves = luaL_checkint(L, 2); - float persistence = luaL_checknumber(L, 3); - float scale = luaL_checknumber(L, 4); - LuaPerlinNoise *o = new LuaPerlinNoise(seed, octaves, persistence, scale); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - return 1; - } - - static LuaPerlinNoise* 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 *(LuaPerlinNoise**)ud; // unbox pointer - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); - // Can be created from Lua (PerlinNoise(seed, octaves, persistence) - lua_register(L, className, create_object); - } -}; -const char LuaPerlinNoise::className[] = "PerlinNoise"; -const luaL_reg LuaPerlinNoise::methods[] = { - method(LuaPerlinNoise, get2d), - method(LuaPerlinNoise, get3d), - {0,0} -}; + // 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); +} -/* - PerlinNoiseMap - */ -class LuaPerlinNoiseMap +void scriptapi_on_dieplayer(lua_State *L, ServerActiveObject *player) { -private: - Noise *noise; - static const char className[]; - static const luaL_reg methods[]; - - static int gc_object(lua_State *L) - { - LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - static int l_get2dMap(lua_State *L) - { - int i = 0; - - LuaPerlinNoiseMap *o = checkobject(L, 1); - v2f p = read_v2f(L, 2); - - Noise *n = o->noise; - n->perlinMap2D(p.X, p.Y); - - lua_newtable(L); - for (int y = 0; y != n->sy; y++) { - lua_newtable(L); - for (int x = 0; x != n->sx; x++) { - float noiseval = n->np->offset + n->np->scale * n->result[i++]; - lua_pushnumber(L, noiseval); - lua_rawseti(L, -2, x + 1); - } - lua_rawseti(L, -2, y + 1); - } - return 1; - } - - static int l_get3dMap(lua_State *L) - { - int i = 0; - - LuaPerlinNoiseMap *o = checkobject(L, 1); - v3f p = read_v3f(L, 2); - - Noise *n = o->noise; - n->perlinMap3D(p.X, p.Y, p.Z); - - lua_newtable(L); - for (int z = 0; z != n->sz; z++) { - lua_newtable(L); - for (int y = 0; y != n->sy; y++) { - lua_newtable(L); - for (int x = 0; x != n->sx; x++) { - lua_pushnumber(L, n->np->offset + n->np->scale * n->result[i++]); - lua_rawseti(L, -2, x + 1); - } - lua_rawseti(L, -2, y + 1); - } - lua_rawseti(L, -2, z + 1); - } - return 1; - } - -public: - LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size) { - noise = new Noise(np, seed, size.X, size.Y, size.Z); - } - - ~LuaPerlinNoiseMap() - { - delete noise->np; - delete noise; - } - - // LuaPerlinNoiseMap(np, size) - // Creates an LuaPerlinNoiseMap and leaves it on top of stack - static int create_object(lua_State *L) - { - NoiseParams *np = read_noiseparams(L, 1); - if (!np) - return 0; - v3s16 size = read_v3s16(L, 2); - - LuaPerlinNoiseMap *o = new LuaPerlinNoiseMap(np, 0, size); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - return 1; - } - - static LuaPerlinNoiseMap *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 *(LuaPerlinNoiseMap **)ud; // unbox pointer - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Can be created from Lua (PerlinNoiseMap(np, size) - lua_register(L, className, create_object); - } -}; -const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap"; -const luaL_reg LuaPerlinNoiseMap::methods[] = { - method(LuaPerlinNoiseMap, get2dMap), - method(LuaPerlinNoiseMap, get3dMap), - {0,0} -}; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -/* - NodeTimerRef -*/ + // 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); +} -class NodeTimerRef +bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player) { -private: - v3s16 m_p; - ServerEnvironment *m_env; - - static const char className[]; - static const luaL_reg methods[]; - - static int gc_object(lua_State *L) { - NodeTimerRef *o = *(NodeTimerRef **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - static NodeTimerRef *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 *(NodeTimerRef**)ud; // unbox pointer - } - - static int l_set(lua_State *L) - { - NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - f32 t = luaL_checknumber(L,2); - f32 e = luaL_checknumber(L,3); - env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e)); - return 0; - } - - static int l_start(lua_State *L) - { - NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - f32 t = luaL_checknumber(L,2); - env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0)); - return 0; - } - - static int l_stop(lua_State *L) - { - NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - env->getMap().removeNodeTimer(o->m_p); - return 0; - } - - static int l_is_started(lua_State *L) - { - NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - NodeTimer t = env->getMap().getNodeTimer(o->m_p); - lua_pushboolean(L,(t.timeout != 0)); - return 1; - } - - static int l_get_timeout(lua_State *L) - { - NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - NodeTimer t = env->getMap().getNodeTimer(o->m_p); - lua_pushnumber(L,t.timeout); - return 1; - } - - static int l_get_elapsed(lua_State *L) - { - NodeTimerRef *o = checkobject(L, 1); - ServerEnvironment *env = o->m_env; - if(env == NULL) return 0; - - NodeTimer t = env->getMap().getNodeTimer(o->m_p); - lua_pushnumber(L,t.elapsed); - return 1; - } - -public: - NodeTimerRef(v3s16 p, ServerEnvironment *env): - m_p(p), - m_env(env) - { - } - - ~NodeTimerRef() - { - } - - // Creates an NodeTimerRef 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, v3s16 p, ServerEnvironment *env) - { - NodeTimerRef *o = new NodeTimerRef(p, env); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - } - - static void set_null(lua_State *L) - { - NodeTimerRef *o = checkobject(L, -1); - o->m_env = NULL; - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Cannot be created from Lua - //lua_register(L, className, create_object); - } -}; -const char NodeTimerRef::className[] = "NodeTimerRef"; -const luaL_reg NodeTimerRef::methods[] = { - method(NodeTimerRef, start), - method(NodeTimerRef, set), - method(NodeTimerRef, stop), - method(NodeTimerRef, is_started), - method(NodeTimerRef, get_timeout), - method(NodeTimerRef, get_elapsed), - {0,0} -}; + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -/* - EnvRef -*/ + // 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; +} -class EnvRef +void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player) { -private: - ServerEnvironment *m_env; - - static const char className[]; - static const luaL_reg methods[]; - - static int gc_object(lua_State *L) { - EnvRef *o = *(EnvRef **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - static 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} - static int 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; - } - - static int l_add_node(lua_State *L) - { - return l_set_node(L); - } - - // EnvRef:remove_node(pos) - // pos = {x=num, y=num, z=num} - static int l_remove_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); - // Do it - bool succeeded = env->removeNode(pos); - lua_pushboolean(L, succeeded); - return 1; - } - - // EnvRef:get_node(pos) - // pos = {x=num, y=num, z=num} - static int 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} - static int 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 - static int 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); - MapNode n = env->getMap().getNodeNoEx(pos); - 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} - static int 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} - static int 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} - static int 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) - static int l_get_meta(lua_State *L) - { - //infostream<<"EnvRef::l_get_meta()"<<std::endl; - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->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) - static int 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} - static int l_add_entity(lua_State *L) - { - //infostream<<"EnvRef::l_add_entity()"<<std::endl; - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->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} - static int l_add_item(lua_State *L) - { - //infostream<<"EnvRef::l_add_item()"<<std::endl; - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->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} - static int l_add_rat(lua_State *L) - { - infostream<<"EnvRef::l_add_rat(): C++ mobs have been removed." - <<" Doing nothing."<<std::endl; - return 0; - } - - // EnvRef:add_firefly(pos) - // pos = {x=num, y=num, z=num} - static int l_add_firefly(lua_State *L) - { - infostream<<"EnvRef::l_add_firefly(): C++ mobs have been removed." - <<" Doing nothing."<<std::endl; - return 0; - } - - // EnvRef:get_player_by_name(name) - static int l_get_player_by_name(lua_State *L) - { - EnvRef *o = checkobject(L, 1); - ServerEnvironment *env = o->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) - static int 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<u16> ids = env->getObjectsInsideRadius(pos, radius); - lua_newtable(L); - int table = lua_gettop(L); - for(std::set<u16>::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 - static int 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 - static int 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" - static int 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<content_t> 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++){ - core::list<v3s16> list; - getFacePositions(list, d); - for(core::list<v3s16>::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" - static int 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<content_t> 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 - static int 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 - static int 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 - static int l_clear_objects(lua_State *L) - { - EnvRef *o = checkobject(L, 1); - o->m_env->clearAllObjects(); - return 0; - } - - static int 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; - } - -public: - EnvRef(ServerEnvironment *env): - m_env(env) - { - //infostream<<"EnvRef created"<<std::endl; - } - - ~EnvRef() - { - //infostream<<"EnvRef destructing"<<std::endl; - } - - // 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) - { - EnvRef *o = new EnvRef(env); - //infostream<<"EnvRef::create: o="<<o<<std::endl; - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - } - - static void set_null(lua_State *L) - { - EnvRef *o = checkobject(L, -1); - o->m_env = NULL; - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Cannot be created from Lua - //lua_register(L, className, create_object); - } -}; -const char EnvRef::className[] = "EnvRef"; -const luaL_reg EnvRef::methods[] = { - method(EnvRef, set_node), - method(EnvRef, add_node), - method(EnvRef, remove_node), - method(EnvRef, get_node), - method(EnvRef, get_node_or_nil), - method(EnvRef, get_node_light), - method(EnvRef, place_node), - method(EnvRef, dig_node), - method(EnvRef, punch_node), - method(EnvRef, add_entity), - method(EnvRef, add_item), - method(EnvRef, add_rat), - method(EnvRef, add_firefly), - method(EnvRef, get_meta), - method(EnvRef, get_node_timer), - method(EnvRef, get_player_by_name), - method(EnvRef, get_objects_inside_radius), - method(EnvRef, set_timeofday), - method(EnvRef, get_timeofday), - method(EnvRef, find_node_near), - method(EnvRef, find_nodes_in_area), - method(EnvRef, get_perlin), - method(EnvRef, get_perlin_map), - method(EnvRef, clear_objects), - method(EnvRef, spawn_tree), - {0,0} -}; - -/* - LuaPseudoRandom -*/ + 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); +} -class LuaPseudoRandom +void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player) { -private: - PseudoRandom m_pseudo; - - static const char className[]; - static const luaL_reg methods[]; - - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) - { - LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1)); - delete o; - return 0; - } - - // next(self, min=0, max=32767) -> get next value - static int l_next(lua_State *L) - { - LuaPseudoRandom *o = checkobject(L, 1); - int min = 0; - int max = 32767; - lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist - if(!lua_isnil(L, 2)) - min = luaL_checkinteger(L, 2); - if(!lua_isnil(L, 3)) - max = luaL_checkinteger(L, 3); - if(max < min){ - errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl; - throw LuaError(L, "PseudoRandom.next(): max < min"); - } - if(max - min != 32767 && max - min > 32767/5) - throw LuaError(L, "PseudoRandom.next() max-min is not 32767 and is > 32768/5. This is disallowed due to the bad random distribution the implementation would otherwise make."); - PseudoRandom &pseudo = o->m_pseudo; - int val = pseudo.next(); - val = (val % (max-min+1)) + min; - lua_pushinteger(L, val); - return 1; - } - -public: - LuaPseudoRandom(int seed): - m_pseudo(seed) - { - } - - ~LuaPseudoRandom() - { - } - - const PseudoRandom& getItem() const - { - return m_pseudo; - } - PseudoRandom& getItem() - { - return m_pseudo; - } - - // LuaPseudoRandom(seed) - // Creates an LuaPseudoRandom and leaves it on top of stack - static int create_object(lua_State *L) - { - int seed = luaL_checknumber(L, 1); - LuaPseudoRandom *o = new LuaPseudoRandom(seed); - *(void **)(lua_newuserdata(L, sizeof(void *))) = o; - luaL_getmetatable(L, className); - lua_setmetatable(L, -2); - return 1; - } - - static LuaPseudoRandom* checkobject(lua_State *L, int narg) - { - luaL_checktype(L, narg, LUA_TUSERDATA); - void *ud = luaL_checkudata(L, narg, className); - if(!ud) luaL_typerror(L, narg, className); - return *(LuaPseudoRandom**)ud; // unbox pointer - } - - static void Register(lua_State *L) - { - lua_newtable(L); - int methodtable = lua_gettop(L); - luaL_newmetatable(L, className); - int metatable = lua_gettop(L); - - lua_pushliteral(L, "__metatable"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() - - lua_pushliteral(L, "__index"); - lua_pushvalue(L, methodtable); - lua_settable(L, metatable); - - lua_pushliteral(L, "__gc"); - lua_pushcfunction(L, gc_object); - lua_settable(L, metatable); - - lua_pop(L, 1); // drop metatable - - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable - - // Can be created from Lua (LuaPseudoRandom(seed)) - lua_register(L, className, create_object); - } -}; -const char LuaPseudoRandom::className[] = "PseudoRandom"; -const luaL_reg LuaPseudoRandom::methods[] = { - method(LuaPseudoRandom, next), - {0,0} -}; - + 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); +} /* - LuaABM + player */ - -class LuaABM : public ActiveBlockModifier +void scriptapi_on_player_receive_fields(lua_State *L, + ServerActiveObject *player, + const std::string &formname, + const std::map<std::string, std::string> &fields) { -private: - lua_State *m_lua; - int m_id; - - std::set<std::string> m_trigger_contents; - std::set<std::string> m_required_neighbors; - float m_trigger_interval; - u32 m_trigger_chance; -public: - LuaABM(lua_State *L, int id, - const std::set<std::string> &trigger_contents, - const std::set<std::string> &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<std::string> getTriggerContents() - { - return m_trigger_contents; - } - virtual std::set<std::string> 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)); - } -}; - -/* - ServerSoundParams -*/ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); -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); + // 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<std::string, std::string>::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + 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); } - -/* - Global functions -*/ +/*****************************************************************************/ +/* Api functions */ +/*****************************************************************************/ // debug(text) // Writes a line to dstream @@ -4644,8 +621,6 @@ static int l_register_biome_groups(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); int index = 1; - if (!lua_istable(L, index)) - throw LuaError(L, "register_biome_groups: parameter is not a table"); BiomeDefManager *bmgr = get_server(L)->getBiomeDef(); if (!bmgr) { @@ -4717,318 +692,53 @@ static int l_register_biome(lua_State *L) return 0; } -// register_item_raw({lots of stuff}) -static 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: "<<name<<std::endl; - } else { - throw LuaError(L, "register_item_raw: name is not defined or not a string"); - } - - // Check if on_use is defined - - ItemDefinition def; - // Set a distinctive default value to check if this is set - def.node_placement_prediction = "__default"; - // Read the item definition - def = read_item_definition(L, table, def); - - // Default to having client-side placement prediction for nodes - // ("" in item definition sets it off) - if(def.node_placement_prediction == "__default"){ - if(def.type == ITEM_NODE) - def.node_placement_prediction = name; - else - def.node_placement_prediction = ""; - } - - // Register item definition - idef->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) -static 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 */ -} - -// helper for register_craft -static bool read_craft_recipe_shaped(lua_State *L, int index, - int &width, std::vector<std::string> &recipe) +static int l_register_ore(lua_State *L) { - if(index < 0) - index = lua_gettop(L) + 1 + index; - - if(!lua_istable(L, index)) - return false; - - lua_pushnil(L); - int rowcount = 0; - while(lua_next(L, index) != 0){ - int colcount = 0; - // key at index -2 and value at index -1 - if(!lua_istable(L, -1)) - return false; - int table2 = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table2) != 0){ - // key at index -2 and value at index -1 - if(!lua_isstring(L, -1)) - return false; - recipe.push_back(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - colcount++; - } - if(rowcount == 0){ - width = colcount; - } else { - if(colcount != width) - return false; - } - // removes value, keeps key for next iteration - lua_pop(L, 1); - rowcount++; - } - return width != 0; -} - -// helper for register_craft -static bool read_craft_recipe_shapeless(lua_State *L, int index, - std::vector<std::string> &recipe) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - - if(!lua_istable(L, index)) - return false; - - lua_pushnil(L); - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - if(!lua_isstring(L, -1)) - return false; - recipe.push_back(lua_tostring(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + IWritableNodeDefManager *ndef = get_server(L)->getWritableNodeDefManager(); + 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; } - return true; -} - -// helper for register_craft -static bool read_craft_replacements(lua_State *L, int index, - CraftReplacements &replacements) -{ - if(index < 0) - index = lua_gettop(L) + 1 + index; - - if(!lua_istable(L, index)) - return false; - - lua_pushnil(L); - while(lua_next(L, index) != 0){ - // key at index -2 and value at index -1 - if(!lua_istable(L, -1)) - return false; - lua_rawgeti(L, -1, 1); - if(!lua_isstring(L, -1)) - return false; - std::string replace_from = lua_tostring(L, -1); - lua_pop(L, 1); - lua_rawgeti(L, -1, 2); - if(!lua_isstring(L, -1)) - return false; - std::string replace_to = lua_tostring(L, -1); - lua_pop(L, 1); - replacements.pairs.push_back( - std::make_pair(replace_from, replace_to)); - // removes value, keeps key for next iteration - lua_pop(L, 1); + + ore->ore_name = getstringfield_default(L, index, "ore", ""); + 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->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"; + delete ore; + return 0; } - return true; + + emerge->ores.push_back(ore); + + verbosestream << "register_ore: ore '" << ore->ore_name + << "' registered" << std::endl; + return 0; } -// register_craft({output=item, recipe={{item00,item10},{item01,item11}}) -static int l_register_craft(lua_State *L) -{ - //infostream<<"register_craft"<<std::endl; - luaL_checktype(L, 1, LUA_TTABLE); - int table = 1; - - // Get the writable craft definition manager from the server - IWritableCraftDefManager *craftdef = - get_server(L)->getWritableCraftDefManager(); - - std::string type = getstringfield_default(L, table, "type", "shaped"); - - /* - CraftDefinitionShaped - */ - if(type == "shaped"){ - std::string output = getstringfield_default(L, table, "output", ""); - if(output == "") - throw LuaError(L, "Crafting definition is missing an output"); - - int width = 0; - std::vector<std::string> recipe; - lua_getfield(L, table, "recipe"); - 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)) - throw LuaError(L, "Invalid crafting recipe" - " (output=\"" + output + "\")"); - - CraftReplacements replacements; - lua_getfield(L, table, "replacements"); - if(!lua_isnil(L, -1)) - { - if(!read_craft_replacements(L, -1, replacements)) - throw LuaError(L, "Invalid replacements" - " (output=\"" + output + "\")"); - } - - CraftDefinition *def = new CraftDefinitionShaped( - output, width, recipe, replacements); - craftdef->registerCraft(def); - } - /* - CraftDefinitionShapeless - */ - else if(type == "shapeless"){ - std::string output = getstringfield_default(L, table, "output", ""); - if(output == "") - throw LuaError(L, "Crafting definition (shapeless)" - " is missing an output"); - - std::vector<std::string> recipe; - lua_getfield(L, table, "recipe"); - if(lua_isnil(L, -1)) - throw LuaError(L, "Crafting definition (shapeless)" - " is missing a recipe" - " (output=\"" + output + "\")"); - if(!read_craft_recipe_shapeless(L, -1, recipe)) - throw LuaError(L, "Invalid crafting recipe" - " (output=\"" + output + "\")"); - - CraftReplacements replacements; - lua_getfield(L, table, "replacements"); - if(!lua_isnil(L, -1)) - { - if(!read_craft_replacements(L, -1, replacements)) - throw LuaError(L, "Invalid replacements" - " (output=\"" + output + "\")"); - } - - CraftDefinition *def = new CraftDefinitionShapeless( - output, recipe, replacements); - craftdef->registerCraft(def); - } - /* - CraftDefinitionToolRepair - */ - else if(type == "toolrepair"){ - float additional_wear = getfloatfield_default(L, table, - "additional_wear", 0.0); - - CraftDefinition *def = new CraftDefinitionToolRepair( - additional_wear); - craftdef->registerCraft(def); - } - /* - CraftDefinitionCooking - */ - else if(type == "cooking"){ - std::string output = getstringfield_default(L, table, "output", ""); - if(output == "") - throw LuaError(L, "Crafting definition (cooking)" - " is missing an output"); - - std::string recipe = getstringfield_default(L, table, "recipe", ""); - if(recipe == "") - throw LuaError(L, "Crafting definition (cooking)" - " is missing a recipe" - " (output=\"" + output + "\")"); - - float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); - - CraftReplacements replacements; - lua_getfield(L, table, "replacements"); - if(!lua_isnil(L, -1)) - { - if(!read_craft_replacements(L, -1, replacements)) - throw LuaError(L, "Invalid replacements" - " (cooking output=\"" + output + "\")"); - } - - CraftDefinition *def = new CraftDefinitionCooking( - output, recipe, cooktime, replacements); - craftdef->registerCraft(def); - } - /* - CraftDefinitionFuel - */ - else if(type == "fuel"){ - std::string recipe = getstringfield_default(L, table, "recipe", ""); - if(recipe == "") - throw LuaError(L, "Crafting definition (fuel)" - " is missing a recipe"); - - float burntime = getfloatfield_default(L, table, "burntime", 1.0); - - CraftReplacements replacements; - lua_getfield(L, table, "replacements"); - if(!lua_isnil(L, -1)) - { - if(!read_craft_replacements(L, -1, replacements)) - throw LuaError(L, "Invalid replacements" - " (fuel recipe=\"" + recipe + "\")"); - } - CraftDefinition *def = new CraftDefinitionFuel( - recipe, burntime, replacements); - craftdef->registerCraft(def); - } - else - { - throw LuaError(L, "Unknown crafting definition type: \"" + type + "\""); - } - - lua_pop(L, 1); - return 0; /* number of results */ -} // setting_set(name, value) static int l_setting_set(lua_State *L) @@ -5164,45 +874,6 @@ static int l_unban_player_of_ip(lua_State *L) return 1; } -// get_inventory(location) -static int 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(get_server(L)->getInventory(loc) != NULL) - InvRef::create(L, loc); - else - lua_pushnil(L); - return 1; -} - -// create_detached_inventory_raw(name) -static int l_create_detached_inventory_raw(lua_State *L) -{ - const char *name = luaL_checkstring(L, 1); - if(get_server(L)->createDetachedInventory(name) != NULL){ - InventoryLocation loc; - loc.setDetached(name); - InvRef::create(L, loc); - }else{ - lua_pushnil(L); - } - return 1; -} - // show_formspec(playername,formname,formspec) static int l_show_formspec(lua_State *L) { @@ -5278,24 +949,24 @@ static int l_get_modpath(lua_State *L) static int l_get_modnames(lua_State *L) { // Get a list of mods - core::list<std::string> mods_unsorted, mods_sorted; + std::list<std::string> 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(core::list<std::string>::Iterator i = mods_unsorted.begin(); - i != mods_unsorted.end(); i++) + for(std::list<std::string>::iterator i = mods_unsorted.begin(); + i != mods_unsorted.end(); ++i) { bool added = false; - for(core::list<std::string>::Iterator x = mods_sorted.begin(); - x != mods_unsorted.end(); x++) + for(std::list<std::string>::iterator x = mods_sorted.begin(); + x != mods_sorted.end(); ++x) { // I doubt anybody using Minetest will be using // anything not ASCII based :) if((*i).compare(*x) <= 0) { - mods_sorted.insert_before(x, *i); + mods_sorted.insert(x, *i); added = true; break; } @@ -5312,7 +983,7 @@ static int l_get_modnames(lua_State *L) // Package them up for Lua lua_newtable(L); int new_table = lua_gettop(L); - core::list<std::string>::Iterator i = mods_sorted.begin(); + std::list<std::string>::iterator i = mods_sorted.begin(); while(i != mods_sorted.end()) { lua_pushvalue(L, insertion_func); @@ -5322,7 +993,7 @@ static int l_get_modnames(lua_State *L) { script_error(L, "error: %s", lua_tostring(L, -1)); } - i++; + ++i; } return 1; } @@ -5383,102 +1054,6 @@ static int l_notify_authentication_modified(lua_State *L) return 0; } -// get_craft_result(input) -static int l_get_craft_result(lua_State *L) -{ - int input_i = 1; - std::string method_s = getstringfield_default(L, input_i, "method", "normal"); - enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method", - es_CraftMethod, CRAFT_METHOD_NORMAL); - int width = 1; - lua_getfield(L, input_i, "width"); - if(lua_isnumber(L, -1)) - width = luaL_checkinteger(L, -1); - lua_pop(L, 1); - lua_getfield(L, input_i, "items"); - std::vector<ItemStack> items = read_items(L, -1); - lua_pop(L, 1); // items - - IGameDef *gdef = get_server(L); - ICraftDefManager *cdef = gdef->cdef(); - CraftInput input(method, width, items); - CraftOutput output; - bool got = cdef->getCraftResult(input, output, true, gdef); - lua_newtable(L); // output table - if(got){ - ItemStack item; - item.deSerialize(output.item, gdef->idef()); - LuaItemStack::create(L, item); - lua_setfield(L, -2, "item"); - setintfield(L, -1, "time", output.time); - } else { - LuaItemStack::create(L, ItemStack()); - lua_setfield(L, -2, "item"); - setintfield(L, -1, "time", 0); - } - lua_newtable(L); // decremented input table - lua_pushstring(L, method_s.c_str()); - lua_setfield(L, -2, "method"); - lua_pushinteger(L, width); - lua_setfield(L, -2, "width"); - push_items(L, input.items); - lua_setfield(L, -2, "items"); - return 2; -} - -// get_craft_recipe(result item) -static int l_get_craft_recipe(lua_State *L) -{ - int k = 0; - char tmp[20]; - int input_i = 1; - std::string o_item = luaL_checkstring(L,input_i); - - IGameDef *gdef = get_server(L); - ICraftDefManager *cdef = gdef->cdef(); - CraftInput input; - CraftOutput output(o_item,0); - bool got = cdef->getCraftRecipe(input, output, gdef); - lua_newtable(L); // output table - if(got){ - lua_newtable(L); - for(std::vector<ItemStack>::const_iterator - i = input.items.begin(); - i != input.items.end(); i++, k++) - { - if (i->empty()) - { - continue; - } - sprintf(tmp,"%d",k); - lua_pushstring(L,tmp); - lua_pushstring(L,i->name.c_str()); - lua_settable(L, -3); - } - lua_setfield(L, -2, "items"); - setintfield(L, -1, "width", input.width); - switch (input.method) { - case CRAFT_METHOD_NORMAL: - lua_pushstring(L,"normal"); - break; - case CRAFT_METHOD_COOKING: - lua_pushstring(L,"cooking"); - break; - case CRAFT_METHOD_FUEL: - lua_pushstring(L,"fuel"); - break; - default: - lua_pushstring(L,"unknown"); - } - lua_setfield(L, -2, "type"); - } else { - lua_pushnil(L); - lua_setfield(L, -2, "items"); - setintfield(L, -1, "width", 0); - } - return 1; -} - // rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds static int l_rollback_get_last_node_actor(lua_State *L) { @@ -5538,6 +1113,7 @@ static const struct luaL_Reg minetest_f [] = { {"register_craft", l_register_craft}, {"register_biome", l_register_biome}, {"register_biome_groups", l_register_biome_groups}, + {"register_ore", l_register_ore}, {"setting_set", l_setting_set}, {"setting_get", l_setting_get}, {"setting_getbool", l_setting_getbool}, @@ -5565,11 +1141,16 @@ static const struct luaL_Reg minetest_f [] = { {"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 */ @@ -5610,1596 +1191,3 @@ void scriptapi_export(lua_State *L, Server *server) LuaPerlinNoise::Register(L); LuaPerlinNoiseMap::Register(L); } - -bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, - const std::string &modname) -{ - ModNameStorer modnamestorer(L, modname); - - if(!string_allowed(modname, "abcdefghijklmnopqrstuvwxyz" - "0123456789_")){ - errorstream<<"Error loading mod \""<<modname - <<"\": modname does not follow naming conventions: " - <<"Only chararacters [a-z0-9_] are allowed."<<std::endl; - return false; - } - - bool success = false; - - try{ - success = script_load(L, scriptpath.c_str()); - } - catch(LuaError &e){ - errorstream<<"Error loading mod \""<<modname - <<"\": "<<e.what()<<std::endl; - } - - return success; -} - -void scriptapi_add_environment(lua_State *L, ServerEnvironment *env) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - verbosestream<<"scriptapi_add_environment"<<std::endl; - StackUnroller stack_unroller(L); - - // Create EnvRef on stack - EnvRef::create(L, env); - int envref = lua_gettop(L); - - // minetest.env = envref - lua_getglobal(L, "minetest"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushvalue(L, envref); - lua_setfield(L, -2, "env"); - - // Store environment as light userdata in registry - lua_pushlightuserdata(L, env); - lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env"); - - /* - Add ActiveBlockModifiers to environment - */ - - // 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); - - if(lua_istable(L, registered_abms)){ - int table = lua_gettop(L); - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - int id = lua_tonumber(L, -2); - int current_abm = lua_gettop(L); - - std::set<std::string> 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<std::string> 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); -} - -#if 0 -// Dump stack top with the dump2 function -static void dump2(lua_State *L, const char *name) -{ - // Dump object (debug) - lua_getglobal(L, "dump2"); - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, -2); // Get previous stack top as first parameter - lua_pushstring(L, name); - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} -#endif - -/* - 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="<<cobj->getId()<<std::endl; - StackUnroller stack_unroller(L); - - // Create object on stack - ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack - int object = lua_gettop(L); - - // Get minetest.object_refs table - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "object_refs"); - luaL_checktype(L, -1, LUA_TTABLE); - int objectstable = lua_gettop(L); - - // object_refs[id] = object - lua_pushnumber(L, cobj->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="<<cobj->getId()<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.object_refs table - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "object_refs"); - luaL_checktype(L, -1, LUA_TTABLE); - int objectstable = lua_gettop(L); - - // Get object_refs[id] - lua_pushnumber(L, cobj->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); -} - -/* - misc -*/ - -// 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. -}; - -// 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 -static 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: - // ... <return value = nil> <table> <arg#1> <arg#2> ... <arg#n> - - 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); -} - -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<std::string> *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); -} - -/* - player -*/ - -void scriptapi_on_player_receive_fields(lua_State *L, - ServerActiveObject *player, - const std::string &formname, - const std::map<std::string, std::string> &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<std::string, std::string>::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; - 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); -} - -/* - item callbacks and node callbacks -*/ - -// 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 -static 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 \""<<name<<"\" not defined"<<std::endl; - lua_pop(L, 1); - - // Try minetest.nodedef_default instead - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "nodedef_default"); - lua_remove(L, -2); - luaL_checktype(L, -1, LUA_TTABLE); - } - lua_getfield(L, -1, callbackname); - lua_remove(L, -2); - // Should be a function or nil - if(lua_type(L, -1) == LUA_TFUNCTION) - { - return true; - } - else if(lua_isnil(L, -1)) - { - lua_pop(L, 1); - return false; - } - else - { - errorstream<<"Item \""<<name<<"\" callback \"" - <<callbackname<<" is not a function"<<std::endl; - lua_pop(L, 1); - return false; - } -} - -bool scriptapi_item_on_drop(lua_State *L, ItemStack &item, - ServerActiveObject *dropper, v3f pos) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_item_callback(L, item.name.c_str(), "on_drop")) - return false; - - // Call function - LuaItemStack::create(L, item); - objectref_get_or_create(L, dropper); - pushFloatPos(L, pos); - if(lua_pcall(L, 3, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if(!lua_isnil(L, -1)) - item = read_item(L, -1); - return true; -} - -bool scriptapi_item_on_place(lua_State *L, ItemStack &item, - ServerActiveObject *placer, const PointedThing &pointed) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_item_callback(L, item.name.c_str(), "on_place")) - return false; - - // Call function - LuaItemStack::create(L, item); - objectref_get_or_create(L, placer); - push_pointed_thing(L, pointed); - if(lua_pcall(L, 3, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if(!lua_isnil(L, -1)) - item = read_item(L, -1); - return true; -} - -bool scriptapi_item_on_use(lua_State *L, ItemStack &item, - ServerActiveObject *user, const PointedThing &pointed) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_item_callback(L, item.name.c_str(), "on_use")) - return false; - - // Call function - LuaItemStack::create(L, item); - objectref_get_or_create(L, user); - push_pointed_thing(L, pointed); - if(lua_pcall(L, 3, 1, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - if(!lua_isnil(L, -1)) - item = read_item(L, -1); - return true; -} - -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<std::string, std::string> &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<std::string, std::string>::const_iterator - i = fields.begin(); i != fields.end(); i++){ - const std::string &name = i->first; - const std::string &value = i->second; - 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)); -} - -/* - 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)); -} - -/* - 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 \""<<name<<"\" not defined"<<std::endl; - lua_pop(L, 1); - return false; - } - lua_getfield(L, -1, callbackname); - lua_remove(L, -2); - // Should be a function or nil - if(lua_type(L, -1) == LUA_TFUNCTION) - { - return true; - } - else if(lua_isnil(L, -1)) - { - lua_pop(L, 1); - return false; - } - else - { - errorstream<<"Detached inventory \""<<name<<"\" callback \"" - <<callbackname<<"\" is not a function"<<std::endl; - lua_pop(L, 1); - return false; - } -} - -// 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) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_detached_inventory_callback(L, 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 - 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_move should return a number"); - return luaL_checkinteger(L, -1); -} - -// 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) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_detached_inventory_callback(L, 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 - 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_put should return a number"); - return luaL_checkinteger(L, -1); -} - -// 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) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_detached_inventory_callback(L, 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 - 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_take should return a number"); - return luaL_checkinteger(L, -1); -} - -// 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) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_detached_inventory_callback(L, 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 - 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_detached_inventory_on_put(lua_State *L, - const std::string &name, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_detached_inventory_callback(L, 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 - 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_detached_inventory_on_take(lua_State *L, - const std::string &name, - const std::string &listname, int index, ItemStack &stack, - ServerActiveObject *player) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - // Push callback function on stack - if(!get_detached_inventory_callback(L, 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 - objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); -} - -/* - environment -*/ - -void scriptapi_environment_step(lua_State *L, float dtime) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_environment_step"<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.registered_globalsteps - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_globalsteps"); - // Call callbacks - lua_pushnumber(L, dtime); - scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); -} - -void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp, - u32 blockseed) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_environment_on_generated"<<std::endl; - StackUnroller stack_unroller(L); - - // 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); - scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST); -} - -/* - 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="<<id<<" name=\"" - <<name<<"\""<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.registered_entities[name] - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_entities"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushstring(L, name); - lua_gettable(L, -2); - // Should be a table, which we will use as a prototype - //luaL_checktype(L, -1, LUA_TTABLE); - if(lua_type(L, -1) != LUA_TTABLE){ - errorstream<<"LuaEntity name \""<<name<<"\" not defined"<<std::endl; - return false; - } - int prototype_table = lua_gettop(L); - //dump2(L, "prototype_table"); - - // Create entity object - lua_newtable(L); - int object = lua_gettop(L); - - // Set object metatable - lua_pushvalue(L, prototype_table); - lua_setmetatable(L, -2); - - // Add object reference - // This should be userdata with metatable ObjectRef - objectref_get(L, id); - luaL_checktype(L, -1, LUA_TUSERDATA); - if(!luaL_checkudata(L, -1, "ObjectRef")) - luaL_typerror(L, -1, "ObjectRef"); - lua_setfield(L, -2, "object"); - - // minetest.luaentities[id] = object - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "luaentities"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); // Push id - lua_pushvalue(L, object); // Copy object to top of stack - lua_settable(L, -3); - - return true; -} - -void scriptapi_luaentity_activate(lua_State *L, u16 id, - const std::string &staticdata, u32 dtime_s) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - verbosestream<<"scriptapi_luaentity_activate: id="<<id<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.luaentities[id] - luaentity_get(L, id); - int object = lua_gettop(L); - - // Get on_activate function - lua_pushvalue(L, object); - lua_getfield(L, -1, "on_activate"); - if(!lua_isnil(L, -1)){ - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, object); // self - lua_pushlstring(L, staticdata.c_str(), staticdata.size()); - lua_pushinteger(L, dtime_s); - // Call with 3 arguments, 0 results - if(lua_pcall(L, 3, 0, 0)) - script_error(L, "error running function on_activate: %s\n", - lua_tostring(L, -1)); - } -} - -void scriptapi_luaentity_rm(lua_State *L, u16 id) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - verbosestream<<"scriptapi_luaentity_rm: id="<<id<<std::endl; - - // Get minetest.luaentities table - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "luaentities"); - luaL_checktype(L, -1, LUA_TTABLE); - int objectstable = lua_gettop(L); - - // Set luaentities[id] = nil - lua_pushnumber(L, id); // Push id - lua_pushnil(L); - lua_settable(L, objectstable); - - lua_pop(L, 2); // pop luaentities, minetest -} - -std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.luaentities[id] - luaentity_get(L, id); - int object = lua_gettop(L); - - // Get get_staticdata function - lua_pushvalue(L, object); - lua_getfield(L, -1, "get_staticdata"); - if(lua_isnil(L, -1)) - return ""; - - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, object); // self - // Call with 1 arguments, 1 results - if(lua_pcall(L, 1, 1, 0)) - script_error(L, "error running function get_staticdata: %s\n", - lua_tostring(L, -1)); - - size_t len=0; - const char *s = lua_tolstring(L, -1, &len); - return std::string(s, len); -} - -void scriptapi_luaentity_get_properties(lua_State *L, u16 id, - ObjectProperties *prop) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.luaentities[id] - luaentity_get(L, id); - //int object = lua_gettop(L); - - // Set default values that differ from ObjectProperties defaults - prop->hp_max = 10; - - /* Read stuff */ - - 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); - - // Deprecated: read object properties directly - read_object_properties(L, -1, prop); - - // Read initial_properties - lua_getfield(L, -1, "initial_properties"); - read_object_properties(L, -1, prop); - lua_pop(L, 1); -} - -void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.luaentities[id] - luaentity_get(L, id); - int object = lua_gettop(L); - // State: object is at top of stack - // Get step function - lua_getfield(L, -1, "on_step"); - if(lua_isnil(L, -1)) - return; - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, object); // self - lua_pushnumber(L, dtime); // dtime - // Call with 2 arguments, 0 results - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error running function 'on_step': %s\n", lua_tostring(L, -1)); -} - -// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch, -// tool_capabilities, direction) -void scriptapi_luaentity_punch(lua_State *L, u16 id, - ServerActiveObject *puncher, float time_from_last_punch, - const ToolCapabilities *toolcap, v3f dir) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.luaentities[id] - luaentity_get(L, id); - int object = lua_gettop(L); - // State: object is at top of stack - // Get function - lua_getfield(L, -1, "on_punch"); - if(lua_isnil(L, -1)) - return; - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, object); // self - objectref_get_or_create(L, puncher); // Clicker reference - lua_pushnumber(L, time_from_last_punch); - push_tool_capabilities(L, *toolcap); - push_v3f(L, dir); - // Call with 5 arguments, 0 results - if(lua_pcall(L, 5, 0, 0)) - script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1)); -} - -// Calls entity:on_rightclick(ObjectRef clicker) -void scriptapi_luaentity_rightclick(lua_State *L, u16 id, - ServerActiveObject *clicker) -{ - realitycheck(L); - assert(lua_checkstack(L, 20)); - //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; - StackUnroller stack_unroller(L); - - // Get minetest.luaentities[id] - luaentity_get(L, id); - int object = lua_gettop(L); - // State: object is at top of stack - // Get function - lua_getfield(L, -1, "on_rightclick"); - if(lua_isnil(L, -1)) - return; - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_pushvalue(L, object); // self - objectref_get_or_create(L, clicker); // Clicker reference - // Call with 2 arguments, 0 results - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error running function 'on_rightclick': %s\n", lua_tostring(L, -1)); -} - diff --git a/src/scriptapi.h b/src/scriptapi.h index e94ca781f..7f19bcef5 100644 --- a/src/scriptapi.h +++ b/src/scriptapi.h @@ -20,41 +20,35 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SCRIPTAPI_HEADER #define SCRIPTAPI_HEADER -#include "irrlichttypes_bloated.h" #include <string> -#include "mapnode.h" #include <set> #include <map> +#include "irr_v3d.h" +#include "irr_v2d.h" + +extern "C" { +#include <lua.h> +} +#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; -class ServerEnvironment; -class ServerActiveObject; -typedef struct lua_State lua_State; -struct ObjectProperties; -struct ItemStack; -struct PointedThing; -//class IGameDef; -struct ToolCapabilities; void scriptapi_export(lua_State *L, Server *server); bool scriptapi_loadmod(lua_State *L, const std::string &scriptpath, const std::string &modname); -void scriptapi_add_environment(lua_State *L, ServerEnvironment *env); - -void scriptapi_add_object_reference(lua_State *L, ServerActiveObject *cobj); -void scriptapi_rm_object_reference(lua_State *L, ServerActiveObject *cobj); // Returns true if script handled message bool scriptapi_on_chat_message(lua_State *L, const std::string &name, const std::string &message); -/* environment */ -// 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); - /* server */ void scriptapi_on_shutdown(lua_State *L); @@ -77,110 +71,5 @@ void scriptapi_on_player_receive_fields(lua_State *L, const std::string &formname, const std::map<std::string, std::string> &fields); -/* item callbacks */ -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); - -/* node callbacks */ -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); -// Node constructor -void scriptapi_node_on_construct(lua_State *L, v3s16 p, MapNode node); -// Node destructor -void scriptapi_node_on_destruct(lua_State *L, v3s16 p, MapNode node); -// Node post-destructor -void scriptapi_node_after_destruct(lua_State *L, v3s16 p, MapNode node); -// Node Timer event -bool scriptapi_node_on_timer(lua_State *L, v3s16 p, MapNode node, f32 dtime); -// Called when a metadata form returns values -void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, - const std::string &formname, - const std::map<std::string, std::string> &fields, - ServerActiveObject *sender); - -/* 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); -// 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); - -/* 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); - -/* luaentity */ -// 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 diff --git a/src/scriptapi_common.cpp b/src/scriptapi_common.cpp new file mode 100644 index 000000000..2d6f6c72b --- /dev/null +++ b/src/scriptapi_common.cpp @@ -0,0 +1,311 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "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<<script_get_backtrace(L)<<std::endl; + infostream<<"WARNING: field \""<<fieldname<<"\": " + <<message<<std::endl; + } + lua_pop(L, 1); +} + +/* + ToolCapabilities +*/ + +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<<script_get_backtrace(L)<<std::endl; + infostream<<"WARNING: field \"maxwear\" is deprecated; " + <<"should replace with uses=1/maxwear"<<std::endl; + } + // Read "times" table + lua_getfield(L, table_groupcap, "times"); + if(lua_istable(L, -1)){ + int table_times = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table_times) != 0){ + // key at index -2 and value at index -1 + int rating = luaL_checkinteger(L, -2); + float time = luaL_checknumber(L, -1); + groupcap.times[rating] = time; + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + // Insert groupcap into toolcap + toolcap.groupcaps[groupname] = groupcap; + } + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + lua_getfield(L, table, "damage_groups"); + if(lua_istable(L, -1)){ + int table_damage_groups = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table_damage_groups) != 0){ + // key at index -2 and value at index -1 + std::string groupname = luaL_checkstring(L, -2); + u16 value = luaL_checkinteger(L, -1); + toolcap.damageGroups[groupname] = value; + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } + lua_pop(L, 1); + return toolcap; +} + +void set_tool_capabilities(lua_State *L, int table, + const ToolCapabilities &toolcap) +{ + setfloatfield(L, table, "full_punch_interval", toolcap.full_punch_interval); + setintfield(L, table, "max_drop_level", toolcap.max_drop_level); + // Create groupcaps table + lua_newtable(L); + // For each groupcap + for(std::map<std::string, ToolGroupCap>::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<int, float>::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<std::string, s16>::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:"<<std::endl; + stackDump(L, dstream); + script_error(L, "Stack is over 30 (reality check)"); + } +} + +/* + PointedThing +*/ + +void push_pointed_thing(lua_State *L, const PointedThing& pointed) +{ + lua_newtable(L); + if(pointed.type == POINTEDTHING_NODE) + { + lua_pushstring(L, "node"); + lua_setfield(L, -2, "type"); + push_v3s16(L, pointed.node_undersurface); + lua_setfield(L, -2, "under"); + push_v3s16(L, pointed.node_abovesurface); + lua_setfield(L, -2, "above"); + } + else if(pointed.type == POINTEDTHING_OBJECT) + { + lua_pushstring(L, "object"); + lua_setfield(L, -2, "type"); + objectref_get(L, pointed.object_id); + lua_setfield(L, -2, "ref"); + } + else + { + lua_pushstring(L, "nothing"); + lua_setfield(L, -2, "type"); + } +} + +void stackDump(lua_State *L, std::ostream &o) +{ + int i; + int top = lua_gettop(L); + for (i = 1; i <= top; i++) { /* repeat for each level */ + int t = lua_type(L, i); + switch (t) { + + case LUA_TSTRING: /* strings */ + o<<"\""<<lua_tostring(L, i)<<"\""; + break; + + case LUA_TBOOLEAN: /* booleans */ + o<<(lua_toboolean(L, i) ? "true" : "false"); + break; + + case LUA_TNUMBER: /* numbers */ { + char buf[10]; + snprintf(buf, 10, "%g", lua_tonumber(L, i)); + o<<buf; + break; } + + default: /* other values */ + o<<lua_typename(L, t); + break; + + } + o<<" "; + } + o<<std::endl; +} + +#if 0 +// Dump stack top with the dump2 function +static void dump2(lua_State *L, const char *name) +{ + // Dump object (debug) + lua_getglobal(L, "dump2"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, -2); // Get previous stack top as first parameter + lua_pushstring(L, name); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} +#endif + +bool string_to_enum(const EnumString *spec, int &result, + const std::string &str) +{ + const EnumString *esp = spec; + while(esp->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 new file mode 100644 index 000000000..d029b48d3 --- /dev/null +++ b/src/scriptapi_common.h @@ -0,0 +1,112 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_COMMON_H_ +#define LUA_COMMON_H_ + +extern "C" { +#include <lua.h> +} + +#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 new file mode 100644 index 000000000..3b7ed5179 --- /dev/null +++ b/src/scriptapi_content.cpp @@ -0,0 +1,322 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "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); + + /* 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_content.h b/src/scriptapi_content.h new file mode 100644 index 000000000..801f3856e --- /dev/null +++ b/src/scriptapi_content.h @@ -0,0 +1,37 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_CONTENT_H_ +#define LUA_CONTENT_H_ + +extern "C" { +#include <lua.h> +} + +#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_ */ diff --git a/src/scriptapi_craft.cpp b/src/scriptapi_craft.cpp new file mode 100644 index 000000000..183eeb840 --- /dev/null +++ b/src/scriptapi_craft.cpp @@ -0,0 +1,454 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_craft.h" + +extern "C" { +#include <lauxlib.h> +} + +#include "script.h" +#include "scriptapi_types.h" +#include "scriptapi_common.h" +#include "scriptapi_item.h" + + +struct EnumString es_CraftMethod[] = +{ + {CRAFT_METHOD_NORMAL, "normal"}, + {CRAFT_METHOD_COOKING, "cooking"}, + {CRAFT_METHOD_FUEL, "fuel"}, + {0, NULL}, +}; + + +// helper for register_craft +bool read_craft_recipe_shaped(lua_State *L, int index, + int &width, std::vector<std::string> &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + int rowcount = 0; + while(lua_next(L, index) != 0){ + int colcount = 0; + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + int table2 = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table2) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.push_back(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + colcount++; + } + if(rowcount == 0){ + width = colcount; + } else { + if(colcount != width) + return false; + } + // removes value, keeps key for next iteration + lua_pop(L, 1); + rowcount++; + } + return width != 0; +} + +// helper for register_craft +bool read_craft_recipe_shapeless(lua_State *L, int index, + std::vector<std::string> &recipe) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.push_back(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} + +// helper for register_craft +bool read_craft_replacements(lua_State *L, int index, + CraftReplacements &replacements) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + lua_rawgeti(L, -1, 1); + if(!lua_isstring(L, -1)) + return false; + std::string replace_from = lua_tostring(L, -1); + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + if(!lua_isstring(L, -1)) + return false; + std::string replace_to = lua_tostring(L, -1); + lua_pop(L, 1); + replacements.pairs.push_back( + std::make_pair(replace_from, replace_to)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} +// register_craft({output=item, recipe={{item00,item10},{item01,item11}}) +int l_register_craft(lua_State *L) +{ + //infostream<<"register_craft"<<std::endl; + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; + + // Get the writable craft definition manager from the server + IWritableCraftDefManager *craftdef = + get_server(L)->getWritableCraftDefManager(); + + std::string type = getstringfield_default(L, table, "type", "shaped"); + + /* + CraftDefinitionShaped + */ + if(type == "shaped"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition is missing an output"); + + int width = 0; + std::vector<std::string> recipe; + lua_getfield(L, table, "recipe"); + 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)) + throw LuaError(L, "Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShaped( + output, width, recipe, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionShapeless + */ + else if(type == "shapeless"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition (shapeless)" + " is missing an output"); + + std::vector<std::string> recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError(L, "Crafting definition (shapeless)" + " is missing a recipe" + " (output=\"" + output + "\")"); + if(!read_craft_recipe_shapeless(L, -1, recipe)) + throw LuaError(L, "Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShapeless( + output, recipe, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionToolRepair + */ + else if(type == "toolrepair"){ + float additional_wear = getfloatfield_default(L, table, + "additional_wear", 0.0); + + CraftDefinition *def = new CraftDefinitionToolRepair( + additional_wear); + craftdef->registerCraft(def); + } + /* + CraftDefinitionCooking + */ + else if(type == "cooking"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition (cooking)" + " is missing an output"); + + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if(recipe == "") + throw LuaError(L, "Crafting definition (cooking)" + " is missing a recipe" + " (output=\"" + output + "\")"); + + float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (cooking output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionCooking( + output, recipe, cooktime, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionFuel + */ + else if(type == "fuel"){ + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if(recipe == "") + throw LuaError(L, "Crafting definition (fuel)" + " is missing a recipe"); + + float burntime = getfloatfield_default(L, table, "burntime", 1.0); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (fuel recipe=\"" + recipe + "\")"); + } + + CraftDefinition *def = new CraftDefinitionFuel( + recipe, burntime, replacements); + craftdef->registerCraft(def); + } + else + { + throw LuaError(L, "Unknown crafting definition type: \"" + type + "\""); + } + + lua_pop(L, 1); + return 0; /* number of results */ +} + +// get_craft_result(input) +int l_get_craft_result(lua_State *L) +{ + int input_i = 1; + std::string method_s = getstringfield_default(L, input_i, "method", "normal"); + enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method", + es_CraftMethod, CRAFT_METHOD_NORMAL); + int width = 1; + lua_getfield(L, input_i, "width"); + if(lua_isnumber(L, -1)) + width = luaL_checkinteger(L, -1); + lua_pop(L, 1); + lua_getfield(L, input_i, "items"); + std::vector<ItemStack> items = read_items(L, -1); + lua_pop(L, 1); // items + + IGameDef *gdef = get_server(L); + ICraftDefManager *cdef = gdef->cdef(); + CraftInput input(method, width, items); + CraftOutput output; + bool got = cdef->getCraftResult(input, output, true, gdef); + lua_newtable(L); // output table + if(got){ + ItemStack item; + item.deSerialize(output.item, gdef->idef()); + LuaItemStack::create(L, item); + lua_setfield(L, -2, "item"); + setintfield(L, -1, "time", output.time); + } else { + LuaItemStack::create(L, ItemStack()); + lua_setfield(L, -2, "item"); + setintfield(L, -1, "time", 0); + } + lua_newtable(L); // decremented input table + lua_pushstring(L, method_s.c_str()); + lua_setfield(L, -2, "method"); + lua_pushinteger(L, width); + lua_setfield(L, -2, "width"); + push_items(L, input.items); + lua_setfield(L, -2, "items"); + return 2; +} + +// get_craft_recipe(result item) +int l_get_craft_recipe(lua_State *L) +{ + int k = 0; + char tmp[20]; + int input_i = 1; + std::string o_item = luaL_checkstring(L,input_i); + + IGameDef *gdef = get_server(L); + ICraftDefManager *cdef = gdef->cdef(); + CraftInput input; + CraftOutput output(o_item,0); + bool got = cdef->getCraftRecipe(input, output, gdef); + lua_newtable(L); // output table + if(got){ + lua_newtable(L); + for(std::vector<ItemStack>::const_iterator + i = input.items.begin(); + i != input.items.end(); i++, k++) + { + if (i->empty()) + { + continue; + } + sprintf(tmp,"%d",k); + lua_pushstring(L,tmp); + lua_pushstring(L,i->name.c_str()); + lua_settable(L, -3); + } + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", input.width); + switch (input.method) { + case CRAFT_METHOD_NORMAL: + lua_pushstring(L,"normal"); + break; + case CRAFT_METHOD_COOKING: + lua_pushstring(L,"cooking"); + break; + case CRAFT_METHOD_FUEL: + lua_pushstring(L,"fuel"); + break; + default: + lua_pushstring(L,"unknown"); + } + lua_setfield(L, -2, "type"); + } else { + lua_pushnil(L); + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", 0); + } + return 1; +} + +// get_all_craft_recipes(result item) +int l_get_all_craft_recipes(lua_State *L) +{ + char tmp[20]; + int input_i = 1; + std::string o_item = luaL_checkstring(L,input_i); + IGameDef *gdef = get_server(L); + ICraftDefManager *cdef = gdef->cdef(); + CraftInput input; + CraftOutput output(o_item,0); + std::vector<CraftDefinition*> recipes_list = cdef->getCraftRecipes(output, gdef); + if (recipes_list.empty()) + { + lua_pushnil(L); + return 1; + } + // 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(std::vector<CraftDefinition*>::const_iterator + i = recipes_list.begin(); + i != recipes_list.end(); i++) + { + CraftOutput tmpout; + tmpout.item = ""; + tmpout.time = 0; + CraftDefinition *def = *i; + tmpout = def->getOutput(input, gdef); + if(tmpout.item.substr(0,output.item.length()) == output.item) + { + input = def->getInput(output, gdef); + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + lua_newtable(L); + int k = 0; + lua_newtable(L); + for(std::vector<ItemStack>::const_iterator + i = input.items.begin(); + i != input.items.end(); i++, k++) + { + if (i->empty()) continue; + sprintf(tmp,"%d",k); + lua_pushstring(L,tmp); + lua_pushstring(L,i->name.c_str()); + lua_settable(L, -3); + } + lua_setfield(L, -2, "items"); + setintfield(L, -1, "width", input.width); + switch (input.method) + { + case CRAFT_METHOD_NORMAL: + lua_pushstring(L,"normal"); + break; + case CRAFT_METHOD_COOKING: + lua_pushstring(L,"cooking"); + break; + case CRAFT_METHOD_FUEL: + lua_pushstring(L,"fuel"); + break; + default: + lua_pushstring(L,"unknown"); + } + lua_setfield(L, -2, "type"); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } + } + return 1; +} diff --git a/src/scriptapi_craft.h b/src/scriptapi_craft.h new file mode 100644 index 000000000..f28989fd9 --- /dev/null +++ b/src/scriptapi_craft.h @@ -0,0 +1,51 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_CRAFT_H_ +#define LUA_CRAFT_H_ + +#include <vector> + +extern "C" { +#include <lua.h> +} + +#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<std::string> &recipe); +bool read_craft_recipe_shaped(lua_State *L, int index, + int &width, std::vector<std::string> &recipe); + +extern struct EnumString es_CraftMethod[]; + +#endif /* LUA_CRAFT_H_ */ diff --git a/src/scriptapi_entity.cpp b/src/scriptapi_entity.cpp new file mode 100644 index 000000000..c15f801a6 --- /dev/null +++ b/src/scriptapi_entity.cpp @@ -0,0 +1,293 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_entity.h" + +extern "C" { +#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) +{ + // 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 +} + +/* + 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="<<id<<" name=\"" + <<name<<"\""<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.registered_entities[name] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_entities"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushstring(L, name); + lua_gettable(L, -2); + // Should be a table, which we will use as a prototype + //luaL_checktype(L, -1, LUA_TTABLE); + if(lua_type(L, -1) != LUA_TTABLE){ + errorstream<<"LuaEntity name \""<<name<<"\" not defined"<<std::endl; + return false; + } + int prototype_table = lua_gettop(L); + //dump2(L, "prototype_table"); + + // Create entity object + lua_newtable(L); + int object = lua_gettop(L); + + // Set object metatable + lua_pushvalue(L, prototype_table); + lua_setmetatable(L, -2); + + // Add object reference + // This should be userdata with metatable ObjectRef + objectref_get(L, id); + luaL_checktype(L, -1, LUA_TUSERDATA); + if(!luaL_checkudata(L, -1, "ObjectRef")) + luaL_typerror(L, -1, "ObjectRef"); + lua_setfield(L, -2, "object"); + + // minetest.luaentities[id] = object + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "luaentities"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnumber(L, id); // Push id + lua_pushvalue(L, object); // Copy object to top of stack + lua_settable(L, -3); + + return true; +} + +void scriptapi_luaentity_activate(lua_State *L, u16 id, + const std::string &staticdata, u32 dtime_s) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + verbosestream<<"scriptapi_luaentity_activate: id="<<id<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.luaentities[id] + luaentity_get(L, id); + int object = lua_gettop(L); + + // Get on_activate function + lua_pushvalue(L, object); + lua_getfield(L, -1, "on_activate"); + if(!lua_isnil(L, -1)){ + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, object); // self + lua_pushlstring(L, staticdata.c_str(), staticdata.size()); + lua_pushinteger(L, dtime_s); + // Call with 3 arguments, 0 results + if(lua_pcall(L, 3, 0, 0)) + script_error(L, "error running function on_activate: %s\n", + lua_tostring(L, -1)); + } +} + +void scriptapi_luaentity_rm(lua_State *L, u16 id) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + verbosestream<<"scriptapi_luaentity_rm: id="<<id<<std::endl; + + // Get minetest.luaentities table + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "luaentities"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + + // Set luaentities[id] = nil + lua_pushnumber(L, id); // Push id + lua_pushnil(L); + lua_settable(L, objectstable); + + lua_pop(L, 2); // pop luaentities, minetest +} + +std::string scriptapi_luaentity_get_staticdata(lua_State *L, u16 id) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_luaentity_get_staticdata: id="<<id<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.luaentities[id] + luaentity_get(L, id); + int object = lua_gettop(L); + + // Get get_staticdata function + lua_pushvalue(L, object); + lua_getfield(L, -1, "get_staticdata"); + if(lua_isnil(L, -1)) + return ""; + + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, object); // self + // Call with 1 arguments, 1 results + if(lua_pcall(L, 1, 1, 0)) + script_error(L, "error running function get_staticdata: %s\n", + lua_tostring(L, -1)); + + size_t len=0; + const char *s = lua_tolstring(L, -1, &len); + return std::string(s, len); +} + +void scriptapi_luaentity_get_properties(lua_State *L, u16 id, + ObjectProperties *prop) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_luaentity_get_properties: id="<<id<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.luaentities[id] + luaentity_get(L, id); + //int object = lua_gettop(L); + + // Set default values that differ from ObjectProperties defaults + prop->hp_max = 10; + + /* Read stuff */ + + 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); + + // Deprecated: read object properties directly + read_object_properties(L, -1, prop); + + // Read initial_properties + lua_getfield(L, -1, "initial_properties"); + read_object_properties(L, -1, prop); + lua_pop(L, 1); +} + +void scriptapi_luaentity_step(lua_State *L, u16 id, float dtime) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.luaentities[id] + luaentity_get(L, id); + int object = lua_gettop(L); + // State: object is at top of stack + // Get step function + lua_getfield(L, -1, "on_step"); + if(lua_isnil(L, -1)) + return; + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, object); // self + lua_pushnumber(L, dtime); // dtime + // Call with 2 arguments, 0 results + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error running function 'on_step': %s\n", lua_tostring(L, -1)); +} + +// Calls entity:on_punch(ObjectRef puncher, time_from_last_punch, +// tool_capabilities, direction) +void scriptapi_luaentity_punch(lua_State *L, u16 id, + ServerActiveObject *puncher, float time_from_last_punch, + const ToolCapabilities *toolcap, v3f dir) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.luaentities[id] + luaentity_get(L, id); + int object = lua_gettop(L); + // State: object is at top of stack + // Get function + lua_getfield(L, -1, "on_punch"); + if(lua_isnil(L, -1)) + return; + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, object); // self + objectref_get_or_create(L, puncher); // Clicker reference + lua_pushnumber(L, time_from_last_punch); + push_tool_capabilities(L, *toolcap); + push_v3f(L, dir); + // Call with 5 arguments, 0 results + if(lua_pcall(L, 5, 0, 0)) + script_error(L, "error running function 'on_punch': %s\n", lua_tostring(L, -1)); +} + +// Calls entity:on_rightclick(ObjectRef clicker) +void scriptapi_luaentity_rightclick(lua_State *L, u16 id, + ServerActiveObject *clicker) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_luaentity_step: id="<<id<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.luaentities[id] + luaentity_get(L, id); + int object = lua_gettop(L); + // State: object is at top of stack + // Get function + lua_getfield(L, -1, "on_rightclick"); + if(lua_isnil(L, -1)) + return; + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_pushvalue(L, object); // self + objectref_get_or_create(L, clicker); // Clicker reference + // Call with 2 arguments, 0 results + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error running function 'on_rightclick': %s\n", lua_tostring(L, -1)); +} diff --git a/src/scriptapi_entity.h b/src/scriptapi_entity.h new file mode 100644 index 000000000..e08743258 --- /dev/null +++ b/src/scriptapi_entity.h @@ -0,0 +1,54 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_ENTITY_H_ +#define LUA_ENTITY_H_ + +extern "C" { +#include <lua.h> +} + +#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 new file mode 100644 index 000000000..4e068e377 --- /dev/null +++ b/src/scriptapi_env.cpp @@ -0,0 +1,908 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "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 "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<std::string> m_trigger_contents; + std::set<std::string> m_required_neighbors; + float m_trigger_interval; + u32 m_trigger_chance; +public: + LuaABM(lua_State *L, int id, + const std::set<std::string> &trigger_contents, + const std::set<std::string> &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<std::string> getTriggerContents() + { + return m_trigger_contents; + } + virtual std::set<std::string> 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); + MapNode n = env->getMap().getNodeNoEx(pos); + 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()"<<std::endl; + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->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()"<<std::endl; + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->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()"<<std::endl; + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->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."<<std::endl; + return 0; +} + +// EnvRef:add_firefly(pos) +// pos = {x=num, y=num, z=num} +int EnvRef::l_add_firefly(lua_State *L) +{ + infostream<<"EnvRef::l_add_firefly(): C++ mobs have been removed." + <<" Doing nothing."<<std::endl; + return 0; +} + +// EnvRef:get_player_by_name(name) +int EnvRef::l_get_player_by_name(lua_State *L) +{ + EnvRef *o = checkobject(L, 1); + ServerEnvironment *env = o->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<u16> ids = env->getObjectsInsideRadius(pos, radius); + lua_newtable(L); + int table = lua_gettop(L); + for(std::set<u16>::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<content_t> 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<v3s16> list; + getFacePositions(list, d); + for(std::list<v3s16>::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<content_t> 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_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"<<std::endl; +} + +EnvRef::~EnvRef() +{ + //infostream<<"EnvRef destructing"<<std::endl; +} + +// Creates an EnvRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void EnvRef::create(lua_State *L, ServerEnvironment *env) +{ + EnvRef *o = new EnvRef(env); + //infostream<<"EnvRef::create: o="<<o<<std::endl; + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void EnvRef::set_null(lua_State *L) +{ + EnvRef *o = checkobject(L, -1); + o->m_env = NULL; +} + +void EnvRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char EnvRef::className[] = "EnvRef"; +const luaL_reg EnvRef::methods[] = { + luamethod(EnvRef, set_node), + luamethod(EnvRef, add_node), + luamethod(EnvRef, remove_node), + luamethod(EnvRef, get_node), + luamethod(EnvRef, get_node_or_nil), + luamethod(EnvRef, get_node_light), + luamethod(EnvRef, place_node), + luamethod(EnvRef, dig_node), + luamethod(EnvRef, punch_node), + luamethod(EnvRef, add_entity), + luamethod(EnvRef, add_item), + luamethod(EnvRef, add_rat), + luamethod(EnvRef, add_firefly), + luamethod(EnvRef, get_meta), + luamethod(EnvRef, get_node_timer), + luamethod(EnvRef, get_player_by_name), + luamethod(EnvRef, get_objects_inside_radius), + luamethod(EnvRef, set_timeofday), + luamethod(EnvRef, get_timeofday), + luamethod(EnvRef, find_node_near), + luamethod(EnvRef, find_nodes_in_area), + luamethod(EnvRef, get_perlin), + luamethod(EnvRef, get_perlin_map), + luamethod(EnvRef, clear_objects), + luamethod(EnvRef, spawn_tree), + {0,0} +}; + +void scriptapi_environment_on_generated(lua_State *L, v3s16 minp, v3s16 maxp, + u32 blockseed) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_environment_on_generated"<<std::endl; + StackUnroller stack_unroller(L); + + // 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); + scriptapi_run_callbacks(L, 3, RUN_CALLBACKS_MODE_FIRST); +} + +void scriptapi_environment_step(lua_State *L, float dtime) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + //infostream<<"scriptapi_environment_step"<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.registered_globalsteps + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_globalsteps"); + // Call callbacks + lua_pushnumber(L, dtime); + scriptapi_run_callbacks(L, 1, RUN_CALLBACKS_MODE_FIRST); +} + +void scriptapi_add_environment(lua_State *L, ServerEnvironment *env) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + verbosestream<<"scriptapi_add_environment"<<std::endl; + StackUnroller stack_unroller(L); + + // Create EnvRef on stack + EnvRef::create(L, env); + int envref = lua_gettop(L); + + // minetest.env = envref + lua_getglobal(L, "minetest"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushvalue(L, envref); + lua_setfield(L, -2, "env"); + + // Store environment as light userdata in registry + lua_pushlightuserdata(L, env); + lua_setfield(L, LUA_REGISTRYINDEX, "minetest_env"); + + /* + Add ActiveBlockModifiers to environment + */ + + // 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); + + if(lua_istable(L, registered_abms)){ + int table = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + int id = lua_tonumber(L, -2); + int current_abm = lua_gettop(L); + + std::set<std::string> 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<std::string> 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_env.h b/src/scriptapi_env.h new file mode 100644 index 000000000..1599969a4 --- /dev/null +++ b/src/scriptapi_env.h @@ -0,0 +1,164 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_ENVIRONMENT_H_ +#define LUA_ENVIRONMENT_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include "environment.h" + +/* + EnvRef +*/ + +class EnvRef +{ +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) + // 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) + // pos = {x=num, y=num, z=num} + static int l_remove_node(lua_State *L); + + // EnvRef:get_node(pos) + // pos = {x=num, y=num, z=num} + static int l_get_node(lua_State *L); + + // EnvRef: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) + // 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) + // pos = {x=num, y=num, z=num} + static int l_place_node(lua_State *L); + + // EnvRef:dig_node(pos) + // pos = {x=num, y=num, z=num} + static int l_dig_node(lua_State *L); + + // EnvRef:punch_node(pos) + // pos = {x=num, y=num, z=num} + static int l_punch_node(lua_State *L); + + // EnvRef:get_meta(pos) + static int l_get_meta(lua_State *L); + + // EnvRef:get_node_timer(pos) + static int l_get_node_timer(lua_State *L); + + // EnvRef: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 + // 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) + static int l_get_player_by_name(lua_State *L); + + // EnvRef:get_objects_inside_radius(pos, radius) + static int l_get_objects_inside_radius(lua_State *L); + + // EnvRef:set_timeofday(val) + // val = 0...1 + static int l_set_timeofday(lua_State *L); + + // EnvRef:get_timeofday() -> 0...1 + static int l_get_timeofday(lua_State *L); + + // EnvRef: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 + // 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 + static int l_get_perlin(lua_State *L); + + // EnvRef:get_perlin_map(noiseparams, size) + // returns world-specific PerlinNoiseMap + static int l_get_perlin_map(lua_State *L); + + // EnvRef:clear_objects() + // clear all objects in the environment + static int l_clear_objects(lua_State *L); + + static int l_spawn_tree(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); +}; + +/*****************************************************************************/ +/* 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); + +#endif /* LUA_ENVIRONMENT_H_ */ diff --git a/src/scriptapi_inventory.cpp b/src/scriptapi_inventory.cpp new file mode 100644 index 000000000..bb40748db --- /dev/null +++ b/src/scriptapi_inventory.cpp @@ -0,0 +1,726 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_inventory.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" + + +/* + InvRef +*/ +InvRef* InvRef::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 *(InvRef**)ud; // unbox pointer +} + +Inventory* InvRef::getinv(lua_State *L, InvRef *ref) +{ + return get_server(L)->getInventory(ref->m_loc); +} + +InventoryList* InvRef::getlist(lua_State *L, InvRef *ref, + const char *listname) +{ + Inventory *inv = getinv(L, ref); + if(!inv) + return NULL; + return inv->getList(listname); +} + +void InvRef::reportInventoryChange(lua_State *L, InvRef *ref) +{ + // Inform other things that the inventory has changed + get_server(L)->setInventoryModified(ref->m_loc); +} + +// Exported functions + +// garbage collector +int InvRef::gc_object(lua_State *L) { + InvRef *o = *(InvRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// is_empty(self, listname) -> true/false +int InvRef::l_is_empty(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list && list->getUsedSlots() > 0){ + lua_pushboolean(L, false); + } else { + lua_pushboolean(L, true); + } + return 1; +} + +// get_size(self, listname) +int InvRef::l_get_size(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushinteger(L, list->getSize()); + } else { + lua_pushinteger(L, 0); + } + return 1; +} + +// get_width(self, listname) +int InvRef::l_get_width(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushinteger(L, list->getWidth()); + } else { + lua_pushinteger(L, 0); + } + return 1; +} + +// set_size(self, listname, size) +int InvRef::l_set_size(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int newsize = luaL_checknumber(L, 3); + Inventory *inv = getinv(L, ref); + if(newsize == 0){ + inv->deleteList(listname); + reportInventoryChange(L, ref); + return 0; + } + InventoryList *list = inv->getList(listname); + if(list){ + list->setSize(newsize); + } else { + list = inv->addList(listname, newsize); + } + reportInventoryChange(L, ref); + return 0; +} + +// set_width(self, listname, size) +int InvRef::l_set_width(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int newwidth = luaL_checknumber(L, 3); + Inventory *inv = getinv(L, ref); + InventoryList *list = inv->getList(listname); + if(list){ + list->setWidth(newwidth); + } else { + return 0; + } + reportInventoryChange(L, ref); + return 0; +} + +// get_stack(self, listname, i) -> itemstack +int InvRef::l_get_stack(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int i = luaL_checknumber(L, 3) - 1; + InventoryList *list = getlist(L, ref, listname); + ItemStack item; + if(list != NULL && i >= 0 && i < (int) list->getSize()) + item = list->getItem(i); + LuaItemStack::create(L, item); + return 1; +} + +// set_stack(self, listname, i, stack) -> true/false +int InvRef::l_set_stack(lua_State *L) +{ + 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); + InventoryList *list = getlist(L, ref, listname); + if(list != NULL && i >= 0 && i < (int) list->getSize()){ + list->changeItem(i, newitem); + reportInventoryChange(L, ref); + lua_pushboolean(L, true); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// get_list(self, listname) -> list or nil +int InvRef::l_get_list(lua_State *L) +{ + 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); + return 1; +} + +// set_list(self, listname, list) +int InvRef::l_set_list(lua_State *L) +{ + 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()); + else + inventory_set_list_from_lua(inv, listname, L, 3); + reportInventoryChange(L, ref); + return 0; +} + +// add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack +// Returns the leftover stack +int InvRef::l_add_item(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3); + InventoryList *list = getlist(L, ref, listname); + if(list){ + ItemStack leftover = list->addItem(item); + if(leftover.count != item.count) + reportInventoryChange(L, ref); + LuaItemStack::create(L, leftover); + } else { + LuaItemStack::create(L, item); + } + return 1; +} + +// room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false +// Returns true if the item completely fits into the list +int InvRef::l_room_for_item(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushboolean(L, list->roomForItem(item)); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false +// Returns true if the list contains the given count of the given item name +int InvRef::l_contains_item(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushboolean(L, list->containsItem(item)); + } else { + lua_pushboolean(L, false); + } + return 1; +} + +// remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack +// Returns the items that were actually removed +int InvRef::l_remove_item(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3); + InventoryList *list = getlist(L, ref, listname); + if(list){ + ItemStack removed = list->removeItem(item); + if(!removed.empty()) + reportInventoryChange(L, ref); + LuaItemStack::create(L, removed); + } else { + LuaItemStack::create(L, ItemStack()); + } + return 1; +} + +// get_location() -> location (like minetest.get_inventory(location)) +int InvRef::l_get_location(lua_State *L) +{ + InvRef *ref = checkobject(L, 1); + const InventoryLocation &loc = ref->m_loc; + switch(loc.type){ + case InventoryLocation::PLAYER: + lua_newtable(L); + lua_pushstring(L, "player"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, loc.name.c_str()); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::NODEMETA: + lua_newtable(L); + lua_pushstring(L, "nodemeta"); + lua_setfield(L, -2, "type"); + push_v3s16(L, loc.p); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::DETACHED: + lua_newtable(L); + lua_pushstring(L, "detached"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, loc.name.c_str()); + lua_setfield(L, -2, "name"); + return 1; + case InventoryLocation::UNDEFINED: + case InventoryLocation::CURRENT_PLAYER: + break; + } + lua_newtable(L); + lua_pushstring(L, "undefined"); + lua_setfield(L, -2, "type"); + return 1; +} + + +InvRef::InvRef(const InventoryLocation &loc): + m_loc(loc) +{ +} + +InvRef::~InvRef() +{ +} + +// Creates an InvRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void InvRef::create(lua_State *L, const InventoryLocation &loc) +{ + InvRef *o = new InvRef(loc); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} +void InvRef::createPlayer(lua_State *L, Player *player) +{ + InventoryLocation loc; + loc.setPlayer(player->getName()); + create(L, loc); +} +void InvRef::createNodeMeta(lua_State *L, v3s16 p) +{ + InventoryLocation loc; + loc.setNodeMeta(p); + create(L, loc); +} + +void InvRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char InvRef::className[] = "InvRef"; +const luaL_reg InvRef::methods[] = { + luamethod(InvRef, is_empty), + luamethod(InvRef, get_size), + luamethod(InvRef, set_size), + luamethod(InvRef, get_width), + luamethod(InvRef, set_width), + luamethod(InvRef, get_stack), + luamethod(InvRef, set_stack), + luamethod(InvRef, get_list), + luamethod(InvRef, set_list), + luamethod(InvRef, add_item), + luamethod(InvRef, room_for_item), + luamethod(InvRef, contains_item), + luamethod(InvRef, remove_item), + luamethod(InvRef, get_location), + {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<ItemStack> items; + for(u32 i=0; i<invlist->getSize(); 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<ItemStack> items = read_items(L, tableindex); + int listsize = (forcesize != -1) ? forcesize : items.size(); + InventoryList *invlist = inv->addList(name, listsize); + int index = 0; + for(std::vector<ItemStack>::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) +{ + 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(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 \""<<name<<"\" not defined"<<std::endl; + lua_pop(L, 1); + return false; + } + lua_getfield(L, -1, callbackname); + lua_remove(L, -2); + // Should be a function or nil + if(lua_type(L, -1) == LUA_TFUNCTION) + { + return true; + } + else if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + return false; + } + else + { + errorstream<<"Detached inventory \""<<name<<"\" callback \"" + <<callbackname<<"\" is not a function"<<std::endl; + lua_pop(L, 1); + return false; + } +} + +// 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) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, 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 + 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_move should return a number"); + return luaL_checkinteger(L, -1); +} + +// 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) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, 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 + 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_put should return a number"); + return luaL_checkinteger(L, -1); +} + +// 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) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, 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 + 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_take should return a number"); + return luaL_checkinteger(L, -1); +} + +// 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) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, 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 + 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_detached_inventory_on_put(lua_State *L, + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, 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 + 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_detached_inventory_on_take(lua_State *L, + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, 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 + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +// create_detached_inventory_raw(name) +int l_create_detached_inventory_raw(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + if(get_server(L)->createDetachedInventory(name) != NULL){ + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + }else{ + lua_pushnil(L); + } + return 1; +} diff --git a/src/scriptapi_inventory.h b/src/scriptapi_inventory.h new file mode 100644 index 000000000..14f4fe026 --- /dev/null +++ b/src/scriptapi_inventory.h @@ -0,0 +1,165 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_INVENTORY_H_ +#define LUA_INVENTORY_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include "inventorymanager.h" +#include "player.h" +#include "serverobject.h" +#include "inventory.h" + +/* + InvRef +*/ + +class InvRef +{ +private: + InventoryLocation m_loc; + + static const char className[]; + static const luaL_reg methods[]; + + static InvRef *checkobject(lua_State *L, int narg); + + static Inventory* getinv(lua_State *L, InvRef *ref); + + static InventoryList* getlist(lua_State *L, InvRef *ref, + const char *listname); + + static void reportInventoryChange(lua_State *L, InvRef *ref); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // is_empty(self, listname) -> true/false + static int l_is_empty(lua_State *L); + + // get_size(self, listname) + static int l_get_size(lua_State *L); + + // get_width(self, listname) + static int l_get_width(lua_State *L); + + // set_size(self, listname, size) + static int l_set_size(lua_State *L); + + // set_width(self, listname, size) + static int l_set_width(lua_State *L); + + // get_stack(self, listname, i) -> itemstack + static int l_get_stack(lua_State *L); + + // set_stack(self, listname, i, stack) -> true/false + static int l_set_stack(lua_State *L); + + // get_list(self, listname) -> list or nil + static int l_get_list(lua_State *L); + + // set_list(self, listname, list) + static int l_set_list(lua_State *L); + + // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack + // Returns the leftover stack + static int l_add_item(lua_State *L); + + // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false + // Returns true if the item completely fits into the list + static int l_room_for_item(lua_State *L); + + // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false + // Returns true if the list contains the given count of the given item name + static int l_contains_item(lua_State *L); + + // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack + // Returns the items that were actually removed + static int l_remove_item(lua_State *L); + + // get_location() -> location (like minetest.get_inventory(location)) + static int l_get_location(lua_State *L); + +public: + InvRef(const InventoryLocation &loc); + + ~InvRef(); + + // Creates an InvRef 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, const InventoryLocation &loc); + static void createPlayer(lua_State *L, Player *player); + static void createNodeMeta(lua_State *L, v3s16 p); + 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); + +/*****************************************************************************/ +/* 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); + +/*****************************************************************************/ +/* Mod API */ +/*****************************************************************************/ +int l_create_detached_inventory_raw(lua_State *L); +int l_get_inventory(lua_State *L); + +#endif /* LUA_INVENTORY_H_ */ diff --git a/src/scriptapi_item.cpp b/src/scriptapi_item.cpp new file mode 100644 index 000000000..b266d856d --- /dev/null +++ b/src/scriptapi_item.cpp @@ -0,0 +1,718 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "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: "<<name<<std::endl; + } else { + throw LuaError(L, "register_item_raw: name is not defined or not a string"); + } + + // Check if on_use is defined + + ItemDefinition def; + // Set a distinctive default value to check if this is set + def.node_placement_prediction = "__default"; + + // Read the item definition + def = read_item_definition(L, table, def); + + // Default to having client-side placement prediction for nodes + // ("" in item definition sets it off) + if(def.node_placement_prediction == "__default"){ + if(def.type == ITEM_NODE) + def.node_placement_prediction = name; + else + def.node_placement_prediction = ""; + } + + // Register item definition + idef->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 \""<<name<<"\" not defined"<<std::endl; + lua_pop(L, 1); + + // Try minetest.nodedef_default instead + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "nodedef_default"); + lua_remove(L, -2); + luaL_checktype(L, -1, LUA_TTABLE); + } + lua_getfield(L, -1, callbackname); + lua_remove(L, -2); + // Should be a function or nil + if(lua_type(L, -1) == LUA_TFUNCTION) + { + return true; + } + else if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + return false; + } + else + { + errorstream<<"Item \""<<name<<"\" callback \"" + <<callbackname<<" is not a function"<<std::endl; + lua_pop(L, 1); + return false; + } +} + +bool scriptapi_item_on_drop(lua_State *L, ItemStack &item, + ServerActiveObject *dropper, v3f pos) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_item_callback(L, item.name.c_str(), "on_drop")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectref_get_or_create(L, dropper); + pushFloatPos(L, pos); + if(lua_pcall(L, 3, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L, -1); + return true; +} + +bool scriptapi_item_on_place(lua_State *L, ItemStack &item, + ServerActiveObject *placer, const PointedThing &pointed) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_item_callback(L, item.name.c_str(), "on_place")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectref_get_or_create(L, placer); + push_pointed_thing(L, pointed); + if(lua_pcall(L, 3, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L, -1); + return true; +} + +bool scriptapi_item_on_use(lua_State *L, ItemStack &item, + ServerActiveObject *user, const PointedThing &pointed) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_item_callback(L, item.name.c_str(), "on_use")) + return false; + + // Call function + LuaItemStack::create(L, item); + objectref_get_or_create(L, user); + push_pointed_thing(L, pointed); + if(lua_pcall(L, 3, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + if(!lua_isnil(L, -1)) + item = read_item(L, -1); + return true; +} + +// garbage collector +int LuaItemStack::gc_object(lua_State *L) +{ + LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// is_empty(self) -> true/false +int LuaItemStack::l_is_empty(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushboolean(L, item.empty()); + return 1; +} + +// get_name(self) -> string +int LuaItemStack::l_get_name(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushstring(L, item.name.c_str()); + return 1; +} + +// get_count(self) -> number +int LuaItemStack::l_get_count(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.count); + return 1; +} + +// get_wear(self) -> number +int LuaItemStack::l_get_wear(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.wear); + return 1; +} + +// get_metadata(self) -> string +int LuaItemStack::l_get_metadata(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); + return 1; +} + +// clear(self) -> true +int LuaItemStack::l_clear(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + o->m_stack.clear(); + lua_pushboolean(L, true); + return 1; +} + +// replace(self, itemstack or itemstring or table or nil) -> true +int LuaItemStack::l_replace(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + o->m_stack = read_item(L, 2); + lua_pushboolean(L, true); + return 1; +} + +// to_string(self) -> string +int LuaItemStack::l_to_string(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + std::string itemstring = o->m_stack.getItemString(); + lua_pushstring(L, itemstring.c_str()); + return 1; +} + +// to_table(self) -> table or nil +int LuaItemStack::l_to_table(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + const ItemStack &item = o->m_stack; + if(item.empty()) + { + lua_pushnil(L); + } + else + { + lua_newtable(L); + lua_pushstring(L, item.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushinteger(L, item.count); + lua_setfield(L, -2, "count"); + lua_pushinteger(L, item.wear); + lua_setfield(L, -2, "wear"); + lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); + lua_setfield(L, -2, "metadata"); + } + return 1; +} + +// get_stack_max(self) -> number +int LuaItemStack::l_get_stack_max(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.getStackMax(get_server(L)->idef())); + return 1; +} + +// get_free_space(self) -> number +int LuaItemStack::l_get_free_space(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.freeSpace(get_server(L)->idef())); + return 1; +} + +// is_known(self) -> true/false +// Checks if the item is defined. +int LuaItemStack::l_is_known(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + bool is_known = item.isKnown(get_server(L)->idef()); + lua_pushboolean(L, is_known); + return 1; +} + +// get_definition(self) -> table +// Returns the item definition table from minetest.registered_items, +// or a fallback one (name="unknown") +int LuaItemStack::l_get_definition(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + // Get minetest.registered_items[name] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_items"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, item.name.c_str()); + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_getfield(L, -1, "unknown"); + } + return 1; +} + +// get_tool_capabilities(self) -> table +// Returns the effective tool digging properties. +// Returns those of the hand ("") if this item has none associated. +int LuaItemStack::l_get_tool_capabilities(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + const ToolCapabilities &prop = + item.getToolCapabilities(get_server(L)->idef()); + push_tool_capabilities(L, prop); + return 1; +} + +// add_wear(self, amount) -> true/false +// The range for "amount" is [0,65535]. Wear is only added if the item +// is a tool. Adding wear might destroy the item. +// Returns true if the item is (or was) a tool. +int LuaItemStack::l_add_wear(lua_State *L) +{ + 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()); + lua_pushboolean(L, result); + return 1; +} + +// add_item(self, itemstack or itemstring or table or nil) -> itemstack +// Returns leftover item stack +int LuaItemStack::l_add_item(lua_State *L) +{ + 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()); + create(L, leftover); + return 1; +} + +// item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack +// First return value is true iff the new item fits fully into the stack +// Second return value is the would-be-left-over item stack +int LuaItemStack::l_item_fits(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + ItemStack newitem = read_item(L, 2); + ItemStack restitem; + bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef()); + lua_pushboolean(L, fits); // first return value + create(L, restitem); // second return value + return 2; +} + +// take_item(self, takecount=1) -> itemstack +int LuaItemStack::l_take_item(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + u32 takecount = 1; + if(!lua_isnone(L, 2)) + takecount = luaL_checkinteger(L, 2); + ItemStack taken = item.takeItem(takecount); + create(L, taken); + return 1; +} + +// peek_item(self, peekcount=1) -> itemstack +int LuaItemStack::l_peek_item(lua_State *L) +{ + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + u32 peekcount = 1; + if(!lua_isnone(L, 2)) + peekcount = lua_tointeger(L, 2); + ItemStack peekaboo = item.peekItem(peekcount); + create(L, peekaboo); + return 1; +} + +LuaItemStack::LuaItemStack(const ItemStack &item): + m_stack(item) +{ +} + +LuaItemStack::~LuaItemStack() +{ +} + +const ItemStack& LuaItemStack::getItem() const +{ + return m_stack; +} +ItemStack& LuaItemStack::getItem() +{ + return m_stack; +} + +// LuaItemStack(itemstack or itemstring or table or nil) +// Creates an LuaItemStack and leaves it on top of stack +int LuaItemStack::create_object(lua_State *L) +{ + ItemStack item = read_item(L, 1); + LuaItemStack *o = new LuaItemStack(item); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} +// Not callable from Lua +int LuaItemStack::create(lua_State *L, const ItemStack &item) +{ + LuaItemStack *o = new LuaItemStack(item); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaItemStack* LuaItemStack::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 *(LuaItemStack**)ud; // unbox pointer +} + +void LuaItemStack::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil)) + lua_register(L, className, create_object); +} + +const char LuaItemStack::className[] = "ItemStack"; +const luaL_reg LuaItemStack::methods[] = { + luamethod(LuaItemStack, is_empty), + luamethod(LuaItemStack, get_name), + luamethod(LuaItemStack, get_count), + luamethod(LuaItemStack, get_wear), + luamethod(LuaItemStack, get_metadata), + luamethod(LuaItemStack, clear), + luamethod(LuaItemStack, replace), + luamethod(LuaItemStack, to_string), + luamethod(LuaItemStack, to_table), + luamethod(LuaItemStack, get_stack_max), + luamethod(LuaItemStack, get_free_space), + luamethod(LuaItemStack, is_known), + luamethod(LuaItemStack, get_definition), + luamethod(LuaItemStack, get_tool_capabilities), + luamethod(LuaItemStack, add_wear), + luamethod(LuaItemStack, add_item), + luamethod(LuaItemStack, item_fits), + luamethod(LuaItemStack, take_item), + luamethod(LuaItemStack, peek_item), + {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" + <<": "<<itemstring<<std::endl; + return ItemStack(); + } + } + else if(lua_istable(L, index)) + { + // Convert from table + IItemDefManager *idef = get_server(L)->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"); + } +} + +std::vector<ItemStack> read_items(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + std::vector<ItemStack> 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); + } + return items; +} + +// creates a table of ItemStacks +void push_items(lua_State *L, const std::vector<ItemStack> &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<items.size(); i++){ + ItemStack item = items[i]; + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + LuaItemStack::create(L, item); + 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 +} diff --git a/src/scriptapi_item.h b/src/scriptapi_item.h new file mode 100644 index 000000000..e0f213990 --- /dev/null +++ b/src/scriptapi_item.h @@ -0,0 +1,166 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_ITEM_H_ +#define LUA_ITEM_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include <vector> + +#include "itemdef.h" +#include "content_sao.h" +#include "util/pointedthing.h" +#include "inventory.h" + +#include "inventory.h" + +ItemStack read_item(lua_State *L, int index); +std::vector<ItemStack> read_items(lua_State *L, int index); +void push_items(lua_State *L, const std::vector<ItemStack> &items); + +class LuaItemStack +{ +private: + ItemStack m_stack; + + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // is_empty(self) -> true/false + static int l_is_empty(lua_State *L); + + // get_name(self) -> string + static int l_get_name(lua_State *L); + + // get_count(self) -> number + static int l_get_count(lua_State *L); + + // get_wear(self) -> number + static int l_get_wear(lua_State *L); + + // get_metadata(self) -> string + static int l_get_metadata(lua_State *L); + + // clear(self) -> true + static int l_clear(lua_State *L); + + // replace(self, itemstack or itemstring or table or nil) -> true + static int l_replace(lua_State *L); + + // to_string(self) -> string + static int l_to_string(lua_State *L); + + // to_table(self) -> table or nil + static int l_to_table(lua_State *L); + + // get_stack_max(self) -> number + static int l_get_stack_max(lua_State *L); + + // get_free_space(self) -> number + static int l_get_free_space(lua_State *L); + + // is_known(self) -> true/false + // Checks if the item is defined. + static int l_is_known(lua_State *L); + + // get_definition(self) -> table + // Returns the item definition table from minetest.registered_items, + // or a fallback one (name="unknown") + static int l_get_definition(lua_State *L); + + // get_tool_capabilities(self) -> table + // Returns the effective tool digging properties. + // Returns those of the hand ("") if this item has none associated. + static int l_get_tool_capabilities(lua_State *L); + + // add_wear(self, amount) -> true/false + // The range for "amount" is [0,65535]. Wear is only added if the item + // is a tool. Adding wear might destroy the item. + // Returns true if the item is (or was) a tool. + static int l_add_wear(lua_State *L); + + // add_item(self, itemstack or itemstring or table or nil) -> itemstack + // Returns leftover item stack + static int l_add_item(lua_State *L); + + // item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack + // First return value is true iff the new item fits fully into the stack + // Second return value is the would-be-left-over item stack + static int l_item_fits(lua_State *L); + + // take_item(self, takecount=1) -> itemstack + static int l_take_item(lua_State *L); + + // peek_item(self, peekcount=1) -> itemstack + static int l_peek_item(lua_State *L); + +public: + LuaItemStack(const ItemStack &item); + ~LuaItemStack(); + + const ItemStack& getItem() const; + ItemStack& getItem(); + + // LuaItemStack(itemstack or itemstring or table or nil) + // Creates an LuaItemStack and leaves it on top of stack + static int create_object(lua_State *L); + // Not callable from Lua + static int create(lua_State *L, const ItemStack &item); + static LuaItemStack* checkobject(lua_State *L, int narg); + static void Register(lua_State *L); + +}; + +/*****************************************************************************/ +/* Mod API */ +/*****************************************************************************/ +int l_register_item_raw(lua_State *L); +int l_register_alias_raw(lua_State *L); + +/*****************************************************************************/ +/* 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()); + +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); + + +#endif /* LUA_ITEM_H_ */ diff --git a/src/scriptapi_node.cpp b/src/scriptapi_node.cpp new file mode 100644 index 000000000..5ffcaeb9d --- /dev/null +++ b/src/scriptapi_node.cpp @@ -0,0 +1,243 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "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_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<std::string, std::string> &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<std::string, std::string>::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + 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 new file mode 100644 index 000000000..665b58bfc --- /dev/null +++ b/src/scriptapi_node.h @@ -0,0 +1,55 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_NODE_H_ +#define LUA_NODE_H_ + +#include <iostream> +#include <map> + +extern "C" { +#include <lua.h> +} + +#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<std::string, std::string> &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/scriptapi_nodemeta.cpp b/src/scriptapi_nodemeta.cpp new file mode 100644 index 000000000..b66c9a023 --- /dev/null +++ b/src/scriptapi_nodemeta.cpp @@ -0,0 +1,571 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_nodemeta.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" + + +/* + NodeMetaRef +*/ +NodeMetaRef* NodeMetaRef::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 *(NodeMetaRef**)ud; // unbox pointer +} + +NodeMetadata* NodeMetaRef::getmeta(NodeMetaRef *ref, bool auto_create) +{ + NodeMetadata *meta = ref->m_env->getMap().getNodeMetadata(ref->m_p); + if(meta == NULL && auto_create) + { + meta = new NodeMetadata(ref->m_env->getGameDef()); + ref->m_env->getMap().setNodeMetadata(ref->m_p, meta); + } + return meta; +} + +void NodeMetaRef::reportMetadataChange(NodeMetaRef *ref) +{ + // NOTE: This same code is in rollback_interface.cpp + // Inform other things that the metadata has changed + v3s16 blockpos = getNodeBlockPos(ref->m_p); + MapEditEvent event; + event.type = MEET_BLOCK_NODE_METADATA_CHANGED; + event.p = blockpos; + ref->m_env->getMap().dispatchEvent(&event); + // Set the block to be saved + MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + "NodeMetaRef::reportMetadataChange"); +} + +// Exported functions + +// garbage collector +int NodeMetaRef::gc_object(lua_State *L) { + NodeMetaRef *o = *(NodeMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// get_string(self, name) +int NodeMetaRef::l_get_string(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + NodeMetadata *meta = getmeta(ref, false); + if(meta == NULL){ + lua_pushlstring(L, "", 0); + return 1; + } + std::string str = meta->getString(name); + lua_pushlstring(L, str.c_str(), str.size()); + return 1; +} + +// set_string(self, name, var) +int NodeMetaRef::l_set_string(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + size_t len = 0; + const char *s = lua_tolstring(L, 3, &len); + std::string str(s, len); + + NodeMetadata *meta = getmeta(ref, !str.empty()); + if(meta == NULL || str == meta->getString(name)) + return 0; + meta->setString(name, str); + reportMetadataChange(ref); + return 0; +} + +// get_int(self, name) +int NodeMetaRef::l_get_int(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + + NodeMetadata *meta = getmeta(ref, false); + if(meta == NULL){ + lua_pushnumber(L, 0); + return 1; + } + std::string str = meta->getString(name); + lua_pushnumber(L, stoi(str)); + return 1; +} + +// set_int(self, name, var) +int NodeMetaRef::l_set_int(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + int a = lua_tointeger(L, 3); + std::string str = itos(a); + + NodeMetadata *meta = getmeta(ref, true); + if(meta == NULL || str == meta->getString(name)) + return 0; + meta->setString(name, str); + reportMetadataChange(ref); + return 0; +} + +// get_float(self, name) +int NodeMetaRef::l_get_float(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + + NodeMetadata *meta = getmeta(ref, false); + if(meta == NULL){ + lua_pushnumber(L, 0); + return 1; + } + std::string str = meta->getString(name); + lua_pushnumber(L, stof(str)); + return 1; +} + +// set_float(self, name, var) +int NodeMetaRef::l_set_float(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + float a = lua_tonumber(L, 3); + std::string str = ftos(a); + + NodeMetadata *meta = getmeta(ref, true); + if(meta == NULL || str == meta->getString(name)) + return 0; + meta->setString(name, str); + reportMetadataChange(ref); + return 0; +} + +// get_inventory(self) +int NodeMetaRef::l_get_inventory(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + getmeta(ref, true); // try to ensure the metadata exists + InvRef::createNodeMeta(L, ref->m_p); + return 1; +} + +// to_table(self) +int NodeMetaRef::l_to_table(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + + NodeMetadata *meta = getmeta(ref, true); + if(meta == NULL){ + lua_pushnil(L); + return 1; + } + lua_newtable(L); + // fields + lua_newtable(L); + { + std::map<std::string, std::string> fields = meta->getStrings(); + for(std::map<std::string, std::string>::const_iterator + i = fields.begin(); i != fields.end(); i++){ + const std::string &name = i->first; + const std::string &value = i->second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + } + lua_setfield(L, -2, "fields"); + // inventory + lua_newtable(L); + Inventory *inv = meta->getInventory(); + if(inv){ + std::vector<const InventoryList*> lists = inv->getLists(); + for(std::vector<const InventoryList*>::const_iterator + i = lists.begin(); i != lists.end(); i++){ + inventory_get_list_to_lua(inv, (*i)->getName().c_str(), L); + lua_setfield(L, -2, (*i)->getName().c_str()); + } + } + lua_setfield(L, -2, "inventory"); + return 1; +} + +// from_table(self, table) +int NodeMetaRef::l_from_table(lua_State *L) +{ + NodeMetaRef *ref = checkobject(L, 1); + int base = 2; + + if(lua_isnil(L, base)){ + // No metadata + ref->m_env->getMap().removeNodeMetadata(ref->m_p); + lua_pushboolean(L, true); + return 1; + } + + // Has metadata; clear old one first + ref->m_env->getMap().removeNodeMetadata(ref->m_p); + // Create new metadata + NodeMetadata *meta = getmeta(ref, true); + // Set fields + lua_getfield(L, base, "fields"); + int fieldstable = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, fieldstable) != 0){ + // key at index -2 and value at index -1 + std::string name = lua_tostring(L, -2); + size_t cl; + const char *cs = lua_tolstring(L, -1, &cl); + std::string value(cs, cl); + meta->setString(name, value); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + // Set inventory + Inventory *inv = meta->getInventory(); + lua_getfield(L, base, "inventory"); + int inventorytable = lua_gettop(L); + lua_pushnil(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); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + reportMetadataChange(ref); + lua_pushboolean(L, true); + return 1; +} + + +NodeMetaRef::NodeMetaRef(v3s16 p, ServerEnvironment *env): + m_p(p), + m_env(env) +{ +} + +NodeMetaRef::~NodeMetaRef() +{ +} + +// Creates an NodeMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void NodeMetaRef::create(lua_State *L, v3s16 p, ServerEnvironment *env) +{ + NodeMetaRef *o = new NodeMetaRef(p, env); + //infostream<<"NodeMetaRef::create: o="<<o<<std::endl; + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void NodeMetaRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char NodeMetaRef::className[] = "NodeMetaRef"; +const luaL_reg NodeMetaRef::methods[] = { + luamethod(NodeMetaRef, get_string), + luamethod(NodeMetaRef, set_string), + luamethod(NodeMetaRef, get_int), + luamethod(NodeMetaRef, set_int), + luamethod(NodeMetaRef, get_float), + luamethod(NodeMetaRef, set_float), + luamethod(NodeMetaRef, get_inventory), + luamethod(NodeMetaRef, to_table), + luamethod(NodeMetaRef, from_table), + {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)); +} diff --git a/src/scriptapi_nodemeta.h b/src/scriptapi_nodemeta.h new file mode 100644 index 000000000..017abe181 --- /dev/null +++ b/src/scriptapi_nodemeta.h @@ -0,0 +1,123 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef LUA_NODEMETA_H_ +#define LUA_NODEMETA_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include "environment.h" +#include "nodemetadata.h" + +/* + NodeMetaRef +*/ + +class NodeMetaRef +{ +private: + v3s16 m_p; + ServerEnvironment *m_env; + + static const char className[]; + static const luaL_reg methods[]; + + static NodeMetaRef *checkobject(lua_State *L, int narg); + + static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create); + + static void reportMetadataChange(NodeMetaRef *ref); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // get_string(self, name) + static int l_get_string(lua_State *L); + + // set_string(self, name, var) + static int l_set_string(lua_State *L); + + // get_int(self, name) + static int l_get_int(lua_State *L); + + // set_int(self, name, var) + static int l_set_int(lua_State *L); + + // get_float(self, name) + static int l_get_float(lua_State *L); + + // set_float(self, name, var) + static int l_set_float(lua_State *L); + + // get_inventory(self) + static int l_get_inventory(lua_State *L); + + // to_table(self) + static int l_to_table(lua_State *L); + + // from_table(self, table) + static int l_from_table(lua_State *L); + +public: + NodeMetaRef(v3s16 p, ServerEnvironment *env); + + ~NodeMetaRef(); + + // Creates an NodeMetaRef 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, v3s16 p, ServerEnvironment *env); + + 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_ diff --git a/src/scriptapi_nodetimer.cpp b/src/scriptapi_nodetimer.cpp new file mode 100644 index 000000000..5e3289aee --- /dev/null +++ b/src/scriptapi_nodetimer.cpp @@ -0,0 +1,166 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_nodetimer.h" +#include "map.h" + + +int NodeTimerRef::gc_object(lua_State *L) { + NodeTimerRef *o = *(NodeTimerRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +NodeTimerRef* NodeTimerRef::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 *(NodeTimerRef**)ud; // unbox pointer +} + +int NodeTimerRef::l_set(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + f32 t = luaL_checknumber(L,2); + f32 e = luaL_checknumber(L,3); + env->getMap().setNodeTimer(o->m_p,NodeTimer(t,e)); + return 0; +} + +int NodeTimerRef::l_start(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + f32 t = luaL_checknumber(L,2); + env->getMap().setNodeTimer(o->m_p,NodeTimer(t,0)); + return 0; +} + +int NodeTimerRef::l_stop(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + env->getMap().removeNodeTimer(o->m_p); + return 0; +} + +int NodeTimerRef::l_is_started(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushboolean(L,(t.timeout != 0)); + return 1; +} + +int NodeTimerRef::l_get_timeout(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushnumber(L,t.timeout); + return 1; +} + +int NodeTimerRef::l_get_elapsed(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, 1); + ServerEnvironment *env = o->m_env; + if(env == NULL) return 0; + + NodeTimer t = env->getMap().getNodeTimer(o->m_p); + lua_pushnumber(L,t.elapsed); + return 1; +} + + +NodeTimerRef::NodeTimerRef(v3s16 p, ServerEnvironment *env): + m_p(p), + m_env(env) +{ +} + +NodeTimerRef::~NodeTimerRef() +{ +} + +// Creates an NodeTimerRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void NodeTimerRef::create(lua_State *L, v3s16 p, ServerEnvironment *env) +{ + NodeTimerRef *o = new NodeTimerRef(p, env); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void NodeTimerRef::set_null(lua_State *L) +{ + NodeTimerRef *o = checkobject(L, -1); + o->m_env = NULL; +} + +void NodeTimerRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char NodeTimerRef::className[] = "NodeTimerRef"; +const luaL_reg NodeTimerRef::methods[] = { + luamethod(NodeTimerRef, start), + luamethod(NodeTimerRef, set), + luamethod(NodeTimerRef, stop), + luamethod(NodeTimerRef, is_started), + luamethod(NodeTimerRef, get_timeout), + luamethod(NodeTimerRef, get_elapsed), + {0,0} +}; diff --git a/src/scriptapi_nodetimer.h b/src/scriptapi_nodetimer.h new file mode 100644 index 000000000..a4536d947 --- /dev/null +++ b/src/scriptapi_nodetimer.h @@ -0,0 +1,70 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_NODETIMER_H_ +#define LUA_NODETIMER_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include "environment.h" + +class NodeTimerRef +{ +private: + v3s16 m_p; + ServerEnvironment *m_env; + + static const char className[]; + static const luaL_reg methods[]; + + static int gc_object(lua_State *L); + + static NodeTimerRef *checkobject(lua_State *L, int narg); + + static int l_set(lua_State *L); + + static int l_start(lua_State *L); + + static int l_stop(lua_State *L); + + static int l_is_started(lua_State *L); + + static int l_get_timeout(lua_State *L); + + static int l_get_elapsed(lua_State *L); + +public: + NodeTimerRef(v3s16 p, ServerEnvironment *env); + ~NodeTimerRef(); + + // Creates an NodeTimerRef 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, v3s16 p, ServerEnvironment *env); + + static void set_null(lua_State *L); + + static void Register(lua_State *L); +}; + + + +#endif /* LUA_NODETIMER_H_ */ diff --git a/src/scriptapi_noise.cpp b/src/scriptapi_noise.cpp new file mode 100644 index 000000000..2c1a83c4c --- /dev/null +++ b/src/scriptapi_noise.cpp @@ -0,0 +1,388 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_noise.h" +#include "scriptapi_types.h" +#include "script.h" + +// garbage collector +int LuaPerlinNoise::gc_object(lua_State *L) +{ + LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +int LuaPerlinNoise::l_get2d(lua_State *L) +{ + 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); + lua_pushnumber(L, val); + return 1; +} +int LuaPerlinNoise::l_get3d(lua_State *L) +{ + 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); + lua_pushnumber(L, val); + return 1; +} + + +LuaPerlinNoise::LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence, + float a_scale): + seed(a_seed), + octaves(a_octaves), + persistence(a_persistence), + scale(a_scale) +{ +} + +LuaPerlinNoise::~LuaPerlinNoise() +{ +} + +// LuaPerlinNoise(seed, octaves, persistence, scale) +// Creates an LuaPerlinNoise and leaves it on top of stack +int LuaPerlinNoise::create_object(lua_State *L) +{ + int seed = luaL_checkint(L, 1); + int octaves = luaL_checkint(L, 2); + float persistence = luaL_checknumber(L, 3); + float scale = luaL_checknumber(L, 4); + LuaPerlinNoise *o = new LuaPerlinNoise(seed, octaves, persistence, scale); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaPerlinNoise* LuaPerlinNoise::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 *(LuaPerlinNoise**)ud; // unbox pointer +} + +void LuaPerlinNoise::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (PerlinNoise(seed, octaves, persistence) + lua_register(L, className, create_object); +} + +const char LuaPerlinNoise::className[] = "PerlinNoise"; +const luaL_reg LuaPerlinNoise::methods[] = { + luamethod(LuaPerlinNoise, get2d), + luamethod(LuaPerlinNoise, get3d), + {0,0} +}; + +/* + PerlinNoiseMap + */ + + +int LuaPerlinNoiseMap::gc_object(lua_State *L) +{ + LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +int LuaPerlinNoiseMap::l_get2dMap(lua_State *L) +{ + int i = 0; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v2f p = read_v2f(L, 2); + + Noise *n = o->noise; + n->perlinMap2D(p.X, p.Y); + + lua_newtable(L); + for (int y = 0; y != n->sy; y++) { + lua_newtable(L); + for (int x = 0; x != n->sx; x++) { + float noiseval = n->np->offset + n->np->scale * n->result[i++]; + lua_pushnumber(L, noiseval); + lua_rawseti(L, -2, x + 1); + } + lua_rawseti(L, -2, y + 1); + } + return 1; +} + +int LuaPerlinNoiseMap::l_get3dMap(lua_State *L) +{ + int i = 0; + + LuaPerlinNoiseMap *o = checkobject(L, 1); + v3f p = read_v3f(L, 2); + + Noise *n = o->noise; + n->perlinMap3D(p.X, p.Y, p.Z); + + lua_newtable(L); + for (int z = 0; z != n->sz; z++) { + lua_newtable(L); + for (int y = 0; y != n->sy; y++) { + lua_newtable(L); + for (int x = 0; x != n->sx; x++) { + lua_pushnumber(L, n->np->offset + n->np->scale * n->result[i++]); + lua_rawseti(L, -2, x + 1); + } + lua_rawseti(L, -2, y + 1); + } + lua_rawseti(L, -2, z + 1); + } + return 1; +} + +LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size) { + noise = new Noise(np, seed, size.X, size.Y, size.Z); +} + +LuaPerlinNoiseMap::~LuaPerlinNoiseMap() +{ + delete noise->np; + delete noise; +} + +// LuaPerlinNoiseMap(np, size) +// Creates an LuaPerlinNoiseMap and leaves it on top of stack +int LuaPerlinNoiseMap::create_object(lua_State *L) +{ + NoiseParams *np = read_noiseparams(L, 1); + if (!np) + return 0; + v3s16 size = read_v3s16(L, 2); + + LuaPerlinNoiseMap *o = new LuaPerlinNoiseMap(np, 0, size); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaPerlinNoiseMap* LuaPerlinNoiseMap::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 *(LuaPerlinNoiseMap **)ud; // unbox pointer +} + +void LuaPerlinNoiseMap::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (PerlinNoiseMap(np, size) + lua_register(L, className, create_object); +} + +const char LuaPerlinNoiseMap::className[] = "PerlinNoiseMap"; +const luaL_reg LuaPerlinNoiseMap::methods[] = { + luamethod(LuaPerlinNoiseMap, get2dMap), + luamethod(LuaPerlinNoiseMap, get3dMap), + {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 +*/ + +// garbage collector +int LuaPseudoRandom::gc_object(lua_State *L) +{ + LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// next(self, min=0, max=32767) -> get next value +int LuaPseudoRandom::l_next(lua_State *L) +{ + LuaPseudoRandom *o = checkobject(L, 1); + int min = 0; + int max = 32767; + lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist + if(!lua_isnil(L, 2)) + min = luaL_checkinteger(L, 2); + if(!lua_isnil(L, 3)) + max = luaL_checkinteger(L, 3); + if(max < min){ + errorstream<<"PseudoRandom.next(): max="<<max<<" min="<<min<<std::endl; + throw LuaError(L, "PseudoRandom.next(): max < min"); + } + if(max - min != 32767 && max - min > 32767/5) + throw LuaError(L, "PseudoRandom.next() max-min is not 32767 and is > 32768/5. This is disallowed due to the bad random distribution the implementation would otherwise make."); + PseudoRandom &pseudo = o->m_pseudo; + int val = pseudo.next(); + val = (val % (max-min+1)) + min; + lua_pushinteger(L, val); + return 1; +} + + +LuaPseudoRandom::LuaPseudoRandom(int seed): + m_pseudo(seed) +{ +} + +LuaPseudoRandom::~LuaPseudoRandom() +{ +} + +const PseudoRandom& LuaPseudoRandom::getItem() const +{ + return m_pseudo; +} +PseudoRandom& LuaPseudoRandom::getItem() +{ + return m_pseudo; +} + +// LuaPseudoRandom(seed) +// Creates an LuaPseudoRandom and leaves it on top of stack +int LuaPseudoRandom::create_object(lua_State *L) +{ + int seed = luaL_checknumber(L, 1); + LuaPseudoRandom *o = new LuaPseudoRandom(seed); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + +LuaPseudoRandom* LuaPseudoRandom::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(LuaPseudoRandom**)ud; // unbox pointer +} + +void LuaPseudoRandom::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (LuaPseudoRandom(seed)) + lua_register(L, className, create_object); +} + +const char LuaPseudoRandom::className[] = "PseudoRandom"; +const luaL_reg LuaPseudoRandom::methods[] = { + luamethod(LuaPseudoRandom, next), + {0,0} +}; diff --git a/src/scriptapi_noise.h b/src/scriptapi_noise.h new file mode 100644 index 000000000..a02383fde --- /dev/null +++ b/src/scriptapi_noise.h @@ -0,0 +1,133 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_PERLIN_H_ +#define LUA_PERLIN_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include "noise.h" + +class LuaPerlinNoise +{ +private: + int seed; + int octaves; + float persistence; + float scale; + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + static int l_get2d(lua_State *L); + static int l_get3d(lua_State *L); + +public: + LuaPerlinNoise(int a_seed, int a_octaves, float a_persistence, + float a_scale); + + ~LuaPerlinNoise(); + + // LuaPerlinNoise(seed, octaves, persistence, scale) + // Creates an LuaPerlinNoise and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPerlinNoise* checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + PerlinNoiseMap + */ +class LuaPerlinNoiseMap +{ +private: + Noise *noise; + static const char className[]; + static const luaL_reg methods[]; + + static int gc_object(lua_State *L); + + static int l_get2dMap(lua_State *L); + + static int l_get3dMap(lua_State *L); + +public: + LuaPerlinNoiseMap(NoiseParams *np, int seed, v3s16 size); + + ~LuaPerlinNoiseMap(); + + // LuaPerlinNoiseMap(np, size) + // Creates an LuaPerlinNoiseMap and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPerlinNoiseMap *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaPseudoRandom +*/ + + +class LuaPseudoRandom +{ +private: + PseudoRandom m_pseudo; + + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // next(self, min=0, max=32767) -> get next value + static int l_next(lua_State *L); + +public: + LuaPseudoRandom(int seed); + + ~LuaPseudoRandom(); + + const PseudoRandom& getItem() const; + PseudoRandom& getItem(); + + // LuaPseudoRandom(seed) + // Creates an LuaPseudoRandom and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPseudoRandom* checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +NoiseParams *read_noiseparams(lua_State *L, int index); + +#endif /* LUA_PERLIN_H_ */ diff --git a/src/scriptapi_object.cpp b/src/scriptapi_object.cpp new file mode 100644 index 000000000..a0f93cbba --- /dev/null +++ b/src/scriptapi_object.cpp @@ -0,0 +1,945 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_object.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" + +/* + ObjectRef +*/ + + +ObjectRef* ObjectRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(ObjectRef**)ud; // unbox pointer +} + +ServerActiveObject* ObjectRef::getobject(ObjectRef *ref) +{ + ServerActiveObject *co = ref->m_object; + return co; +} + +LuaEntitySAO* ObjectRef::getluaobject(ObjectRef *ref) +{ + ServerActiveObject *obj = getobject(ref); + if(obj == NULL) + return NULL; + if(obj->getType() != ACTIVEOBJECT_TYPE_LUAENTITY) + return NULL; + return (LuaEntitySAO*)obj; +} + +PlayerSAO* ObjectRef::getplayersao(ObjectRef *ref) +{ + ServerActiveObject *obj = getobject(ref); + if(obj == NULL) + return NULL; + if(obj->getType() != ACTIVEOBJECT_TYPE_PLAYER) + return NULL; + return (PlayerSAO*)obj; +} + +Player* ObjectRef::getplayer(ObjectRef *ref) +{ + PlayerSAO *playersao = getplayersao(ref); + if(playersao == NULL) + return NULL; + return playersao->getPlayer(); +} + +// Exported functions + +// garbage collector +int ObjectRef::gc_object(lua_State *L) { + ObjectRef *o = *(ObjectRef **)(lua_touserdata(L, 1)); + //infostream<<"ObjectRef::gc_object: o="<<o<<std::endl; + delete o; + return 0; +} + +// remove(self) +int ObjectRef::l_remove(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + verbosestream<<"ObjectRef::l_remove(): id="<<co->getId()<<std::endl; + co->m_removed = true; + return 0; +} + +// getpos(self) +// returns: {x=num, y=num, z=num} +int ObjectRef::l_getpos(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + v3f pos = co->getBasePosition() / BS; + lua_newtable(L); + lua_pushnumber(L, pos.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, pos.Y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, pos.Z); + lua_setfield(L, -2, "z"); + return 1; +} + +// setpos(self, pos) +int ObjectRef::l_setpos(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + //LuaEntitySAO *co = getluaobject(ref); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // pos + v3f pos = checkFloatPos(L, 2); + // Do it + co->setPos(pos); + return 0; +} + +// moveto(self, pos, continuous=false) +int ObjectRef::l_moveto(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + //LuaEntitySAO *co = getluaobject(ref); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // pos + v3f pos = checkFloatPos(L, 2); + // continuous + bool continuous = lua_toboolean(L, 3); + // Do it + co->moveTo(pos, continuous); + return 0; +} + +// punch(self, puncher, time_from_last_punch, tool_capabilities, dir) +int ObjectRef::l_punch(lua_State *L) +{ + 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; + 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); + dir.normalize(); + // Do it + co->punch(dir, &toolcap, puncher, time_from_last_punch); + return 0; +} + +// right_click(self, clicker); clicker = an another ObjectRef +int ObjectRef::l_right_click(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ObjectRef *ref2 = checkobject(L, 2); + ServerActiveObject *co = getobject(ref); + ServerActiveObject *co2 = getobject(ref2); + if(co == NULL) return 0; + if(co2 == NULL) return 0; + // Do it + co->rightClick(co2); + return 0; +} + +// set_hp(self, hp) +// hp = number of hitpoints (2 * number of hearts) +// returns: nil +int ObjectRef::l_set_hp(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + luaL_checknumber(L, 2); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + int hp = lua_tonumber(L, 2); + /*infostream<<"ObjectRef::l_set_hp(): id="<<co->getId() + <<" hp="<<hp<<std::endl;*/ + // Do it + co->setHP(hp); + // Return + return 0; +} + +// get_hp(self) +// returns: number of hitpoints (2 * number of hearts) +// 0 if not applicable to this type of object +int ObjectRef::l_get_hp(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL){ + // Default hp is 1 + lua_pushnumber(L, 1); + return 1; + } + int hp = co->getHP(); + /*infostream<<"ObjectRef::l_get_hp(): id="<<co->getId() + <<" hp="<<hp<<std::endl;*/ + // Return + lua_pushnumber(L, hp); + return 1; +} + +// get_inventory(self) +int ObjectRef::l_get_inventory(lua_State *L) +{ + 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) + InvRef::create(L, loc); + else + lua_pushnil(L); // An object may have no inventory (nil) + return 1; +} + +// get_wield_list(self) +int ObjectRef::l_get_wield_list(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + lua_pushstring(L, co->getWieldList().c_str()); + return 1; +} + +// get_wield_index(self) +int ObjectRef::l_get_wield_index(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + lua_pushinteger(L, co->getWieldIndex() + 1); + return 1; +} + +// get_wielded_item(self) +int ObjectRef::l_get_wielded_item(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL){ + // Empty ItemStack + LuaItemStack::create(L, ItemStack()); + return 1; + } + // Do it + LuaItemStack::create(L, co->getWieldedItem()); + return 1; +} + +// set_wielded_item(self, itemstack or itemstring or table or nil) +int ObjectRef::l_set_wielded_item(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + ItemStack item = read_item(L, 2); + bool success = co->setWieldedItem(item); + lua_pushboolean(L, success); + return 1; +} + +// set_armor_groups(self, groups) +int ObjectRef::l_set_armor_groups(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + ItemGroupList groups; + read_groups(L, 2, groups); + co->setArmorGroups(groups); + return 0; +} + +// set_animation(self, frame_range, frame_speed, frame_blend) +int ObjectRef::l_set_animation(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + v2f frames = v2f(1, 1); + if(!lua_isnil(L, 2)) + frames = read_v2f(L, 2); + float frame_speed = 15; + if(!lua_isnil(L, 3)) + frame_speed = lua_tonumber(L, 3); + float frame_blend = 0; + if(!lua_isnil(L, 4)) + frame_blend = lua_tonumber(L, 4); + co->setAnimation(frames, frame_speed, frame_blend); + return 0; +} + +// set_bone_position(self, std::string bone, v3f position, v3f rotation) +int ObjectRef::l_set_bone_position(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + std::string bone = ""; + if(!lua_isnil(L, 2)) + bone = lua_tostring(L, 2); + v3f position = v3f(0, 0, 0); + if(!lua_isnil(L, 3)) + position = read_v3f(L, 3); + v3f rotation = v3f(0, 0, 0); + if(!lua_isnil(L, 4)) + rotation = read_v3f(L, 4); + co->setBonePosition(bone, position, rotation); + return 0; +} + +// set_attach(self, parent, bone, position, rotation) +int ObjectRef::l_set_attach(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ObjectRef *parent_ref = checkobject(L, 2); + ServerActiveObject *co = getobject(ref); + ServerActiveObject *parent = getobject(parent_ref); + if(co == NULL) return 0; + if(parent == NULL) return 0; + // Do it + std::string bone = ""; + if(!lua_isnil(L, 3)) + bone = lua_tostring(L, 3); + v3f position = v3f(0, 0, 0); + if(!lua_isnil(L, 4)) + position = read_v3f(L, 4); + v3f rotation = v3f(0, 0, 0); + if(!lua_isnil(L, 5)) + rotation = read_v3f(L, 5); + co->setAttachment(parent->getId(), bone, position, rotation); + return 0; +} + +// set_detach(self) +int ObjectRef::l_set_detach(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + // Do it + co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0)); + return 0; +} + +// set_properties(self, properties) +int ObjectRef::l_set_properties(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + ObjectProperties *prop = co->accessObjectProperties(); + if(!prop) + return 0; + read_object_properties(L, 2, prop); + co->notifyObjectPropertiesModified(); + return 0; +} + +/* LuaEntitySAO-only */ + +// setvelocity(self, {x=num, y=num, z=num}) +int ObjectRef::l_setvelocity(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + v3f pos = checkFloatPos(L, 2); + // Do it + co->setVelocity(pos); + return 0; +} + +// getvelocity(self) +int ObjectRef::l_getvelocity(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + v3f v = co->getVelocity(); + pushFloatPos(L, v); + return 1; +} + +// setacceleration(self, {x=num, y=num, z=num}) +int ObjectRef::l_setacceleration(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // pos + v3f pos = checkFloatPos(L, 2); + // Do it + co->setAcceleration(pos); + return 0; +} + +// getacceleration(self) +int ObjectRef::l_getacceleration(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + v3f v = co->getAcceleration(); + pushFloatPos(L, v); + return 1; +} + +// setyaw(self, radians) +int ObjectRef::l_setyaw(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + float yaw = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setYaw(yaw); + return 0; +} + +// getyaw(self) +int ObjectRef::l_getyaw(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + float yaw = co->getYaw() * core::DEGTORAD; + lua_pushnumber(L, yaw); + return 1; +} + +// settexturemod(self, mod) +int ObjectRef::l_settexturemod(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + std::string mod = luaL_checkstring(L, 2); + co->setTextureMod(mod); + return 0; +} + +// setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, +// select_horiz_by_yawpitch=false) +int ObjectRef::l_setsprite(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + v2s16 p(0,0); + if(!lua_isnil(L, 2)) + p = read_v2s16(L, 2); + int num_frames = 1; + if(!lua_isnil(L, 3)) + num_frames = lua_tonumber(L, 3); + float framelength = 0.2; + if(!lua_isnil(L, 4)) + framelength = lua_tonumber(L, 4); + bool select_horiz_by_yawpitch = false; + if(!lua_isnil(L, 5)) + select_horiz_by_yawpitch = lua_toboolean(L, 5); + co->setSprite(p, num_frames, framelength, select_horiz_by_yawpitch); + return 0; +} + +// DEPRECATED +// get_entity_name(self) +int ObjectRef::l_get_entity_name(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + std::string name = co->getName(); + lua_pushstring(L, name.c_str()); + return 1; +} + +// get_luaentity(self) +int ObjectRef::l_get_luaentity(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; + // Do it + luaentity_get(L, co->getId()); + return 1; +} + +/* Player-only */ + +// is_player(self) +int ObjectRef::l_is_player(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + lua_pushboolean(L, (player != NULL)); + return 1; +} + +// get_player_name(self) +int ObjectRef::l_get_player_name(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL){ + lua_pushlstring(L, "", 0); + return 1; + } + // Do it + lua_pushstring(L, player->getName()); + return 1; +} + +// get_look_dir(self) +int ObjectRef::l_get_look_dir(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL) return 0; + // Do it + float pitch = player->getRadPitch(); + float yaw = player->getRadYaw(); + v3f v(cos(pitch)*cos(yaw), sin(pitch), cos(pitch)*sin(yaw)); + push_v3f(L, v); + return 1; +} + +// get_look_pitch(self) +int ObjectRef::l_get_look_pitch(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL) return 0; + // Do it + lua_pushnumber(L, player->getRadPitch()); + return 1; +} + +// get_look_yaw(self) +int ObjectRef::l_get_look_yaw(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL) return 0; + // Do it + lua_pushnumber(L, player->getRadYaw()); + return 1; +} + +// set_look_pitch(self, radians) +int ObjectRef::l_set_look_pitch(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if(co == NULL) return 0; + float pitch = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setPitch(pitch); + return 1; +} + +// set_look_yaw(self, radians) +int ObjectRef::l_set_look_yaw(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if(co == NULL) return 0; + float yaw = luaL_checknumber(L, 2) * core::RADTODEG; + // Do it + co->setYaw(yaw); + return 1; +} + +// set_inventory_formspec(self, formspec) +int ObjectRef::l_set_inventory_formspec(lua_State *L) +{ + 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()); + lua_pushboolean(L, true); + return 1; +} + +// get_inventory_formspec(self) -> formspec +int ObjectRef::l_get_inventory_formspec(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL) return 0; + + std::string formspec = player->inventory_formspec; + lua_pushlstring(L, formspec.c_str(), formspec.size()); + return 1; +} + +// get_player_control(self) +int ObjectRef::l_get_player_control(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL){ + lua_pushlstring(L, "", 0); + return 1; + } + // Do it + PlayerControl control = player->getPlayerControl(); + lua_newtable(L); + lua_pushboolean(L, control.up); + lua_setfield(L, -2, "up"); + lua_pushboolean(L, control.down); + lua_setfield(L, -2, "down"); + lua_pushboolean(L, control.left); + lua_setfield(L, -2, "left"); + lua_pushboolean(L, control.right); + lua_setfield(L, -2, "right"); + lua_pushboolean(L, control.jump); + lua_setfield(L, -2, "jump"); + lua_pushboolean(L, control.aux1); + lua_setfield(L, -2, "aux1"); + lua_pushboolean(L, control.sneak); + lua_setfield(L, -2, "sneak"); + lua_pushboolean(L, control.LMB); + lua_setfield(L, -2, "LMB"); + lua_pushboolean(L, control.RMB); + lua_setfield(L, -2, "RMB"); + return 1; +} + +// get_player_control_bits(self) +int ObjectRef::l_get_player_control_bits(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + Player *player = getplayer(ref); + if(player == NULL){ + lua_pushlstring(L, "", 0); + return 1; + } + // Do it + lua_pushnumber(L, player->keyPressed); + return 1; +} + + +ObjectRef::ObjectRef(ServerActiveObject *object): + m_object(object) +{ + //infostream<<"ObjectRef created for id="<<m_object->getId()<<std::endl; +} + +ObjectRef::~ObjectRef() +{ + /*if(m_object) + infostream<<"ObjectRef destructing for id=" + <<m_object->getId()<<std::endl; + else + infostream<<"ObjectRef destructing for id=unknown"<<std::endl;*/ +} + +// Creates an ObjectRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void ObjectRef::create(lua_State *L, ServerActiveObject *object) +{ + ObjectRef *o = new ObjectRef(object); + //infostream<<"ObjectRef::create: o="<<o<<std::endl; + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void ObjectRef::set_null(lua_State *L) +{ + ObjectRef *o = checkobject(L, -1); + o->m_object = NULL; +} + +void ObjectRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); +} + +const char ObjectRef::className[] = "ObjectRef"; +const luaL_reg ObjectRef::methods[] = { + // ServerActiveObject + luamethod(ObjectRef, remove), + luamethod(ObjectRef, getpos), + luamethod(ObjectRef, setpos), + luamethod(ObjectRef, moveto), + luamethod(ObjectRef, punch), + luamethod(ObjectRef, right_click), + luamethod(ObjectRef, set_hp), + luamethod(ObjectRef, get_hp), + luamethod(ObjectRef, get_inventory), + luamethod(ObjectRef, get_wield_list), + luamethod(ObjectRef, get_wield_index), + luamethod(ObjectRef, get_wielded_item), + luamethod(ObjectRef, set_wielded_item), + luamethod(ObjectRef, set_armor_groups), + luamethod(ObjectRef, set_animation), + luamethod(ObjectRef, set_bone_position), + luamethod(ObjectRef, set_attach), + luamethod(ObjectRef, set_detach), + luamethod(ObjectRef, set_properties), + // LuaEntitySAO-only + luamethod(ObjectRef, setvelocity), + luamethod(ObjectRef, getvelocity), + luamethod(ObjectRef, setacceleration), + luamethod(ObjectRef, getacceleration), + luamethod(ObjectRef, setyaw), + luamethod(ObjectRef, getyaw), + luamethod(ObjectRef, settexturemod), + luamethod(ObjectRef, setsprite), + luamethod(ObjectRef, get_entity_name), + luamethod(ObjectRef, get_luaentity), + // Player-only + luamethod(ObjectRef, is_player), + luamethod(ObjectRef, get_player_name), + luamethod(ObjectRef, get_look_dir), + luamethod(ObjectRef, get_look_pitch), + luamethod(ObjectRef, get_look_yaw), + luamethod(ObjectRef, set_look_yaw), + luamethod(ObjectRef, set_look_pitch), + luamethod(ObjectRef, set_inventory_formspec), + luamethod(ObjectRef, get_inventory_formspec), + luamethod(ObjectRef, get_player_control), + luamethod(ObjectRef, get_player_control_bits), + {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="<<cobj->getId()<<std::endl; + StackUnroller stack_unroller(L); + + // Create object on stack + ObjectRef::create(L, cobj); // Puts ObjectRef (as userdata) on stack + int object = lua_gettop(L); + + // Get minetest.object_refs table + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "object_refs"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + + // object_refs[id] = object + lua_pushnumber(L, cobj->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="<<cobj->getId()<<std::endl; + StackUnroller stack_unroller(L); + + // Get minetest.object_refs table + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "object_refs"); + luaL_checktype(L, -1, LUA_TTABLE); + int objectstable = lua_gettop(L); + + // Get object_refs[id] + lua_pushnumber(L, cobj->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); +} diff --git a/src/scriptapi_object.h b/src/scriptapi_object.h new file mode 100644 index 000000000..a37abbb78 --- /dev/null +++ b/src/scriptapi_object.h @@ -0,0 +1,220 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_OBJECT_H_ +#define LUA_OBJECT_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +#include "serverobject.h" +#include "content_sao.h" +#include "player.h" + +/* + ObjectRef +*/ + +class ObjectRef +{ +private: + ServerActiveObject *m_object; + + static const char className[]; + static const luaL_reg methods[]; +public: + static ObjectRef *checkobject(lua_State *L, int narg); + + static ServerActiveObject* getobject(ObjectRef *ref); +private: + static LuaEntitySAO* getluaobject(ObjectRef *ref); + + static PlayerSAO* getplayersao(ObjectRef *ref); + + static Player* getplayer(ObjectRef *ref); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // remove(self) + static int l_remove(lua_State *L); + + // getpos(self) + // returns: {x=num, y=num, z=num} + static int l_getpos(lua_State *L); + + // setpos(self, pos) + static int l_setpos(lua_State *L); + + // moveto(self, pos, continuous=false) + static int l_moveto(lua_State *L); + + // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) + static int l_punch(lua_State *L); + + // right_click(self, clicker); clicker = an another ObjectRef + static int l_right_click(lua_State *L); + + // set_hp(self, hp) + // hp = number of hitpoints (2 * number of hearts) + // returns: nil + static int l_set_hp(lua_State *L); + + // get_hp(self) + // returns: number of hitpoints (2 * number of hearts) + // 0 if not applicable to this type of object + static int l_get_hp(lua_State *L); + + // get_inventory(self) + static int l_get_inventory(lua_State *L); + + // get_wield_list(self) + static int l_get_wield_list(lua_State *L); + + // get_wield_index(self) + static int l_get_wield_index(lua_State *L); + + // get_wielded_item(self) + static int l_get_wielded_item(lua_State *L); + + // set_wielded_item(self, itemstack or itemstring or table or nil) + static int l_set_wielded_item(lua_State *L); + + // set_armor_groups(self, groups) + static int l_set_armor_groups(lua_State *L); + + // set_animation(self, frame_range, frame_speed, frame_blend) + static int l_set_animation(lua_State *L); + + // set_bone_position(self, std::string bone, v3f position, v3f rotation) + static int l_set_bone_position(lua_State *L); + + // set_attach(self, parent, bone, position, rotation) + static int l_set_attach(lua_State *L); + + // set_detach(self) + static int l_set_detach(lua_State *L); + + // set_properties(self, properties) + static int l_set_properties(lua_State *L); + + /* LuaEntitySAO-only */ + + // setvelocity(self, {x=num, y=num, z=num}) + static int l_setvelocity(lua_State *L); + + // getvelocity(self) + static int l_getvelocity(lua_State *L); + + // setacceleration(self, {x=num, y=num, z=num}) + static int l_setacceleration(lua_State *L); + + // getacceleration(self) + static int l_getacceleration(lua_State *L); + + // setyaw(self, radians) + static int l_setyaw(lua_State *L); + + // getyaw(self) + static int l_getyaw(lua_State *L); + + // settexturemod(self, mod) + static int l_settexturemod(lua_State *L); + + // setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, + // select_horiz_by_yawpitch=false) + static int l_setsprite(lua_State *L); + + // DEPRECATED + // get_entity_name(self) + static int l_get_entity_name(lua_State *L); + + // get_luaentity(self) + static int l_get_luaentity(lua_State *L); + + /* Player-only */ + + // is_player(self) + static int l_is_player(lua_State *L); + + // get_player_name(self) + static int l_get_player_name(lua_State *L); + + // get_look_dir(self) + static int l_get_look_dir(lua_State *L); + + // get_look_pitch(self) + static int l_get_look_pitch(lua_State *L); + + // get_look_yaw(self) + static int l_get_look_yaw(lua_State *L); + + // set_look_pitch(self, radians) + static int l_set_look_pitch(lua_State *L); + + // set_look_yaw(self, radians) + static int l_set_look_yaw(lua_State *L); + + // set_inventory_formspec(self, formspec) + static int l_set_inventory_formspec(lua_State *L); + + // get_inventory_formspec(self) -> formspec + static int l_get_inventory_formspec(lua_State *L); + + // get_player_control(self) + static int l_get_player_control(lua_State *L); + + // get_player_control_bits(self) + static int l_get_player_control_bits(lua_State *L); + +public: + ObjectRef(ServerActiveObject *object); + + ~ObjectRef(); + + // Creates an ObjectRef 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, ServerActiveObject *object); + + static void set_null(lua_State *L); + + 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_ */ diff --git a/src/scriptapi_particles.cpp b/src/scriptapi_particles.cpp new file mode 100644 index 000000000..dc9b3776e --- /dev/null +++ b/src/scriptapi_particles.cpp @@ -0,0 +1,143 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_particles.h" +#include "server.h" +#include "script.h" +#include "scriptapi_types.h" +#include "scriptapi_common.h" + +// add_particle(pos, velocity, acceleration, expirationtime, +// size, collisiondetection, texture, player) +// pos/velocity/acceleration = {x=num, y=num, z=num} +// expirationtime = num (seconds) +// size = num +// texture = e.g."default_wood.png" +int 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); + v3f acc = check_v3f(L, 3); + float expirationtime = luaL_checknumber(L, 4); + float size = luaL_checknumber(L, 5); + bool collisiondetection = lua_toboolean(L, 6); + std::string texture = luaL_checkstring(L, 7); + + if (lua_gettop(L) == 8) // only spawn for a single player + { + const char *playername = luaL_checkstring(L, 8); + server->spawnParticle(playername, + pos, vel, acc, expirationtime, + size, collisiondetection, texture); + } + else // spawn for all players + { + server->spawnParticleAll(pos, vel, acc, + expirationtime, size, collisiondetection, texture); + } + return 1; +} + +// add_particlespawner(amount, time, +// minpos, maxpos, +// minvel, maxvel, +// minacc, maxacc, +// minexptime, maxexptime, +// minsize, maxsize, +// collisiondetection, +// texture, +// player) +// minpos/maxpos/minvel/maxvel/minacc/maxacc = {x=num, y=num, z=num} +// minexptime/maxexptime = num (seconds) +// minsize/maxsize = num +// collisiondetection = bool +// texture = e.g."default_wood.png" +int 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); + v3f minpos = check_v3f(L, 3); + v3f maxpos = check_v3f(L, 4); + v3f minvel = check_v3f(L, 5); + v3f maxvel = check_v3f(L, 6); + v3f minacc = check_v3f(L, 7); + v3f maxacc = check_v3f(L, 8); + float minexptime = luaL_checknumber(L, 9); + float maxexptime = luaL_checknumber(L, 10); + float minsize = luaL_checknumber(L, 11); + float maxsize = luaL_checknumber(L, 12); + bool collisiondetection = lua_toboolean(L, 13); + std::string texture = luaL_checkstring(L, 14); + + if (lua_gettop(L) == 15) // only spawn for a single player + { + const char *playername = luaL_checkstring(L, 15); + u32 id = server->addParticleSpawner(playername, + amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, + texture); + lua_pushnumber(L, id); + } + else // spawn for all players + { + u32 id = server->addParticleSpawnerAll( amount, time, + minpos, maxpos, + minvel, maxvel, + minacc, maxacc, + minexptime, maxexptime, + minsize, maxsize, + collisiondetection, + texture); + lua_pushnumber(L, id); + } + return 1; +} + +// delete_particlespawner(id, player) +// player (string) is optional +int 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); + } + else // delete for all players + { + server->deleteParticleSpawnerAll(id); + } + return 1; +} diff --git a/src/scriptapi_particles.h b/src/scriptapi_particles.h new file mode 100644 index 000000000..4b37d7ce1 --- /dev/null +++ b/src/scriptapi_particles.h @@ -0,0 +1,32 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_PARTICLES_H_ +#define LUA_PARTICLES_H_ + +extern "C" { +#include <lua.h> +#include <lauxlib.h> +} + +int l_add_particle(lua_State *L); +int l_add_particlespawner(lua_State *L); +int l_delete_particlespawner(lua_State *L); + +#endif diff --git a/src/scriptapi_types.cpp b/src/scriptapi_types.cpp new file mode 100644 index 000000000..3d06f1623 --- /dev/null +++ b/src/scriptapi_types.cpp @@ -0,0 +1,372 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "scriptapi.h" +#include "scriptapi_types.h" + +extern "C" { +#include <lauxlib.h> +} + +#include "util/numeric.h" +#include "nodedef.h" + +/* + C struct <-> Lua table converter functions +*/ + +void push_v3f(lua_State *L, v3f p) +{ + lua_newtable(L); + lua_pushnumber(L, p.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, p.Y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, p.Z); + lua_setfield(L, -2, "z"); +} + +v2s16 read_v2s16(lua_State *L, int index) +{ + v2s16 p; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + p.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + p.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + return p; +} + +v2f read_v2f(lua_State *L, int index) +{ + v2f p; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + p.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + p.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + return p; +} + +v3f read_v3f(lua_State *L, int index) +{ + v3f pos; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + pos.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + pos.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "z"); + pos.Z = lua_tonumber(L, -1); + lua_pop(L, 1); + return pos; +} + +v3f check_v3f(lua_State *L, int index) +{ + v3f pos; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + pos.X = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + pos.Y = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "z"); + pos.Z = luaL_checknumber(L, -1); + lua_pop(L, 1); + return pos; +} + +void pushFloatPos(lua_State *L, v3f p) +{ + p /= BS; + push_v3f(L, p); +} + +v3f checkFloatPos(lua_State *L, int index) +{ + return check_v3f(L, index) * BS; +} + +void push_v3s16(lua_State *L, v3s16 p) +{ + lua_newtable(L); + lua_pushnumber(L, p.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, p.Y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, p.Z); + lua_setfield(L, -2, "z"); +} + +v3s16 read_v3s16(lua_State *L, int index) +{ + // Correct rounding at <0 + v3f pf = read_v3f(L, index); + return floatToInt(pf, 1.0); +} + +v3s16 check_v3s16(lua_State *L, int index) +{ + // Correct rounding at <0 + v3f pf = check_v3f(L, index); + return floatToInt(pf, 1.0); +} + +video::SColor readARGB8(lua_State *L, int index) +{ + video::SColor color; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "a"); + if(lua_isnumber(L, -1)) + color.setAlpha(lua_tonumber(L, -1)); + lua_pop(L, 1); + lua_getfield(L, index, "r"); + color.setRed(lua_tonumber(L, -1)); + lua_pop(L, 1); + lua_getfield(L, index, "g"); + color.setGreen(lua_tonumber(L, -1)); + lua_pop(L, 1); + lua_getfield(L, index, "b"); + color.setBlue(lua_tonumber(L, -1)); + lua_pop(L, 1); + return color; +} + +aabb3f read_aabb3f(lua_State *L, int index, f32 scale) +{ + aabb3f box; + if(lua_istable(L, index)){ + lua_rawgeti(L, index, 1); + box.MinEdge.X = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, index, 2); + box.MinEdge.Y = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, index, 3); + box.MinEdge.Z = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, index, 4); + box.MaxEdge.X = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, index, 5); + box.MaxEdge.Y = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, index, 6); + box.MaxEdge.Z = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + } + return box; +} + +std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale) +{ + std::vector<aabb3f> boxes; + if(lua_istable(L, index)){ + int n = lua_objlen(L, index); + // Check if it's a single box or a list of boxes + bool possibly_single_box = (n == 6); + for(int i = 1; i <= n && possibly_single_box; i++){ + lua_rawgeti(L, index, i); + if(!lua_isnumber(L, -1)) + possibly_single_box = false; + lua_pop(L, 1); + } + if(possibly_single_box){ + // Read a single box + boxes.push_back(read_aabb3f(L, index, scale)); + } else { + // Read a list of boxes + for(int i = 1; i <= n; i++){ + lua_rawgeti(L, index, i); + boxes.push_back(read_aabb3f(L, -1, scale)); + lua_pop(L, 1); + } + } + } + return boxes; +} + +/* + Table field getters +*/ + +bool getstringfield(lua_State *L, int table, + const char *fieldname, std::string &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isstring(L, -1)){ + size_t len = 0; + const char *ptr = lua_tolstring(L, -1, &len); + result.assign(ptr, len); + got = true; + } + lua_pop(L, 1); + return got; +} + +bool getintfield(lua_State *L, int table, + const char *fieldname, int &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +bool getfloatfield(lua_State *L, int table, + const char *fieldname, float &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isnumber(L, -1)){ + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +bool getboolfield(lua_State *L, int table, + const char *fieldname, bool &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + if(lua_isboolean(L, -1)){ + result = lua_toboolean(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + +std::string checkstringfield(lua_State *L, int table, + const char *fieldname) +{ + lua_getfield(L, table, fieldname); + std::string s = luaL_checkstring(L, -1); + lua_pop(L, 1); + return s; +} + +std::string getstringfield_default(lua_State *L, int table, + const char *fieldname, const std::string &default_) +{ + std::string result = default_; + getstringfield(L, table, fieldname, result); + return result; +} + +int getintfield_default(lua_State *L, int table, + const char *fieldname, int default_) +{ + int result = default_; + getintfield(L, table, fieldname, result); + return result; +} + +float getfloatfield_default(lua_State *L, int table, + const char *fieldname, float default_) +{ + float result = default_; + getfloatfield(L, table, fieldname, result); + return result; +} + +bool getboolfield_default(lua_State *L, int table, + const char *fieldname, bool default_) +{ + bool result = default_; + getboolfield(L, table, fieldname, result); + return result; +} + +void setintfield(lua_State *L, int table, + const char *fieldname, int value) +{ + lua_pushinteger(L, value); + if(table < 0) + table -= 1; + lua_setfield(L, table, fieldname); +} + +void setfloatfield(lua_State *L, int table, + const char *fieldname, float value) +{ + lua_pushnumber(L, value); + if(table < 0) + table -= 1; + lua_setfield(L, table, fieldname); +} + +void setboolfield(lua_State *L, int table, + const char *fieldname, bool value) +{ + lua_pushboolean(L, value); + if(table < 0) + table -= 1; + lua_setfield(L, table, fieldname); +} + + +/* 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/scriptapi_types.h new file mode 100644 index 000000000..e3a611a9d --- /dev/null +++ b/src/scriptapi_types.h @@ -0,0 +1,87 @@ +/* +Minetest-c55 +Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef LUA_TYPES_H_ +#define LUA_TYPES_H_ + +#include <iostream> +#include <map> +#include <vector> + +#include "irrlichttypes_bloated.h" +#include "porting.h" +#include "map.h" + +extern "C" { +#include <lua.h> +} + +std::string getstringfield_default (lua_State *L, int table, + const char *fieldname, const std::string &default_); +bool getboolfield_default(lua_State *L, int table, + const char *fieldname, bool default_); +float getfloatfield_default(lua_State *L, int table, + const char *fieldname, float default_); +int getintfield_default (lua_State *L, int table, + const char *fieldname, int default_); + +bool getstringfield(lua_State *L, int table, + const char *fieldname, std::string &result); +bool getintfield(lua_State *L, int table, + const char *fieldname, int &result); +void read_groups (lua_State *L, int index, + std::map<std::string, int> &result); +bool getboolfield(lua_State *L, int table, + const char *fieldname, bool &result); +bool getfloatfield(lua_State *L, int table, + const char *fieldname, float &result); + +std::string checkstringfield(lua_State *L, int table, + const char *fieldname); + +void setintfield(lua_State *L, int table, + const char *fieldname, int value); +void setfloatfield(lua_State *L, int table, + const char *fieldname, float value); +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 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<aabb3f> + 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); + + +MapNode readnode(lua_State *L, int index, INodeDefManager *ndef); +void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef); + +#endif /* LUA_TYPES_H_ */ diff --git a/src/server.cpp b/src/server.cpp index 41a7a4289..c4dd0ab0f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include <iostream> #include <queue> +#include <algorithm> #include "clientserver.h" #include "map.h" #include "jmutexautolock.h" @@ -58,6 +59,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/mathconstants.h" #include "rollback.h" #include "util/serialize.h" +#include "defaultsettings.h" void * ServerThread::Thread() { @@ -126,7 +128,7 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const } void RemoteClient::GetNextBlocks(Server *server, float dtime, - core::array<PrioritySortedBlockTransfer> &dest) + std::vector<PrioritySortedBlockTransfer> &dest) { DSTACK(__FUNCTION_NAME); @@ -274,11 +276,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Get the border/face dot coordinates of a "d-radiused" box */ - core::list<v3s16> list; + std::list<v3s16> list; getFacePositions(list, d); - core::list<v3s16>::Iterator li; - for(li=list.begin(); li!=list.end(); li++) + std::list<v3s16>::iterator li; + for(li=list.begin(); li!=list.end(); ++li) { v3s16 p = *li + center; @@ -305,7 +307,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } // Don't send blocks that are currently being transferred - if(m_blocks_sending.find(p) != NULL) + if(m_blocks_sending.find(p) != m_blocks_sending.end()) continue; /* @@ -382,7 +384,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Don't send already sent blocks */ { - if(m_blocks_sent.find(p) != NULL) + if(m_blocks_sent.find(p) != m_blocks_sent.end()) { continue; } @@ -554,21 +556,21 @@ queue_full_break: void RemoteClient::GotBlock(v3s16 p) { - if(m_blocks_sending.find(p) != NULL) - m_blocks_sending.remove(p); + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); else { /*infostream<<"RemoteClient::GotBlock(): Didn't find in" " m_blocks_sending"<<std::endl;*/ m_excess_gotblocks++; } - m_blocks_sent.insert(p, true); + m_blocks_sent.insert(p); } void RemoteClient::SentBlock(v3s16 p) { - if(m_blocks_sending.find(p) == NULL) - m_blocks_sending.insert(p, 0.0); + if(m_blocks_sending.find(p) == m_blocks_sending.end()) + m_blocks_sending[p] = 0.0; else infostream<<"RemoteClient::SentBlock(): Sent block" " already in m_blocks_sending"<<std::endl; @@ -578,26 +580,26 @@ void RemoteClient::SetBlockNotSent(v3s16 p) { m_nearest_unsent_d = 0; - if(m_blocks_sending.find(p) != NULL) - m_blocks_sending.remove(p); - if(m_blocks_sent.find(p) != NULL) - m_blocks_sent.remove(p); + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); + if(m_blocks_sent.find(p) != m_blocks_sent.end()) + m_blocks_sent.erase(p); } -void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks) +void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks) { m_nearest_unsent_d = 0; - for(core::map<v3s16, MapBlock*>::Iterator - i = blocks.getIterator(); - i.atEnd()==false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = blocks.begin(); + i != blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = i->first; - if(m_blocks_sending.find(p) != NULL) - m_blocks_sending.remove(p); - if(m_blocks_sent.find(p) != NULL) - m_blocks_sent.remove(p); + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); + if(m_blocks_sent.find(p) != m_blocks_sent.end()) + m_blocks_sent.erase(p); } } @@ -651,7 +653,6 @@ Server::Server( m_craftdef(createCraftDefManager()), m_event(new EventManager()), m_thread(this), - //m_emergethread(this), m_time_of_day_send_timer(0), m_uptime(0), m_shutdown_requested(false), @@ -687,9 +688,19 @@ Server::Server( infostream<<"- config: "<<m_path_config<<std::endl; infostream<<"- game: "<<m_gamespec.path<<std::endl; + // Initialize default settings and override defaults with those provided + // by the game + set_default_settings(g_settings); + Settings gamedefaults; + getGameMinetestConfig(gamespec.path, gamedefaults); + override_default_settings(g_settings, &gamedefaults); + // Create biome definition manager m_biomedef = new BiomeDefManager(this); - + + // Create emerge manager + m_emerge = new EmergeManager(this, m_biomedef); + // Create rollback manager std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt"; m_rollback = createRollbackManager(rollback_path, this); @@ -736,10 +747,10 @@ Server::Server( } // complain about mods declared to be loaded, but not found for(std::vector<ModSpec>::iterator it = m_mods.begin(); - it != m_mods.end(); ++it) + it != m_mods.end(); ++it) load_mod_names.erase((*it).name); for(std::list<ModSpec>::iterator it = unsatisfied_mods.begin(); - it != unsatisfied_mods.end(); ++it) + it != unsatisfied_mods.end(); ++it) load_mod_names.erase((*it).name); if(!load_mod_names.empty()) { @@ -805,9 +816,6 @@ Server::Server( // Add default biomes after nodedef had its aliases added m_biomedef->addDefaultBiomes(); - // Create emerge manager - m_emerge = new EmergeManager(this, m_biomedef); - // Initialize Environment ServerMap *servermap = new ServerMap(path_world, this, m_emerge); m_env = new ServerEnvironment(servermap, m_lua, this, this); @@ -854,13 +862,13 @@ Server::~Server() /* Send the message to clients */ - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -909,13 +917,13 @@ Server::~Server() { JMutexAutoLock clientslock(m_con_mutex); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Delete client - delete i.getNode()->getValue(); + delete i->second; } } @@ -1073,11 +1081,11 @@ void Server::AsyncRunStep() //JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY( m_env->getTimeOfDay(), g_settings->getFloat("time_speed")); // Send as reliable @@ -1117,11 +1125,11 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: handle players"); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; PlayerSAO *playersao = getPlayerSAO(client->peer_id); if(playersao == NULL) continue; @@ -1161,7 +1169,7 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: liquid transform"); - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; m_env->getMap().transformLiquids(modified_blocks); #if 0 /* @@ -1186,11 +1194,11 @@ void Server::AsyncRunStep() JMutexAutoLock lock2(m_con_mutex); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; if(modified_blocks.size() > 0) { @@ -1212,12 +1220,12 @@ void Server::AsyncRunStep() m_clients_number = 0; if(m_clients.size() != 0) infostream<<"Players:"<<std::endl; - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { //u16 peer_id = i.getNode()->getKey(); - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; Player *player = m_env->getPlayer(client->peer_id); if(player==NULL) continue; @@ -1235,7 +1243,7 @@ void Server::AsyncRunStep() float &counter = m_masterserver_timer; if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true) { - ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number); + ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number, m_uptime.get(), m_gamespec.id); counter = 0.01; } counter += dtime; @@ -1259,11 +1267,11 @@ void Server::AsyncRunStep() s16 radius = g_settings->getS16("active_object_send_range_blocks"); radius *= MAP_BLOCKSIZE; - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; // If definitions and textures have not been sent, don't // send objects either @@ -1281,8 +1289,8 @@ void Server::AsyncRunStep() } v3s16 pos = floatToInt(player->getPosition(), BS); - core::map<u16, bool> removed_objects; - core::map<u16, bool> added_objects; + std::set<u16> removed_objects; + std::set<u16> added_objects; m_env->getRemovedActiveObjects(pos, radius, client->m_known_objects, removed_objects); m_env->getAddedActiveObjects(pos, radius, @@ -1302,20 +1310,20 @@ void Server::AsyncRunStep() // Handle removed objects writeU16((u8*)buf, removed_objects.size()); data_buffer.append(buf, 2); - for(core::map<u16, bool>::Iterator - i = removed_objects.getIterator(); - i.atEnd()==false; i++) + for(std::set<u16>::iterator + i = removed_objects.begin(); + i != removed_objects.end(); ++i) { // Get object - u16 id = i.getNode()->getKey(); + u16 id = *i; ServerActiveObject* obj = m_env->getActiveObject(id); // Add to data buffer for sending - writeU16((u8*)buf, i.getNode()->getKey()); + writeU16((u8*)buf, id); data_buffer.append(buf, 2); // Remove from known objects - client->m_known_objects.remove(i.getNode()->getKey()); + client->m_known_objects.erase(id); if(obj && obj->m_known_by_count > 0) obj->m_known_by_count--; @@ -1324,12 +1332,12 @@ void Server::AsyncRunStep() // Handle added objects writeU16((u8*)buf, added_objects.size()); data_buffer.append(buf, 2); - for(core::map<u16, bool>::Iterator - i = added_objects.getIterator(); - i.atEnd()==false; i++) + for(std::set<u16>::iterator + i = added_objects.begin(); + i != added_objects.end(); ++i) { // Get object - u16 id = i.getNode()->getKey(); + u16 id = *i; ServerActiveObject* obj = m_env->getActiveObject(id); // Get object type @@ -1353,7 +1361,7 @@ void Server::AsyncRunStep() data_buffer.append(serializeLongString("")); // Add to known objects - client->m_known_objects.insert(i.getNode()->getKey(), false); + client->m_known_objects.insert(id); if(obj) obj->m_known_by_count++; @@ -1412,7 +1420,7 @@ void Server::AsyncRunStep() // Key = object id // Value = data sent by object - core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages; + std::map<u16, std::list<ActiveObjectMessage>* > buffered_messages; // Get active object messages from environment for(;;) @@ -1421,43 +1429,43 @@ void Server::AsyncRunStep() if(aom.id == 0) break; - core::list<ActiveObjectMessage>* message_list = NULL; - core::map<u16, core::list<ActiveObjectMessage>* >::Node *n; + std::list<ActiveObjectMessage>* message_list = NULL; + std::map<u16, std::list<ActiveObjectMessage>* >::iterator n; n = buffered_messages.find(aom.id); - if(n == NULL) + if(n == buffered_messages.end()) { - message_list = new core::list<ActiveObjectMessage>; - buffered_messages.insert(aom.id, message_list); + message_list = new std::list<ActiveObjectMessage>; + buffered_messages[aom.id] = message_list; } else { - message_list = n->getValue(); + message_list = n->second; } message_list->push_back(aom); } // Route data to every client - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; std::string reliable_data; std::string unreliable_data; // Go through all objects in message buffer - for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator - j = buffered_messages.getIterator(); - j.atEnd()==false; j++) + for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator + j = buffered_messages.begin(); + j != buffered_messages.end(); ++j) { // If object is not known by client, skip it - u16 id = j.getNode()->getKey(); - if(client->m_known_objects.find(id) == NULL) + u16 id = j->first; + if(client->m_known_objects.find(id) == client->m_known_objects.end()) continue; // Get message list of object - core::list<ActiveObjectMessage>* list = j.getNode()->getValue(); + std::list<ActiveObjectMessage>* list = j->second; // Go through every message - for(core::list<ActiveObjectMessage>::Iterator - k = list->begin(); k != list->end(); k++) + for(std::list<ActiveObjectMessage>::iterator + k = list->begin(); k != list->end(); ++k) { // Compose the full new data with header ActiveObjectMessage aom = *k; @@ -1508,11 +1516,11 @@ void Server::AsyncRunStep() } // Clear buffered_messages - for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator - i = buffered_messages.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator + i = buffered_messages.begin(); + i != buffered_messages.end(); ++i) { - delete i.getNode()->getValue(); + delete i->second; } } @@ -1546,7 +1554,7 @@ void Server::AsyncRunStep() // Players far away from the change are stored here. // Instead of sending the changes, MapBlocks are set not sent // for them. - core::list<u16> far_players; + std::list<u16> far_players; if(event->type == MEET_ADDNODE) { @@ -1580,12 +1588,11 @@ void Server::AsyncRunStep() { infostream<<"Server: MEET_OTHER"<<std::endl; prof.add("MEET_OTHER", 1); - for(core::map<v3s16, bool>::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) + for(std::set<v3s16>::iterator + i = event->modified_blocks.begin(); + i != event->modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - setBlockNotSent(p); + setBlockNotSent(*i); } } else @@ -1601,19 +1608,18 @@ void Server::AsyncRunStep() if(far_players.size() > 0) { // Convert list format to that wanted by SetBlocksNotSent - core::map<v3s16, MapBlock*> modified_blocks2; - for(core::map<v3s16, bool>::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) + std::map<v3s16, MapBlock*> modified_blocks2; + for(std::set<v3s16>::iterator + i = event->modified_blocks.begin(); + i != event->modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - modified_blocks2.insert(p, - m_env->getMap().getBlockNoCreateNoEx(p)); + modified_blocks2[*i] = + m_env->getMap().getBlockNoCreateNoEx(*i); } // Set blocks not sent - for(core::list<u16>::Iterator + for(std::list<u16>::iterator i = far_players.begin(); - i != far_players.end(); i++) + i != far_players.end(); ++i) { u16 peer_id = *i; RemoteClient *client = getClient(peer_id); @@ -1792,7 +1798,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) u8 client_max = data[2]; u8 our_max = SER_FMT_VER_HIGHEST; // Use the highest version supported by both - u8 deployed = core::min_(client_max, our_max); + u8 deployed = std::min(client_max, our_max); // If it's lower than the lowest supported, give up. if(deployed < SER_FMT_VER_LOWEST) deployed = SER_FMT_VER_INVALID; @@ -2074,7 +2080,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendMovement(m_con, peer_id); // Send item definitions - SendItemDef(m_con, peer_id, m_itemdef); + SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version); // Send node definitions SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version); @@ -2143,12 +2149,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ { std::ostringstream os(std::ios_base::binary); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; // Get player @@ -2520,13 +2526,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) /* Send the message to clients */ - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -2651,7 +2657,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - core::list<MediaRequest> tosend; + std::list<MediaRequest> tosend; u16 numfiles = readU16(is); infostream<<"Sending "<<numfiles<<" files to " @@ -3189,19 +3195,19 @@ void Server::setInventoryModified(const InventoryLocation &loc) } } -core::list<PlayerInfo> Server::getPlayerInfo() +std::list<PlayerInfo> Server::getPlayerInfo() { DSTACK(__FUNCTION_NAME); JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - core::list<PlayerInfo> list; + std::list<PlayerInfo> list; - core::list<Player*> players = m_env->getPlayers(); + std::list<Player*> players = m_env->getPlayers(); - core::list<Player*>::Iterator i; + std::list<Player*>::iterator i; for(i = players.begin(); - i != players.end(); i++) + i != players.end(); ++i) { PlayerInfo info; @@ -3336,7 +3342,7 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id, } void Server::SendItemDef(con::Connection &con, u16 peer_id, - IItemDefManager *itemdef) + IItemDefManager *itemdef, u16 protocol_version) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3348,7 +3354,7 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id, */ writeU16(os, TOCLIENT_ITEMDEF); std::ostringstream tmp_os(std::ios::binary); - itemdef->serialize(tmp_os); + itemdef->serialize(tmp_os, protocol_version); std::ostringstream tmp_os2(std::ios::binary); compressZlib(tmp_os.str(), tmp_os2); os<<serializeLongString(tmp_os2.str()); @@ -3468,15 +3474,141 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, co m_con.Send(peer_id, 0, data, true); } +// Spawns a particle on peer with peer_id +void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_SPAWN_PARTICLE); + writeV3F1000(os, pos); + writeV3F1000(os, velocity); + writeV3F1000(os, acceleration); + writeF1000(os, expirationtime); + writeF1000(os, size); + writeU8(os, collisiondetection); + os<<serializeLongString(texture); + + // Make data buffer + std::string s = os.str(); + SharedBuffer<u8> data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +// Spawns a particle on all peers +void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, std::string texture) +{ + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendSpawnParticle(client->peer_id, pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); + } +} + +// Adds a ParticleSpawner on peer with peer_id +void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, + float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER); + + writeU16(os, amount); + writeF1000(os, spawntime); + writeV3F1000(os, minpos); + writeV3F1000(os, maxpos); + writeV3F1000(os, minvel); + writeV3F1000(os, maxvel); + writeV3F1000(os, minacc); + writeV3F1000(os, maxacc); + writeF1000(os, minexptime); + writeF1000(os, maxexptime); + writeF1000(os, minsize); + writeF1000(os, maxsize); + writeU8(os, collisiondetection); + os<<serializeLongString(texture); + writeU32(os, id); + + // Make data buffer + std::string s = os.str(); + SharedBuffer<u8> data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +// Adds a ParticleSpawner on all peers +void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, + float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) +{ + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendAddParticleSpawner(client->peer_id, amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id); + } +} + +void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER); + + writeU16(os, id); + + // Make data buffer + std::string s = os.str(); + SharedBuffer<u8> data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendDeleteParticleSpawnerAll(u32 id) +{ + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendDeleteParticleSpawner(client->peer_id, id); + } +} + void Server::BroadcastChatMessage(const std::wstring &message) { - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -3595,10 +3727,10 @@ s32 Server::playSound(const SimpleSoundSpec &spec, } else { - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; Player *player = m_env->getPlayer(client->peer_id); if(!player) continue; @@ -3668,7 +3800,7 @@ void Server::stopSound(s32 handle) } void Server::sendRemoveNode(v3s16 p, u16 ignore_id, - core::list<u16> *far_players, float far_d_nodes) + std::list<u16> *far_players, float far_d_nodes) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); @@ -3681,13 +3813,13 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -3717,18 +3849,18 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, } void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, - core::list<u16> *far_players, float far_d_nodes) + std::list<u16> *far_players, float far_d_nodes) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -3768,11 +3900,11 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, void Server::setBlockNotSent(v3s16 p) { - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; client->SetBlockNotSent(p); } } @@ -3839,19 +3971,19 @@ void Server::SendBlocks(float dtime) ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients"); - core::array<PrioritySortedBlockTransfer> queue; + std::vector<PrioritySortedBlockTransfer> queue; s32 total_sending = 0; { ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); // If definitions and textures have not been sent, don't // send MapBlocks either @@ -3870,7 +4002,7 @@ void Server::SendBlocks(float dtime) // Sort. // Lowest priority number comes first. // Lowest is most important. - queue.sort(); + std::sort(queue.begin(), queue.end()); for(u32 i=0; i<queue.size(); i++) { @@ -4017,7 +4149,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) verbosestream<<"Server: Announcing files to id("<<peer_id<<")" <<std::endl; - core::list<SendableMediaAnnouncement> file_announcements; + std::list<SendableMediaAnnouncement> file_announcements; for(std::map<std::string, MediaInfo>::iterator i = m_media.begin(); i != m_media.end(); i++){ @@ -4043,9 +4175,9 @@ void Server::sendMediaAnnouncement(u16 peer_id) writeU16(os, TOCLIENT_ANNOUNCE_MEDIA); writeU16(os, file_announcements.size()); - for(core::list<SendableMediaAnnouncement>::Iterator + for(std::list<SendableMediaAnnouncement>::iterator j = file_announcements.begin(); - j != file_announcements.end(); j++){ + j != file_announcements.end(); ++j){ os<<serializeString(j->name); os<<serializeString(j->sha1_digest); } @@ -4074,7 +4206,7 @@ struct SendableMedia }; void Server::sendRequestedMedia(u16 peer_id, - const core::list<MediaRequest> &tosend) + const std::list<MediaRequest> &tosend) { DSTACK(__FUNCTION_NAME); @@ -4086,13 +4218,13 @@ void Server::sendRequestedMedia(u16 peer_id, // Put 5kB in one bunch (this is not accurate) u32 bytes_per_bunch = 5000; - core::array< core::list<SendableMedia> > file_bunches; - file_bunches.push_back(core::list<SendableMedia>()); + std::vector< std::list<SendableMedia> > file_bunches; + file_bunches.push_back(std::list<SendableMedia>()); u32 file_size_bunch_total = 0; - for(core::list<MediaRequest>::ConstIterator i = tosend.begin(); - i != tosend.end(); i++) + for(std::list<MediaRequest>::const_iterator i = tosend.begin(); + i != tosend.end(); ++i) { if(m_media.find(i->name) == m_media.end()){ errorstream<<"Server::sendRequestedMedia(): Client asked for " @@ -4138,7 +4270,7 @@ void Server::sendRequestedMedia(u16 peer_id, // Start next bunch if got enough data if(file_size_bunch_total >= bytes_per_bunch){ - file_bunches.push_back(core::list<SendableMedia>()); + file_bunches.push_back(std::list<SendableMedia>()); file_size_bunch_total = 0; } @@ -4169,9 +4301,9 @@ void Server::sendRequestedMedia(u16 peer_id, writeU16(os, i); writeU32(os, file_bunches[i].size()); - for(core::list<SendableMedia>::Iterator + for(std::list<SendableMedia>::iterator j = file_bunches[i].begin(); - j != file_bunches[i].end(); j++){ + j != file_bunches[i].end(); ++j){ os<<serializeString(j->name); os<<serializeLongString(j->data); } @@ -4212,10 +4344,10 @@ void Server::sendDetachedInventoryToAll(const std::string &name) { DSTACK(__FUNCTION_NAME); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++){ - RemoteClient *client = i.getNode()->getValue(); + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i){ + RemoteClient *client = i->second; sendDetachedInventory(name, client->peer_id); } } @@ -4299,11 +4431,11 @@ RemoteClient* Server::getClient(u16 peer_id) { DSTACK(__FUNCTION_NAME); //JMutexAutoLock lock(m_con_mutex); - core::map<u16, RemoteClient*>::Node *n; + std::map<u16, RemoteClient*>::iterator n; n = m_clients.find(peer_id); // A client should exist for all peers - assert(n != NULL); - return n->getValue(); + assert(n != m_clients.end()); + return n->second; } std::wstring Server::getStatusString() @@ -4315,15 +4447,15 @@ std::wstring Server::getStatusString() // Uptime os<<L", uptime="<<m_uptime.get(); // Information about clients - core::map<u16, RemoteClient*>::Iterator i; + std::map<u16, RemoteClient*>::iterator i; bool first; os<<L", clients={"; - for(i = m_clients.getIterator(), first = true; - i.atEnd() == false; i++) + for(i = m_clients.begin(), first = true; + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; // Get player @@ -4363,10 +4495,10 @@ bool Server::checkPriv(const std::string &name, const std::string &priv) void Server::reportPrivsModified(const std::string &name) { if(name == ""){ - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++){ - RemoteClient *client = i.getNode()->getValue(); + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i){ + RemoteClient *client = i->second; Player *player = m_env->getPlayer(client->peer_id); reportPrivsModified(player->getName()); } @@ -4426,6 +4558,111 @@ void Server::notifyPlayers(const std::wstring msg) BroadcastChatMessage(msg); } +void Server::spawnParticle(const char *playername, v3f pos, + v3f velocity, v3f acceleration, + float expirationtime, float size, bool + collisiondetection, std::string texture) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return; + SendSpawnParticle(player->peer_id, pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); +} + +void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture) +{ + SendSpawnParticleAll(pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); +} + +u32 Server::addParticleSpawner(const char *playername, + u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return -1; + + u32 id = 0; + for(;;) // look for unused particlespawner id + { + id++; + if (std::find(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id) + == m_particlespawner_ids.end()) + { + m_particlespawner_ids.push_back(id); + break; + } + } + + SendAddParticleSpawner(player->peer_id, amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, + collisiondetection, texture, id); + + return id; +} + +u32 Server::addParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture) +{ + u32 id = 0; + for(;;) // look for unused particlespawner id + { + id++; + if (std::find(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id) + == m_particlespawner_ids.end()) + { + m_particlespawner_ids.push_back(id); + break; + } + } + + SendAddParticleSpawnerAll(amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, + collisiondetection, texture, id); + + return id; +} + +void Server::deleteParticleSpawner(const char *playername, u32 id) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return; + + m_particlespawner_ids.erase( + std::remove(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id), + m_particlespawner_ids.end()); + SendDeleteParticleSpawner(player->peer_id, id); +} + +void Server::deleteParticleSpawnerAll(u32 id) +{ + m_particlespawner_ids.erase( + std::remove(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id), + m_particlespawner_ids.end()); + SendDeleteParticleSpawnerAll(id); +} + void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) { m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate); @@ -4579,11 +4816,11 @@ const ModSpec* Server::getModSpec(const std::string &modname) } return NULL; } -void Server::getModNames(core::list<std::string> &modlist) +void Server::getModNames(std::list<std::string> &modlist) { for(std::vector<ModSpec>::iterator i = m_mods.begin(); i != m_mods.end(); i++) { - modlist.push_back((*i).name); + modlist.push_back(i->name); } } std::string Server::getBuiltinLuaPath() @@ -4733,15 +4970,15 @@ void Server::handlePeerChange(PeerChange &c) */ // Error check - core::map<u16, RemoteClient*>::Node *n; + std::map<u16, RemoteClient*>::iterator n; n = m_clients.find(c.peer_id); // The client shouldn't already exist - assert(n == NULL); + assert(n == m_clients.end()); // Create client RemoteClient *client = new RemoteClient(); client->peer_id = c.peer_id; - m_clients.insert(client->peer_id, client); + m_clients[client->peer_id] = client; } // PEER_ADDED else if(c.type == PEER_REMOVED) @@ -4751,22 +4988,22 @@ void Server::handlePeerChange(PeerChange &c) */ // Error check - core::map<u16, RemoteClient*>::Node *n; + std::map<u16, RemoteClient*>::iterator n; n = m_clients.find(c.peer_id); // The client should exist - assert(n != NULL); + assert(n != m_clients.end()); /* Mark objects to be not known by the client */ - RemoteClient *client = n->getValue(); + RemoteClient *client = n->second; // Handle objects - for(core::map<u16, bool>::Iterator - i = client->m_known_objects.getIterator(); - i.atEnd()==false; i++) + for(std::set<u16>::iterator + i = client->m_known_objects.begin(); + i != client->m_known_objects.end(); ++i) { // Get object - u16 id = i.getNode()->getKey(); + u16 id = *i; ServerActiveObject* obj = m_env->getActiveObject(id); if(obj && obj->m_known_by_count > 0) @@ -4824,12 +5061,12 @@ void Server::handlePeerChange(PeerChange &c) if(player != NULL) { std::ostringstream os(std::ios_base::binary); - for(core::map<u16, RemoteClient*>::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; // Get player @@ -4849,7 +5086,7 @@ void Server::handlePeerChange(PeerChange &c) // Delete client delete m_clients[c.peer_id]; - m_clients.remove(c.peer_id); + m_clients.erase(c.peer_id); // Send player info to all remaining clients //SendPlayerInfos(); diff --git a/src/server.h b/src/server.h index d7700791c..04e693fc8 100644 --- a/src/server.h +++ b/src/server.h @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "rollback_interface.h" // Needed for rollbackRevertActions() #include <list> // Needed for rollbackRevertActions() +#include <algorithm> #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -166,7 +167,7 @@ struct PrioritySortedBlockTransfer pos = a_pos; peer_id = a_peer_id; } - bool operator < (PrioritySortedBlockTransfer &other) + bool operator < (const PrioritySortedBlockTransfer &other) const { return priority < other.priority; } @@ -271,14 +272,14 @@ public: dtime is used for resetting send radius at slow interval */ void GetNextBlocks(Server *server, float dtime, - core::array<PrioritySortedBlockTransfer> &dest); + std::vector<PrioritySortedBlockTransfer> &dest); void GotBlock(v3s16 p); void SentBlock(v3s16 p); void SetBlockNotSent(v3s16 p); - void SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks); + void SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks); s32 SendingCount() { @@ -314,7 +315,7 @@ public: List of active objects that the client knows of. Value is dummy. */ - core::map<u16, bool> m_known_objects; + std::set<u16> m_known_objects; private: /* @@ -326,7 +327,7 @@ private: Key is position, value is dummy. No MapBlock* is stored here because the blocks can get deleted. */ - core::map<v3s16, bool> m_blocks_sent; + std::set<v3s16> m_blocks_sent; s16 m_nearest_unsent_d; v3s16 m_last_center; float m_nearest_unsent_reset_timer; @@ -339,7 +340,7 @@ private: Block is removed when GOTBLOCKS is received. Value is time from sending. (not used at the moment) */ - core::map<v3s16, float> m_blocks_sending; + std::map<v3s16, float> m_blocks_sending; /* Count of excess GotBlocks(). @@ -381,7 +382,7 @@ public: void Receive(); void ProcessData(u8 *data, u32 datasize, u16 peer_id); - core::list<PlayerInfo> getPlayerInfo(); + std::list<PlayerInfo> getPlayerInfo(); // Environment must be locked when called void setTimeOfDay(u32 time) @@ -455,6 +456,35 @@ public: // Envlock and conlock should be locked when calling this void notifyPlayer(const char *name, const std::wstring msg); void notifyPlayers(const std::wstring msg); + void spawnParticle(const char *playername, + v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + void spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + u32 addParticleSpawner(const char *playername, + u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture); + + u32 addParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture); + + void deleteParticleSpawner(const char *playername, u32 id); + void deleteParticleSpawnerAll(u32 id); + void queueBlockEmerge(v3s16 blockpos, bool allow_generate); @@ -494,7 +524,7 @@ public: IWritableCraftDefManager* getWritableCraftDefManager(); const ModSpec* getModSpec(const std::string &modname); - void getModNames(core::list<std::string> &modlist); + void getModNames(std::list<std::string> &modlist); std::string getBuiltinLuaPath(); std::string getWorldPath(){ return m_path_world; } @@ -526,7 +556,7 @@ private: static void SendDeathscreen(con::Connection &con, u16 peer_id, bool set_camera_point_target, v3f camera_point_target); static void SendItemDef(con::Connection &con, u16 peer_id, - IItemDefManager *itemdef); + IItemDefManager *itemdef, u16 protocol_version); static void SendNodeDef(con::Connection &con, u16 peer_id, INodeDefManager *nodedef, u16 protocol_version); @@ -553,9 +583,9 @@ private: */ // Envlock and conlock should be locked when calling these void sendRemoveNode(v3s16 p, u16 ignore_id=0, - core::list<u16> *far_players=NULL, float far_d_nodes=100); + std::list<u16> *far_players=NULL, float far_d_nodes=100); void sendAddNode(v3s16 p, MapNode n, u16 ignore_id=0, - core::list<u16> *far_players=NULL, float far_d_nodes=100); + std::list<u16> *far_players=NULL, float far_d_nodes=100); void setBlockNotSent(v3s16 p); // Environment and Connection must be locked when called @@ -567,12 +597,47 @@ private: void fillMediaCache(); void sendMediaAnnouncement(u16 peer_id); void sendRequestedMedia(u16 peer_id, - const core::list<MediaRequest> &tosend); + const std::list<MediaRequest> &tosend); void sendDetachedInventory(const std::string &name, u16 peer_id); void sendDetachedInventoryToAll(const std::string &name); void sendDetachedInventories(u16 peer_id); + // Adds a ParticleSpawner on peer with peer_id + void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture, u32 id); + + // Adds a ParticleSpawner on all peers + void SendAddParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture, u32 id); + + // Deletes ParticleSpawner on a single client + void SendDeleteParticleSpawner(u16 peer_id, u32 id); + + // Deletes ParticleSpawner on all clients + void SendDeleteParticleSpawnerAll(u32 id); + + // Spawns particle on single client + void SendSpawnParticle(u16 peer_id, + v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + + // Spawns particle on all clients + void SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture); + /* Something random */ @@ -655,7 +720,7 @@ private: con::Connection m_con; JMutex m_con_mutex; // Connected clients (behind the con mutex) - core::map<u16, RemoteClient*> m_clients; + std::map<u16, RemoteClient*> m_clients; u16 m_clients_number; //for announcing masterserver // Bann checking @@ -735,7 +800,7 @@ private: */ // Mod parent directory paths - core::list<std::string> m_modspaths; + std::list<std::string> m_modspaths; bool m_shutdown_requested; @@ -789,6 +854,11 @@ private: */ // key = name std::map<std::string, Inventory*> m_detached_inventories; + + /* + Particles + */ + std::vector<u32> m_particlespawner_ids; }; /* diff --git a/src/serverlist.cpp b/src/serverlist.cpp index d37b5d637..93f9d2435 100644 --- a/src/serverlist.cpp +++ b/src/serverlist.cpp @@ -232,7 +232,7 @@ static size_t ServerAnnounceCallback(void *contents, size_t size, size_t nmemb, //((std::string*)userp)->append((char*)contents, size * nmemb); //return size * nmemb; } -void sendAnnounce(std::string action, u16 clients) { +void sendAnnounce(std::string action, u16 clients, double uptime, std::string gameid) { Json::Value server; if (action.size()) server["action"] = action; @@ -250,6 +250,9 @@ void sendAnnounce(std::string action, u16 clients) { server["pvp"] = g_settings->getBool("enable_pvp"); server["clients"] = clients; server["clients_max"] = g_settings->get("max_users"); + if (uptime >=1) server["uptime"] = (int)uptime; + if (gameid!="") server["gameid"] = gameid; + } if(server["action"] == "start") actionstream << "announcing to " << g_settings->get("serverlist_url") << std::endl; @@ -259,6 +262,7 @@ void sendAnnounce(std::string action, u16 clients) { if (curl) { CURLcode res; + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(curl, CURLOPT_URL, (g_settings->get("serverlist_url")+std::string("/announce?json=")+curl_easy_escape(curl, writer.write( server ).c_str(), 0)).c_str()); //curl_easy_setopt(curl, CURLOPT_USERAGENT, "minetest"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ServerList::ServerAnnounceCallback); diff --git a/src/serverlist.h b/src/serverlist.h index e81e64c5b..d01415c50 100644 --- a/src/serverlist.h +++ b/src/serverlist.h @@ -39,7 +39,7 @@ namespace ServerList std::vector<ServerListSpec> deSerializeJson(std::string liststring); std::string serializeJson(std::vector<ServerListSpec>); #if USE_CURL - void sendAnnounce(std::string action = "", u16 clients = 0); + void sendAnnounce(std::string action = "", u16 clients = 0, double uptime = 0, std::string gameid = ""); #endif } //ServerList namespace diff --git a/src/serverobject.cpp b/src/serverobject.cpp index beb17d31f..95735de17 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -43,9 +43,9 @@ ServerActiveObject* ServerActiveObject::create(u8 type, const std::string &data) { // Find factory function - core::map<u16, Factory>::Node *n; + std::map<u16, Factory>::iterator n; n = m_types.find(type); - if(n == NULL) + if(n == m_types.end()) { // If factory is not found, just return. dstream<<"WARNING: ServerActiveObject: No factory for type=" @@ -53,18 +53,18 @@ ServerActiveObject* ServerActiveObject::create(u8 type, return NULL; } - Factory f = n->getValue(); + Factory f = n->second; ServerActiveObject *object = (*f)(env, pos, data); return object; } void ServerActiveObject::registerType(u16 type, Factory f) { - core::map<u16, Factory>::Node *n; + std::map<u16, Factory>::iterator n; n = m_types.find(type); - if(n) + if(n != m_types.end()) return; - m_types.insert(type, f); + m_types[type] = f; } float ServerActiveObject::getMinimumSavedMovement() diff --git a/src/serverobject.h b/src/serverobject.h index 6525270f6..7a5b47bd1 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -235,7 +235,7 @@ protected: private: // Used for creating objects based on type - static core::map<u16, Factory> m_types; + static std::map<u16, Factory> m_types; }; #endif diff --git a/src/settings.h b/src/settings.h index 7ac308cc0..1b7e3cb09 100644 --- a/src/settings.h +++ b/src/settings.h @@ -33,6 +33,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "util/string.h" #include "porting.h" +#include <list> +#include <map> +#include <set> enum ValueType { @@ -63,12 +66,12 @@ public: { JMutexAutoLock lock(m_mutex); - for(core::map<std::string, std::string>::Iterator - i = m_settings.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, std::string>::iterator + i = m_settings.begin(); + i != m_settings.end(); ++i) { - std::string name = i.getNode()->getKey(); - std::string value = i.getNode()->getValue(); + std::string name = i->first; + std::string value = i->second; os<<name<<" = "<<value<<"\n"; } } @@ -76,12 +79,11 @@ public: // return all keys used std::vector<std::string> getNames(){ std::vector<std::string> names; - for(core::map<std::string, std::string>::Iterator - i = m_settings.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, std::string>::iterator + i = m_settings.begin(); + i != m_settings.end(); ++i) { - std::string name = i.getNode()->getKey(); - names.push_back(name); + names.push_back(i->first); } return names; } @@ -89,7 +91,7 @@ public: // remove a setting bool remove(const std::string& name) { - return m_settings.remove(name); + return m_settings.erase(name); } @@ -188,8 +190,8 @@ public: Returns false on EOF */ bool getUpdatedConfigObject(std::istream &is, - core::list<std::string> &dst, - core::map<std::string, bool> &updated, + std::list<std::string> &dst, + std::set<std::string> &updated, bool &value_changed) { JMutexAutoLock lock(m_mutex); @@ -228,7 +230,7 @@ public: std::string value = sf.next("\n"); value = trim(value); - if(m_settings.find(name)) + if(m_settings.find(name) != m_settings.end()) { std::string newvalue = m_settings[name]; @@ -242,7 +244,7 @@ public: dst.push_back(name + " = " + newvalue + line_end); - updated[name] = true; + updated.insert(name); } else //file contains a setting which is not in m_settings value_changed=true; @@ -260,8 +262,8 @@ public: infostream<<"Updating configuration file: \"" <<filename<<"\""<<std::endl; - core::list<std::string> objects; - core::map<std::string, bool> updated; + std::list<std::string> objects; + std::set<std::string> updated; bool something_actually_changed = false; // Read and modify stuff @@ -286,11 +288,11 @@ public: // If something not yet determined to have been changed, check if // any new stuff was added if(!something_actually_changed){ - for(core::map<std::string, std::string>::Iterator - i = m_settings.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, std::string>::iterator + i = m_settings.begin(); + i != m_settings.end(); ++i) { - if(updated.find(i.getNode()->getKey())) + if(updated.find(i->first) != updated.end()) continue; something_actually_changed = true; break; @@ -318,9 +320,9 @@ public: /* Write updated stuff */ - for(core::list<std::string>::Iterator + for(std::list<std::string>::iterator i = objects.begin(); - i != objects.end(); i++) + i != objects.end(); ++i) { os<<(*i); } @@ -328,14 +330,14 @@ public: /* Write stuff that was not already in the file */ - for(core::map<std::string, std::string>::Iterator - i = m_settings.getIterator(); - i.atEnd() == false; i++) + for(std::map<std::string, std::string>::iterator + i = m_settings.begin(); + i != m_settings.end(); ++i) { - if(updated.find(i.getNode()->getKey())) + if(updated.find(i->first) != updated.end()) continue; - std::string name = i.getNode()->getKey(); - std::string value = i.getNode()->getValue(); + std::string name = i->first; + std::string value = i->second; infostream<<"Adding \""<<name<<"\" = \""<<value<<"\"" <<std::endl; os<<name<<" = "<<value<<"\n"; @@ -351,7 +353,7 @@ public: returns true on success */ bool parseCommandLine(int argc, char *argv[], - core::map<std::string, ValueSpec> &allowed_options) + std::map<std::string, ValueSpec> &allowed_options) { int nonopt_index = 0; int i=1; @@ -379,16 +381,16 @@ public: std::string name = argname.substr(2); - core::map<std::string, ValueSpec>::Node *n; + std::map<std::string, ValueSpec>::iterator n; n = allowed_options.find(name); - if(n == NULL) + if(n == allowed_options.end()) { errorstream<<"Unknown command-line parameter \"" <<argname<<"\""<<std::endl; return false; } - ValueType type = n->getValue().type; + ValueType type = n->second.type; std::string value = ""; @@ -444,25 +446,25 @@ public: { JMutexAutoLock lock(m_mutex); - return (m_settings.find(name) || m_defaults.find(name)); + return (m_settings.find(name) != m_settings.end() || m_defaults.find(name) != m_defaults.end()); } std::string get(std::string name) { JMutexAutoLock lock(m_mutex); - core::map<std::string, std::string>::Node *n; + std::map<std::string, std::string>::iterator n; n = m_settings.find(name); - if(n == NULL) + if(n == m_settings.end()) { n = m_defaults.find(name); - if(n == NULL) + if(n == m_defaults.end()) { - throw SettingNotFoundException("Setting not found"); + throw SettingNotFoundException(("Setting [" + name + "] not found ").c_str()); } } - return n->getValue(); + return n->second; } bool getBool(std::string name) @@ -919,19 +921,8 @@ fail: if(&other == this) return; - for(core::map<std::string, std::string>::Iterator - i = other.m_settings.getIterator(); - i.atEnd() == false; i++) - { - m_settings[i.getNode()->getKey()] = i.getNode()->getValue(); - } - - for(core::map<std::string, std::string>::Iterator - i = other.m_defaults.getIterator(); - i.atEnd() == false; i++) - { - m_defaults[i.getNode()->getKey()] = i.getNode()->getValue(); - } + m_settings.insert(other.m_settings.begin(), other.m_settings.end()); + m_defaults.insert(other.m_defaults.begin(), other.m_defaults.end()); return; } @@ -944,21 +935,7 @@ fail: if(&other == this) return *this; - for(core::map<std::string, std::string>::Iterator - i = other.m_settings.getIterator(); - i.atEnd() == false; i++) - { - m_settings.insert(i.getNode()->getKey(), - i.getNode()->getValue()); - } - - for(core::map<std::string, std::string>::Iterator - i = other.m_defaults.getIterator(); - i.atEnd() == false; i++) - { - m_defaults.insert(i.getNode()->getKey(), - i.getNode()->getValue()); - } + update(other); return *this; @@ -979,8 +956,8 @@ fail: } private: - core::map<std::string, std::string> m_settings; - core::map<std::string, std::string> m_defaults; + std::map<std::string, std::string> m_settings; + std::map<std::string, std::string> m_defaults; // All methods that access m_settings/m_defaults directly should lock this. JMutex m_mutex; }; diff --git a/src/shader.cpp b/src/shader.cpp index 7e3d16e8b..a224c82bb 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -125,10 +125,10 @@ public: const std::string &filename) { std::string combined = name_of_shader + DIR_DELIM + filename; - core::map<std::string, std::string>::Node *n; + std::map<std::string, std::string>::iterator n; n = m_programs.find(combined); - if(n) - return n->getValue(); + if(n != m_programs.end()) + return n->second; return ""; } // Primarily fetches from cache, secondarily tries to read from filesystem @@ -136,10 +136,10 @@ public: const std::string &filename) { std::string combined = name_of_shader + DIR_DELIM + filename; - core::map<std::string, std::string>::Node *n; + std::map<std::string, std::string>::iterator n; n = m_programs.find(combined); - if(n) - return n->getValue(); + if(n != m_programs.end()) + return n->second; std::string path = getShaderPath(name_of_shader, filename); if(path == ""){ infostream<<"SourceShaderCache::getOrLoad(): No path found for \"" @@ -156,7 +156,7 @@ public: return ""; } private: - core::map<std::string, std::string> m_programs; + std::map<std::string, std::string> m_programs; std::string readFile(const std::string &path) { std::ifstream is(path.c_str(), std::ios::binary); @@ -332,9 +332,9 @@ private: // A shader id is index in this array. // The first position contains a dummy shader. - core::array<ShaderInfo> m_shaderinfo_cache; + std::vector<ShaderInfo> m_shaderinfo_cache; // Maps a shader name to an index in the former. - core::map<std::string, u32> m_name_to_id; + std::map<std::string, u32> m_name_to_id; // The two former containers are behind this mutex JMutex m_shaderinfo_cache_mutex; @@ -343,7 +343,7 @@ private: // Global constant setters // TODO: Delete these in the destructor - core::array<IShaderConstantSetter*> m_global_setters; + std::vector<IShaderConstantSetter*> m_global_setters; }; IWritableShaderSource* createShaderSource(IrrlichtDevice *device) @@ -399,10 +399,10 @@ u32 ShaderSource::getShaderId(const std::string &name) See if shader already exists */ JMutexAutoLock lock(m_shaderinfo_cache_mutex); - core::map<std::string, u32>::Node *n; + std::map<std::string, u32>::iterator n; n = m_name_to_id.find(name); - if(n != NULL) - return n->getValue(); + if(n != m_name_to_id.end()) + return n->second; } /* @@ -471,12 +471,12 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name) { JMutexAutoLock lock(m_shaderinfo_cache_mutex); - core::map<std::string, u32>::Node *n; + std::map<std::string, u32>::iterator n; n = m_name_to_id.find(name); - if(n != NULL){ + if(n != m_name_to_id.end()){ /*infostream<<"getShaderIdDirect(): \""<<name <<"\" found in cache"<<std::endl;*/ - return n->getValue(); + return n->second; } } @@ -494,7 +494,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name) u32 id = m_shaderinfo_cache.size(); m_shaderinfo_cache.push_back(info); - m_name_to_id.insert(name, id); + m_name_to_id[name] = id; /*infostream<<"getShaderIdDirect(): " <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/ @@ -531,7 +531,7 @@ void ShaderSource::processQueue() /* Fetch shaders */ - if(m_get_shader_queue.size() > 0){ + if(!m_get_shader_queue.empty()){ GetRequest<std::string, u32, u8, u8> request = m_get_shader_queue.pop(); diff --git a/src/staticobject.cpp b/src/staticobject.cpp index 48fadaf06..973257fcf 100644 --- a/src/staticobject.cpp +++ b/src/staticobject.cpp @@ -58,18 +58,18 @@ void StaticObjectList::serialize(std::ostream &os) u16 count = m_stored.size() + m_active.size(); writeU16((u8*)buf, count); os.write(buf, 2); - for(core::list<StaticObject>::Iterator + for(std::list<StaticObject>::iterator i = m_stored.begin(); - i != m_stored.end(); i++) + i != m_stored.end(); ++i) { StaticObject &s_obj = *i; s_obj.serialize(os); } - for(core::map<u16, StaticObject>::Iterator - i = m_active.getIterator(); - i.atEnd()==false; i++) + for(std::map<u16, StaticObject>::iterator + i = m_active.begin(); + i != m_active.end(); ++i) { - StaticObject s_obj = i.getNode()->getValue(); + StaticObject s_obj = i->second; s_obj.serialize(os); } } diff --git a/src/staticobject.h b/src/staticobject.h index c8427fe47..640747e96 100644 --- a/src/staticobject.h +++ b/src/staticobject.h @@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include <string> #include <sstream> +#include <list> +#include <map> #include "debug.h" struct StaticObject @@ -62,27 +64,27 @@ public: } else { - if(m_active.find(id) != NULL) + if(m_active.find(id) != m_active.end()) { dstream<<"ERROR: StaticObjectList::insert(): " <<"id already exists"<<std::endl; assert(0); return; } - m_active.insert(id, obj); + m_active[id] = obj; } } void remove(u16 id) { assert(id != 0); - if(m_active.find(id) == NULL) + if(m_active.find(id) == m_active.end()) { dstream<<"WARNING: StaticObjectList::remove(): id="<<id <<" not found"<<std::endl; return; } - m_active.remove(id); + m_active.erase(id); } void serialize(std::ostream &os); @@ -93,8 +95,8 @@ public: from m_stored and inserted to m_active. The caller directly manipulates these containers. */ - core::list<StaticObject> m_stored; - core::map<u16, StaticObject> m_active; + std::list<StaticObject> m_stored; + std::map<u16, StaticObject> m_active; private: }; diff --git a/src/strfnd.h b/src/strfnd.h index d28aa73b2..4a72edf3c 100644 --- a/src/strfnd.h +++ b/src/strfnd.h @@ -65,6 +65,25 @@ public: //std::cout<<"palautus=\""<<palautus<<"\""<<std::endl; return palautus; } + + // Returns substr of tek up to the next occurence of plop that isn't escaped with '\' + std::string next_esc(std::string plop) { + size_t n, realp; + + if (p >= tek.size()) + return ""; + + realp = p; + do { + n = tek.find(plop, p); + if (n == std::string::npos || plop == "") + n = tek.length(); + p = n + plop.length(); + } while (n > 0 && tek[n - 1] == '\\'); + + return tek.substr(realp, n - realp); + } + void skip_over(std::string chars){ while(p < tek.size()){ bool is = false; @@ -128,6 +147,24 @@ public: //std::cout<<"palautus=\""<<palautus<<"\""<<std::endl; return palautus; } + + std::wstring next_esc(std::wstring plop) { + size_t n, realp; + + if (p >= tek.size()) + return L""; + + realp = p; + do { + n = tek.find(plop, p); + if (n == std::wstring::npos || plop == L"") + n = tek.length(); + p = n + plop.length(); + } while (n > 0 && tek[n - 1] == '\\'); + + return tek.substr(realp, n - realp); + } + bool atend(){ if(p>=tek.size()) return true; return false; diff --git a/src/subgame.cpp b/src/subgame.cpp index 3c8bf53c5..19ad4e636 100644 --- a/src/subgame.cpp +++ b/src/subgame.cpp @@ -24,12 +24,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "util/string.h" -std::string getGameName(const std::string &game_path) +bool getGameMinetestConfig(const std::string &game_path, Settings &conf) +{ + std::string conf_path = game_path + DIR_DELIM + "minetest.conf"; + return conf.readConfigFile(conf_path.c_str()); +} + +bool getGameConfig(const std::string &game_path, Settings &conf) { std::string conf_path = game_path + DIR_DELIM + "game.conf"; + return conf.readConfigFile(conf_path.c_str()); +} + +std::string getGameName(const std::string &game_path) +{ Settings conf; - bool succeeded = conf.readConfigFile(conf_path.c_str()); - if(!succeeded) + if(!getGameConfig(game_path, conf)) return ""; if(!conf.exists("name")) return ""; @@ -117,6 +127,11 @@ std::set<std::string> getAvailableGameIds() for(u32 j=0; j<dirlist.size(); j++){ if(!dirlist[j].dir) continue; + // If configuration file is not found or broken, ignore game + Settings conf; + if(!getGameConfig(*i + DIR_DELIM + dirlist[j].name, conf)) + continue; + // Add it to result const char *ends[] = {"_game", NULL}; std::string shorter = removeStringEnd(dirlist[j].name, ends); if(shorter != "") diff --git a/src/subgame.h b/src/subgame.h index 8561c1a52..b120b7542 100644 --- a/src/subgame.h +++ b/src/subgame.h @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <set> #include <vector> +class Settings; + #define WORLDNAME_BLACKLISTED_CHARS "/\\" struct SubgameSpec @@ -52,6 +54,11 @@ struct SubgameSpec } }; +// minetest.conf +bool getGameMinetestConfig(const std::string &game_path, Settings &conf); +// game.conf +bool getGameConfig(const std::string &game_path, Settings &conf); + std::string getGameName(const std::string &game_path); SubgameSpec findSubgame(const std::string &id); diff --git a/src/test.cpp b/src/test.cpp index d86868118..d18bd8b93 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -42,6 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "noise.h" // PseudoRandom used for random data for compression #include "clientserver.h" // LATEST_PROTOCOL_VERSION +#include <algorithm> /* Asserts that the exception occurs @@ -508,26 +509,26 @@ struct TestVoxelManipulator: public TestBase // An area that is 1 bigger in x+ and z- VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2)); - core::list<VoxelArea> aa; + std::list<VoxelArea> aa; d.diff(c, aa); // Correct results - core::array<VoxelArea> results; + std::vector<VoxelArea> results; results.push_back(VoxelArea(v3s16(-2,-2,-3),v3s16(3,2,-3))); results.push_back(VoxelArea(v3s16(3,-2,-2),v3s16(3,2,2))); UASSERT(aa.size() == results.size()); infostream<<"Result of diff:"<<std::endl; - for(core::list<VoxelArea>::Iterator - i = aa.begin(); i != aa.end(); i++) + for(std::list<VoxelArea>::const_iterator + i = aa.begin(); i != aa.end(); ++i) { i->print(infostream); infostream<<std::endl; - s32 j = results.linear_search(*i); - UASSERT(j != -1); - results.erase(j, 1); + std::vector<VoxelArea>::iterator j = std::find(results.begin(), results.end(), *i); + UASSERT(j != results.end()); + results.erase(j); } @@ -582,7 +583,7 @@ struct TestVoxelAlgorithms: public TestBase } VoxelArea a(v3s16(0,0,0), v3s16(2,2,2)); { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, true, light_sources, ndef); @@ -593,7 +594,7 @@ struct TestVoxelAlgorithms: public TestBase } v.setNodeNoRef(v3s16(0,0,0), MapNode(CONTENT_STONE)); { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, true, light_sources, ndef); @@ -602,7 +603,7 @@ struct TestVoxelAlgorithms: public TestBase == LIGHT_SUN); } { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, false, light_sources, ndef); @@ -612,7 +613,7 @@ struct TestVoxelAlgorithms: public TestBase } v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_STONE)); { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, true, light_sources, ndef); @@ -621,7 +622,7 @@ struct TestVoxelAlgorithms: public TestBase == 0); } { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, false, light_sources, ndef); @@ -635,14 +636,14 @@ struct TestVoxelAlgorithms: public TestBase v.setNodeNoRef(v3s16(1,-1,2), n); } { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, true, light_sources, ndef); UASSERT(res.bottom_sunlight_valid == true); } { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, false, light_sources, ndef); @@ -654,14 +655,14 @@ struct TestVoxelAlgorithms: public TestBase v.setNodeNoRef(v3s16(1,-1,2), n); } { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, true, light_sources, ndef); UASSERT(res.bottom_sunlight_valid == false); } { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, false, light_sources, ndef); @@ -669,7 +670,7 @@ struct TestVoxelAlgorithms: public TestBase } v.setNodeNoRef(v3s16(1,3,2), MapNode(CONTENT_IGNORE)); { - core::map<v3s16, bool> light_sources; + std::set<v3s16> light_sources; voxalgo::setLight(v, a, 0, ndef); voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( v, a, true, light_sources, ndef); @@ -697,16 +698,16 @@ struct TestVoxelAlgorithms: public TestBase v.setNode(v3s16(1,1,2), n); } { - core::map<v3s16, bool> light_sources; - core::map<v3s16, u8> unlight_from; + std::set<v3s16> light_sources; + std::map<v3s16, u8> unlight_from; voxalgo::clearLightAndCollectSources(v, a, LIGHTBANK_DAY, ndef, light_sources, unlight_from); //v.print(dstream, ndef, VOXELPRINT_LIGHT_DAY); UASSERT(v.getNode(v3s16(0,1,1)).getLight(LIGHTBANK_DAY, ndef) == 0); - UASSERT(light_sources.find(v3s16(1,1,1)) != NULL); + UASSERT(light_sources.find(v3s16(1,1,1)) != light_sources.end()); UASSERT(light_sources.size() == 1); - UASSERT(unlight_from.find(v3s16(1,1,2)) != NULL); + UASSERT(unlight_from.find(v3s16(1,1,2)) != unlight_from.end()); UASSERT(unlight_from.size() == 1); } } diff --git a/src/tile.cpp b/src/tile.cpp index 7286293d8..aea9665f5 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -206,10 +206,10 @@ public: { assert(img); // Remove old image - core::map<std::string, video::IImage*>::Node *n; + std::map<std::string, video::IImage*>::iterator n; n = m_images.find(name); - if(n){ - video::IImage *oldimg = n->getValue(); + if(n != m_images.end()){ + video::IImage *oldimg = n->second; if(oldimg) oldimg->drop(); } @@ -229,20 +229,20 @@ public: } video::IImage* get(const std::string &name) { - core::map<std::string, video::IImage*>::Node *n; + std::map<std::string, video::IImage*>::iterator n; n = m_images.find(name); - if(n) - return n->getValue(); + if(n != m_images.end()) + return n->second; return NULL; } // Primarily fetches from cache, secondarily tries to read from filesystem video::IImage* getOrLoad(const std::string &name, IrrlichtDevice *device) { - core::map<std::string, video::IImage*>::Node *n; + std::map<std::string, video::IImage*>::iterator n; n = m_images.find(name); - if(n){ - n->getValue()->grab(); // Grab for caller - return n->getValue(); + if(n != m_images.end()){ + n->second->grab(); // Grab for caller + return n->second; } video::IVideoDriver* driver = device->getVideoDriver(); std::string path = getTexturePath(name.c_str()); @@ -263,7 +263,7 @@ public: return img; } private: - core::map<std::string, video::IImage*> m_images; + std::map<std::string, video::IImage*> m_images; }; /* @@ -417,9 +417,9 @@ private: // A texture id is index in this array. // The first position contains a NULL texture. - core::array<SourceAtlasPointer> m_atlaspointer_cache; + std::vector<SourceAtlasPointer> m_atlaspointer_cache; // Maps a texture name to an index in the former. - core::map<std::string, u32> m_name_to_id; + std::map<std::string, u32> m_name_to_id; // The two former containers are behind this mutex JMutex m_atlaspointer_cache_mutex; @@ -465,11 +465,11 @@ u32 TextureSource::getTextureId(const std::string &name) See if texture already exists */ JMutexAutoLock lock(m_atlaspointer_cache_mutex); - core::map<std::string, u32>::Node *n; + std::map<std::string, u32>::iterator n; n = m_name_to_id.find(name); - if(n != NULL) + if(n != m_name_to_id.end()) { - return n->getValue(); + return n->second; } } @@ -579,13 +579,13 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) { JMutexAutoLock lock(m_atlaspointer_cache_mutex); - core::map<std::string, u32>::Node *n; + std::map<std::string, u32>::iterator n; n = m_name_to_id.find(name); - if(n != NULL) + if(n != m_name_to_id.end()) { /*infostream<<"getTextureIdDirect(): \""<<name <<"\" found in cache"<<std::endl;*/ - return n->getValue(); + return n->second; } } @@ -724,7 +724,7 @@ u32 TextureSource::getTextureIdDirect(const std::string &name) baseimg_dim = baseimg->getDimension(); SourceAtlasPointer nap(name, ap, baseimg, v2s32(0,0), baseimg_dim); m_atlaspointer_cache.push_back(nap); - m_name_to_id.insert(name, id); + m_name_to_id[name] = id; /*infostream<<"getTextureIdDirect(): " <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/ @@ -769,7 +769,7 @@ void TextureSource::processQueue() /* Fetch textures */ - if(m_get_texture_queue.size() > 0) + if(!m_get_texture_queue.empty()) { GetRequest<std::string, u32, u8, u8> request = m_get_texture_queue.pop(); @@ -872,7 +872,7 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) main content features */ - core::map<std::string, bool> sourcelist; + std::set<std::string> sourcelist; for(u16 j=0; j<MAX_CONTENT+1; j++) { @@ -882,16 +882,16 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) for(u32 i=0; i<6; i++) { std::string name = f.tiledef[i].name; - sourcelist[name] = true; + sourcelist.insert(name); } } infostream<<"Creating texture atlas out of textures: "; - for(core::map<std::string, bool>::Iterator - i = sourcelist.getIterator(); - i.atEnd() == false; i++) + for(std::set<std::string>::iterator + i = sourcelist.begin(); + i != sourcelist.end(); ++i) { - std::string name = i.getNode()->getKey(); + std::string name = *i; infostream<<"\""<<name<<"\" "; } infostream<<std::endl; @@ -910,11 +910,11 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) pos_in_atlas.X = column_padding; pos_in_atlas.Y = padding; - for(core::map<std::string, bool>::Iterator - i = sourcelist.getIterator(); - i.atEnd() == false; i++) + for(std::set<std::string>::iterator + i = sourcelist.begin(); + i != sourcelist.end(); ++i) { - std::string name = i.getNode()->getKey(); + std::string name = *i; // Generate image by name video::IImage *img2 = generate_image_from_scratch(name, m_device, @@ -1026,11 +1026,11 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) bool reuse_old_id = false; u32 id = m_atlaspointer_cache.size(); // Check old id without fetching a texture - core::map<std::string, u32>::Node *n; + std::map<std::string, u32>::iterator n; n = m_name_to_id.find(name); // If it exists, we will replace the old definition - if(n){ - id = n->getValue(); + if(n != m_name_to_id.end()){ + id = n->second; reuse_old_id = true; /*infostream<<"TextureSource::buildMainAtlas(): " <<"Replacing old AtlasPointer"<<std::endl;*/ @@ -1066,12 +1066,12 @@ void TextureSource::buildMainAtlas(class IGameDef *gamedef) /* Second pass: set texture pointer in generated AtlasPointers */ - for(core::map<std::string, bool>::Iterator - i = sourcelist.getIterator(); - i.atEnd() == false; i++) + for(std::set<std::string>::iterator + i = sourcelist.begin(); + i != sourcelist.end(); ++i) { - std::string name = i.getNode()->getKey(); - if(m_name_to_id.find(name) == NULL) + std::string name = *i; + if(m_name_to_id.find(name) == m_name_to_id.end()) continue; u32 id = m_name_to_id[name]; //infostream<<"id of name "<<name<<" is "<<id<<std::endl; diff --git a/src/tile.h b/src/tile.h index 07e5bcb56..c5c7f9303 100644 --- a/src/tile.h +++ b/src/tile.h @@ -205,7 +205,8 @@ struct TileSpec texture == other.texture && alpha == other.alpha && material_type == other.material_type && - material_flags == other.material_flags + material_flags == other.material_flags && + rotation == other.rotation ); } @@ -264,6 +265,7 @@ struct TileSpec // Animation parameters u8 animation_frame_count; u16 animation_frame_length_ms; + u8 rotation; }; #endif diff --git a/src/tool.cpp b/src/tool.cpp index 04f19749c..4d809e2c4 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -24,9 +24,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "util/numeric.h" -void ToolCapabilities::serialize(std::ostream &os) const +void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const { - writeU8(os, 1); // version + if(protocol_version <= 17) + writeU8(os, 1); // version + else + writeU8(os, 2); // version writeF1000(os, full_punch_interval); writeS16(os, max_drop_level); writeU32(os, groupcaps.size()); @@ -44,12 +47,20 @@ void ToolCapabilities::serialize(std::ostream &os) const writeF1000(os, i->second); } } + if(protocol_version > 17){ + writeU32(os, damageGroups.size()); + for(std::map<std::string, s16>::const_iterator + i = damageGroups.begin(); i != damageGroups.end(); i++){ + os<<serializeString(i->first); + writeS16(os, i->second); + } + } } void ToolCapabilities::deSerialize(std::istream &is) { int version = readU8(is); - if(version != 1) throw SerializationError( + if(version != 1 && version != 2) throw SerializationError( "unsupported ToolCapabilities version"); full_punch_interval = readF1000(is); max_drop_level = readS16(is); @@ -68,6 +79,15 @@ void ToolCapabilities::deSerialize(std::istream &is) } groupcaps[name] = cap; } + if(version == 2) + { + u32 damage_groups_size = readU32(is); + for(u32 i=0; i<damage_groups_size; i++){ + std::string name = deSerializeString(is); + s16 rating = readS16(is); + damageGroups[name] = rating; + } + } } DigParams getDigParams(const ItemGroupList &groups, @@ -136,28 +156,26 @@ DigParams getDigParams(const ItemGroupList &groups, return getDigParams(groups, tp, 1000000); } -HitParams getHitParams(const ItemGroupList &groups, +HitParams getHitParams(const ItemGroupList &armor_groups, const ToolCapabilities *tp, float time_from_last_punch) { - DigParams digprop = getDigParams(groups, tp, - time_from_last_punch); - - if(time_from_last_punch > tp->full_punch_interval) - time_from_last_punch = tp->full_punch_interval; - // Damage in hp is equivalent to nodes dug in time_from_last_punch - s16 hp = 0; - if(digprop.diggable) - hp = time_from_last_punch / digprop.time; - // Wear is the same as for digging a single node - s16 wear = (float)digprop.wear; - - return HitParams(hp, wear, digprop.main_group); + s16 damage = 0; + float full_punch_interval = tp->full_punch_interval; + + for(std::map<std::string, s16>::const_iterator + i = tp->damageGroups.begin(); i != tp->damageGroups.end(); i++){ + s16 armor = itemgroup_get(armor_groups, i->first); + damage += i->second * rangelim(time_from_last_punch * full_punch_interval, 0.0, 1.0) + * armor / 100.0; + } + + return HitParams(damage, 0); } -HitParams getHitParams(const ItemGroupList &groups, +HitParams getHitParams(const ItemGroupList &armor_groups, const ToolCapabilities *tp) { - return getHitParams(groups, tp, 1000000); + return getHitParams(armor_groups, tp, 1000000); } PunchDamageResult getPunchDamage( @@ -187,7 +205,6 @@ PunchDamageResult getPunchDamage( result.did_punch = true; result.wear = hitparams.wear; result.damage = hitparams.hp; - result.main_group = hitparams.main_group; } return result; diff --git a/src/tool.h b/src/tool.h index e812a9e36..509561a16 100644 --- a/src/tool.h +++ b/src/tool.h @@ -52,6 +52,7 @@ struct ToolGroupCap // CLANG SUCKS DONKEY BALLS typedef std::map<std::string, struct ToolGroupCap> ToolGCMap; +typedef std::map<std::string, s16> DamageGroup; struct ToolCapabilities { @@ -59,19 +60,22 @@ struct ToolCapabilities int max_drop_level; // CLANG SUCKS DONKEY BALLS ToolGCMap groupcaps; + DamageGroup damageGroups; ToolCapabilities( float full_punch_interval_=1.4, int max_drop_level_=1, // CLANG SUCKS DONKEY BALLS - ToolGCMap groupcaps_=ToolGCMap() + ToolGCMap groupcaps_=ToolGCMap(), + DamageGroup damageGroups_=DamageGroup() ): full_punch_interval(full_punch_interval_), max_drop_level(max_drop_level_), - groupcaps(groupcaps_) + groupcaps(groupcaps_), + damageGroups(damageGroups_) {} - void serialize(std::ostream &os) const; + void serialize(std::ostream &os, u16 version) const; void deSerialize(std::istream &is); }; @@ -103,19 +107,17 @@ struct HitParams { s16 hp; s16 wear; - std::string main_group; - HitParams(s16 hp_=0, s16 wear_=0, std::string main_group_=""): + HitParams(s16 hp_=0, s16 wear_=0): hp(hp_), - wear(wear_), - main_group(main_group_) + wear(wear_) {} }; -HitParams getHitParams(const ItemGroupList &groups, +HitParams getHitParams(const ItemGroupList &armor_groups, const ToolCapabilities *tp, float time_from_last_punch); -HitParams getHitParams(const ItemGroupList &groups, +HitParams getHitParams(const ItemGroupList &armor_groups, const ToolCapabilities *tp); struct PunchDamageResult @@ -123,7 +125,6 @@ struct PunchDamageResult bool did_punch; int damage; int wear; - std::string main_group; PunchDamageResult(): did_punch(false), diff --git a/src/treegen.cpp b/src/treegen.cpp index 7b6152329..808cf916a 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -28,15 +28,16 @@ with this program; if not, write to the Free Software Foundation, Inc., namespace treegen { + void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef,int seed) + bool is_apple_tree, INodeDefManager *ndef, int seed) { MapNode treenode(ndef->getId("mapgen_tree")); MapNode leavesnode(ndef->getId("mapgen_leaves")); MapNode applenode(ndef->getId("mapgen_apple")); - PseudoRandom ps(seed); - s16 trunk_h = ps.range(4, 5); + PseudoRandom pr(seed); + s16 trunk_h = pr.range(4, 5); v3s16 p1 = p0; for(s16 ii=0; ii<trunk_h; ii++) { @@ -72,9 +73,9 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, s16 d = 1; v3s16 p( - ps.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), - ps.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), - ps.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) + pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), + pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), + pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) ); for(s16 z=0; z<=d; z++) @@ -100,7 +101,7 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, continue; u32 i = leaves_a.index(x,y,z); if(leaves_d[i] == 1) { - bool is_apple = ps.range(0,99) < 10; + bool is_apple = pr.range(0,99) < 10; if(is_apple_tree && is_apple) { vmanip.m_data[vi] = applenode; } else { @@ -111,10 +112,10 @@ void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, } // L-System tree LUA spawner -void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition) +void spawn_ltree(ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeDef tree_definition) { ServerMap *map = &env->getServerMap(); - core::map<v3s16, MapBlock*> modified_blocks; + std::map<v3s16, MapBlock*> modified_blocks; ManualMapVoxelManipulator vmanip(map); v3s16 tree_blockp = getNodeBlockPos(p0); vmanip.initialEmerge(tree_blockp - v3s16(1,1,1), tree_blockp + v3s16(1,3,1)); @@ -122,23 +123,17 @@ void spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef, TreeD vmanip.blitBackAll(&modified_blocks); // update lighting - core::map<v3s16, MapBlock*> lighting_modified_blocks; - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) - { - lighting_modified_blocks.insert(i.getNode()->getKey(), i.getNode()->getValue()); - } + std::map<v3s16, MapBlock*> lighting_modified_blocks; + lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); map->updateLighting(lighting_modified_blocks, modified_blocks); // Send a MEET_OTHER event MapEditEvent event; event.type = MEET_OTHER; - for(core::map<v3s16, MapBlock*>::Iterator - i = modified_blocks.getIterator(); - i.atEnd() == false; i++) + for(std::map<v3s16, MapBlock*>::iterator + i = modified_blocks.begin(); + i != modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - event.modified_blocks.insert(p, true); + event.modified_blocks.insert(i->first); } map->dispatchEvent(&event); } @@ -512,33 +507,48 @@ v3f transposeMatrix(irr::core::matrix4 M, v3f v) return translated; } -#if 0 -static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) +void make_jungletree(VoxelManipulator &vmanip, v3s16 p0, + INodeDefManager *ndef, int seed) { - MapNode treenode(ndef->getId("mapgen_jungletree")); - MapNode leavesnode(ndef->getId("mapgen_leaves")); + content_t c_tree = ndef->getId("mapgen_jungletree"); + content_t c_leaves = ndef->getId("mapgen_jungleleaves"); + if (c_tree == CONTENT_IGNORE) + c_tree = ndef->getId("mapgen_tree"); + if (c_leaves == CONTENT_IGNORE) + c_leaves = ndef->getId("mapgen_leaves"); + + MapNode treenode(c_tree); + MapNode leavesnode(c_leaves); + PseudoRandom pr(seed); for(s16 x=-1; x<=1; x++) for(s16 z=-1; z<=1; z++) { - if(myrand_range(0, 2) == 0) + if(pr.range(0, 2) == 0) continue; v3s16 p1 = p0 + v3s16(x,0,z); v3s16 p2 = p0 + v3s16(x,-1,z); - if(vmanip.m_area.contains(p2) - && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR) - vmanip.m_data[vmanip.m_area.index(p2)] = treenode; - else if(vmanip.m_area.contains(p1)) - vmanip.m_data[vmanip.m_area.index(p1)] = treenode; + u32 vi1 = vmanip.m_area.index(p1); + u32 vi2 = vmanip.m_area.index(p2); + + if (vmanip.m_area.contains(p2) && + vmanip.m_data[vi2].getContent() == CONTENT_AIR) + vmanip.m_data[vi2] = treenode; + else if (vmanip.m_area.contains(p1) && + vmanip.m_data[vi1].getContent() == CONTENT_AIR) + vmanip.m_data[vi1] = treenode; } + vmanip.m_data[vmanip.m_area.index(p0)] = treenode; - s16 trunk_h = myrand_range(8, 12); + s16 trunk_h = pr.range(8, 12); v3s16 p1 = p0; - for(s16 ii=0; ii<trunk_h; ii++) + for (s16 ii=0; ii<trunk_h; ii++) { - if(vmanip.m_area.contains(p1)) - vmanip.m_data[vmanip.m_area.index(p1)] = treenode; + if (vmanip.m_area.contains(p1)) { + u32 vi = vmanip.m_area.index(p1); + if (vmanip.m_data[vi].getContent() == CONTENT_AIR) + vmanip.m_data[vi] = treenode; + } p1.Y++; } @@ -568,9 +578,9 @@ static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0, s16 d = 1; v3s16 p( - myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), - myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), - myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) + pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), + pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), + pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) ); for(s16 z=0; z<=d; z++) @@ -591,14 +601,13 @@ static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0, if(vmanip.m_area.contains(p) == false) continue; u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() != CONTENT_AIR - && vmanip.m_data[vi].getContent() != CONTENT_IGNORE) + if (vmanip.m_data[vi].getContent() != CONTENT_AIR && + vmanip.m_data[vi].getContent() != CONTENT_IGNORE) continue; u32 i = leaves_a.index(x,y,z); if(leaves_d[i] == 1) vmanip.m_data[vi] = leavesnode; } } -#endif }; // namespace treegen diff --git a/src/treegen.h b/src/treegen.h index ca4d3e23d..16c85cf0a 100644 --- a/src/treegen.h +++ b/src/treegen.h @@ -27,33 +27,36 @@ class ManualMapVoxelManipulator; class INodeDefManager; -namespace treegen -{ +namespace treegen { -struct TreeDef -{ -std::string initial_axiom; -std::string rules_a; -std::string rules_b; -std::string rules_c; -std::string rules_d; -MapNode trunknode; -MapNode leavesnode; -MapNode leaves2node; -int leaves2_chance; -int angle; -int iterations; -int iterations_random_level; -std::string trunk_type; -bool thin_branches; -MapNode fruitnode; -int fruit_chance; -int seed; -}; + struct TreeDef { + std::string initial_axiom; + std::string rules_a; + std::string rules_b; + std::string rules_c; + std::string rules_d; + + MapNode trunknode; + MapNode leavesnode; + MapNode leaves2node; + + int leaves2_chance; + int angle; + int iterations; + int iterations_random_level; + std::string trunk_type; + bool thin_branches; + MapNode fruitnode; + int fruit_chance; + int seed; + }; // Add default tree void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef,int seed); + bool is_apple_tree, INodeDefManager *ndef, int seed); + // Add jungle tree + void make_jungletree(VoxelManipulator &vmanip, v3s16 p0, + INodeDefManager *ndef, int seed); // Add L-Systems tree (used by engine) void make_ltree(ManualMapVoxelManipulator &vmanip, v3s16 p0, INodeDefManager *ndef, @@ -73,7 +76,7 @@ int seed; PseudoRandom ps, TreeDef &tree_definition); void tree_fruit_placement(ManualMapVoxelManipulator &vmanip, v3f p0, TreeDef &tree_definition); - irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle,v3f axis); + irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis); v3f transposeMatrix(irr::core::matrix4 M ,v3f v); diff --git a/src/util/container.h b/src/util/container.h index 775372649..9bb388f0e 100644 --- a/src/util/container.h +++ b/src/util/container.h @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <jmutex.h> #include <jmutexautolock.h> #include "../porting.h" // For sleep_ms +#include <list> +#include <vector> /* Queue with unique values with fast checking of value existence @@ -43,11 +45,11 @@ public: bool push_back(Value value) { // Check if already exists - if(m_map.find(value) != NULL) + if(m_map.find(value) != m_map.end()) return false; // Add - m_map.insert(value, 0); + m_map[value] = 0; m_list.push_back(value); return true; @@ -55,22 +57,21 @@ public: Value pop_front() { - typename core::list<Value>::Iterator i = m_list.begin(); + typename std::list<Value>::iterator i = m_list.begin(); Value value = *i; - m_map.remove(value); + m_map.erase(value); m_list.erase(i); return value; } u32 size() { - assert(m_list.size() == m_map.size()); - return m_list.size(); + return m_map.size(); } private: - core::map<Value, u8> m_map; - core::list<Value> m_list; + std::map<Value, u8> m_map; + std::list<Value> m_list; }; #if 1 @@ -95,31 +96,31 @@ public: { JMutexAutoLock lock(m_mutex); - typename core::map<Key, Value>::Node *n; + typename std::map<Key, Value>::iterator n; n = m_values.find(name); - if(n == NULL) + if(n == m_values.end()) return false; if(result != NULL) - *result = n->getValue(); + *result = n->second; return true; } - core::list<Value> getValues() + std::list<Value> getValues() { - core::list<Value> result; - for(typename core::map<Key, Value>::Iterator - i = m_values.getIterator(); - i.atEnd() == false; i++){ - result.push_back(i.getNode()->getValue()); + std::list<Value> result; + for(typename std::map<Key, Value>::iterator + i = m_values.begin(); + i != m_values.end(); ++i){ + result.push_back(i->second); } return result; } private: - core::map<Key, Value> m_values; + std::map<Key, Value> m_values; JMutex m_mutex; }; #endif @@ -163,10 +164,10 @@ public: u32 getId(const T &value) { JMutexAutoLock lock(m_mutex); - typename core::map<T, u32>::Node *n; + typename std::map<T, u32>::iterator n; n = m_value_to_id.find(value); - if(n != NULL) - return n->getValue(); + if(n != m_value_to_id.end()) + return n->second; m_id_to_value.push_back(value); u32 new_id = m_id_to_value.size(); m_value_to_id.insert(value, new_id); @@ -176,8 +177,8 @@ public: private: JMutex m_mutex; // Values are stored here at id-1 position (id 1 = [0]) - core::array<T> m_id_to_value; - core::map<T, u32> m_value_to_id; + std::vector<T> m_id_to_value; + std::map<T, u32> m_value_to_id; }; /* @@ -187,39 +188,52 @@ template<typename T> class Queue { public: + Queue(): + m_list_size(0) + {} + void push_back(T t) { m_list.push_back(t); + ++m_list_size; } T pop_front() { - if(m_list.size() == 0) + if(m_list.empty()) throw ItemNotFoundException("Queue: queue is empty"); - typename core::list<T>::Iterator begin = m_list.begin(); + typename std::list<T>::iterator begin = m_list.begin(); T t = *begin; m_list.erase(begin); + --m_list_size; return t; } T pop_back() { - if(m_list.size() == 0) + if(m_list.empty()) throw ItemNotFoundException("Queue: queue is empty"); - typename core::list<T>::Iterator last = m_list.getLast(); + typename std::list<T>::iterator last = m_list.back(); T t = *last; m_list.erase(last); + --m_list_size; return t; } u32 size() { - return m_list.size(); + return m_list_size; + } + + bool empty() + { + return m_list.empty(); } protected: - core::list<T> m_list; + std::list<T> m_list; + u32 m_list_size; }; /* @@ -234,10 +248,10 @@ public: { m_mutex.Init(); } - u32 size() + bool empty() { JMutexAutoLock lock(m_mutex); - return m_list.size(); + return m_list.empty(); } void push_back(T t) { @@ -253,9 +267,9 @@ public: { JMutexAutoLock lock(m_mutex); - if(m_list.size() > 0) + if(!m_list.empty()) { - typename core::list<T>::Iterator begin = m_list.begin(); + typename std::list<T>::iterator begin = m_list.begin(); T t = *begin; m_list.erase(begin); return t; @@ -279,9 +293,9 @@ public: { JMutexAutoLock lock(m_mutex); - if(m_list.size() > 0) + if(!m_list.empty()) { - typename core::list<T>::Iterator last = m_list.getLast(); + typename std::list<T>::iterator last = m_list.back(); T t = *last; m_list.erase(last); return t; @@ -302,14 +316,14 @@ public: return m_mutex; } - core::list<T> & getList() + std::list<T> & getList() { return m_list; } protected: JMutex m_mutex; - core::list<T> m_list; + std::list<T> m_list; }; #endif diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index a79454628..ed83df7d7 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <iostream> // Calculate the borders of a "d-radius" cube -void getFacePositions(core::list<v3s16> &list, u16 d) +void getFacePositions(std::list<v3s16> &list, u16 d) { if(d == 0) { diff --git a/src/util/numeric.h b/src/util/numeric.h index 450a98e40..e66af2376 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -25,9 +25,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "../irr_v3d.h" #include "../irr_aabb3d.h" #include <irrList.h> +#include <list> // Calculate the borders of a "d-radius" cube -void getFacePositions(core::list<v3s16> &list, u16 d); +void getFacePositions(std::list<v3s16> &list, u16 d); class IndentationRaiser { diff --git a/src/util/string.h b/src/util/string.h index 2f0264bd4..6c48adeb3 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -286,6 +286,22 @@ inline std::string wrap_rows(const std::string &from, u32 rowlen) return to; } +/* + Removes all \\ from a string that had been escaped (FormSpec strings) +*/ +inline std::string unescape_string(std::string &s) +{ + std::string res; + + for (size_t i = 0; i <= s.length(); i++) { + if (s[i] == '\\') + i++; + res += s[i]; + } + + return res; +} + std::string translatePassword(std::string playername, std::wstring password); size_t curl_write_data(char *ptr, size_t size, size_t nmemb, void *userdata); u32 readFlagString(std::string str, FlagDesc *flagdesc); diff --git a/src/util/thread.h b/src/util/thread.h index 949bb4204..6b2cf5b6c 100644 --- a/src/util/thread.h +++ b/src/util/thread.h @@ -120,7 +120,7 @@ class GetResult public: Key key; T item; - core::list<CallerInfo<Caller, CallerData> > callers; + std::list<CallerInfo<Caller, CallerData> > callers; }; template<typename Key, typename T, typename Caller, typename CallerData> @@ -152,16 +152,16 @@ public: Key key; ResultQueue<Key, T, Caller, CallerData> *dest; - core::list<CallerInfo<Caller, CallerData> > callers; + std::list<CallerInfo<Caller, CallerData> > callers; }; template<typename Key, typename T, typename Caller, typename CallerData> class RequestQueue { public: - u32 size() + bool empty() { - return m_queue.size(); + return m_queue.empty(); } void add(Key key, Caller caller, CallerData callerdata, @@ -172,17 +172,17 @@ public: /* If the caller is already on the list, only update CallerData */ - for(typename core::list< GetRequest<Key, T, Caller, CallerData> >::Iterator + for(typename std::list< GetRequest<Key, T, Caller, CallerData> >::iterator i = m_queue.getList().begin(); - i != m_queue.getList().end(); i++) + i != m_queue.getList().end(); ++i) { GetRequest<Key, T, Caller, CallerData> &request = *i; if(request.key == key) { - for(typename core::list< CallerInfo<Caller, CallerData> >::Iterator + for(typename std::list< CallerInfo<Caller, CallerData> >::iterator i = request.callers.begin(); - i != request.callers.end(); i++) + i != request.callers.end(); ++i) { CallerInfo<Caller, CallerData> &ca = *i; if(ca.caller == caller) diff --git a/src/util/timetaker.cpp b/src/util/timetaker.cpp index 910fea822..720a9e1a9 100644 --- a/src/util/timetaker.cpp +++ b/src/util/timetaker.cpp @@ -23,19 +23,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "../log.h" #include <ostream> -TimeTaker::TimeTaker(const char *name, u32 *result) +TimeTaker::TimeTaker(const char *name, u32 *result, TimePrecision prec) { m_name = name; m_result = result; m_running = true; - m_time1 = getTimeMs(); + m_precision = prec; + m_time1 = getTime(prec); } u32 TimeTaker::stop(bool quiet) { if(m_running) { - u32 time2 = getTimeMs(); + u32 time2 = getTime(m_precision); u32 dtime = time2 - m_time1; if(m_result != NULL) { @@ -52,9 +53,9 @@ u32 TimeTaker::stop(bool quiet) return 0; } -u32 TimeTaker::getTime() +u32 TimeTaker::getTimerTime() { - u32 time2 = getTimeMs(); + u32 time2 = getTime(m_precision); u32 dtime = time2 - m_time1; return dtime; } diff --git a/src/util/timetaker.h b/src/util/timetaker.h index 0b9d9ca04..5512c205f 100644 --- a/src/util/timetaker.h +++ b/src/util/timetaker.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define UTIL_TIMETAKER_HEADER #include "../irrlichttypes.h" +#include "../gettime.h" /* TimeTaker @@ -29,7 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc., class TimeTaker { public: - TimeTaker(const char *name, u32 *result=NULL); + TimeTaker(const char *name, u32 *result=NULL, + TimePrecision=PRECISION_MILLI); ~TimeTaker() { @@ -38,12 +40,13 @@ public: u32 stop(bool quiet=false); - u32 getTime(); + u32 getTimerTime(); private: const char *m_name; u32 m_time1; bool m_running; + TimePrecision m_precision; u32 *m_result; }; diff --git a/src/voxel.cpp b/src/voxel.cpp index c55f3f539..f859a1f03 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -302,7 +302,7 @@ void VoxelManipulator::clearFlag(u8 flags) } void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight, - core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr) + std::set<v3s16> & light_sources, INodeDefManager *nodemgr) { v3s16 dirs[6] = { v3s16(0,0,1), // back @@ -360,7 +360,7 @@ void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight, } } else{ - light_sources.insert(n2pos, true); + light_sources.insert(n2pos); } } } @@ -384,24 +384,16 @@ void VoxelManipulator::unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight, values of from_nodes are lighting values. */ void VoxelManipulator::unspreadLight(enum LightBank bank, - core::map<v3s16, u8> & from_nodes, - core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr) + std::map<v3s16, u8> & from_nodes, + std::set<v3s16> & light_sources, INodeDefManager *nodemgr) { if(from_nodes.size() == 0) return; - core::map<v3s16, u8>::Iterator j; - j = from_nodes.getIterator(); - - for(; j.atEnd() == false; j++) + for(std::map<v3s16, u8>::iterator j = from_nodes.begin(); + j != from_nodes.end(); ++j) { - v3s16 pos = j.getNode()->getKey(); - - //MapNode &n = m_data[m_area.index(pos)]; - - u8 oldlight = j.getNode()->getValue(); - - unspreadLight(bank, pos, oldlight, light_sources, nodemgr); + unspreadLight(bank, j->first, j->second, light_sources, nodemgr); } } #endif @@ -609,7 +601,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank, goes on recursively. */ void VoxelManipulator::spreadLight(enum LightBank bank, - core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr) + std::set<v3s16> & from_nodes, INodeDefManager *nodemgr) { const v3s16 dirs[6] = { v3s16(0,0,1), // back @@ -623,13 +615,12 @@ void VoxelManipulator::spreadLight(enum LightBank bank, if(from_nodes.size() == 0) return; - core::map<v3s16, bool> lighted_nodes; - core::map<v3s16, bool>::Iterator j; - j = from_nodes.getIterator(); + std::set<v3s16> lighted_nodes; - for(; j.atEnd() == false; j++) + for(std::set<v3s16>::iterator j = from_nodes.begin(); + j != from_nodes.end(); ++j) { - v3s16 pos = j.getNode()->getKey(); + v3s16 pos = *j; emerge(VoxelArea(pos - v3s16(1,1,1), pos + v3s16(1,1,1))); @@ -666,7 +657,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank, */ if(light2 > undiminish_light(oldlight)) { - lighted_nodes.insert(n2pos, true); + lighted_nodes.insert(n2pos); } /* If the neighbor is dimmer than how much light this node @@ -677,7 +668,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank, if(nodemgr->get(n2).light_propagates) { n2.setLight(bank, newlight, nodemgr); - lighted_nodes.insert(n2pos, true); + lighted_nodes.insert(n2pos); } } } diff --git a/src/voxel.h b/src/voxel.h index b48943624..bed35b57e 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <iostream> #include "debug.h" #include "mapnode.h" +#include <set> +#include <list> class INodeDefManager; @@ -186,7 +188,7 @@ public: a: area inside *this */ - void diff(const VoxelArea &a, core::list<VoxelArea> &result) + void diff(const VoxelArea &a, std::list<VoxelArea> &result) { /* This can result in a maximum of 6 areas @@ -519,14 +521,14 @@ public: // TODO: Move to voxelalgorithms.h void unspreadLight(enum LightBank bank, v3s16 p, u8 oldlight, - core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr); + std::set<v3s16> & light_sources, INodeDefManager *nodemgr); void unspreadLight(enum LightBank bank, - core::map<v3s16, u8> & from_nodes, - core::map<v3s16, bool> & light_sources, INodeDefManager *nodemgr); + std::map<v3s16, u8> & from_nodes, + std::set<v3s16> & light_sources, INodeDefManager *nodemgr); void spreadLight(enum LightBank bank, v3s16 p, INodeDefManager *nodemgr); void spreadLight(enum LightBank bank, - core::map<v3s16, bool> & from_nodes, INodeDefManager *nodemgr); + std::set<v3s16> & from_nodes, INodeDefManager *nodemgr); /* Virtual functions diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index bd8f816b8..14638a827 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -39,8 +39,8 @@ void setLight(VoxelManipulator &v, VoxelArea a, u8 light, void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a, enum LightBank bank, INodeDefManager *ndef, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, u8> & unlight_from) + std::set<v3s16> & light_sources, + std::map<v3s16, u8> & unlight_from) { // The full area we shall touch VoxelArea required_a = a; @@ -60,7 +60,7 @@ void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a, // If node sources light, add to list u8 source = ndef->get(n).light_source; if(source != 0) - light_sources[p] = true; + light_sources.insert(p); // Collect borders for unlighting if((x==a.MinEdge.X || x == a.MaxEdge.X @@ -68,14 +68,14 @@ void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a, || z==a.MinEdge.Z || z == a.MaxEdge.Z) && oldlight != 0) { - unlight_from.insert(p, oldlight); + unlight_from[p] = oldlight; } } } SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a, bool inexistent_top_provides_sunlight, - core::map<v3s16, bool> & light_sources, + std::set<v3s16> & light_sources, INodeDefManager *ndef) { // Return values @@ -127,7 +127,7 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a, n.setLight(LIGHTBANK_DAY, incoming_light, ndef); if(diminish_light(incoming_light) != 0) - light_sources.insert(p, true); + light_sources.insert(p); } // Check validity of sunlight at top of block below if it diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index 2a5fc394e..2eba6a176 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "mapnode.h" +#include <set> +#include <map> namespace voxalgo { @@ -33,8 +35,8 @@ void setLight(VoxelManipulator &v, VoxelArea a, u8 light, void clearLightAndCollectSources(VoxelManipulator &v, VoxelArea a, enum LightBank bank, INodeDefManager *ndef, - core::map<v3s16, bool> & light_sources, - core::map<v3s16, u8> & unlight_from); + std::set<v3s16> & light_sources, + std::map<v3s16, u8> & unlight_from); struct SunlightPropagateResult { @@ -47,7 +49,7 @@ struct SunlightPropagateResult SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a, bool inexistent_top_provides_sunlight, - core::map<v3s16, bool> & light_sources, + std::set<v3s16> & light_sources, INodeDefManager *ndef); } // namespace voxalgo diff --git a/textures/base/pack/logo.png b/textures/base/pack/logo.png Binary files differnew file mode 100644 index 000000000..f341e8892 --- /dev/null +++ b/textures/base/pack/logo.png diff --git a/textures/base/pack/menufooter.png b/textures/base/pack/menufooter.png Binary files differnew file mode 100644 index 000000000..fd431846e --- /dev/null +++ b/textures/base/pack/menufooter.png diff --git a/textures/base/pack/menuheader.png b/textures/base/pack/menuheader.png Binary files differnew file mode 100644 index 000000000..66fd6def5 --- /dev/null +++ b/textures/base/pack/menuheader.png diff --git a/textures/base/pack/menulogo.png b/textures/base/pack/menulogo.png Binary files differdeleted file mode 100644 index 76595c48d..000000000 --- a/textures/base/pack/menulogo.png +++ /dev/null diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index fc42db8a7..0c0d7cf21 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -53,6 +53,9 @@ 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) @@ -66,6 +69,7 @@ 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 @@ -86,6 +90,13 @@ 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 diff --git a/util/master/list.js b/util/master/list.js index dcc30e091..990bb6ded 100644 --- a/util/master/list.js +++ b/util/master/list.js @@ -2,11 +2,12 @@ function e(s) { if (typeof s === "undefined") s = ''; return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); //mc" } -function human_time(t) { + +function human_time(t, abs) { var n = 's'; if (!t || t < 0) t = 0; var f = 0; - var s = parseInt((new Date().getTime() / 1000 - (t || 0))); + var s = parseInt(abs ? (t || 0) : (new Date().getTime() / 1000 - (t || 0))); if (!s || s <= 0) s = 0; if (s == 0) return 'now'; if (s >= 60) { @@ -35,38 +36,36 @@ function human_time(t) { } return ((f ? parseFloat(s).toFixed(1) : parseInt(s)) + n); } + function success(r) { if (!r || !r.list) return; - var h = '<table><tr><th>ip:port</th><th>clients, max</th><th>version</th><th>name</th><th>desc</th><th>flags</th><th>updated/started</th><th>ping</th></tr>'; + var h = '<table class="mts_table"><tr class="mts_head"><th>ip[:port]</th><th>clients/max</th><th>version gameid</th><th>name</th><th>desc</th><th>flags</th><th>uptime</th><th>ping</th></tr>'; for (var i = 0; i < r.list.length; ++i) { var s = r.list[i]; if (!s) continue; - h += '<tr>'; - h += '<td>' + e(s.address) + ':' + e(s.port) + '</td>'; - h += '<td>' + e(s.clients) + (s.clients_max ? '/' + e(s.clients_max) : '') + (s.clients_top ? ', ' + s.clients_top : '') + '</td>'; - h += '<td>' + e(s.version) + '</td>'; - h += '<td>'; + h += '<tr class="mts_row">'; + h += '<td class="mts_address">' + e(s.address) + (s.port != 30000 ? (':' + e(s.port)) : '') + '</td>'; + h += '<td class="mts_clients">' + e(s.clients) + (s.clients_max ? '/' + e(s.clients_max) : '') + (s.clients_top ? ', ' + s.clients_top : '') + '</td>'; + h += '<td class="mts_version">' + e(s.version) + ' ' + e(s.gameid) + '</td>'; + h += '<td class="mts_url">'; if (s.url) h += '<a href="' + e(s.url) + '">'; h += e(s.name || s.url); if (s.url) h += '</a>'; h += '</td>'; - h += '<td>' + e(s.description) + '</td>'; - h += '<td>' + e(s.password ? 'Pwd ' : '') + (s.creative ? 'Cre ' : '') + (s.damage ? 'Dmg ' : '') + (s.pvp ? 'Pvp ' : '') + (s.dedicated ? 'Ded ' : '') + '</td>'; - if (!s.time || s.time < 0) s.time = 0; + h += '<td class="mts_description">' + e(s.description) + '</td>'; + h += '<td class="mts_flags">' + e(s.password ? 'Pwd ' : '') + (s.creative ? 'Cre ' : '') + (s.damage ? 'Dmg ' : '') + (s.pvp ? 'Pvp ' : '') + (s.dedicated ? 'Ded ' : '') + '</td>'; if (!s.start || s.start < 0) s.start = 0; - h += '<td>' + human_time(s.time) + (s.start ? '/' + human_time(s.start) : '') + '</td>'; - h += '<td>' + (s.ping ? parseFloat(s.ping).toFixed(3)*1000 : '') + '</td>'; + h += '<td class="mts_time">' + (s.uptime ? human_time(s.uptime, 1) : s.start ? human_time(s.start) : '') + '</td>'; + h += '<td class="mts_ping">' + (s.ping ? parseFloat(s.ping).toFixed(3) * 1000 : '') + '</td>'; h += '</tr>'; } h += '</table>' jQuery('#table').html(h); } +var master_root; + function get() { - jQuery.ajax({ - url: 'list', - dataType: 'json', - success: success - }); + jQuery.getJSON((master_root || '') + 'list', success); setTimeout(get, 60000); } -get();
\ No newline at end of file +get(); diff --git a/util/master/master.cgi b/util/master/master.cgi index b918876bd..0e456ed0c 100755 --- a/util/master/master.cgi +++ b/util/master/master.cgi @@ -18,6 +18,7 @@ nginx: location / { index index.html; + add_header Access-Control-Allow-Origin *; } location /announce { fastcgi_pass unix:/var/run/fcgiwrap/fcgiwrap.sock; @@ -35,6 +36,10 @@ apache .htaccess: Allow from all </FilesMatch> Deny from all + <ifModule mod_headers.c> + Header set Access-Control-Allow-Origin: * + </ifModule> + =cut |