From a95f983ea8665bfd14289b142cef1185838cc531 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 22 Dec 2016 22:52:15 +0100 Subject: Continue with 0.4.15-dev --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11ebe94a1..99f54ba54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ set(VERSION_PATCH 15) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases -set(DEVELOPMENT_BUILD FALSE) +set(DEVELOPMENT_BUILD TRUE) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") if(VERSION_EXTRA) -- cgit v1.2.3 From a76e7698b21d51594d82e329d3825ee86e653295 Mon Sep 17 00:00:00 2001 From: Rogier Date: Mon, 25 Jul 2016 18:43:15 +0200 Subject: Make minetest abort on lua panic Currently, lua does a regular exit() after a lua panic, which can make a problem hard to debug. Invoking FATAL_ERROR() instead will print some useful information, and abort() minetest, so that a debugger can be used to analyze the situation. --- src/script/cpp_api/s_base.cpp | 13 +++++++++++++ src/script/cpp_api/s_base.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index 679a517ee..cbe5735a7 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -40,6 +40,7 @@ extern "C" { #include #include +#include class ModNameStorer @@ -77,6 +78,8 @@ ScriptApiBase::ScriptApiBase() : m_luastack = luaL_newstate(); FATAL_ERROR_IF(!m_luastack, "luaL_newstate() failed"); + lua_atpanic(m_luastack, &luaPanic); + luaL_openlibs(m_luastack); // Make the ScriptApiBase* accessible to ModApiBase @@ -120,6 +123,16 @@ ScriptApiBase::~ScriptApiBase() lua_close(m_luastack); } +int ScriptApiBase::luaPanic(lua_State *L) +{ + std::ostringstream oss; + oss << "LUA PANIC: unprotected error in call to Lua API (" + << lua_tostring(L, -1) << ")"; + FATAL_ERROR(oss.str().c_str()); + // NOTREACHED + return 0; +} + void ScriptApiBase::loadMod(const std::string &script_path, const std::string &mod_name) { diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index f52474f00..c27235255 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -118,6 +118,8 @@ protected: #endif private: + static int luaPanic(lua_State *L); + lua_State* m_luastack; Server* m_server; -- cgit v1.2.3 From 923a8f1983271f65c80cbbde7c46153ce58560b4 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Wed, 7 Dec 2016 21:14:18 -0800 Subject: Shaders: Remove unnecessary 'if' statements Pull if GENERATE_NORMALMAPS == 1 into the template to avoid evaluating it for each fragment. Remove if (fogDistance != 0.0). --- client/shaders/nodes_shader/opengl_fragment.glsl | 31 +++++++++++----------- .../water_surface_shader/opengl_fragment.glsl | 30 ++++++++++----------- client/shaders/wielded_shader/opengl_fragment.glsl | 31 +++++++++++----------- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 71ded2b9d..914f9f873 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -163,7 +163,8 @@ void main(void) } #endif - if (GENERATE_NORMALMAPS == 1 && normalTexturePresent == false) { +#if GENERATE_NORMALMAPS == 1 + if (normalTexturePresent == false) { float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); @@ -177,7 +178,7 @@ void main(void) bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); use_normalmap = true; } - +#endif vec4 base = texture2D(baseTexture, uv).rgba; #ifdef ENABLE_BUMPMAPPING @@ -200,20 +201,18 @@ void main(void) col = applyToneMapping(col); #endif - if (fogDistance != 0.0) { - // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), - // the fog will only be rendered correctly if the last operation before the - // clamp() is an addition. Else, the clamp() seems to be ignored. - // E.g. the following won't work: - // float clarity = clamp(fogShadingParameter - // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); - // As additions usually come for free following a multiplication, the new formula - // should be more efficient as well. - // Note: clarity = (1 - fogginess) - float clarity = clamp(fogShadingParameter - - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(skyBgColor, col, clarity); - } + // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), + // the fog will only be rendered correctly if the last operation before the + // clamp() is an addition. Else, the clamp() seems to be ignored. + // E.g. the following won't work: + // float clarity = clamp(fogShadingParameter + // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); + // As additions usually come for free following a multiplication, the new formula + // should be more efficient as well. + // Note: clarity = (1 - fogginess) + float clarity = clamp(fogShadingParameter + - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); + col = mix(skyBgColor, col, clarity); col = vec4(col.rgb, base.a); gl_FragColor = col; diff --git a/client/shaders/water_surface_shader/opengl_fragment.glsl b/client/shaders/water_surface_shader/opengl_fragment.glsl index c4e78470d..19f6ac80f 100644 --- a/client/shaders/water_surface_shader/opengl_fragment.glsl +++ b/client/shaders/water_surface_shader/opengl_fragment.glsl @@ -114,7 +114,8 @@ void main(void) } #endif - if (GENERATE_NORMALMAPS == 1 && use_normalmap == false) { +#if GENERATE_NORMALMAPS == 1 + if (use_normalmap == false) { float tl = get_rgb_height (vec2(uv.x-SAMPLE_STEP,uv.y+SAMPLE_STEP)); float t = get_rgb_height (vec2(uv.x-SAMPLE_STEP,uv.y-SAMPLE_STEP)); float tr = get_rgb_height (vec2(uv.x+SAMPLE_STEP,uv.y+SAMPLE_STEP)); @@ -128,6 +129,7 @@ void main(void) bump = vec4 (normalize(vec3 (dX, -dY, NORMALMAPS_STRENGTH)),1.0); use_normalmap = true; } +#endif vec4 base = texture2D(baseTexture, uv).rgba; @@ -156,20 +158,18 @@ vec4 base = texture2D(baseTexture, uv).rgba; col = applyToneMapping(col); #endif - if (fogDistance != 0.0) { - // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), - // the fog will only be rendered correctly if the last operation before the - // clamp() is an addition. Else, the clamp() seems to be ignored. - // E.g. the following won't work: - // float clarity = clamp(fogShadingParameter - // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); - // As additions usually come for free following a multiplication, the new formula - // should be more efficient as well. - // Note: clarity = (1 - fogginess) - float clarity = clamp(fogShadingParameter - - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(skyBgColor, col, clarity); - } + // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), + // the fog will only be rendered correctly if the last operation before the + // clamp() is an addition. Else, the clamp() seems to be ignored. + // E.g. the following won't work: + // float clarity = clamp(fogShadingParameter + // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); + // As additions usually come for free following a multiplication, the new formula + // should be more efficient as well. + // Note: clarity = (1 - fogginess) + float clarity = clamp(fogShadingParameter + - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); + col = mix(skyBgColor, col, clarity); col = vec4(col.rgb, base.a); gl_FragColor = col; diff --git a/client/shaders/wielded_shader/opengl_fragment.glsl b/client/shaders/wielded_shader/opengl_fragment.glsl index ba7a8f12d..546aef71d 100644 --- a/client/shaders/wielded_shader/opengl_fragment.glsl +++ b/client/shaders/wielded_shader/opengl_fragment.glsl @@ -75,7 +75,8 @@ void main(void) } #endif - if (GENERATE_NORMALMAPS == 1 && normalTexturePresent == false) { +#if GENERATE_NORMALMAPS == 1 + if (normalTexturePresent == false) { float tl = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y + SAMPLE_STEP)); float t = get_rgb_height(vec2(uv.x - SAMPLE_STEP, uv.y - SAMPLE_STEP)); float tr = get_rgb_height(vec2(uv.x + SAMPLE_STEP, uv.y + SAMPLE_STEP)); @@ -89,6 +90,7 @@ void main(void) bump = vec4(normalize(vec3 (dX, dY, NORMALMAPS_STRENGTH)), 1.0); use_normalmap = true; } +#endif vec4 base = texture2D(baseTexture, uv).rgba; @@ -108,19 +110,18 @@ void main(void) vec4 col = vec4(color.rgb, base.a); col *= gl_Color; - if (fogDistance != 0.0) { - // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), - // the fog will only be rendered correctly if the last operation before the - // clamp() is an addition. Else, the clamp() seems to be ignored. - // E.g. the following won't work: - // float clarity = clamp(fogShadingParameter - // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); - // As additions usually come for free following a multiplication, the new formula - // should be more efficient as well. - // Note: clarity = (1 - fogginess) - float clarity = clamp(fogShadingParameter - - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(skyBgColor, col, clarity); - } + // Due to a bug in some (older ?) graphics stacks (possibly in the glsl compiler ?), + // the fog will only be rendered correctly if the last operation before the + // clamp() is an addition. Else, the clamp() seems to be ignored. + // E.g. the following won't work: + // float clarity = clamp(fogShadingParameter + // * (fogDistance - length(eyeVec)) / fogDistance), 0.0, 1.0); + // As additions usually come for free following a multiplication, the new formula + // should be more efficient as well. + // Note: clarity = (1 - fogginess) + float clarity = clamp(fogShadingParameter + - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); + col = mix(skyBgColor, col, clarity); + gl_FragColor = vec4(col.rgb, base.a); } -- cgit v1.2.3 From 2f59a0c840e9101c87e2d59fabf34116b3328121 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sat, 10 Dec 2016 10:31:17 -0800 Subject: Process ABMs in a spherical volume instead of cubic Increase active_block_range default to a 3 mapblock radius. --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- src/environment.cpp | 7 +++++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 1818b5a18..3a740f3ca 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -758,7 +758,7 @@ active_object_send_range_blocks (Active object send range) int 3 # How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes). # In active blocks objects are loaded and ABMs run. -active_block_range (Active block range) int 2 +active_block_range (Active block range) int 3 # From how far blocks are sent to clients, stated in mapblocks (16 nodes). max_block_send_distance (Max block send distance) int 10 diff --git a/minetest.conf.example b/minetest.conf.example index 867d98584..b1d6c86bd 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -913,7 +913,7 @@ # How large area of blocks are subject to the active block stuff, stated in mapblocks (16 nodes). # In active blocks objects are loaded and ABMs run. # type: int -# active_block_range = 2 +# active_block_range = 3 # From how far blocks are sent to clients, stated in mapblocks (16 nodes). # type: int diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 0b4be6322..5bcd7da3d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -273,7 +273,7 @@ void set_default_settings(Settings *settings) settings->setDefault("profiler_print_interval", "0"); settings->setDefault("enable_mapgen_debug_info", "false"); settings->setDefault("active_object_send_range_blocks", "3"); - settings->setDefault("active_block_range", "2"); + settings->setDefault("active_block_range", "3"); //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? settings->setDefault("max_simultaneous_block_sends_per_client", "10"); diff --git a/src/environment.cpp b/src/environment.cpp index 13c64b37c..3d4a2efc1 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -397,8 +397,11 @@ void fillRadiusBlock(v3s16 p0, s16 r, std::set &list) for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++) for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++) { - // Set in list - list.insert(p); + // limit to a sphere + if (p.getDistanceFrom(p0) <= r) { + // Set in list + list.insert(p); + } } } -- cgit v1.2.3 From 4d4b8bb8a46b6472d86fa848954dbc26b4fadb50 Mon Sep 17 00:00:00 2001 From: Rogier Date: Tue, 13 Dec 2016 23:16:26 +0100 Subject: Move PP() and PP2() macros to basic_macros.h Instead of redefining them everywhere. --- src/clientmap.cpp | 3 +-- src/content_abm.cpp | 2 -- src/content_cao.cpp | 3 +-- src/database.h | 5 +--- src/environment.cpp | 3 +-- src/inventorymanager.cpp | 3 +-- src/map.cpp | 3 +-- src/mapblock.cpp | 3 +-- src/object_properties.cpp | 4 +-- src/pathfinder.cpp | 64 ++++++++++++++++++++++------------------------ src/rollback_interface.cpp | 3 +-- src/server.h | 3 +-- src/util/basic_macros.h | 9 +++++++ 13 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/clientmap.cpp b/src/clientmap.cpp index faa1461f6..27f9dea38 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -30,10 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "camera.h" // CameraModes #include "util/mathconstants.h" +#include "util/basic_macros.h" #include -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - ClientMap::ClientMap( Client *client, IGameDef *gamedef, diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 8694ef981..ee444ae77 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -29,8 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scripting_game.h" #include "log.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { } diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 6b35d5881..a02d5168e 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" // For IntervalLimiter #include "util/serialize.h" #include "util/mathconstants.h" +#include "util/basic_macros.h" #include "client/tile.h" #include "environment.h" #include "collision.h" @@ -49,8 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., class Settings; struct ToolCapabilities; -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - UNORDERED_MAP ClientActiveObject::m_types; SmoothTranslator::SmoothTranslator(): diff --git a/src/database.h b/src/database.h index 0cf75232f..459789306 100644 --- a/src/database.h +++ b/src/database.h @@ -24,10 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irr_v3d.h" #include "irrlichttypes.h" - -#ifndef PP - #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -#endif +#include "util/basic_macros.h" class Database { diff --git a/src/environment.cpp b/src/environment.cpp index 3d4a2efc1..707d89659 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -44,10 +44,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "emerge.h" #include "util/serialize.h" +#include "util/basic_macros.h" #include "threading/mutex_auto_lock.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" // A number that is much smaller than the timeout for particle spawners should/could ever be diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 3d8513492..f36a2fa4e 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -26,8 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "craftdef.h" #include "rollback_interface.h" #include "util/strfnd.h" - -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#include "util/basic_macros.h" #define PLAYER_TO_SA(p) p->getEnv()->getScriptIface() diff --git a/src/map.cpp b/src/map.cpp index 7bb8c4a13..c3b62ded0 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "util/directiontables.h" #include "util/mathconstants.h" +#include "util/basic_macros.h" #include "rollback_interface.h" #include "environment.h" #include "reflowscan.h" @@ -56,8 +57,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database-postgresql.h" #endif -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - /* Map diff --git a/src/mapblock.cpp b/src/mapblock.cpp index f8747f52b..f8c3197bc 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -35,8 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #include "util/string.h" #include "util/serialize.h" - -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" +#include "util/basic_macros.h" static const char *modified_reason_strings[] = { "initial", diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 89ca26274..f4e4953ba 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -21,11 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "exceptions.h" #include "util/serialize.h" +#include "util/basic_macros.h" #include -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" -#define PP2(x) "("<<(x).X<<","<<(x).Y<<")" - ObjectProperties::ObjectProperties(): hp_max(1), physical(false), diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 57a008ead..073670c6d 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "log.h" #include "irr_aabb3d.h" +#include "util/basic_macros.h" //#define PATHFINDER_DEBUG //#define PATHFINDER_CALC_TIME @@ -47,9 +48,6 @@ with this program; if not, write to the Free Software Foundation, Inc., /* Typedefs and macros */ /******************************************************************************/ -/** shortcut to print a 3d pos */ -#define PPOS(pos) "(" << pos.X << "," << pos.Y << "," << pos.Z << ")" - #define LVL "(" << level << ")" << #ifdef PATHFINDER_DEBUG @@ -531,25 +529,25 @@ void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node) if ((current.param0 == CONTENT_IGNORE) || (below.param0 == CONTENT_IGNORE)) { - DEBUG_OUT("Pathfinder: " << PPOS(realpos) << + DEBUG_OUT("Pathfinder: " << PP(realpos) << " current or below is invalid element" << std::endl); if (current.param0 == CONTENT_IGNORE) { elem.type = 'i'; - DEBUG_OUT(PPOS(ipos) << ": " << 'i' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << 'i' << std::endl); } return; } //don't add anything if it isn't an air node if (ndef->get(current).walkable || !ndef->get(below).walkable) { - DEBUG_OUT("Pathfinder: " << PPOS(realpos) + DEBUG_OUT("Pathfinder: " << PP(realpos) << " not on surface" << std::endl); if (ndef->get(current).walkable) { elem.type = 's'; - DEBUG_OUT(PPOS(ipos) << ": " << 's' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << 's' << std::endl); } else { elem.type = '-'; - DEBUG_OUT(PPOS(ipos) << ": " << '-' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << '-' << std::endl); } return; } @@ -557,7 +555,7 @@ void GridNodeContainer::initNode(v3s16 ipos, PathGridnode *p_node) elem.valid = true; elem.pos = realpos; elem.type = 'g'; - DEBUG_OUT(PPOS(ipos) << ": " << 'a' << std::endl); + DEBUG_OUT(PP(ipos) << ": " << 'a' << std::endl); if (m_pathf->m_prefetch) { elem.directions[DIR_XP] = m_pathf->calcCost(realpos, v3s16( 1, 0, 0)); @@ -686,14 +684,14 @@ std::vector Pathfinder::getPath(ServerEnvironment *env, if (!startpos.valid) { VERBOSE_TARGET << "invalid startpos" << - "Index: " << PPOS(StartIndex) << - "Realpos: " << PPOS(getRealPos(StartIndex)) << std::endl; + "Index: " << PP(StartIndex) << + "Realpos: " << PP(getRealPos(StartIndex)) << std::endl; return retval; } if (!endpos.valid) { VERBOSE_TARGET << "invalid stoppos" << - "Index: " << PPOS(EndIndex) << - "Realpos: " << PPOS(getRealPos(EndIndex)) << std::endl; + "Index: " << PP(EndIndex) << + "Realpos: " << PP(getRealPos(EndIndex)) << std::endl; return retval; } @@ -809,7 +807,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) //check limits if (!m_limits.isPointInside(pos2)) { - DEBUG_OUT("Pathfinder: " << PPOS(pos2) << + DEBUG_OUT("Pathfinder: " << PP(pos2) << " no cost -> out of limits" << std::endl); return retval; } @@ -819,7 +817,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) //did we get information about node? if (node_at_pos2.param0 == CONTENT_IGNORE ) { VERBOSE_TARGET << "Pathfinder: (1) area at pos: " - << PPOS(pos2) << " not loaded"; + << PP(pos2) << " not loaded"; return retval; } @@ -830,7 +828,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) //did we get information about node? if (node_below_pos2.param0 == CONTENT_IGNORE ) { VERBOSE_TARGET << "Pathfinder: (2) area at pos: " - << PPOS((pos2 + v3s16(0, -1, 0))) << " not loaded"; + << PP((pos2 + v3s16(0, -1, 0))) << " not loaded"; return retval; } @@ -838,7 +836,7 @@ PathCost Pathfinder::calcCost(v3s16 pos, v3s16 dir) retval.valid = true; retval.value = 1; retval.direction = 0; - DEBUG_OUT("Pathfinder: "<< PPOS(pos) + DEBUG_OUT("Pathfinder: "<< PP(pos) << " cost same height found" << std::endl); } else { @@ -991,8 +989,8 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, v3s16 ipos2 = ipos + directions[i]; if (!isValidIndex(ipos2)) { - DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) << - " out of range, max=" << PPOS(m_limits.MaxEdge) << std::endl); + DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) << + " out of range, max=" << PP(m_limits.MaxEdge) << std::endl); continue; } @@ -1000,7 +998,7 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, if (!g_pos2.valid) { VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " - << PPOS(ipos2) << std::endl; + << PP(ipos2) << std::endl; continue; } @@ -1017,7 +1015,7 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, if ((g_pos2.totalcost < 0) || (g_pos2.totalcost > new_cost)) { DEBUG_OUT(LVL "Pathfinder: updating path at: "<< - PPOS(ipos2) << " from: " << g_pos2.totalcost << " to "<< + PP(ipos2) << " from: " << g_pos2.totalcost << " to "<< new_cost << std::endl); if (updateAllCosts(ipos2, invert(directions[i]), new_cost, level)) { @@ -1027,13 +1025,13 @@ bool Pathfinder::updateAllCosts(v3s16 ipos, else { DEBUG_OUT(LVL "Pathfinder:" " already found shorter path to: " - << PPOS(ipos2) << std::endl); + << PP(ipos2) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " not moving to invalid direction: " - << PPOS(directions[i]) << std::endl); + << PP(directions[i]) << std::endl); } } } @@ -1147,8 +1145,8 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, v3s16 ipos2 = ipos + direction; if (!isValidIndex(ipos2)) { - DEBUG_OUT(LVL " Pathfinder: " << PPOS(ipos2) << - " out of range, max=" << PPOS(m_limits.MaxEdge) << std::endl); + DEBUG_OUT(LVL " Pathfinder: " << PP(ipos2) << + " out of range, max=" << PP(m_limits.MaxEdge) << std::endl); direction = getDirHeuristic(directions, g_pos); continue; } @@ -1157,7 +1155,7 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, if (!g_pos2.valid) { VERBOSE_TARGET << LVL "Pathfinder: no data for new position: " - << PPOS(ipos2) << std::endl; + << PP(ipos2) << std::endl; direction = getDirHeuristic(directions, g_pos); continue; } @@ -1171,16 +1169,16 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, (m_min_target_distance < new_cost)) { DEBUG_OUT(LVL "Pathfinder:" " already longer than best already found path " - << PPOS(ipos2) << std::endl); + << PP(ipos2) << std::endl); return false; } if ((g_pos2.totalcost < 0) || (g_pos2.totalcost > new_cost)) { DEBUG_OUT(LVL "Pathfinder: updating path at: "<< - PPOS(ipos2) << " from: " << g_pos2.totalcost << " to "<< + PP(ipos2) << " from: " << g_pos2.totalcost << " to "<< new_cost << " srcdir=" << - PPOS(invert(direction))<< std::endl); + PP(invert(direction))<< std::endl); if (updateCostHeuristic(ipos2, invert(direction), new_cost, level)) { retval = true; @@ -1189,19 +1187,19 @@ bool Pathfinder::updateCostHeuristic( v3s16 ipos, else { DEBUG_OUT(LVL "Pathfinder:" " already found shorter path to: " - << PPOS(ipos2) << std::endl); + << PP(ipos2) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " not moving to invalid direction: " - << PPOS(direction) << std::endl); + << PP(direction) << std::endl); } } else { DEBUG_OUT(LVL "Pathfinder:" " skipping srcdir: " - << PPOS(direction) << std::endl); + << PP(direction) << std::endl); } direction = getDirHeuristic(directions, g_pos); } @@ -1409,7 +1407,7 @@ void Pathfinder::printPath(std::vector path) unsigned int current = 0; for (std::vector::iterator i = path.begin(); i != path.end(); ++i) { - std::cout << std::setw(3) << current << ":" << PPOS((*i)) << std::endl; + std::cout << std::setw(3) << current << ":" << PP((*i)) << std::endl; current++; } } diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp index bffe0a82c..7345c4a7d 100644 --- a/src/rollback_interface.cpp +++ b/src/rollback_interface.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "util/string.h" #include "util/numeric.h" +#include "util/basic_macros.h" #include "map.h" #include "gamedef.h" #include "nodedef.h" @@ -32,8 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventory.h" #include "mapblock.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef) { diff --git a/src/server.h b/src/server.h index 4425d139b..cab7e2445 100644 --- a/src/server.h +++ b/src/server.h @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "subgame.h" #include "util/numeric.h" #include "util/thread.h" +#include "util/basic_macros.h" #include "environment.h" #include "chat_interface.h" #include "clientiface.h" @@ -41,8 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - class IWritableItemDefManager; class IWritableNodeDefManager; class IWritableCraftDefManager; diff --git a/src/util/basic_macros.h b/src/util/basic_macros.h index c100b4f25..bd4b890eb 100644 --- a/src/util/basic_macros.h +++ b/src/util/basic_macros.h @@ -50,4 +50,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #define STATIC_ASSERT(expr, msg) \ UNUSED_ATTRIBUTE typedef char msg[!!(expr) * 2 - 1] +// Macros to facilitate writing position vectors to a stream +// Usage: +// v3s16 pos(1,2,3); +// mystream << "message " << PP(pos) << std::endl; + +#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + +#define PP2(x) "("<<(x).X<<","<<(x).Y<<")" + #endif -- cgit v1.2.3 From f4d718c53843c5e18243d0851b9c7790b0853334 Mon Sep 17 00:00:00 2001 From: Rogier Date: Wed, 14 Dec 2016 16:23:25 +0100 Subject: Minimal game: Use field 'tiles' instead of 'tile_images' --- games/minimal/mods/experimental/init.lua | 6 +++--- games/minimal/mods/stairs/init.lua | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 729191bce..142734cda 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -28,7 +28,7 @@ minetest.after(1.0, switch_player_visual) ]] minetest.register_node("experimental:soundblock", { - tile_images = {"unknown_node.png", "default_tnt_bottom.png", + tiles = {"unknown_node.png", "default_tnt_bottom.png", "default_tnt_side.png", "default_tnt_side.png", "default_tnt_side.png", "default_tnt_side.png"}, inventory_image = minetest.inventorycube("unknown_node.png", @@ -120,7 +120,7 @@ minetest.register_craft({ }) minetest.register_node("experimental:tnt", { - tile_images = {"default_tnt_top.png", "default_tnt_bottom.png", + tiles = {"default_tnt_top.png", "default_tnt_bottom.png", "default_tnt_side.png", "default_tnt_side.png", "default_tnt_side.png", "default_tnt_side.png"}, inventory_image = minetest.inventorycube("default_tnt_top.png", @@ -453,7 +453,7 @@ minetest.register_abm({ minetest.register_node("experimental:tester_node_1", { description = "Tester Node 1 (construct/destruct/timer)", - tile_images = {"wieldhand.png"}, + tiles = {"wieldhand.png"}, groups = {oddly_breakable_by_hand=2}, sounds = default.node_sound_wood_defaults(), -- This was known to cause a bug in minetest.item_place_node() when used diff --git a/games/minimal/mods/stairs/init.lua b/games/minimal/mods/stairs/init.lua index 4929d1370..c3bbc561e 100644 --- a/games/minimal/mods/stairs/init.lua +++ b/games/minimal/mods/stairs/init.lua @@ -5,7 +5,7 @@ function stairs.register_stair(subname, recipeitem, groups, images, description) minetest.register_node("stairs:stair_" .. subname, { description = description, drawtype = "nodebox", - tile_images = images, + tiles = images, paramtype = "light", paramtype2 = "facedir", is_ground_content = true, @@ -34,7 +34,7 @@ function stairs.register_slab(subname, recipeitem, groups, images, description) minetest.register_node("stairs:slab_" .. subname, { description = description, drawtype = "nodebox", - tile_images = images, + tiles = images, paramtype = "light", is_ground_content = true, groups = groups, -- cgit v1.2.3 From e3cbe521fc72191da5a3e8ead499cf19e02aca7b Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 24 Dec 2016 07:34:19 +0000 Subject: Update Android build tools to latest version (#4872) --- build/android/build.gradle | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build/android/build.gradle b/build/android/build.gradle index 20c13e385..745952a22 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -10,8 +10,8 @@ buildscript { apply plugin: "com.android.application" android { - compileSdkVersion 23 - buildToolsVersion "23.0.3" + compileSdkVersion 25 + buildToolsVersion "25.0.1" defaultConfig { versionCode 16 @@ -46,4 +46,3 @@ android { } } } - -- cgit v1.2.3 From b95f543da9d56974c4db8e34ce93e6f3bf7e776c Mon Sep 17 00:00:00 2001 From: Ner'zhul Date: Sat, 24 Dec 2016 12:30:18 +0100 Subject: Add gradle wrapper (#4954) Gradle wrapper permit to use multiple gradle versions across OS versions --- build/android/Makefile | 4 +- build/android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + build/android/gradlew | 164 +++++++++++++++++++++ build/android/gradlew.bat | 90 +++++++++++ 5 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 build/android/gradle/wrapper/gradle-wrapper.jar create mode 100644 build/android/gradle/wrapper/gradle-wrapper.properties create mode 100755 build/android/gradlew create mode 100644 build/android/gradlew.bat diff --git a/build/android/Makefile b/build/android/Makefile index 6e7a389c9..d261a0e95 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -781,7 +781,7 @@ apk: local.properties assets $(ICONV_LIB) $(IRRLICHT_LIB) $(CURL_LIB) $(GMP_LIB) fi; \ export VERSION_STR="${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}" && \ export BUILD_TYPE_C=$$(echo "$${BUILD_TYPE}" | sed 's/./\U&/') && \ - gradle assemble$$BUILD_TYPE_C && \ + ./gradlew assemble$$BUILD_TYPE_C && \ echo "APK stored at: build/outputs/apk/Minetest-$$BUILD_TYPE.apk" && \ echo "You can install it with \`make install_$$BUILD_TYPE\`" @@ -799,7 +799,7 @@ prep_srcdir : fi clean_apk : - gradle clean + ./gradlew clean clean_all : @$(MAKE) clean_apk; \ diff --git a/build/android/gradle/wrapper/gradle-wrapper.jar b/build/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 000000000..8c0fb64a8 Binary files /dev/null and b/build/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..980438b75 --- /dev/null +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sat Aug 27 20:10:09 CEST 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/build/android/gradlew b/build/android/gradlew new file mode 100755 index 000000000..91a7e269e --- /dev/null +++ b/build/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/build/android/gradlew.bat b/build/android/gradlew.bat new file mode 100644 index 000000000..8a0b282aa --- /dev/null +++ b/build/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega -- cgit v1.2.3 From b16252dcae8c6b0e79c20fa4c3cbddc37ad377cb Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 22 Dec 2016 19:29:15 +0100 Subject: Various anticheat improvements * Calculate maximum interact distance from wielded tool * New "interacted_while_dead" cheat_type for the Lua API * Disallow dropping items while dead * Move player to spawn before resurrecting them --- doc/lua_api.txt | 7 ++-- src/network/serverpackethandler.cpp | 84 +++++++++++++++++++++++-------------- src/server.cpp | 8 ++-- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 34c64b8df..d2ddc635b 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2036,9 +2036,10 @@ Call these functions only at load time! * `minetest.register_on_cheat(func(ObjectRef, cheat))` * Called when a player cheats * `cheat`: `{type=}`, where `` is one of: - * `"moved_too_fast"` - * `"interacted_too_far"` - * `"finished_unknown_dig"` + * `moved_too_fast` + * `interacted_too_far` + * `interacted_while_dead` + * `finished_unknown_dig` * `dug_unbreakable` * `dug_too_fast` * `minetest.register_on_chat_message(func(name, message))` diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index dca9aabc4..f99e104ec 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1021,6 +1021,15 @@ void Server::handleCommand_InventoryAction(NetworkPacket* pkt) delete a; return; } + + // Disallow dropping items if dead + if (playersao->isDead()) { + infostream << "Ignoring IDropAction from " + << (da->from_inv.dump()) << ":" << da->from_list + << " because player is dead." << std::endl; + delete a; + return; + } } /* Handle restrictions and special cases of the craft action @@ -1313,6 +1322,7 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) 2: digging completed 3: place block or item (to abovesurface) 4: use item + 5: rightclick air ("activate") */ u8 action; u16 item_i; @@ -1345,8 +1355,16 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) } if (playersao->isDead()) { - verbosestream << "TOSERVER_INTERACT: " << player->getName() - << " is dead. Ignoring packet"; + actionstream << "Server: NoCheat: " << player->getName() + << " tried to interact while dead; ignoring." << std::endl; + if (pointed.type == POINTEDTHING_NODE) { + // Re-send block to revert change on client-side + RemoteClient *client = getClient(pkt->getPeerId()); + v3s16 blockpos = getNodeBlockPos(pointed.node_undersurface); + client->SetBlockNotSent(blockpos); + } + // Call callbacks + m_script->on_cheat(playersao, "interacted_while_dead"); return; } @@ -1384,16 +1402,45 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) pointed_pos_above = pointed_pos_under; } + /* + Make sure the player is allowed to do it + */ + if (!checkPriv(player->getName(), "interact")) { + actionstream<getName()<<" attempted to interact with " + <getPeerId()); + // Digging completed -> under + if (action == 2) { + v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); + client->SetBlockNotSent(blockpos); + } + // Placement -> above + if (action == 3) { + v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); + client->SetBlockNotSent(blockpos); + } + return; + } + /* Check that target is reasonably close (only when digging or placing things) */ static const bool enable_anticheat = !g_settings->getBool("disable_anticheat"); - if ((action == 0 || action == 2 || action == 3) && + if ((action == 0 || action == 2 || action == 3 || action == 4) && (enable_anticheat && !isSingleplayer())) { float d = player_pos.getDistanceFrom(pointed_pos_under); - float max_d = BS * 14; // Just some large enough value - if (d > max_d) { + const ItemDefinition &playeritem_def = + playersao->getWieldedItem().getDefinition(m_itemdef); + float max_d = BS * playeritem_def.range; + float max_d_hand = BS * m_itemdef->get("").range; + if (max_d < 0 && max_d_hand >= 0) + max_d = max_d_hand; + else if (max_d < 0) + max_d = BS * 4.0; + if (d > max_d * 1.5) { actionstream << "Player " << player->getName() << " tried to access " << pointed.dump() << " from too far: " @@ -1410,28 +1457,6 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) } } - /* - Make sure the player is allowed to do it - */ - if (!checkPriv(player->getName(), "interact")) { - actionstream<getName()<<" attempted to interact with " - <getPeerId()); - // Digging completed -> under - if (action == 2) { - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); - client->SetBlockNotSent(blockpos); - } - // Placement -> above - if (action == 3) { - v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); - client->SetBlockNotSent(blockpos); - } - return; - } - /* If something goes wrong, this player is to blame */ @@ -1443,11 +1468,6 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) */ if (action == 0) { if (pointed.type == POINTEDTHING_NODE) { - /* - NOTE: This can be used in the future to check if - somebody is cheating, by checking the timing. - */ - MapNode n(CONTENT_IGNORE); bool pos_ok; diff --git a/src/server.cpp b/src/server.cpp index c9d5c7129..fa7a838d4 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2557,15 +2557,15 @@ void Server::RespawnPlayer(u16 peer_id) playersao->setHP(PLAYER_MAX_HP); playersao->setBreath(PLAYER_MAX_BREATH); - SendPlayerHP(peer_id); - SendPlayerBreath(peer_id); - bool repositioned = m_script->on_respawnplayer(playersao); - if(!repositioned){ + if (!repositioned) { v3f pos = findSpawnPos(); // setPos will send the new position to client playersao->setPos(pos); } + + SendPlayerHP(peer_id); + SendPlayerBreath(peer_id); } -- cgit v1.2.3 From 084cdea6862cb65fe4bb807a211a9e1c17cffec8 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 24 Dec 2016 14:26:03 +0100 Subject: Irrlicht 1.9 support --- src/cguittfont/CGUITTFont.h | 7 +++++++ src/client/tile.cpp | 2 +- src/drawscene.cpp | 5 +++++ src/game.cpp | 2 ++ src/guiChatConsole.cpp | 2 +- src/irrlicht_changes/static_text.cpp | 4 ++++ 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/cguittfont/CGUITTFont.h b/src/cguittfont/CGUITTFont.h index 0aa540c5c..77c9e34f8 100644 --- a/src/cguittfont/CGUITTFont.h +++ b/src/cguittfont/CGUITTFont.h @@ -125,6 +125,10 @@ namespace gui bool flgmip = driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS); driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false); +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + bool flgcpy = driver->getTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY); + driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, true); +#endif // Set the texture color format. switch (pixel_mode) @@ -140,6 +144,9 @@ namespace gui // Restore our texture creation flags. driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, flgmip); +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + driver->setTextureCreationFlag(video::ETCF_ALLOW_MEMORY_COPY, flgcpy); +#endif return texture ? true : false; } diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 8f0c39465..7f7535df6 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -938,7 +938,7 @@ video::ITexture* TextureSource::generateTextureFromMesh( smgr->drop(); // Unset render target - driver->setRenderTarget(0, false, true, 0); + driver->setRenderTarget(0, false, true, video::SColor(0,0,0,0)); if (params.delete_texture_on_shutdown) m_texture_trash.push_back(rtt); diff --git a/src/drawscene.cpp b/src/drawscene.cpp index c6abda4ac..32a078b8e 100644 --- a/src/drawscene.cpp +++ b/src/drawscene.cpp @@ -383,6 +383,10 @@ void draw_pageflip_3d_mode(Camera& camera, bool show_hud, bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv, video::SColor skycolor) { +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR > 8 + errorstream << "Pageflip 3D mode is not supported" + << " with your Irrlicht version!" << std::endl; +#else /* preserve old setup*/ irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition(); irr::core::vector3df oldTarget = camera.getCameraNode()->getTarget(); @@ -451,6 +455,7 @@ void draw_pageflip_3d_mode(Camera& camera, bool show_hud, camera.getCameraNode()->setPosition(oldPosition); camera.getCameraNode()->setTarget(oldTarget); +#endif } void draw_plain(Camera &camera, bool show_hud, Hud &hud, diff --git a/src/game.cpp b/src/game.cpp index 966c23073..5bdbea617 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2071,9 +2071,11 @@ void Game::run() void Game::shutdown() { +#if IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8 if (g_settings->get("3d_mode") == "pageflip") { driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS); } +#endif showOverlayMessage(wgettext("Shutting down..."), 0, 0, false); diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index 8dd5ab032..867576175 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -630,7 +630,7 @@ bool GUIChatConsole::OnEvent(const SEvent& event) } else if(event.KeyInput.Char != 0 && !event.KeyInput.Control) { - #if (defined(__linux__)) + #if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9) wchar_t wc = L'_'; mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) ); prompt.input(wc); diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp index 703287eb3..50c6c6a68 100644 --- a/src/irrlicht_changes/static_text.cpp +++ b/src/irrlicht_changes/static_text.cpp @@ -20,6 +20,10 @@ #if USE_FREETYPE #include "cguittfont/xCGUITTFont.h" #endif +#ifndef _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX + // newer Irrlicht versions no longer have this + #define _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX +#endif #include "util/string.h" -- cgit v1.2.3 From 5a0a59ad463143d4452524837d69c108908f20d5 Mon Sep 17 00:00:00 2001 From: adrido Date: Wed, 28 Dec 2016 21:22:01 +0100 Subject: Dont compare short with bool (#4963) Fixes a compiler warning on MSVC --- src/script/lua_api/l_util.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 26e2b985c..c26791646 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -398,7 +398,8 @@ int ModApiUtil::l_get_dir_list(lua_State *L) { NO_MAP_LOCK_REQUIRED; const char *path = luaL_checkstring(L, 1); - short is_dir = lua_isboolean(L, 2) ? lua_toboolean(L, 2) : -1; + bool list_all = !lua_isboolean(L, 2); // if its not a boolean list all + bool list_dirs = lua_toboolean(L, 2); // true: list dirs, false: list files CHECK_SECURE_PATH(L, path, false); @@ -408,7 +409,7 @@ int ModApiUtil::l_get_dir_list(lua_State *L) lua_newtable(L); for (size_t i = 0; i < list.size(); i++) { - if (is_dir == -1 || is_dir == list[i].dir) { + if (list_all || list_dirs == list[i].dir) { lua_pushstring(L, list[i].name.c_str()); lua_rawseti(L, -2, ++index); } -- cgit v1.2.3 From 094a5a73d3b9817d07c52754749f0828c4005b8c Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 8 Dec 2016 17:37:13 -0800 Subject: Redo light.cpp. Remake the light_decode_table. The table starts out without pre-filled in values since those are always discarded by the code apparently. We calculate a pseudo curve with gamma power function, and then apply a new adjustment table. The adjustment table is setup to make the default gamma of 2.2 look decent: not too dark at light level 3 or so, but too dark at 1 and below to be playable. The curve is much smoother than before and looks reasonable at the whole range, offering a pleasant decay of light levels away from lights. The `display_gamma` setting now actually does something logical: the game is darker at values below 2.2, and brighter at values above 2.2. At 3.0, the game is very bright, but still has a good light scale. At 1.1 or so, the bottom 5 light levels are virtually black, but you can still see enough detail at light levels 7-8, so the range and spread is adequate. I must add that my monitor is somewhat dark to begin with, since I have a `hc` screen that doesn't dynamic range colors or try to pull up `black` pixels for me (it is tuned for accurate color and light levels), so this should look even better on more dynamic display tunings. --- builtin/settingtypes.txt | 4 +- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- src/light.cpp | 351 ++++------------------------------------------- 4 files changed, 28 insertions(+), 331 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 3a740f3ca..8668e1472 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -442,9 +442,9 @@ fov (Field of view) int 72 30 160 # This requires the "zoom" privilege on the server. zoom_fov (Field of view for zoom) int 15 15 160 -# Adjust the gamma encoding for the light tables. Lower numbers are brighter. +# Adjust the gamma encoding for the light tables. Higher numbers are brighter. # This setting is for the client only and is ignored by the server. -display_gamma (Gamma) float 1.8 1.0 3.0 +display_gamma (Gamma) float 2.2 1.0 3.0 # Path to texture directory. All textures are first searched from here. texture_path (Texture path) path diff --git a/minetest.conf.example b/minetest.conf.example index b1d6c86bd..262f95115 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -508,7 +508,7 @@ # Adjust the gamma encoding for the light tables. Lower numbers are brighter. # This setting is for the client only and is ignored by the server. # type: float min: 1 max: 3 -# display_gamma = 1.8 +# display_gamma = 2.2 # Path to texture directory. All textures are first searched from here. # type: path diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 5bcd7da3d..52f4cfec8 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -112,7 +112,7 @@ void set_default_settings(Settings *settings) settings->setDefault("leaves_style", "fancy"); settings->setDefault("connected_glass", "false"); settings->setDefault("smooth_lighting", "true"); - settings->setDefault("display_gamma", "1.8"); + settings->setDefault("display_gamma", "2.2"); settings->setDefault("texture_path", ""); settings->setDefault("shader_path", ""); settings->setDefault("video_driver", "opengl"); diff --git a/src/light.cpp b/src/light.cpp index 5dc01fcf0..4a3ca5a60 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -26,29 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc., // Length of LIGHT_MAX+1 means LIGHT_MAX is the last value. // LIGHT_SUN is read as LIGHT_MAX from here. -u8 light_LUT[LIGHT_MAX+1] = -{ - /* Middle-raised variation of a_n+1 = a_n * 0.786 - * Length of LIGHT_MAX+1 means LIGHT_MAX is the last value. - * LIGHT_SUN is read as LIGHT_MAX from here. - */ - 8, - 11+2, - 14+7, - 18+10, - 22+15, - 29+20, - 37+20, - 47+15, - 60+10, - 76+7, - 97+5, - 123+2, - 157, - 200, - 255, -}; +u8 light_LUT[LIGHT_MAX+1]; +// the const ref to light_LUT is what is actually used in the code. const u8 *light_decode_table = light_LUT; /** Initialize or update the light value tables using the specified \p gamma. @@ -65,26 +45,26 @@ void set_light_table(float gamma) { static const float brightness_step = 255.0f / (LIGHT_MAX + 1); - /* These are adjustment values that are added to the calculated light value - * after gamma is applied. Currently they are used so that given a gamma - * of 1.8 the light values set by this function are the same as those - * hardcoded in the initalizer list for the declaration of light_LUT. - */ + // this table is pure arbitrary values, made so that + // at gamma 2.2 the game looks not too dark at light=1, + // and mostly linear for the rest of the scale. + // we could try to inverse the gamma power function, but this + // is simpler and quicker. static const int adjustments[LIGHT_MAX + 1] = { - 7, - 7, - 7, - 5, - 2, - 0, - -7, - -20, - -31, - -39, - -43, - -45, - -40, - -25, + -67, + -91, + -125, + -115, + -104, + -85, + -70, + -63, + -56, + -49, + -42, + -35, + -28, + -22, 0 }; @@ -93,296 +73,13 @@ void set_light_table(float gamma) float brightness = brightness_step; for (size_t i = 0; i < LIGHT_MAX; i++) { - light_LUT[i] = (u8)(255 * powf(brightness / 255.0f, gamma)); + light_LUT[i] = (u8)(255 * powf(brightness / 255.0f, 1.0 / gamma)); light_LUT[i] = rangelim(light_LUT[i] + adjustments[i], 0, 255); - if (i > 1 && light_LUT[i] < light_LUT[i-1]) - light_LUT[i] = light_LUT[i-1] + 1; + if (i > 1 && light_LUT[i] < light_LUT[i - 1]) + light_LUT[i] = light_LUT[i - 1] + 1; brightness += brightness_step; } light_LUT[LIGHT_MAX] = 255; } #endif - - -#if 0 -/* -Made using this and: -- adding 220 as the second last one -- replacing the third last one (212) with 195 - -#!/usr/bin/python - -from math import * -from sys import stdout - -# We want 0 at light=0 and 255 at light=LIGHT_MAX -LIGHT_MAX = 14 -#FACTOR = 0.69 -#FACTOR = 0.75 -FACTOR = 0.83 -START_FROM_ZERO = False - -L = [] -if START_FROM_ZERO: - for i in range(1,LIGHT_MAX+1): - L.append(int(round(255.0 * FACTOR ** (i-1)))) - L.append(0) -else: - for i in range(1,LIGHT_MAX+1): - L.append(int(round(255.0 * FACTOR ** (i-1)))) - L.append(255) - -L.reverse() -for i in L: - stdout.write(str(i)+",\n") -*/ -u8 light_decode_table[LIGHT_MAX+1] = -{ -23, -27, -33, -40, -48, -57, -69, -83, -100, -121, -146, -176, -195, -220, -255, -}; -#endif - -#if 0 -// This is good -// a_n+1 = a_n * 0.786 -// Length of LIGHT_MAX+1 means LIGHT_MAX is the last value. -// LIGHT_SUN is read as LIGHT_MAX from here. -u8 light_decode_table[LIGHT_MAX+1] = -{ -8, -11, -14, -18, -22, -29, -37, -47, -60, -76, -97, -123, -157, -200, -255, -}; -#endif - -#if 0 -// Use for debugging in dark -u8 light_decode_table[LIGHT_MAX+1] = -{ -58, -64, -72, -80, -88, -98, -109, -121, -135, -150, -167, -185, -206, -229, -255, -}; -#endif - -// This is reasonable with classic lighting with a light source -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -2, -3, -4, -6, -9, -13, -18, -25, -32, -35, -45, -57, -69, -79, -255 -};*/ - - -// As in minecraft, a_n+1 = a_n * 0.8 -// NOTE: This doesn't really work that well because this defines -// LIGHT_MAX as dimmer than LIGHT_SUN -// NOTE: Uh, this has had 34 left out; forget this. -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -8, -11, -14, -17, -21, -27, -42, -53, -66, -83, -104, -130, -163, -204, -255, -};*/ - -// This was a quick try of more light, manually quickly made -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -0, -7, -11, -15, -21, -29, -42, -53, -69, -85, -109, -135, -167, -205, -255, -};*/ - -// This was used for a long time, manually made -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -0, -6, -8, -11, -14, -19, -26, -34, -45, -61, -81, -108, -143, -191, -255, -};*/ - -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -0, -3, -6, -10, -18, -25, -35, -50, -75, -95, -120, -150, -185, -215, -255, -};*/ -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -0, -5, -12, -22, -35, -50, -65, -85, -100, -120, -140, -160, -185, -215, -255, -};*/ -// LIGHT_MAX is 14, 0-14 is 15 values -/*u8 light_decode_table[LIGHT_MAX+1] = -{ -0, -9, -12, -14, -16, -20, -26, -34, -45, -61, -81, -108, -143, -191, -255, -};*/ - -#if 0 -/* -#!/usr/bin/python - -from math import * -from sys import stdout - -# We want 0 at light=0 and 255 at light=LIGHT_MAX -LIGHT_MAX = 14 -#FACTOR = 0.69 -FACTOR = 0.75 - -L = [] -for i in range(1,LIGHT_MAX+1): - L.append(int(round(255.0 * FACTOR ** (i-1)))) -L.append(0) - -L.reverse() -for i in L: - stdout.write(str(i)+",\n") -*/ -u8 light_decode_table[LIGHT_MAX+1] = -{ -0, -6, -8, -11, -14, -19, -26, -34, -45, -61, -81, -108, -143, -191, -255, -}; -#endif - - -- cgit v1.2.3 From e509ead680cc1c0bde35dfd467d18b8288cc1b86 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 29 Dec 2016 13:17:24 +0100 Subject: Buildbot: Update Gettext version (#4971) --- util/buildbot/buildwin32.sh | 9 ++++----- util/buildbot/buildwin64.sh | 3 +-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/util/buildbot/buildwin32.sh b/util/buildbot/buildwin32.sh index e58c25ccc..d807003d2 100755 --- a/util/buildbot/buildwin32.sh +++ b/util/buildbot/buildwin32.sh @@ -17,7 +17,7 @@ irrlicht_version=1.8.4 ogg_version=1.3.2 vorbis_version=1.3.5 curl_version=7.50.3 -gettext_version=0.14.4 +gettext_version=0.19.8.1 freetype_version=2.7 sqlite3_version=3.14.2 luajit_version=2.1.0-beta2 @@ -40,7 +40,7 @@ cd $builddir -c -O $packagedir/libvorbis-$vorbis_version.zip [ -e $packagedir/curl-$curl_version.zip ] || wget http://minetest.kitsunemimi.pw/curl-$curl_version-win32.zip \ -c -O $packagedir/curl-$curl_version.zip -[ -e $packagedir/gettext-$gettext_version.zip ] || wget http://minetest.kitsunemimi.pw/gettext-$gettext_version.zip \ +[ -e $packagedir/gettext-$gettext_version.zip ] || wget http://minetest.kitsunemimi.pw/gettext-$gettext_version-win32.zip \ -c -O $packagedir/gettext-$gettext_version.zip [ -e $packagedir/freetype2-$freetype_version.zip ] || wget http://minetest.kitsunemimi.pw/freetype2-$freetype_version-win32.zip \ -c -O $packagedir/freetype2-$freetype_version.zip @@ -130,10 +130,9 @@ cmake .. \ -DCURL_INCLUDE_DIR=$libdir/libcurl/include \ -DCURL_LIBRARY=$libdir/libcurl/lib/libcurl.dll.a \ \ - -DCUSTOM_GETTEXT_PATH=$libdir/gettext \ -DGETTEXT_MSGFMT=`which msgfmt` \ - -DGETTEXT_DLL=$libdir/gettext/bin/libintl3.dll \ - -DGETTEXT_ICONV_DLL=$libdir/gettext/bin/libiconv2.dll \ + -DGETTEXT_DLL=$libdir/gettext/bin/libintl-8.dll \ + -DGETTEXT_ICONV_DLL=$libdir/gettext/bin/libiconv-2.dll \ -DGETTEXT_INCLUDE_DIR=$libdir/gettext/include \ -DGETTEXT_LIBRARY=$libdir/gettext/lib/libintl.dll.a \ \ diff --git a/util/buildbot/buildwin64.sh b/util/buildbot/buildwin64.sh index 1c31fe4bd..3d6965f1e 100755 --- a/util/buildbot/buildwin64.sh +++ b/util/buildbot/buildwin64.sh @@ -17,7 +17,7 @@ irrlicht_version=1.8.4 ogg_version=1.3.2 vorbis_version=1.3.5 curl_version=7.50.3 -gettext_version=0.18.2 +gettext_version=0.19.8.1 freetype_version=2.7 sqlite3_version=3.14.2 luajit_version=2.1.0-beta2 @@ -131,7 +131,6 @@ cmake .. \ -DCURL_INCLUDE_DIR=$libdir/libcurl/include \ -DCURL_LIBRARY=$libdir/libcurl/lib/libcurl.dll.a \ \ - -DCUSTOM_GETTEXT_PATH=$libdir/gettext \ -DGETTEXT_MSGFMT=`which msgfmt` \ -DGETTEXT_DLL=$libdir/gettext/bin/libintl-8.dll \ -DGETTEXT_ICONV_DLL=$libdir/gettext/bin/libiconv-2.dll \ -- cgit v1.2.3 From abd68d3466b7f2155cf0f1c4172a254f10c1f02e Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Thu, 29 Dec 2016 13:44:47 +0100 Subject: Use the outgoing split sequence number for every outgoing packet (#4864) (instead of the last incoming sequence number...) Fixes #4848 --- src/network/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/connection.cpp b/src/network/connection.cpp index b711cae11..e11b4a953 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -1237,7 +1237,7 @@ void UDPPeer::RunCommandQueues( u16 UDPPeer::getNextSplitSequenceNumber(u8 channel) { assert(channel < CHANNEL_COUNT); // Pre-condition - return channels[channel].readNextIncomingSeqNum(); + return channels[channel].readNextSplitSeqNum(); } void UDPPeer::setNextSplitSequenceNumber(u8 channel, u16 seqnum) -- cgit v1.2.3 From dd3cda6bedf0ed7443258f33d5a5cf68fa120534 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 29 Dec 2016 19:35:22 +0100 Subject: Fix interact range check (thanks to @lhofhansl) --- src/network/serverpackethandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index f99e104ec..d0f4d948d 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1440,7 +1440,8 @@ void Server::handleCommand_Interact(NetworkPacket* pkt) max_d = max_d_hand; else if (max_d < 0) max_d = BS * 4.0; - if (d > max_d * 1.5) { + // cube diagonal: sqrt(3) = 1.73 + if (d > max_d * 1.73) { actionstream << "Player " << player->getName() << " tried to access " << pointed.dump() << " from too far: " -- cgit v1.2.3 From a1346c916e1d0f0cde2ccecc680857896c717a3d Mon Sep 17 00:00:00 2001 From: Dorian Wouters Date: Sat, 31 Dec 2016 18:12:26 +0100 Subject: Fix /grant & /revoke not working with custom auth handler (#4974) core.auth_table is not supposed to be accessed directly. --- builtin/game/chatcommands.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 2bd93855b..71edeb26a 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -162,7 +162,7 @@ local function handle_grant_command(caller, grantname, grantprivstr) return false, "Your privileges are insufficient." end - if not core.auth_table[grantname] then + if not core.get_auth_handler().get_auth(grantname) then return false, "Player " .. grantname .. " does not exist." end local grantprivs = core.string_to_privs(grantprivstr) @@ -232,7 +232,7 @@ core.register_chatcommand("revoke", { local revoke_name, revoke_priv_str = string.match(param, "([^ ]+) (.+)") if not revoke_name or not revoke_priv_str then return false, "Invalid parameters (see /help revoke)" - elseif not core.auth_table[revoke_name] then + elseif not core.get_auth_handler().get_auth(revoke_name) then return false, "Player " .. revoke_name .. " does not exist." end local revoke_privs = core.string_to_privs(revoke_priv_str) -- cgit v1.2.3 From 52ba1f867e5edb579a59a44fbb8286d4f1e54931 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 1 Jan 2017 16:13:01 +0100 Subject: Breath cheat fix: server side Breath is now handled server side. Changing this behaviour required some modifications to core: * Ignore TOSERVER_BREATH package, marking it as obsolete * Clients doesn't send the breath to server anymore * Use PlayerSAO pointer instead of peer_id in Server::SendPlayerBreath to prevent a useless lookup (little perf gain) * drop a useless static_cast in emergePlayer --- src/client.cpp | 11 +++-- src/content_sao.cpp | 39 +++++++++++++++-- src/content_sao.h | 7 +++- src/environment.cpp | 84 ++++++++++++++++++------------------- src/network/clientopcodes.cpp | 2 +- src/network/networkprotocol.h | 6 ++- src/network/serveropcodes.cpp | 2 +- src/network/serverpackethandler.cpp | 40 ------------------ src/remoteplayer.cpp | 2 +- src/script/lua_api/l_object.cpp | 5 --- src/server.cpp | 15 +++---- src/server.h | 3 +- src/unittest/test_player.cpp | 4 +- 13 files changed, 107 insertions(+), 113 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 5476aad0e..1446ebad8 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -499,9 +499,10 @@ void Client::step(float dtime) m_client_event_queue.push(event); } } - else if(event.type == CEE_PLAYER_BREATH) { - u16 breath = event.player_breath.amount; - sendBreath(breath); + // Protocol v29 or greater obsoleted this event + else if (event.type == CEE_PLAYER_BREATH && m_proto_ver < 29) { + u16 breath = event.player_breath.amount; + sendBreath(breath); } } @@ -1270,6 +1271,10 @@ void Client::sendBreath(u16 breath) { DSTACK(FUNCTION_NAME); + // Protocol v29 make this obsolete + if (m_proto_ver >= 29) + return; + NetworkPacket pkt(TOSERVER_BREATH, sizeof(u16)); pkt << breath; Send(&pkt); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 77ab51a02..f866d4372 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" // For compressZlib #include "tool.h" // For ToolCapabilities #include "gamedef.h" +#include "nodedef.h" #include "remoteplayer.h" #include "server.h" #include "scripting_game.h" @@ -940,8 +941,35 @@ bool PlayerSAO::isAttached() void PlayerSAO::step(float dtime, bool send_recommended) { - if(!m_properties_sent) - { + if (m_drowning_interval.step(dtime, 2.0)) { + // get head position + v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_env->getMap().getNodeNoEx(p); + const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + // If node generates drown + if (c.drowning > 0) { + if (m_hp > 0 && m_breath > 0) + setBreath(m_breath - 1); + + // No more breath, damage player + if (m_breath == 0) { + setHP(m_hp - c.drowning); + ((Server*) m_env->getGameDef())->SendPlayerHPOrDie(this); + } + } + } + + if (m_breathing_interval.step(dtime, 0.5)) { + // get head position + v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_env->getMap().getNodeNoEx(p); + const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + // If player is alive & no drowning, breath + if (m_hp > 0 && c.drowning == 0) + setBreath(m_breath + 1); + } + + if (!m_properties_sent) { m_properties_sent = true; std::string str = getPropertyPacket(); // create message and add to list @@ -1237,12 +1265,15 @@ void PlayerSAO::setHP(s16 hp) m_properties_sent = false; } -void PlayerSAO::setBreath(const u16 breath) +void PlayerSAO::setBreath(const u16 breath, bool send) { if (m_player && breath != m_breath) m_player->setDirty(true); - m_breath = breath; + m_breath = MYMIN(breath, PLAYER_MAX_BREATH); + + if (send) + ((Server *) m_env->getGameDef())->SendPlayerBreath(this); } void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) diff --git a/src/content_sao.h b/src/content_sao.h index 86255183d..9c66068b3 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef CONTENT_SAO_HEADER #define CONTENT_SAO_HEADER +#include #include "serverobject.h" #include "itemgroup.h" #include "object_properties.h" @@ -232,7 +233,7 @@ public: void setHPRaw(s16 hp) { m_hp = hp; } s16 readDamage(); u16 getBreath() const { return m_breath; } - void setBreath(const u16 breath); + void setBreath(const u16 breath, bool send = true); void setArmorGroups(const ItemGroupList &armor_groups); ItemGroupList getArmorGroups(); void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); @@ -339,6 +340,10 @@ private: v3s16 m_nocheat_dig_pos; float m_nocheat_dig_time; + // Timers + IntervalLimiter m_breathing_interval; + IntervalLimiter m_drowning_interval; + int m_wield_index; bool m_position_not_sent; ItemGroupList m_armor_groups; diff --git a/src/environment.cpp b/src/environment.cpp index 707d89659..ac9b5b079 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -2511,51 +2511,51 @@ void ClientEnvironment::step(float dtime) } } - /* - Drowning - */ - if(m_drowning_interval.step(dtime, 2.0)) - { - v3f pf = lplayer->getPosition(); - - // head - v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS); - MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); - u8 drowning_damage = c.drowning; - if(drowning_damage > 0 && lplayer->hp > 0){ - u16 breath = lplayer->getBreath(); - if(breath > 10){ - breath = 11; - } - if(breath > 0){ - breath -= 1; + // Protocol v29 make this behaviour obsolete + if (((Client*) getGameDef())->getProtoVersion() < 29) { + /* + Drowning + */ + if (m_drowning_interval.step(dtime, 2.0)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNodeNoEx(p); + ContentFeatures c = m_gamedef->ndef()->get(n); + u8 drowning_damage = c.drowning; + if (drowning_damage > 0 && lplayer->hp > 0) { + u16 breath = lplayer->getBreath(); + if (breath > 10) { + breath = 11; + } + if (breath > 0) { + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); } - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); - } - if(lplayer->getBreath() == 0 && drowning_damage > 0){ - damageLocalPlayer(drowning_damage, true); + if (lplayer->getBreath() == 0 && drowning_damage > 0) { + damageLocalPlayer(drowning_damage, true); + } } - } - if(m_breathing_interval.step(dtime, 0.5)) - { - v3f pf = lplayer->getPosition(); - - // head - v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS); - MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); - if (!lplayer->hp){ - lplayer->setBreath(11); - } - else if(c.drowning == 0){ - u16 breath = lplayer->getBreath(); - if(breath <= 10){ - breath += 1; - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); + if (m_breathing_interval.step(dtime, 0.5)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNodeNoEx(p); + ContentFeatures c = m_gamedef->ndef()->get(n); + if (!lplayer->hp) { + lplayer->setBreath(11); + } else if (c.drowning == 0) { + u16 breath = lplayer->getBreath(); + if (breath <= 10) { + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } } } } diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 3364de8c5..6defdcf1b 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -193,7 +193,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x3f { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41 - { "TOSERVER_BREATH", 0, true }, // 0x42 + null_command_factory, // 0x42 old TOSERVER_BREATH. Ignored by servers { "TOSERVER_CLIENT_READY", 0, true }, // 0x43 null_command_factory, // 0x44 null_command_factory, // 0x45 diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 018b392b6..f65167380 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -138,9 +138,11 @@ with this program; if not, write to the Free Software Foundation, Inc., Add nodedef v3 - connected nodeboxes PROTOCOL_VERSION 28: CPT2_MESHOPTIONS + PROTOCOL_VERSION 29: + Server doesn't accept TOSERVER_BREATH anymore */ -#define LATEST_PROTOCOL_VERSION 28 +#define LATEST_PROTOCOL_VERSION 29 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 @@ -833,7 +835,7 @@ enum ToServerCommand */ - TOSERVER_BREATH = 0x42, + TOSERVER_BREATH = 0x42, // Obsolete /* u16 breath */ diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 9b14a1be3..642dd376a 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -90,7 +90,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = null_command_handler, // 0x3f { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41 - { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42 + { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Deprecated }, // 0x42 Old breath model which is now deprecated for anticheating { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43 null_command_handler, // 0x44 null_command_handler, // 0x45 diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index d0f4d948d..eeabcca71 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1136,46 +1136,6 @@ void Server::handleCommand_Damage(NetworkPacket* pkt) } } -void Server::handleCommand_Breath(NetworkPacket* pkt) -{ - u16 breath; - - *pkt >> breath; - - RemotePlayer *player = m_env->getPlayer(pkt->getPeerId()); - - if (player == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - m_con.DisconnectPeer(pkt->getPeerId()); - return; - } - - - PlayerSAO *playersao = player->getPlayerSAO(); - if (playersao == NULL) { - errorstream << "Server::ProcessData(): Canceling: " - "No player object for peer_id=" << pkt->getPeerId() - << " disconnecting peer!" << std::endl; - m_con.DisconnectPeer(pkt->getPeerId()); - return; - } - - /* - * If player is dead, we don't need to update the breath - * He is dead ! - */ - if (playersao->isDead()) { - verbosestream << "TOSERVER_BREATH: " << player->getName() - << " is dead. Ignoring packet"; - return; - } - - playersao->setBreath(breath); - SendPlayerBreath(pkt->getPeerId()); -} - void Server::handleCommand_Password(NetworkPacket* pkt) { if (pkt->getSize() != PASSWORD_SIZE * 2) diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 67ab89113..18bfa1030 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -148,7 +148,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, } catch (SettingNotFoundException &e) {} try { - sao->setBreath(args.getS32("breath")); + sao->setBreath(args.getS32("breath"), false); } catch (SettingNotFoundException &e) {} } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 2a8b8a64e..cfdceb28e 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1152,13 +1152,8 @@ int ObjectRef::l_set_breath(lua_State *L) PlayerSAO* co = getplayersao(ref); if (co == NULL) return 0; u16 breath = luaL_checknumber(L, 2); - // Do it co->setBreath(breath); - // If the object is a player sent the breath to client - if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) - getServer(L)->SendPlayerBreath(((PlayerSAO*)co)->getPeerID()); - return 0; } diff --git a/src/server.cpp b/src/server.cpp index fa7a838d4..60dbef0d2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1076,8 +1076,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) } m_clients.unlock(); - RemotePlayer *player = - static_cast(m_env->getPlayer(playername.c_str())); + RemotePlayer *player = m_env->getPlayer(playername.c_str()); // If failed, cancel if ((playersao == NULL) || (player == NULL)) { @@ -1113,7 +1112,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) SendPlayerHPOrDie(playersao); // Send Breath - SendPlayerBreath(peer_id); + SendPlayerBreath(playersao); // Show death screen if necessary if (playersao->isDead()) @@ -1857,14 +1856,13 @@ void Server::SendPlayerHP(u16 peer_id) playersao->m_messages_out.push(aom); } -void Server::SendPlayerBreath(u16 peer_id) +void Server::SendPlayerBreath(PlayerSAO *sao) { DSTACK(FUNCTION_NAME); - PlayerSAO *playersao = getPlayerSAO(peer_id); - assert(playersao); + assert(sao); - m_script->player_event(playersao, "breath_changed"); - SendBreath(peer_id, playersao->getBreath()); + m_script->player_event(sao, "breath_changed"); + SendBreath(sao->getPeerID(), sao->getBreath()); } void Server::SendMovePlayer(u16 peer_id) @@ -2565,7 +2563,6 @@ void Server::RespawnPlayer(u16 peer_id) } SendPlayerHP(peer_id); - SendPlayerBreath(peer_id); } diff --git a/src/server.h b/src/server.h index cab7e2445..f0df0f9ec 100644 --- a/src/server.h +++ b/src/server.h @@ -180,7 +180,6 @@ public: void handleCommand_InventoryAction(NetworkPacket* pkt); void handleCommand_ChatMessage(NetworkPacket* pkt); void handleCommand_Damage(NetworkPacket* pkt); - void handleCommand_Breath(NetworkPacket* pkt); void handleCommand_Password(NetworkPacket* pkt); void handleCommand_PlayerItem(NetworkPacket* pkt); void handleCommand_Respawn(NetworkPacket* pkt); @@ -358,7 +357,7 @@ public: void printToConsoleOnly(const std::string &text); void SendPlayerHPOrDie(PlayerSAO *player); - void SendPlayerBreath(u16 peer_id); + void SendPlayerBreath(PlayerSAO *sao); void SendInventory(PlayerSAO* playerSAO); void SendMovePlayer(u16 peer_id); diff --git a/src/unittest/test_player.cpp b/src/unittest/test_player.cpp index 85fbc8b2d..655ee08fd 100644 --- a/src/unittest/test_player.cpp +++ b/src/unittest/test_player.cpp @@ -49,7 +49,7 @@ void TestPlayer::testSave(IGameDef *gamedef) PlayerSAO sao(NULL, 1, false); sao.initialize(&rplayer, std::set()); rplayer.setPlayerSAO(&sao); - sao.setBreath(10); + sao.setBreath(10, false); sao.setHPRaw(8); sao.setYaw(0.1f); sao.setPitch(0.6f); @@ -64,7 +64,7 @@ void TestPlayer::testLoad(IGameDef *gamedef) PlayerSAO sao(NULL, 1, false); sao.initialize(&rplayer, std::set()); rplayer.setPlayerSAO(&sao); - sao.setBreath(10); + sao.setBreath(10, false); sao.setHPRaw(8); sao.setYaw(0.1f); sao.setPitch(0.6f); -- cgit v1.2.3 From e2e8da5ee44f71c22996a780caff67e48e79fae4 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 1 Jan 2017 23:57:37 +0100 Subject: Fix non reverted change on TOSERVER_BREATH compat --- src/network/clientopcodes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 6defdcf1b..cee402acd 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -193,7 +193,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x3f { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41 - null_command_factory, // 0x42 old TOSERVER_BREATH. Ignored by servers + { "TOSERVER_BREATH", 0, true }, // 0x42 old TOSERVER_BREATH. Ignored by servers { "TOSERVER_CLIENT_READY", 0, true }, // 0x43 null_command_factory, // 0x44 null_command_factory, // 0x45 -- cgit v1.2.3 From 523f0e8c5bce0cb58215dc1f9d027cf32394e3c3 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 23 Dec 2016 13:48:32 +0100 Subject: Move TileAnimation code to seperate file --- src/CMakeLists.txt | 1 + src/client/tile.h | 4 +- src/mapblock_mesh.cpp | 3 +- src/network/networkprotocol.h | 2 + src/nodedef.cpp | 35 +++++++---------- src/nodedef.h | 16 ++------ src/particles.cpp | 13 +++--- src/script/common/c_content.cpp | 6 +-- src/tileanimation.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ src/tileanimation.h | 49 +++++++++++++++++++++++ 10 files changed, 168 insertions(+), 48 deletions(-) create mode 100644 src/tileanimation.cpp create mode 100644 src/tileanimation.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3aa645df9..51cf88063 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -459,6 +459,7 @@ set(common_SRCS staticobject.cpp subgame.cpp terminal_chat_console.cpp + tileanimation.cpp tool.cpp treegen.cpp version.cpp diff --git a/src/client/tile.h b/src/client/tile.h index b75916841..452804801 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -161,9 +161,7 @@ enum MaterialType{ // Should the crack be drawn on transparent pixels (unset) or not (set)? // Ignored if MATERIAL_FLAG_CRACK is not set. #define MATERIAL_FLAG_CRACK_OVERLAY 0x04 -// Animation made up by splitting the texture to vertical frames, as -// defined by extra parameters -#define MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES 0x08 +#define MATERIAL_FLAG_ANIMATION 0x08 #define MATERIAL_FLAG_HIGHLIGHTED 0x10 #define MATERIAL_FLAG_TILEABLE_HORIZONTAL 0x20 #define MATERIAL_FLAG_TILEABLE_VERTICAL 0x40 diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 00f83e7ab..977eabb6e 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -1131,8 +1131,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): &p.tile.texture_id); } // - Texture animation - if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) - { + if (p.tile.material_flags & MATERIAL_FLAG_ANIMATION) { // Add to MapBlockMesh in order to animate these tiles m_animation_tiles[i] = p.tile; m_animation_frames[i] = 0; diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index f65167380..45bf76ff8 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -140,6 +140,8 @@ with this program; if not, write to the Free Software Foundation, Inc., CPT2_MESHOPTIONS PROTOCOL_VERSION 29: Server doesn't accept TOSERVER_BREATH anymore + serialization of TileAnimation params changed + TAT_SHEET_2D */ #define LATEST_PROTOCOL_VERSION 29 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index ccbb42c66..21bceb94d 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -188,17 +188,16 @@ void NodeBox::deSerialize(std::istream &is) void TileDef::serialize(std::ostream &os, u16 protocol_version) const { - if (protocol_version >= 26) + if (protocol_version >= 29) + writeU8(os, 3); + else if (protocol_version >= 26) writeU8(os, 2); else if (protocol_version >= 17) writeU8(os, 1); else writeU8(os, 0); os<= 17) writeU8(os, backface_culling); if (protocol_version >= 26) { @@ -211,10 +210,7 @@ void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, con { int version = readU8(is); name = deSerializeString(is); - animation.type = (TileAnimationType)readU8(is); - animation.aspect_w = readU16(is); - animation.aspect_h = readU16(is); - animation.length = readF1000(is); + animation.deSerialize(is, version >= 3 ? 29 : 26); if (version >= 1) backface_culling = readU8(is); if (version >= 2) { @@ -533,7 +529,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, if (backface_culling) tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; if (tiledef->animation.type == TAT_VERTICAL_FRAMES) - tile->material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + tile->material_flags |= MATERIAL_FLAG_ANIMATION; if (tiledef->tileable_horizontal) tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; if (tiledef->tileable_vertical) @@ -541,20 +537,16 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, // Animation parameters int frame_count = 1; - if (tile->material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { - // Get texture size to determine frame count by aspect ratio - v2u32 size = tile->texture->getOriginalSize(); - int frame_height = (float)size.X / - (float)tiledef->animation.aspect_w * - (float)tiledef->animation.aspect_h; - frame_count = size.Y / frame_height; - int frame_length_ms = 1000.0 * tiledef->animation.length / frame_count; + if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { + int frame_length_ms; + tiledef->animation.determineParams(tile->texture->getOriginalSize(), + &frame_count, &frame_length_ms); tile->animation_frame_count = frame_count; tile->animation_frame_length_ms = frame_length_ms; } if (frame_count == 1) { - tile->material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; + tile->material_flags &= ~MATERIAL_FLAG_ANIMATION; } else { std::ostringstream os(std::ios::binary); tile->frames.resize(frame_count); @@ -564,8 +556,9 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, FrameSpec frame; os.str(""); - os << tiledef->name << "^[verticalframe:" - << frame_count << ":" << i; + os << tiledef->name; + tiledef->animation.getTextureModifer(os, + tile->texture->getOriginalSize(), i); frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); if (tile->normal_texture) diff --git a/src/nodedef.h b/src/nodedef.h index 80396f992..b5639b516 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "sound.h" // SimpleSoundSpec #include "constants.h" // BS +#include "tileanimation.h" class INodeDefManager; class IItemDefManager; @@ -161,22 +162,14 @@ enum NodeDrawType /* Stand-alone definition of a TileSpec (basically a server-side TileSpec) */ -enum TileAnimationType{ - TAT_NONE=0, - TAT_VERTICAL_FRAMES=1, -}; + struct TileDef { std::string name; bool backface_culling; // Takes effect only in special cases bool tileable_horizontal; bool tileable_vertical; - struct{ - enum TileAnimationType type; - int aspect_w; // width for aspect ratio - int aspect_h; // height for aspect ratio - float length; // seconds - } animation; + struct TileAnimationParams animation; TileDef() { @@ -185,9 +178,6 @@ struct TileDef tileable_horizontal = true; tileable_vertical = true; animation.type = TAT_NONE; - animation.aspect_w = 1; - animation.aspect_h = 1; - animation.length = 1.0; } void serialize(std::ostream &os, u16 protocol_version) const; diff --git a/src/particles.cpp b/src/particles.cpp index acf9cc815..e9ddba986 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -567,19 +567,20 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s { // Texture u8 texid = myrand_range(0, 5); - video::ITexture *texture = tiles[texid].texture; + video::ITexture *texture; // Only use first frame of animated texture - f32 ymax = 1; - if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) - ymax /= tiles[texid].animation_frame_count; + if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) + texture = tiles[texid].frames[0].texture; + else + texture = tiles[texid].texture; float size = rand() % 64 / 512.; float visual_size = BS * size; - v2f texsize(size * 2, ymax * size * 2); + v2f texsize(size * 2, size * 2); v2f texpos; texpos.X = ((rand() % 64) / 64. - texsize.X); - texpos.Y = ymax * ((rand() % 64) / 64. - texsize.Y); + texpos.Y = ((rand() % 64) / 64. - texsize.Y); // Physics v3f velocity((rand() % 100 / 50. - 1) / 1.5, diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 541744895..6cd1d040b 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -338,11 +338,11 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) tiledef.animation.type = (TileAnimationType) getenumfield(L, -1, "type", es_TileAnimationType, TAT_NONE); - tiledef.animation.aspect_w = + tiledef.animation.vertical_frames.aspect_w = getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.aspect_h = + tiledef.animation.vertical_frames.aspect_h = getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.length = + tiledef.animation.vertical_frames.length = getfloatfield_default(L, -1, "length", 1.0); } lua_pop(L, 1); diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp new file mode 100644 index 000000000..891478c9f --- /dev/null +++ b/src/tileanimation.cpp @@ -0,0 +1,87 @@ +/* +Minetest +Copyright (C) 2016 sfan5 + +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 "tileanimation.h" +#include "util/serialize.h" + +void TileAnimationParams::serialize(std::ostream &os, u16 protocol_version) const +{ + if(protocol_version < 29 /* TODO bump */) { + if (type == TAT_VERTICAL_FRAMES) { + writeU8(os, type); + writeU16(os, vertical_frames.aspect_w); + writeU16(os, vertical_frames.aspect_h); + writeF1000(os, vertical_frames.length); + } else { + writeU8(os, TAT_NONE); + writeU16(os, 1); + writeU16(os, 1); + writeF1000(os, 1.0); + } + return; + } + + writeU8(os, type); + if (type == TAT_VERTICAL_FRAMES) { + writeU16(os, vertical_frames.aspect_w); + writeU16(os, vertical_frames.aspect_h); + writeF1000(os, vertical_frames.length); + } +} + +void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version) +{ + type = (TileAnimationType) readU8(is); + if(protocol_version < 29 /* TODO bump */) { + vertical_frames.aspect_w = readU16(is); + vertical_frames.aspect_h = readU16(is); + vertical_frames.length = readF1000(is); + return; + } + + if(type == TAT_VERTICAL_FRAMES) { + vertical_frames.aspect_w = readU16(is); + vertical_frames.aspect_h = readU16(is); + vertical_frames.length = readF1000(is); + } +} + +void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const +{ + if (type == TAT_NONE) { + *frame_count = 1; + *frame_length_ms = 1000; + return; + } + int frame_height = (float)texture_size.X / + (float)vertical_frames.aspect_w * + (float)vertical_frames.aspect_h; + if (frame_count) + *frame_count = texture_size.Y / frame_height; + if (frame_length_ms) + *frame_length_ms = 1000.0 * vertical_frames.length / (texture_size.Y / frame_height); +} + +void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const +{ + if (type == TAT_NONE) + return; + int frame_count; + determineParams(texture_size, &frame_count, NULL); + os << "^[verticalframe:" << frame_count << ":" << frame; +} diff --git a/src/tileanimation.h b/src/tileanimation.h new file mode 100644 index 000000000..d5172ed50 --- /dev/null +++ b/src/tileanimation.h @@ -0,0 +1,49 @@ +/* +Minetest +Copyright (C) 2016 sfan5 + +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 TILEANIMATION_HEADER +#define TILEANIMATION_HEADER + +#include "irrlichttypes_bloated.h" +#include + +enum TileAnimationType { + TAT_NONE = 0, + TAT_VERTICAL_FRAMES = 1, +}; + +struct TileAnimationParams { + enum TileAnimationType type; + union { + // struct { + // } none; + struct { + int aspect_w; // width for aspect ratio + int aspect_h; // height for aspect ratio + float length; // seconds + } vertical_frames; + }; + + void serialize(std::ostream &os, u16 protocol_version) const; + void deSerialize(std::istream &is, u16 protocol_version); + void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const; + void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const; +}; + +#endif -- cgit v1.2.3 From 7057c196c442ff3484b53f48d940f4c9e0ffe23a Mon Sep 17 00:00:00 2001 From: Luke Puchner-Hardman Date: Tue, 23 Sep 2014 14:39:34 +0200 Subject: Added "[sheet" to the texture special commands. "[sheet:WxH:X,Y" assumes the base image is a tilesheet with W*H tiles on it and crops to the tile at position X,Y. Basically it works like "[verticalframe" but in 2D. For testing, I combined the four default_chest images into one. --- doc/lua_api.txt | 5 +++ games/minimal/mods/default/init.lua | 10 +++-- .../mods/default/textures/default_chest.png | Bin 0 -> 263 bytes .../mods/default/textures/default_chest_front.png | Bin 114 -> 0 bytes .../mods/default/textures/default_chest_lock.png | Bin 145 -> 0 bytes .../mods/default/textures/default_chest_side.png | Bin 98 -> 0 bytes .../mods/default/textures/default_chest_top.png | Bin 93 -> 0 bytes src/client/tile.cpp | 43 +++++++++++++++++++++ 8 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 games/minimal/mods/default/textures/default_chest.png delete mode 100644 games/minimal/mods/default/textures/default_chest_front.png delete mode 100644 games/minimal/mods/default/textures/default_chest_lock.png delete mode 100644 games/minimal/mods/default/textures/default_chest_side.png delete mode 100644 games/minimal/mods/default/textures/default_chest_top.png diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d2ddc635b..648a29303 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -403,6 +403,11 @@ Apply a mask to the base image. The mask is applied using binary AND. +#### `[sheet:x:,` +Retrieves a tile at position x,y from the base image +which it assumes to be a tilesheet with dimensions w,h. + + #### `[colorize::` Colorize the textures with the given color. `` is specified as a `ColorString`. diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index bff7860e3..f532e7193 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1130,8 +1130,9 @@ minetest.register_node("default:sign_wall", { minetest.register_node("default:chest", { description = "Chest", - tiles ={"default_chest_top.png", "default_chest_top.png", "default_chest_side.png", - "default_chest_side.png", "default_chest_side.png", "default_chest_front.png"}, + tiles ={"default_chest.png^[sheet:2x2:0,0", "default_chest.png^[sheet:2x2:0,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:1,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:0,1"}, paramtype2 = "facedir", groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2}, legacy_facedir_simple = true, @@ -1164,8 +1165,9 @@ end minetest.register_node("default:chest_locked", { description = "Locked Chest", - tiles ={"default_chest_top.png", "default_chest_top.png", "default_chest_side.png", - "default_chest_side.png", "default_chest_side.png", "default_chest_lock.png"}, + tiles ={"default_chest.png^[sheet:2x2:0,0", "default_chest.png^[sheet:2x2:0,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:1,0", + "default_chest.png^[sheet:2x2:1,0", "default_chest.png^[sheet:2x2:1,1"}, paramtype2 = "facedir", groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2}, legacy_facedir_simple = true, diff --git a/games/minimal/mods/default/textures/default_chest.png b/games/minimal/mods/default/textures/default_chest.png new file mode 100644 index 000000000..9746a3fd9 Binary files /dev/null and b/games/minimal/mods/default/textures/default_chest.png differ diff --git a/games/minimal/mods/default/textures/default_chest_front.png b/games/minimal/mods/default/textures/default_chest_front.png deleted file mode 100644 index 55b076c35..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_front.png and /dev/null differ diff --git a/games/minimal/mods/default/textures/default_chest_lock.png b/games/minimal/mods/default/textures/default_chest_lock.png deleted file mode 100644 index 4b2d1af6c..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_lock.png and /dev/null differ diff --git a/games/minimal/mods/default/textures/default_chest_side.png b/games/minimal/mods/default/textures/default_chest_side.png deleted file mode 100644 index ae4847cb6..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_side.png and /dev/null differ diff --git a/games/minimal/mods/default/textures/default_chest_top.png b/games/minimal/mods/default/textures/default_chest_top.png deleted file mode 100644 index ac41551b0..000000000 Binary files a/games/minimal/mods/default/textures/default_chest_top.png and /dev/null differ diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 7f7535df6..4d2166342 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1827,6 +1827,49 @@ bool TextureSource::generateImagePart(std::string part_of_name, baseimg->setPixel(x, y, c); } } + /* + [sheet:WxH:X,Y + Retrieves a tile at position X,Y (in tiles) + from the base image it assumes to be a + tilesheet with dimensions W,H (in tiles). + */ + else if (part_of_name.substr(0,7) == "[sheet:") { + if (baseimg == NULL) { + errorstream << "generateImagePart(): baseimg != NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + Strfnd sf(part_of_name); + sf.next(":"); + u32 w0 = stoi(sf.next("x")); + u32 h0 = stoi(sf.next(":")); + u32 x0 = stoi(sf.next(",")); + u32 y0 = stoi(sf.next(":")); + + core::dimension2d img_dim = baseimg->getDimension(); + core::dimension2d tile_dim(v2u32(img_dim) / v2u32(w0, h0)); + + video::IImage *img = driver->createImage( + video::ECF_A8R8G8B8, tile_dim); + if (!img) { + errorstream << "generateImagePart(): Could not create image " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + img->fill(video::SColor(0,0,0,0)); + v2u32 vdim(tile_dim); + core::rect rect(v2s32(x0 * vdim.X, y0 * vdim.Y), tile_dim); + baseimg->copyToWithAlpha(img, v2s32(0), rect, + video::SColor(255,255,255,255), NULL); + + // Replace baseimg + baseimg->drop(); + baseimg = img; + } else { errorstream << "generateImagePart(): Invalid " -- cgit v1.2.3 From a07b032245bef76a7695e139a9daca7cb646a73d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 23 Dec 2016 14:43:56 +0100 Subject: Add 2D sheet animation for nodes --- doc/lua_api.txt | 21 ++++++++- games/minimal/mods/default/init.lua | 7 ++- .../textures/default_lava_source_animated.png | Bin 2902 -> 3145 bytes src/nodedef.cpp | 2 +- src/particles.cpp | 2 +- src/script/common/c_content.cpp | 25 +++++++--- src/tileanimation.cpp | 52 +++++++++++++++------ src/tileanimation.h | 6 +++ 8 files changed, 88 insertions(+), 27 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 648a29303..6166826af 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3702,7 +3702,26 @@ Definition tables * `image` (name) ### Tile animation definition -* `{type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}` + + { + type = "vertical_frames", + aspect_w = 16, + -- ^ specify width of a frame in pixels + aspect_h = 16, + -- ^ specify height of a frame in pixels + length = 3.0, + -- ^ specify full loop length + } + + { + type = "sheet_2d", + frames_w = 5, + -- ^ specify width in number of frames + frames_h = 3, + -- ^ specify height in number of frames + frame_length = 0.5, + -- ^ specify length of a single frame + } ### Node definition (`register_node`) diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index f532e7193..2f73b53ef 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1044,8 +1044,11 @@ minetest.register_node("default:lava_source", { inventory_image = minetest.inventorycube("default_lava.png"), drawtype = "liquid", --tiles ={"default_lava.png"}, - tiles ={ - {name="default_lava_source_animated.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=3.0}} + tiles = { + { + name = "default_lava_source_animated.png", + animation = {type="sheet_2d", frames_w=3, frames_h=2, frame_length=0.5} + } }, special_tiles = { -- New-style lava source material (mostly unused) diff --git a/games/minimal/mods/default/textures/default_lava_source_animated.png b/games/minimal/mods/default/textures/default_lava_source_animated.png index aa9d57cf1..54f4c0ddd 100644 Binary files a/games/minimal/mods/default/textures/default_lava_source_animated.png and b/games/minimal/mods/default/textures/default_lava_source_animated.png differ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 21bceb94d..92cdf738e 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -528,7 +528,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, tile->material_flags = 0; if (backface_culling) tile->material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if (tiledef->animation.type == TAT_VERTICAL_FRAMES) + if (tiledef->animation.type != TAT_NONE) tile->material_flags |= MATERIAL_FLAG_ANIMATION; if (tiledef->tileable_horizontal) tile->material_flags |= MATERIAL_FLAG_TILEABLE_HORIZONTAL; diff --git a/src/particles.cpp b/src/particles.cpp index e9ddba986..97f42e2c4 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -570,7 +570,7 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s video::ITexture *texture; // Only use first frame of animated texture - if(tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) + if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) texture = tiles[texid].frames[0].texture; else texture = tiles[texid].texture; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 6cd1d040b..b9bcfef69 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -39,6 +39,7 @@ struct EnumString es_TileAnimationType[] = { {TAT_NONE, "none"}, {TAT_VERTICAL_FRAMES, "vertical_frames"}, + {TAT_SHEET_2D, "sheet_2d"}, {0, NULL}, }; @@ -334,16 +335,26 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) // 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.vertical_frames.aspect_w = - getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.vertical_frames.aspect_h = - getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.vertical_frames.length = - getfloatfield_default(L, -1, "length", 1.0); + if (tiledef.animation.type == TAT_VERTICAL_FRAMES) { + // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} + tiledef.animation.vertical_frames.aspect_w = + getintfield_default(L, -1, "aspect_w", 16); + tiledef.animation.vertical_frames.aspect_h = + getintfield_default(L, -1, "aspect_h", 16); + tiledef.animation.vertical_frames.length = + getfloatfield_default(L, -1, "length", 1.0); + } else if (tiledef.animation.type == TAT_SHEET_2D) { + // {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5} + getintfield(L, -1, "frames_w", + tiledef.animation.sheet_2d.frames_w); + getintfield(L, -1, "frames_h", + tiledef.animation.sheet_2d.frames_h); + getfloatfield(L, -1, "frame_length", + tiledef.animation.sheet_2d.frame_length); + } } lua_pop(L, 1); } diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp index 891478c9f..a23eecc2e 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., void TileAnimationParams::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version < 29 /* TODO bump */) { + if (protocol_version < 29) { if (type == TAT_VERTICAL_FRAMES) { writeU8(os, type); writeU16(os, vertical_frames.aspect_w); @@ -41,47 +41,69 @@ void TileAnimationParams::serialize(std::ostream &os, u16 protocol_version) cons writeU16(os, vertical_frames.aspect_w); writeU16(os, vertical_frames.aspect_h); writeF1000(os, vertical_frames.length); + } else if (type == TAT_SHEET_2D) { + writeU8(os, sheet_2d.frames_w); + writeU8(os, sheet_2d.frames_h); + writeF1000(os, sheet_2d.frame_length); } } void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version) { type = (TileAnimationType) readU8(is); - if(protocol_version < 29 /* TODO bump */) { + if (protocol_version < 29) { vertical_frames.aspect_w = readU16(is); vertical_frames.aspect_h = readU16(is); vertical_frames.length = readF1000(is); return; } - if(type == TAT_VERTICAL_FRAMES) { + if (type == TAT_VERTICAL_FRAMES) { vertical_frames.aspect_w = readU16(is); vertical_frames.aspect_h = readU16(is); vertical_frames.length = readF1000(is); + } else if (type == TAT_SHEET_2D) { + sheet_2d.frames_w = readU8(is); + sheet_2d.frames_h = readU8(is); + sheet_2d.frame_length = readF1000(is); } } void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const { - if (type == TAT_NONE) { + if (type == TAT_VERTICAL_FRAMES) { + int frame_height = (float)texture_size.X / + (float)vertical_frames.aspect_w * + (float)vertical_frames.aspect_h; + int _frame_count = texture_size.Y / frame_height; + if (frame_count) + *frame_count = _frame_count; + if (frame_length_ms) + *frame_length_ms = 1000.0 * vertical_frames.length / _frame_count; + } else if (type == TAT_SHEET_2D) { + if (frame_count) + *frame_count = sheet_2d.frames_w * sheet_2d.frames_h; + if (frame_length_ms) + *frame_length_ms = 1000 * sheet_2d.frame_length; + } else { // TAT_NONE *frame_count = 1; *frame_length_ms = 1000; - return; } - int frame_height = (float)texture_size.X / - (float)vertical_frames.aspect_w * - (float)vertical_frames.aspect_h; - if (frame_count) - *frame_count = texture_size.Y / frame_height; - if (frame_length_ms) - *frame_length_ms = 1000.0 * vertical_frames.length / (texture_size.Y / frame_height); } void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const { if (type == TAT_NONE) return; - int frame_count; - determineParams(texture_size, &frame_count, NULL); - os << "^[verticalframe:" << frame_count << ":" << frame; + if (type == TAT_VERTICAL_FRAMES) { + int frame_count; + determineParams(texture_size, &frame_count, NULL); + os << "^[verticalframe:" << frame_count << ":" << frame; + } else if (type == TAT_SHEET_2D) { + int q, r; + q = frame / sheet_2d.frames_w; + r = frame % sheet_2d.frames_w; + os << "^[sheet:" << sheet_2d.frames_w << "x" << sheet_2d.frames_h + << ":" << r << "," << q; + } } diff --git a/src/tileanimation.h b/src/tileanimation.h index d5172ed50..289ce515b 100644 --- a/src/tileanimation.h +++ b/src/tileanimation.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., enum TileAnimationType { TAT_NONE = 0, TAT_VERTICAL_FRAMES = 1, + TAT_SHEET_2D = 2, }; struct TileAnimationParams { @@ -38,6 +39,11 @@ struct TileAnimationParams { int aspect_h; // height for aspect ratio float length; // seconds } vertical_frames; + struct { + int frames_w; // number of frames left-to-right + int frames_h; // number of frames top-to-bottom + float frame_length; // seconds + } sheet_2d; }; void serialize(std::ostream &os, u16 protocol_version) const; -- cgit v1.2.3 From 6718a95103b2a6c308da121a1340dfdcad2e069d Mon Sep 17 00:00:00 2001 From: Thomas--S Date: Mon, 2 Jan 2017 22:42:50 +0100 Subject: Fix display gamma documentation Overlooked in #4873 --- minetest.conf.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minetest.conf.example b/minetest.conf.example index 262f95115..bf70f5e51 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -505,7 +505,7 @@ # type: int min: 15 max: 160 # zoom_fov = 15 -# Adjust the gamma encoding for the light tables. Lower numbers are brighter. +# Adjust the gamma encoding for the light tables. Higher numbers are brighter. # This setting is for the client only and is ignored by the server. # type: float min: 1 max: 3 # display_gamma = 2.2 -- cgit v1.2.3 From 7387b190213996e453d0a7447027a71615034f5e Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sat, 31 Dec 2016 12:40:31 -0800 Subject: Pull occlusion check out of loop, and minor code cleanups. --- src/clientmap.cpp | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 27f9dea38..7d76e6e8b 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -213,6 +213,16 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) // Distance to farthest drawn block float farthest_drawn = 0; + // No occlusion culling when free_move is on and camera is + // inside ground + bool occlusion_culling_enabled = true; + if (g_settings->getBool("free_move")) { + MapNode n = getNodeNoEx(cam_pos_nodes); + if (n.getContent() == CONTENT_IGNORE || + nodemgr->get(n).solidness == 2) + occlusion_culling_enabled = false; + } + for (std::map::iterator si = m_sectors.begin(); si != m_sectors.end(); ++si) { MapSector *sector = si->second; @@ -254,39 +264,19 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) camera_direction, camera_fov, range, &d)) continue; - // This is ugly (spherical distance limit?) - /*if(m_control.range_all == false && - d - 0.5*BS*MAP_BLOCKSIZE > range) - continue;*/ - blocks_in_range++; /* Ignore if mesh doesn't exist */ - { - //MutexAutoLock lock(block->mesh_mutex); - - if (block->mesh == NULL) { - blocks_in_range_without_mesh++; - continue; - } + if (block->mesh == NULL) { + blocks_in_range_without_mesh++; + continue; } /* Occlusion culling */ - - // No occlusion culling when free_move is on and camera is - // inside ground - bool occlusion_culling_enabled = true; - if (g_settings->getBool("free_move")) { - MapNode n = getNodeNoEx(cam_pos_nodes); - if (n.getContent() == CONTENT_IGNORE || - nodemgr->get(n).solidness == 2) - occlusion_culling_enabled = false; - } - v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2); float step = BS * 1; @@ -447,11 +437,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) Get all blocks and draw all visible ones */ - v3s16 cam_pos_nodes = floatToInt(camera_position, BS); - v3s16 p_blocks_min; - v3s16 p_blocks_max; - getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); - u32 vertex_count = 0; u32 meshbuffer_count = 0; -- cgit v1.2.3 From 8aadc62856cc3789ed345ddf3870e311af60afe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Wed, 4 Jan 2017 14:36:51 +0100 Subject: Travis: Build server too for UNIX --- util/travis/script.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/util/travis/script.sh b/util/travis/script.sh index 1bafb26cd..24a74d186 100755 --- a/util/travis/script.sh +++ b/util/travis/script.sh @@ -21,6 +21,7 @@ if [[ $PLATFORM == "Unix" ]]; then cmake -DCMAKE_BUILD_TYPE=Debug \ -DRUN_IN_PLACE=TRUE \ -DENABLE_GETTEXT=TRUE \ + -DBUILD_SERVER=TRUE \ $CMAKE_FLAGS .. make -j2 echo "Running unit tests." -- cgit v1.2.3 From 3f8261830e0503cd59d8713d5c9aab12fc1491db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Wed, 4 Jan 2017 19:18:40 +0100 Subject: Improve getPointedThing() (#4346) * Improved getPointedThing() The new algorithm checks every node exactly once. Now the point and normal vector of the collision is also returned in the PointedThing (currently they are not used outside of the function). Now the CNodeDefManager keeps the union of all possible nodeboxes, so the raycast won't miss any nodes. Also if there are only small nodeboxes, getPointedThing() is exceptionally fast. Also adds unit test for VoxelLineIterator. * Cleanup, code move This commit moves getPointedThing() and Client::getSelectedActiveObject() to ClientEnvironment. The map nodes now can decide which neighbors they are connecting to (MapNode::getNeighbors()). --- src/CMakeLists.txt | 1 + src/client.cpp | 39 +--- src/client.h | 8 - src/clientmap.cpp | 33 ++- src/environment.cpp | 241 +++++++++++++++++++++ src/environment.h | 36 ++++ src/game.cpp | 392 ++++++++++------------------------ src/map.cpp | 91 ++++---- src/map.h | 5 + src/mapnode.cpp | 46 ++++ src/mapnode.h | 8 + src/nodedef.cpp | 141 ++++++++++++ src/nodedef.h | 6 + src/raycast.cpp | 89 ++++++++ src/raycast.h | 38 ++++ src/unittest/test_voxelalgorithms.cpp | 59 +++++ src/util/pointedthing.cpp | 75 +++---- src/util/pointedthing.h | 40 ++++ src/voxelalgorithms.cpp | 68 ++++++ src/voxelalgorithms.h | 61 ++++++ 20 files changed, 1045 insertions(+), 432 deletions(-) create mode 100644 src/raycast.cpp create mode 100644 src/raycast.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51cf88063..507624753 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -447,6 +447,7 @@ set(common_SRCS quicktune.cpp reflowscan.cpp remoteplayer.cpp + raycast.cpp rollback.cpp rollback_interface.cpp serialization.cpp diff --git a/src/client.cpp b/src/client.cpp index 1446ebad8..693a90604 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -53,6 +53,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database-sqlite3.h" #include "serialization.h" #include "guiscalingfilter.h" +#include "raycast.h" extern gui::IGUIEnvironment* guienv; @@ -1500,44 +1501,6 @@ void Client::inventoryAction(InventoryAction *a) delete a; } -ClientActiveObject * Client::getSelectedActiveObject( - f32 max_d, - v3f from_pos_f_on_map, - core::line3d shootline_on_map - ) -{ - std::vector objects; - - m_env.getActiveObjects(from_pos_f_on_map, max_d, objects); - - // Sort them. - // After this, the closest object is the first in the array. - std::sort(objects.begin(), objects.end()); - - for(unsigned int i=0; igetSelectionBox(); - if(selection_box == NULL) - continue; - - v3f pos = obj->getPosition(); - - aabb3f offsetted_box( - selection_box->MinEdge + pos, - selection_box->MaxEdge + pos - ); - - if(offsetted_box.intersectsWithLine(shootline_on_map)) - { - return obj; - } - } - - return NULL; -} - float Client::getAnimationTime() { return m_animation_time; diff --git a/src/client.h b/src/client.h index 9f5bda059..891fe62f8 100644 --- a/src/client.h +++ b/src/client.h @@ -445,14 +445,6 @@ public: Inventory* getInventory(const InventoryLocation &loc); void inventoryAction(InventoryAction *a); - // Gets closest object pointed by the shootline - // Returns NULL if not found - ClientActiveObject * getSelectedActiveObject( - f32 max_d, - v3f from_pos_f_on_map, - core::line3d shootline_on_map - ); - const std::list &getConnectedPlayerNames() { return m_env.getPlayerNames(); diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 7d76e6e8b..542eb03e8 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -172,8 +172,6 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) ScopeProfiler sp(g_profiler, "CM::updateDrawList()", SPT_AVG); g_profiler->add("CM::updateDrawList() count", 1); - INodeDefManager *nodemgr = m_gamedef->ndef(); - for (std::map::iterator i = m_drawlist.begin(); i != m_drawlist.end(); ++i) { MapBlock *block = i->second; @@ -219,7 +217,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) if (g_settings->getBool("free_move")) { MapNode n = getNodeNoEx(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || - nodemgr->get(n).solidness == 2) + m_nodedef->get(n).solidness == 2) occlusion_culling_enabled = false; } @@ -297,23 +295,23 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) if (occlusion_culling_enabled && // For the central point of the mapblock 'endoff' can be halved isOccluded(this, spn, cpn, - step, stepfac, startoff, endoff / 2.0f, needed_count, nodemgr) && + step, stepfac, startoff, endoff / 2.0f, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr) && + step, stepfac, startoff, endoff, needed_count, m_nodedef) && isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, nodemgr)) { + step, stepfac, startoff, endoff, needed_count, m_nodedef)) { blocks_occlusion_culled++; continue; } @@ -656,7 +654,6 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int oldvalue, bool *sunlight_seen_result) { const bool debugprint = false; - INodeDefManager *ndef = m_gamedef->ndef(); static v3f z_directions[50] = { v3f(-100, 0, 0) }; @@ -694,7 +691,7 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, float off = step * z_offsets[i]; bool sunlight_seen_now = false; bool ok = getVisibleBrightness(this, m_camera_position, dir, - step, 1.0, max_d*0.6+off, max_d, ndef, daylight_factor, + step, 1.0, max_d*0.6+off, max_d, m_nodedef, daylight_factor, sunlight_min_d, &br, &sunlight_seen_now); if(sunlight_seen_now) @@ -734,8 +731,8 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, int ret = 0; if(brightness_count == 0){ MapNode n = getNodeNoEx(floatToInt(m_camera_position, BS)); - if(ndef->get(n).param_type == CPT_LIGHT){ - ret = decode_light(n.getLightBlend(daylight_factor, ndef)); + if(m_nodedef->get(n).param_type == CPT_LIGHT){ + ret = decode_light(n.getLightBlend(daylight_factor, m_nodedef)); } else { ret = oldvalue; } @@ -758,8 +755,6 @@ int ClientMap::getBackgroundBrightness(float max_d, u32 daylight_factor, void ClientMap::renderPostFx(CameraMode cam_mode) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - // Sadly ISceneManager has no "post effects" render pass, in that case we // could just register for that and handle it in renderMap(). @@ -768,7 +763,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) // - If the player is in a solid node, make everything black. // - If the player is in liquid, draw a semi-transparent overlay. // - Do not if player is in third person mode - const ContentFeatures& features = nodemgr->get(n); + const ContentFeatures& features = m_nodedef->get(n); video::SColor post_effect_color = features.post_effect_color; if(features.solidness == 2 && !(g_settings->getBool("noclip") && m_gamedef->checkLocalPrivilege("noclip")) && diff --git a/src/environment.cpp b/src/environment.cpp index ac9b5b079..8c0daf87b 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -43,8 +43,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "daynightratio.h" #include "map.h" #include "emerge.h" +#include "raycast.h" +#include "voxelalgorithms.h" #include "util/serialize.h" #include "util/basic_macros.h" +#include "util/pointedthing.h" #include "threading/mutex_auto_lock.h" #define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" @@ -2848,4 +2851,242 @@ ClientEnvEvent ClientEnvironment::getClientEvent() return event; } +ClientActiveObject * ClientEnvironment::getSelectedActiveObject( + const core::line3d &shootline_on_map, v3f *intersection_point, + v3s16 *intersection_normal) +{ + std::vector objects; + getActiveObjects(shootline_on_map.start, + shootline_on_map.getLength() + 3, objects); + const v3f line_vector = shootline_on_map.getVector(); + + // Sort them. + // After this, the closest object is the first in the array. + std::sort(objects.begin(), objects.end()); + + /* Because objects can have different nodebox sizes, + * the object whose center is the nearest isn't necessarily + * the closest one. If an object is found, don't stop + * immediately. */ + + f32 d_min = shootline_on_map.getLength(); + ClientActiveObject *nearest_obj = NULL; + for (u32 i = 0; i < objects.size(); i++) { + ClientActiveObject *obj = objects[i].obj; + + aabb3f *selection_box = obj->getSelectionBox(); + if (selection_box == NULL) + continue; + + v3f pos = obj->getPosition(); + + aabb3f offsetted_box(selection_box->MinEdge + pos, + selection_box->MaxEdge + pos); + + if (offsetted_box.getCenter().getDistanceFrom( + shootline_on_map.start) > d_min + 9.6f*BS) { + // Probably there is no active object that has bigger nodebox than + // (-5.5,-5.5,-5.5,5.5,5.5,5.5) + // 9.6 > 5.5*sqrt(3) + break; + } + + v3f current_intersection; + v3s16 current_normal; + if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, + ¤t_intersection, ¤t_normal)) { + f32 d_current = current_intersection.getDistanceFrom( + shootline_on_map.start); + if (d_current <= d_min) { + d_min = d_current; + nearest_obj = obj; + *intersection_point = current_intersection; + *intersection_normal = current_normal; + } + } + } + + return nearest_obj; +} + +/* + Check if a node is pointable +*/ +static inline bool isPointableNode(const MapNode &n, + INodeDefManager *ndef, bool liquids_pointable) +{ + const ContentFeatures &features = ndef->get(n); + return features.pointable || + (liquids_pointable && features.isLiquid()); +} + +PointedThing ClientEnvironment::getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object) +{ + PointedThing result; + + INodeDefManager *nodedef = m_map->getNodeDefManager(); + + core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); + // The code needs to search these nodes + core::aabbox3d search_range(-maximal_exceed.MaxEdge, + -maximal_exceed.MinEdge); + // If a node is found, there might be a larger node behind. + // To find it, we have to go further. + s16 maximal_overcheck = + std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) + + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) + + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); + + const v3f original_vector = shootline.getVector(); + const f32 original_length = original_vector.getLength(); + + f32 min_distance = original_length; + + // First try to find an active object + if (look_for_object) { + ClientActiveObject *selected_object = getSelectedActiveObject( + shootline, &result.intersection_point, + &result.intersection_normal); + + if (selected_object != NULL) { + min_distance = + (result.intersection_point - shootline.start).getLength(); + + result.type = POINTEDTHING_OBJECT; + result.object_id = selected_object->getId(); + } + } + + // Reduce shootline + if (original_length > 0) { + shootline.end = shootline.start + + shootline.getVector() / original_length * min_distance; + } + + // Try to find a node that is closer than the selected active + // object (if it exists). + + voxalgo::VoxelLineIterator iterator(shootline.start / BS, + shootline.getVector() / BS); + v3s16 oldnode = iterator.m_current_node_pos; + // Indicates that a node was found. + bool is_node_found = false; + // If a node is found, it is possible that there's a node + // behind it with a large nodebox, so continue the search. + u16 node_foundcounter = 0; + // If a node is found, this is the center of the + // first nodebox the shootline meets. + v3f found_boxcenter(0, 0, 0); + // The untested nodes are in this range. + core::aabbox3d new_nodes; + while (true) { + // Test the nodes around the current node in search_range. + new_nodes = search_range; + new_nodes.MinEdge += iterator.m_current_node_pos; + new_nodes.MaxEdge += iterator.m_current_node_pos; + + // Only check new nodes + v3s16 delta = iterator.m_current_node_pos - oldnode; + if (delta.X > 0) + new_nodes.MinEdge.X = new_nodes.MaxEdge.X; + else if (delta.X < 0) + new_nodes.MaxEdge.X = new_nodes.MinEdge.X; + else if (delta.Y > 0) + new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; + else if (delta.Y < 0) + new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; + else if (delta.Z > 0) + new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; + else if (delta.Z < 0) + new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; + + // For each untested node + for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { + for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { + for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { + MapNode n; + v3s16 np(x, y, z); + bool is_valid_position; + + n = m_map->getNodeNoEx(np, &is_valid_position); + if (!(is_valid_position && + isPointableNode(n, nodedef, liquids_pointable))) { + continue; + } + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(np, m_map)); + + v3f npf = intToFloat(np, BS); + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += npf; + box.MaxEdge += npf; + v3f intersection_point; + v3s16 intersection_normal; + if (!boxLineCollision(box, shootline.start, shootline.getVector(), + &intersection_point, &intersection_normal)) { + continue; + } + f32 distance = (intersection_point - shootline.start).getLength(); + if (distance >= min_distance) { + continue; + } + result.type = POINTEDTHING_NODE; + result.node_undersurface = np; + result.intersection_point = intersection_point; + result.intersection_normal = intersection_normal; + found_boxcenter = box.getCenter(); + min_distance = distance; + is_node_found = true; + } + } + } + } + if (is_node_found) { + node_foundcounter++; + if (node_foundcounter > maximal_overcheck) { + break; + } + } + // Next node + if (iterator.hasNext()) { + oldnode = iterator.m_current_node_pos; + iterator.next(); + } else { + break; + } + } + + if (is_node_found) { + // Set undersurface and abovesurface nodes + f32 d = 0.002 * BS; + v3f fake_intersection = result.intersection_point; + // Move intersection towards its source block. + if (fake_intersection.X < found_boxcenter.X) + fake_intersection.X += d; + else + fake_intersection.X -= d; + + if (fake_intersection.Y < found_boxcenter.Y) + fake_intersection.Y += d; + else + fake_intersection.Y -= d; + + if (fake_intersection.Z < found_boxcenter.Z) + fake_intersection.Z += d; + else + fake_intersection.Z -= d; + + result.node_real_undersurface = floatToInt(fake_intersection, BS); + result.node_abovesurface = result.node_real_undersurface + + result.intersection_normal; + } + return result; +} + #endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index 4bee40e57..84805b462 100644 --- a/src/environment.h +++ b/src/environment.h @@ -55,6 +55,7 @@ class GameScripting; class Player; class RemotePlayer; class PlayerSAO; +class PointedThing; class Environment { @@ -627,6 +628,41 @@ public: // Get event from queue. CEE_NONE is returned if queue is empty. ClientEnvEvent getClientEvent(); + /*! + * Gets closest object pointed by the shootline. + * Returns NULL if not found. + * + * \param[in] shootline_on_map the shootline for + * the test in world coordinates + * \param[out] intersection_point the first point where + * the shootline meets the object. Valid only if + * not NULL is returned. + * \param[out] intersection_normal the normal vector of + * the intersection, pointing outwards. Zero vector if + * the shootline starts in an active object. + * Valid only if not NULL is returned. + */ + ClientActiveObject * getSelectedActiveObject( + const core::line3d &shootline_on_map, + v3f *intersection_point, + v3s16 *intersection_normal + ); + + /*! + * Performs a raycast on the world. + * Returns the first thing the shootline meets. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + */ + PointedThing getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object); + u16 attachement_parent_ids[USHRT_MAX + 1]; const std::list &getPlayerNames() { return m_player_names; } diff --git a/src/game.cpp b/src/game.cpp index 5bdbea617..cfa6234ff 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -265,271 +265,6 @@ public: Client *m_client; }; -/* - Check if a node is pointable -*/ -inline bool isPointableNode(const MapNode &n, - Client *client, bool liquids_pointable) -{ - const ContentFeatures &features = client->getNodeDefManager()->get(n); - return features.pointable || - (liquids_pointable && features.isLiquid()); -} - -static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, - ClientMap *map, MapNode n, u8 bitmask, u8 *neighbors) -{ - MapNode n2 = map->getNodeNoEx(p); - if (nodedef->nodeboxConnects(n, n2, bitmask)) - *neighbors |= bitmask; -} - -static inline u8 getNeighbors(v3s16 p, INodeDefManager *nodedef, ClientMap *map, MapNode n) -{ - u8 neighbors = 0; - const ContentFeatures &f = nodedef->get(n); - // locate possible neighboring nodes to connect to - if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) { - v3s16 p2 = p; - - p2.Y++; - getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors); - - p2 = p; - p2.Y--; - getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors); - - p2 = p; - p2.Z--; - getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors); - - p2 = p; - p2.X--; - getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors); - - p2 = p; - p2.Z++; - getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors); - - p2 = p; - p2.X++; - getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors); - } - - return neighbors; -} - -/* - Find what the player is pointing at -*/ -PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_position, - const v3f &camera_direction, const v3f &camera_position, - core::line3d shootline, f32 d, bool liquids_pointable, - bool look_for_object, const v3s16 &camera_offset, - ClientActiveObject *&selected_object) -{ - PointedThing result; - - std::vector *selectionboxes = hud->getSelectionBoxes(); - selectionboxes->clear(); - static const bool show_entity_selectionbox = g_settings->getBool("show_entity_selectionbox"); - - selected_object = NULL; - - INodeDefManager *nodedef = client->getNodeDefManager(); - ClientMap &map = client->getEnv().getClientMap(); - - f32 min_distance = BS * 1001; - - // First try to find a pointed at active object - if (look_for_object) { - selected_object = client->getSelectedActiveObject(d * BS, - camera_position, shootline); - - if (selected_object != NULL) { - if (show_entity_selectionbox && - selected_object->doShowSelectionBox()) { - aabb3f *selection_box = selected_object->getSelectionBox(); - // Box should exist because object was - // returned in the first place - assert(selection_box); - - v3f pos = selected_object->getPosition(); - selectionboxes->push_back(aabb3f( - selection_box->MinEdge, selection_box->MaxEdge)); - hud->setSelectionPos(pos, camera_offset); - } - - min_distance = (selected_object->getPosition() - camera_position).getLength(); - - hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0)); - result.type = POINTEDTHING_OBJECT; - result.object_id = selected_object->getId(); - } - } - - // That didn't work, try to find a pointed at node - - v3s16 pos_i = floatToInt(player_position, BS); - - /*infostream<<"pos_i=("< 0 ? a : 1); - s16 zend = pos_i.Z + (camera_direction.Z > 0 ? a : 1); - s16 xend = pos_i.X + (camera_direction.X > 0 ? a : 1); - - // Prevent signed number overflow - if (yend == 32767) - yend = 32766; - - if (zend == 32767) - zend = 32766; - - if (xend == 32767) - xend = 32766; - - v3s16 pointed_pos(0, 0, 0); - - for (s16 y = ystart; y <= yend; y++) { - for (s16 z = zstart; z <= zend; z++) { - for (s16 x = xstart; x <= xend; x++) { - MapNode n; - bool is_valid_position; - v3s16 p(x, y, z); - - n = map.getNodeNoEx(p, &is_valid_position); - if (!is_valid_position) { - continue; - } - if (!isPointableNode(n, client, liquids_pointable)) { - continue; - } - - std::vector boxes; - n.getSelectionBoxes(nodedef, &boxes, getNeighbors(p, nodedef, &map, n)); - - v3s16 np(x, y, z); - v3f npf = intToFloat(np, BS); - for (std::vector::const_iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += npf; - box.MaxEdge += npf; - - v3f centerpoint = box.getCenter(); - f32 distance = (centerpoint - camera_position).getLength(); - if (distance >= min_distance) { - continue; - } - if (!box.intersectsWithLine(shootline)) { - continue; - } - result.type = POINTEDTHING_NODE; - min_distance = distance; - pointed_pos = np; - } - } - } - } - - if (result.type == POINTEDTHING_NODE) { - f32 d = 0.001 * BS; - MapNode n = map.getNodeNoEx(pointed_pos); - v3f npf = intToFloat(pointed_pos, BS); - std::vector boxes; - n.getSelectionBoxes(nodedef, &boxes, getNeighbors(pointed_pos, nodedef, &map, n)); - f32 face_min_distance = 1000 * BS; - for (std::vector::const_iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += npf; - box.MaxEdge += npf; - for (u16 j = 0; j < 6; j++) { - v3s16 facedir = g_6dirs[j]; - aabb3f facebox = box; - if (facedir.X > 0) { - facebox.MinEdge.X = facebox.MaxEdge.X - d; - } else if (facedir.X < 0) { - facebox.MaxEdge.X = facebox.MinEdge.X + d; - } else if (facedir.Y > 0) { - facebox.MinEdge.Y = facebox.MaxEdge.Y - d; - } else if (facedir.Y < 0) { - facebox.MaxEdge.Y = facebox.MinEdge.Y + d; - } else if (facedir.Z > 0) { - facebox.MinEdge.Z = facebox.MaxEdge.Z - d; - } else if (facedir.Z < 0) { - facebox.MaxEdge.Z = facebox.MinEdge.Z + d; - } - v3f centerpoint = facebox.getCenter(); - f32 distance = (centerpoint - camera_position).getLength(); - if (distance >= face_min_distance) - continue; - if (!facebox.intersectsWithLine(shootline)) - continue; - result.node_abovesurface = pointed_pos + facedir; - hud->setSelectedFaceNormal(v3f(facedir.X, facedir.Y, facedir.Z)); - face_min_distance = distance; - } - } - selectionboxes->clear(); - for (std::vector::const_iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += v3f(-d, -d, -d); - box.MaxEdge += v3f(d, d, d); - selectionboxes->push_back(box); - } - hud->setSelectionPos(intToFloat(pointed_pos, BS), camera_offset); - result.node_undersurface = pointed_pos; - } - - // Update selection mesh light level and vertex colors - if (selectionboxes->size() > 0) { - v3f pf = hud->getSelectionPos(); - v3s16 p = floatToInt(pf, BS); - - // Get selection mesh light level - MapNode n = map.getNodeNoEx(p); - u16 node_light = getInteriorLight(n, -1, nodedef); - u16 light_level = node_light; - - for (u8 i = 0; i < 6; i++) { - n = map.getNodeNoEx(p + g_6dirs[i]); - node_light = getInteriorLight(n, -1, nodedef); - if (node_light > light_level) - light_level = node_light; - } - - video::SColor c = MapBlock_LightColor(255, light_level, 0); - u8 day = c.getRed(); - u8 night = c.getGreen(); - u32 daynight_ratio = client->getEnv().getDayNightRatio(); - finalColorBlend(c, day, night, daynight_ratio); - - // Modify final color a bit with time - u32 timer = porting::getTimeMs() % 5000; - float timerf = (float)(irr::core::PI * ((timer / 2500.0) - 0.5)); - float sin_r = 0.08 * sin(timerf); - float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5); - float sin_b = 0.08 * sin(timerf + irr::core::PI); - c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255)); - c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255)); - c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255)); - - // Set mesh final color - hud->setSelectionMeshColor(c); - } - return result; -} - /* Profiler display */ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe, @@ -1668,6 +1403,23 @@ protected: void updateSound(f32 dtime); void processPlayerInteraction(GameRunData *runData, f32 dtime, bool show_hud, bool show_debug); + /*! + * Returns the object or node the player is pointing at. + * Also updates the selected thing in the Hud. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + * @param[in] camera_offset offset of the camera + * @param[out] selected_object the selected object or + * NULL if not found + */ + PointedThing updatePointedThing( + const core::line3d &shootline, bool liquids_pointable, + bool look_for_object, const v3s16 &camera_offset, + ClientActiveObject *&selected_object); void handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem); void handlePointingAtNode(GameRunData *runData, const PointedThing &pointed, const ItemDefinition &playeritem_def, @@ -3823,14 +3575,11 @@ void Game::processPlayerInteraction(GameRunData *runData, core::line3d shootline; if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d(camera_position, - camera_position + camera_direction * BS * (d + 1)); - + camera_position + camera_direction * BS * d); } else { // prevent player pointing anything in front-view - if (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) - shootline = core::line3d(0, 0, 0, 0, 0, 0); + shootline = core::line3d(camera_position,camera_position); } #ifdef HAVE_TOUCHSCREENGUI @@ -3843,10 +3592,7 @@ void Game::processPlayerInteraction(GameRunData *runData, #endif - PointedThing pointed = getPointedThing( - // input - client, hud, player_position, camera_direction, - camera_position, shootline, d, + PointedThing pointed = updatePointedThing(shootline, playeritem_def.liquids_pointable, !runData->ldown_for_dig, camera_offset, @@ -3940,6 +3686,104 @@ void Game::processPlayerInteraction(GameRunData *runData, } +PointedThing Game::updatePointedThing( + const core::line3d &shootline, + bool liquids_pointable, + bool look_for_object, + const v3s16 &camera_offset, + ClientActiveObject *&selected_object) +{ + std::vector *selectionboxes = hud->getSelectionBoxes(); + selectionboxes->clear(); + static const bool show_entity_selectionbox = g_settings->getBool( + "show_entity_selectionbox"); + + ClientMap &map = client->getEnv().getClientMap(); + INodeDefManager *nodedef=client->getNodeDefManager(); + + selected_object = NULL; + + PointedThing result=client->getEnv().getPointedThing( + shootline, liquids_pointable, look_for_object); + if (result.type == POINTEDTHING_OBJECT) { + selected_object = client->getEnv().getActiveObject(result.object_id); + if (show_entity_selectionbox && selected_object->doShowSelectionBox()) { + aabb3f *selection_box = selected_object->getSelectionBox(); + + // Box should exist because object was + // returned in the first place + + assert(selection_box); + + v3f pos = selected_object->getPosition(); + selectionboxes->push_back(aabb3f( + selection_box->MinEdge, selection_box->MaxEdge)); + selectionboxes->push_back( + aabb3f(selection_box->MinEdge, selection_box->MaxEdge)); + hud->setSelectionPos(pos, camera_offset); + } + } else if (result.type == POINTEDTHING_NODE) { + // Update selection boxes + MapNode n = map.getNodeNoEx(result.node_undersurface); + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(result.node_undersurface, &map)); + + f32 d = 0.002 * BS; + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge -= v3f(d, d, d); + box.MaxEdge += v3f(d, d, d); + selectionboxes->push_back(box); + } + hud->setSelectionPos(intToFloat(result.node_undersurface, BS), + camera_offset); + } + + // Update selection mesh light level and vertex colors + if (selectionboxes->size() > 0) { + v3f pf = hud->getSelectionPos(); + v3s16 p = floatToInt(pf, BS); + + // Get selection mesh light level + MapNode n = map.getNodeNoEx(p); + u16 node_light = getInteriorLight(n, -1, nodedef); + u16 light_level = node_light; + + for (u8 i = 0; i < 6; i++) { + n = map.getNodeNoEx(p + g_6dirs[i]); + node_light = getInteriorLight(n, -1, nodedef); + if (node_light > light_level) + light_level = node_light; + } + + video::SColor c = MapBlock_LightColor(255, light_level, 0); + u8 day = c.getRed(); + u8 night = c.getGreen(); + u32 daynight_ratio = client->getEnv().getDayNightRatio(); + finalColorBlend(c, day, night, daynight_ratio); + + // Modify final color a bit with time + u32 timer = porting::getTimeMs() % 5000; + float timerf = (float) (irr::core::PI * ((timer / 2500.0) - 0.5)); + float sin_r = 0.08 * sin(timerf); + float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5); + float sin_b = 0.08 * sin(timerf + irr::core::PI); + c.setRed( + core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255)); + c.setGreen( + core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255)); + c.setBlue( + core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255)); + + // Set mesh final color + hud->setSelectionMeshColor(c); + } + return result; +} + + void Game::handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem) { infostream << "Right Clicked in Air" << std::endl; diff --git a/src/map.cpp b/src/map.cpp index c3b62ded0..53657ce6b 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -66,6 +66,7 @@ Map::Map(std::ostream &dout, IGameDef *gamedef): m_dout(dout), m_gamedef(gamedef), m_sector_cache(NULL), + m_nodedef(gamedef->ndef()), m_transforming_liquid_loop_count_multiplier(1.0f), m_unprocessed_count(0), m_inc_trending_up_start_time(0), @@ -227,7 +228,7 @@ void Map::setNode(v3s16 p, MapNode & n) bool temp_bool; errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE" <<" while trying to replace \"" - <ndef()->get(block->getNodeNoCheck(relpos, &temp_bool)).name + <get(block->getNodeNoCheck(relpos, &temp_bool)).name <<"\" at "< & light_sources, std::map & modified_blocks) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top @@ -353,20 +352,20 @@ void Map::unspreadLight(enum LightBank bank, If the neighbor is dimmer than what was specified as oldlight (the light of the previous node) */ - if(n2.getLight(bank, nodemgr) < oldlight) + if(n2.getLight(bank, m_nodedef) < oldlight) { /* And the neighbor is transparent and it has some light */ - if(nodemgr->get(n2).light_propagates - && n2.getLight(bank, nodemgr) != 0) + if(m_nodedef->get(n2).light_propagates + && n2.getLight(bank, m_nodedef) != 0) { /* Set light to 0 and add to queue */ - u8 current_light = n2.getLight(bank, nodemgr); - n2.setLight(bank, 0, nodemgr); + u8 current_light = n2.getLight(bank, m_nodedef); + n2.setLight(bank, 0, m_nodedef); block->setNode(relpos, n2); unlighted_nodes[n2pos] = current_light; @@ -421,8 +420,6 @@ void Map::spreadLight(enum LightBank bank, std::set & from_nodes, std::map & modified_blocks) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - const v3s16 dirs[6] = { v3s16(0,0,1), // back v3s16(0,1,0), // top @@ -476,7 +473,7 @@ void Map::spreadLight(enum LightBank bank, bool is_valid_position; MapNode n = block->getNode(relpos, &is_valid_position); - u8 oldlight = is_valid_position ? n.getLight(bank, nodemgr) : 0; + u8 oldlight = is_valid_position ? n.getLight(bank, m_nodedef) : 0; u8 newlight = diminish_light(oldlight); // Loop through 6 neighbors @@ -512,7 +509,7 @@ void Map::spreadLight(enum LightBank bank, If the neighbor is brighter than the current node, add to list (it will light up this node on its turn) */ - if(n2.getLight(bank, nodemgr) > undiminish_light(oldlight)) + if(n2.getLight(bank, m_nodedef) > undiminish_light(oldlight)) { lighted_nodes.insert(n2pos); changed = true; @@ -521,11 +518,11 @@ void Map::spreadLight(enum LightBank bank, If the neighbor is dimmer than how much light this node would spread on it, add to list */ - if(n2.getLight(bank, nodemgr) < newlight) + if(n2.getLight(bank, m_nodedef) < newlight) { - if(nodemgr->get(n2).light_propagates) + if(m_nodedef->get(n2).light_propagates) { - n2.setLight(bank, newlight, nodemgr); + n2.setLight(bank, newlight, m_nodedef); block->setNode(relpos, n2); lighted_nodes.insert(n2pos); changed = true; @@ -558,8 +555,6 @@ void Map::updateLighting(enum LightBank bank, std::map & a_blocks, std::map & modified_blocks) { - INodeDefManager *nodemgr = m_gamedef->ndef(); - /*m_dout<<"Map::updateLighting(): " <setNode(p, n); // If node sources light, add to list - u8 source = nodemgr->get(n).light_source; + u8 source = m_nodedef->get(n).light_source; if(source != 0) light_sources.insert(p + posnodes); @@ -810,8 +805,6 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, std::map &modified_blocks, bool remove_metadata) { - INodeDefManager *ndef = m_gamedef->ndef(); - // Collect old node for rollback RollbackNode rollback_oldnode(this, p, m_gamedef); @@ -825,14 +818,14 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, // Set the node on the map // Ignore light (because calling voxalgo::update_lighting_nodes) - n.setLight(LIGHTBANK_DAY, 0, ndef); - n.setLight(LIGHTBANK_NIGHT, 0, ndef); + n.setLight(LIGHTBANK_DAY, 0, m_nodedef); + n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef); setNode(p, n); // Update lighting std::vector > oldnodes; oldnodes.push_back(std::pair(p, oldnode)); - voxalgo::update_lighting_nodes(this, ndef, oldnodes, modified_blocks); + voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks); for(std::map::iterator i = modified_blocks.begin(); @@ -869,11 +862,10 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, bool is_valid_position; MapNode n2 = getNodeNoEx(p2, &is_valid_position); - if(is_valid_position - && (ndef->get(n2).isLiquid() || n2.getContent() == CONTENT_AIR)) - { + if(is_valid_position && + (m_nodedef->get(n2).isLiquid() || + n2.getContent() == CONTENT_AIR)) m_transforming_liquid.push_back(p2); - } } } @@ -1213,9 +1205,6 @@ s32 Map::transforming_liquid_size() { void Map::transformLiquids(std::map &modified_blocks) { - - INodeDefManager *nodemgr = m_gamedef->ndef(); - DSTACK(FUNCTION_NAME); //TimeTaker timer("transformLiquids()"); @@ -1275,12 +1264,12 @@ void Map::transformLiquids(std::map &modified_blocks) // The node which will be placed there if liquid // can't flow into this node. content_t floodable_node = CONTENT_AIR; - const ContentFeatures &cf = nodemgr->get(n0); + const ContentFeatures &cf = m_nodedef->get(n0); LiquidType liquid_type = cf.liquid_type; switch (liquid_type) { case LIQUID_SOURCE: liquid_level = LIQUID_LEVEL_SOURCE; - liquid_kind = nodemgr->getId(cf.liquid_alternative_flowing); + liquid_kind = m_nodedef->getId(cf.liquid_alternative_flowing); break; case LIQUID_FLOWING: liquid_level = (n0.param2 & LIQUID_LEVEL_MASK); @@ -1322,8 +1311,8 @@ void Map::transformLiquids(std::map &modified_blocks) } v3s16 npos = p0 + dirs[i]; NodeNeighbor nb(getNodeNoEx(npos), nt, npos); - const ContentFeatures &cfnb = nodemgr->get(nb.n); - switch (nodemgr->get(nb.n.getContent()).liquid_type) { + const ContentFeatures &cfnb = m_nodedef->get(nb.n); + switch (m_nodedef->get(nb.n.getContent()).liquid_type) { case LIQUID_NONE: if (cfnb.floodable) { airs[num_airs++] = nb; @@ -1351,8 +1340,8 @@ void Map::transformLiquids(std::map &modified_blocks) case LIQUID_SOURCE: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing); - if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); + if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { neutrals[num_neutrals++] = nb; } else { // Do not count bottom source, it will screw things up @@ -1363,8 +1352,8 @@ void Map::transformLiquids(std::map &modified_blocks) case LIQUID_FLOWING: // if this node is not (yet) of a liquid type, choose the first liquid type we encounter if (liquid_kind == CONTENT_AIR) - liquid_kind = nodemgr->getId(cfnb.liquid_alternative_flowing); - if (nodemgr->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { + liquid_kind = m_nodedef->getId(cfnb.liquid_alternative_flowing); + if (m_nodedef->getId(cfnb.liquid_alternative_flowing) != liquid_kind) { neutrals[num_neutrals++] = nb; } else { flows[num_flows++] = nb; @@ -1382,15 +1371,15 @@ void Map::transformLiquids(std::map &modified_blocks) s8 new_node_level = -1; s8 max_node_level = -1; - u8 range = nodemgr->get(liquid_kind).liquid_range; + u8 range = m_nodedef->get(liquid_kind).liquid_range; if (range > LIQUID_LEVEL_MAX + 1) range = LIQUID_LEVEL_MAX + 1; - if ((num_sources >= 2 && nodemgr->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) { + if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) { // liquid_kind will be set to either the flowing alternative of the node (if it's a liquid) // or the flowing alternative of the first of the surrounding sources (if it's air), so // it's perfectly safe to use liquid_kind here to determine the new node content. - new_node_content = nodemgr->getId(nodemgr->get(liquid_kind).liquid_alternative_source); + new_node_content = m_nodedef->getId(m_nodedef->get(liquid_kind).liquid_alternative_source); } else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) { // liquid_kind is set properly, see above max_node_level = new_node_level = LIQUID_LEVEL_MAX; @@ -1427,7 +1416,7 @@ void Map::transformLiquids(std::map &modified_blocks) } } - u8 viscosity = nodemgr->get(liquid_kind).liquid_viscosity; + u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity; if (viscosity > 1 && max_node_level != liquid_level) { // amount to gain, limited by viscosity // must be at least 1 in absolute value @@ -1455,7 +1444,7 @@ void Map::transformLiquids(std::map &modified_blocks) 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 || + (m_nodedef->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))) @@ -1467,7 +1456,7 @@ void Map::transformLiquids(std::map &modified_blocks) */ MapNode n00 = n0; //bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK)); - if (nodemgr->get(new_node_content).liquid_type == LIQUID_FLOWING) { + if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) { // set level to last 3 bits, flowing down bit to 4th bit n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK); } else { @@ -1477,8 +1466,8 @@ void Map::transformLiquids(std::map &modified_blocks) n0.setContent(new_node_content); // Ignore light (because calling voxalgo::update_lighting_nodes) - n0.setLight(LIGHTBANK_DAY, 0, nodemgr); - n0.setLight(LIGHTBANK_NIGHT, 0, nodemgr); + n0.setLight(LIGHTBANK_DAY, 0, m_nodedef); + n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef); // Find out whether there is a suspect for this action std::string suspect; @@ -1512,7 +1501,7 @@ void Map::transformLiquids(std::map &modified_blocks) /* enqueue neighbors for update if neccessary */ - switch (nodemgr->get(n0.getContent()).liquid_type) { + switch (m_nodedef->get(n0.getContent()).liquid_type) { case LIQUID_SOURCE: case LIQUID_FLOWING: // make sure source flows into all neighboring nodes @@ -1535,7 +1524,7 @@ void Map::transformLiquids(std::map &modified_blocks) for (std::deque::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter) m_transforming_liquid.push_back(*iter); - voxalgo::update_lighting_nodes(this, nodemgr, changed_nodes, modified_blocks); + voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks); /* ---------------------------------------------------------------------- @@ -1900,7 +1889,7 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) data->blockpos_min = bpmin; data->blockpos_max = bpmax; data->blockpos_requested = blockpos; - data->nodedef = m_gamedef->ndef(); + data->nodedef = m_nodedef; /* Create the whole area of this and the neighboring blocks diff --git a/src/map.h b/src/map.h index e8d40e982..19c94ee80 100644 --- a/src/map.h +++ b/src/map.h @@ -193,6 +193,8 @@ public: virtual MapBlock * emergeBlock(v3s16 p, bool create_blank=true) { return getBlockNoCreateNoEx(p); } + inline INodeDefManager * getNodeDefManager() { return m_nodedef; } + // Returns InvalidPositionException if not found bool isNodeUnderground(v3s16 p); @@ -346,6 +348,9 @@ protected: // Queued transforming water nodes UniqueQueue m_transforming_liquid; + // This stores the properties of the nodes on the map. + INodeDefManager *m_nodedef; + private: f32 m_transforming_liquid_loop_count_multiplier; u32 m_unprocessed_count; diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 5efebf3d8..f1a7f3e61 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapnode.h" #include "porting.h" #include "nodedef.h" +#include "map.h" #include "content_mapnode.h" // For mapnode_translate_*_internal #include "serialization.h" // For ser_ver_supported #include "util/serialize.h" @@ -453,6 +454,51 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, } } +static inline void getNeighborConnectingFace( + v3s16 p, INodeDefManager *nodedef, + Map *map, MapNode n, u8 bitmask, u8 *neighbors) +{ + MapNode n2 = map->getNodeNoEx(p); + if (nodedef->nodeboxConnects(n, n2, bitmask)) + *neighbors |= bitmask; +} + +u8 MapNode::getNeighbors(v3s16 p, Map *map) +{ + INodeDefManager *nodedef=map->getNodeDefManager(); + u8 neighbors = 0; + const ContentFeatures &f = nodedef->get(*this); + // locate possible neighboring nodes to connect to + if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) { + v3s16 p2 = p; + + p2.Y++; + getNeighborConnectingFace(p2, nodedef, map, *this, 1, &neighbors); + + p2 = p; + p2.Y--; + getNeighborConnectingFace(p2, nodedef, map, *this, 2, &neighbors); + + p2 = p; + p2.Z--; + getNeighborConnectingFace(p2, nodedef, map, *this, 4, &neighbors); + + p2 = p; + p2.X--; + getNeighborConnectingFace(p2, nodedef, map, *this, 8, &neighbors); + + p2 = p; + p2.Z++; + getNeighborConnectingFace(p2, nodedef, map, *this, 16, &neighbors); + + p2 = p; + p2.X++; + getNeighborConnectingFace(p2, nodedef, map, *this, 32, &neighbors); + } + + return neighbors; +} + void MapNode::getNodeBoxes(INodeDefManager *nodemgr, std::vector *boxes, u8 neighbors) { const ContentFeatures &f = nodemgr->get(*this); diff --git a/src/mapnode.h b/src/mapnode.h index 0bd61c554..a3c20e8ff 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class INodeDefManager; +class Map; /* Naming scheme: @@ -246,6 +247,13 @@ struct MapNode void rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot); + /*! + * Checks which neighbors does this node connect to. + * + * \param p coordinates of the node + */ + u8 getNeighbors(v3s16 p, Map *map); + /* Gets list of node boxes (used for rendering (NDT_NODEBOX)) */ diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 92cdf738e..dbbdf95d2 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -790,9 +790,18 @@ public: virtual void resetNodeResolveState(); virtual void mapNodeboxConnections(); virtual bool nodeboxConnects(MapNode from, MapNode to, u8 connect_face); + virtual core::aabbox3d getSelectionBoxIntUnion() const + { + return m_selection_box_int_union; + } private: void addNameIdMapping(content_t i, std::string name); + /*! + * Recalculates m_selection_box_int_union based on + * m_selection_box_union. + */ + void fixSelectionBoxIntUnion(); // Features indexed by id std::vector m_content_features; @@ -819,6 +828,14 @@ private: // True when all nodes have been registered bool m_node_registration_complete; + + //! The union of all nodes' selection boxes. + aabb3f m_selection_box_union; + /*! + * The smallest box in node coordinates that + * contains all nodes' selection boxes. + */ + core::aabbox3d m_selection_box_int_union; }; @@ -849,6 +866,8 @@ void CNodeDefManager::clear() m_name_id_mapping_with_aliases.clear(); m_group_to_items.clear(); m_next_id = 0; + m_selection_box_union.reset(0,0,0); + m_selection_box_int_union.reset(0,0,0); resetNodeResolveState(); @@ -1007,6 +1026,123 @@ content_t CNodeDefManager::allocateId() } +/*! + * Returns the smallest box that contains all boxes + * in the vector. Box_union is expanded. + * @param[in] boxes the vector containing the boxes + * @param[in, out] box_union the union of the arguments + */ +void boxVectorUnion(const std::vector &boxes, aabb3f *box_union) +{ + for (std::vector::const_iterator it = boxes.begin(); + it != boxes.end(); ++it) { + box_union->addInternalBox(*it); + } +} + + +/*! + * Returns a box that contains the nodebox in every case. + * The argument node_union is expanded. + * @param[in] nodebox the nodebox to be measured + * @param[in] features used to decide whether the nodebox + * can be rotated + * @param[in, out] box_union the union of the arguments + */ +void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features, + aabb3f *box_union) +{ + switch(nodebox.type) { + case NODEBOX_FIXED: + case NODEBOX_LEVELED: { + // Raw union + aabb3f half_processed(0, 0, 0, 0, 0, 0); + boxVectorUnion(nodebox.fixed, &half_processed); + // Set leveled boxes to maximal + if (nodebox.type == NODEBOX_LEVELED) { + half_processed.MaxEdge.Y = +BS / 2; + } + if (features.param_type_2 == CPT2_FACEDIR) { + // Get maximal coordinate + f32 coords[] = { + fabsf(half_processed.MinEdge.X), + fabsf(half_processed.MinEdge.Y), + fabsf(half_processed.MinEdge.Z), + fabsf(half_processed.MaxEdge.X), + fabsf(half_processed.MaxEdge.Y), + fabsf(half_processed.MaxEdge.Z) }; + f32 max = 0; + for (int i = 0; i < 6; i++) { + if (max < coords[i]) { + max = coords[i]; + } + } + // Add the union of all possible rotated boxes + box_union->addInternalPoint(-max, -max, -max); + box_union->addInternalPoint(+max, +max, +max); + } else { + box_union->addInternalBox(half_processed); + } + break; + } + case NODEBOX_WALLMOUNTED: { + // Add fix boxes + box_union->addInternalBox(nodebox.wall_top); + box_union->addInternalBox(nodebox.wall_bottom); + // Find maximal coordinate in the X-Z plane + f32 coords[] = { + fabsf(nodebox.wall_side.MinEdge.X), + fabsf(nodebox.wall_side.MinEdge.Z), + fabsf(nodebox.wall_side.MaxEdge.X), + fabsf(nodebox.wall_side.MaxEdge.Z) }; + f32 max = 0; + for (int i = 0; i < 4; i++) { + if (max < coords[i]) { + max = coords[i]; + } + } + // Add the union of all possible rotated boxes + box_union->addInternalPoint(-max, nodebox.wall_side.MinEdge.Y, -max); + box_union->addInternalPoint(max, nodebox.wall_side.MaxEdge.Y, max); + break; + } + case NODEBOX_CONNECTED: { + // Add all possible connected boxes + boxVectorUnion(nodebox.fixed, box_union); + boxVectorUnion(nodebox.connect_top, box_union); + boxVectorUnion(nodebox.connect_bottom, box_union); + boxVectorUnion(nodebox.connect_front, box_union); + boxVectorUnion(nodebox.connect_left, box_union); + boxVectorUnion(nodebox.connect_back, box_union); + boxVectorUnion(nodebox.connect_right, box_union); + break; + } + default: { + // NODEBOX_REGULAR + box_union->addInternalPoint(-BS / 2, -BS / 2, -BS / 2); + box_union->addInternalPoint(+BS / 2, +BS / 2, +BS / 2); + } + } +} + + +inline void CNodeDefManager::fixSelectionBoxIntUnion() +{ + m_selection_box_int_union.MinEdge.X = floorf( + m_selection_box_union.MinEdge.X / BS + 0.5f); + m_selection_box_int_union.MinEdge.Y = floorf( + m_selection_box_union.MinEdge.Y / BS + 0.5f); + m_selection_box_int_union.MinEdge.Z = floorf( + m_selection_box_union.MinEdge.Z / BS + 0.5f); + m_selection_box_int_union.MaxEdge.X = ceilf( + m_selection_box_union.MaxEdge.X / BS - 0.5f); + m_selection_box_int_union.MaxEdge.Y = ceilf( + m_selection_box_union.MaxEdge.Y / BS - 0.5f); + m_selection_box_int_union.MaxEdge.Z = ceilf( + m_selection_box_union.MaxEdge.Z / BS - 0.5f); +} + + // IWritableNodeDefManager content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &def) { @@ -1037,6 +1173,8 @@ content_t CNodeDefManager::set(const std::string &name, const ContentFeatures &d verbosestream << "NodeDefManager: registering content id \"" << id << "\": name=\"" << def.name << "\""< getSelectionBoxIntUnion() const=0; }; class IWritableNodeDefManager : public INodeDefManager { @@ -406,6 +411,7 @@ public: virtual void runNodeResolveCallbacks()=0; virtual void resetNodeResolveState()=0; virtual void mapNodeboxConnections()=0; + virtual core::aabbox3d getSelectionBoxIntUnion() const=0; }; IWritableNodeDefManager *createNodeDefManager(); diff --git a/src/raycast.cpp b/src/raycast.cpp new file mode 100644 index 000000000..58102c993 --- /dev/null +++ b/src/raycast.cpp @@ -0,0 +1,89 @@ +/* +Minetest +Copyright (C) 2016 juhdanad, Daniel Juhasz + +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 "irr_v3d.h" +#include "irr_aabb3d.h" + +bool boxLineCollision(const aabb3f &box, const v3f &start, + const v3f &dir, v3f *collision_point, v3s16 *collision_normal) { + if (box.isPointInside(start)) { + *collision_point = start; + collision_normal->set(0, 0, 0); + return true; + } + float m = 0; + + // Test X collision + if (dir.X != 0) { + if (dir.X > 0) + m = (box.MinEdge.X - start.X) / dir.X; + else + m = (box.MaxEdge.X - start.X) / dir.X; + + if (m >= 0 && m <= 1) { + *collision_point = start + dir * m; + if ((collision_point->Y >= box.MinEdge.Y) + && (collision_point->Y <= box.MaxEdge.Y) + && (collision_point->Z >= box.MinEdge.Z) + && (collision_point->Z <= box.MaxEdge.Z)) { + collision_normal->set((dir.X > 0) ? -1 : 1, 0, 0); + return true; + } + } + } + + // Test Y collision + if (dir.Y != 0) { + if (dir.Y > 0) + m = (box.MinEdge.Y - start.Y) / dir.Y; + else + m = (box.MaxEdge.Y - start.Y) / dir.Y; + + if (m >= 0 && m <= 1) { + *collision_point = start + dir * m; + if ((collision_point->X >= box.MinEdge.X) + && (collision_point->X <= box.MaxEdge.X) + && (collision_point->Z >= box.MinEdge.Z) + && (collision_point->Z <= box.MaxEdge.Z)) { + collision_normal->set(0, (dir.Y > 0) ? -1 : 1, 0); + return true; + } + } + } + + // Test Z collision + if (dir.Z != 0) { + if (dir.Z > 0) + m = (box.MinEdge.Z - start.Z) / dir.Z; + else + m = (box.MaxEdge.Z - start.Z) / dir.Z; + + if (m >= 0 && m <= 1) { + *collision_point = start + dir * m; + if ((collision_point->X >= box.MinEdge.X) + && (collision_point->X <= box.MaxEdge.X) + && (collision_point->Y >= box.MinEdge.Y) + && (collision_point->Y <= box.MaxEdge.Y)) { + collision_normal->set(0, 0, (dir.Z > 0) ? -1 : 1); + return true; + } + } + } + return false; +} diff --git a/src/raycast.h b/src/raycast.h new file mode 100644 index 000000000..d7ec8c843 --- /dev/null +++ b/src/raycast.h @@ -0,0 +1,38 @@ +/* +Minetest +Copyright (C) 2016 juhdanad, Daniel Juhasz + +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 SRC_RAYCAST_H_ +#define SRC_RAYCAST_H_ + + +/*! + * Checks if a line and a box intersects. + * @param[in] box box to test collision + * @param[in] start starting point of the line + * @param[in] dir direction and length of the line + * @param[out] collision_point first point of the collision + * @param[out] collision_normal normal vector at the collision, points + * outwards of the surface. If start is in the box, zero vector. + * @returns true if a collision point was found + */ +bool boxLineCollision(const aabb3f &box, const v3f &start, const v3f &dir, + v3f *collision_point, v3s16 *collision_normal); + + +#endif /* SRC_RAYCAST_H_ */ diff --git a/src/unittest/test_voxelalgorithms.cpp b/src/unittest/test_voxelalgorithms.cpp index 31b9cadd5..fd83844af 100644 --- a/src/unittest/test_voxelalgorithms.cpp +++ b/src/unittest/test_voxelalgorithms.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "voxelalgorithms.h" +#include "util/numeric.h" class TestVoxelAlgorithms : public TestBase { public: @@ -31,6 +32,7 @@ public: void testPropogateSunlight(INodeDefManager *ndef); void testClearLightAndCollectSources(INodeDefManager *ndef); + void testVoxelLineIterator(INodeDefManager *ndef); }; static TestVoxelAlgorithms g_test_instance; @@ -41,6 +43,7 @@ void TestVoxelAlgorithms::runTests(IGameDef *gamedef) TEST(testPropogateSunlight, ndef); TEST(testClearLightAndCollectSources, ndef); + TEST(testVoxelLineIterator, ndef); } //////////////////////////////////////////////////////////////////////////////// @@ -202,3 +205,59 @@ void TestVoxelAlgorithms::testClearLightAndCollectSources(INodeDefManager *ndef) UASSERT(unlight_from.size() == 1); } } + +void TestVoxelAlgorithms::testVoxelLineIterator(INodeDefManager *ndef) +{ + // Test some lines + // Do not test lines that start or end on the border of + // two voxels as rounding errors can make the test fail! + std::vector > lines; + for (f32 x = -9.1; x < 9; x += 3.124) { + for (f32 y = -9.2; y < 9; y += 3.123) { + for (f32 z = -9.3; z < 9; z += 3.122) { + lines.push_back(core::line3d(-x, -y, -z, x, y, z)); + } + } + } + lines.push_back(core::line3d(0, 0, 0, 0, 0, 0)); + // Test every line + std::vector >::iterator it = lines.begin(); + for (; it < lines.end(); it++) { + core::line3d l = *it; + + // Initialize test + voxalgo::VoxelLineIterator iterator(l.start, l.getVector()); + + //Test the first voxel + v3s16 start_voxel = floatToInt(l.start, 1); + UASSERT(iterator.m_current_node_pos == start_voxel); + + // Values for testing + v3s16 end_voxel = floatToInt(l.end, 1); + v3s16 voxel_vector = end_voxel - start_voxel; + int nodecount = abs(voxel_vector.X) + abs(voxel_vector.Y) + + abs(voxel_vector.Z); + int actual_nodecount = 0; + v3s16 old_voxel = iterator.m_current_node_pos; + + while (iterator.hasNext()) { + iterator.next(); + actual_nodecount++; + v3s16 new_voxel = iterator.m_current_node_pos; + // This must be a neighbor of the old voxel + UASSERTEQ(f32, (new_voxel - old_voxel).getLengthSQ(), 1); + // The line must intersect with the voxel + v3f voxel_center = intToFloat(iterator.m_current_node_pos, 1); + aabb3f box(voxel_center - v3f(0.5, 0.5, 0.5), + voxel_center + v3f(0.5, 0.5, 0.5)); + UASSERT(box.intersectsWithLine(l)); + // Update old voxel + old_voxel = new_voxel; + } + + // Test last node + UASSERT(iterator.m_current_node_pos == end_voxel); + // Test node count + UASSERTEQ(int, actual_nodecount, nodecount); + } +} diff --git a/src/util/pointedthing.cpp b/src/util/pointedthing.cpp index cd13000b5..ed3d4bb67 100644 --- a/src/util/pointedthing.cpp +++ b/src/util/pointedthing.cpp @@ -27,29 +27,25 @@ PointedThing::PointedThing(): type(POINTEDTHING_NOTHING), node_undersurface(0,0,0), node_abovesurface(0,0,0), + node_real_undersurface(0,0,0), + intersection_point(0,0,0), + intersection_normal(0,0,0), object_id(-1) {} std::string PointedThing::dump() const { std::ostringstream os(std::ios::binary); - if(type == POINTEDTHING_NOTHING) - { + if (type == POINTEDTHING_NOTHING) { os<<"[nothing]"; - } - else if(type == POINTEDTHING_NODE) - { + } else if (type == POINTEDTHING_NODE) { const v3s16 &u = node_undersurface; const v3s16 &a = node_abovesurface; os<<"[node under="< 0) { + m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + 1.5 + - m_start_position.X) / m_line_vector.X; + m_intersection_multi_inc.X = 1 / m_line_vector.X; + } else if (m_line_vector.X < 0) { + m_next_intersection_multi.X = (floorf(m_start_position.X - 0.5) + - m_start_position.X + 0.5) / m_line_vector.X; + m_intersection_multi_inc.X = -1 / m_line_vector.X; + m_step_directions.X = -1; + } + + if (m_line_vector.Y > 0) { + m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + 1.5 + - m_start_position.Y) / m_line_vector.Y; + m_intersection_multi_inc.Y = 1 / m_line_vector.Y; + } else if (m_line_vector.Y < 0) { + m_next_intersection_multi.Y = (floorf(m_start_position.Y - 0.5) + - m_start_position.Y + 0.5) / m_line_vector.Y; + m_intersection_multi_inc.Y = -1 / m_line_vector.Y; + m_step_directions.Y = -1; + } + + if (m_line_vector.Z > 0) { + m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + 1.5 + - m_start_position.Z) / m_line_vector.Z; + m_intersection_multi_inc.Z = 1 / m_line_vector.Z; + } else if (m_line_vector.Z < 0) { + m_next_intersection_multi.Z = (floorf(m_start_position.Z - 0.5) + - m_start_position.Z + 0.5) / m_line_vector.Z; + m_intersection_multi_inc.Z = -1 / m_line_vector.Z; + m_step_directions.Z = -1; + } + + m_has_next = (m_next_intersection_multi.X <= 1) + || (m_next_intersection_multi.Y <= 1) + || (m_next_intersection_multi.Z <= 1); +} + +void VoxelLineIterator::next() +{ + if ((m_next_intersection_multi.X < m_next_intersection_multi.Y) + && (m_next_intersection_multi.X < m_next_intersection_multi.Z)) { + m_next_intersection_multi.X += m_intersection_multi_inc.X; + m_current_node_pos.X += m_step_directions.X; + } else if ((m_next_intersection_multi.Y < m_next_intersection_multi.Z)) { + m_next_intersection_multi.Y += m_intersection_multi_inc.Y; + m_current_node_pos.Y += m_step_directions.Y; + } else { + m_next_intersection_multi.Z += m_intersection_multi_inc.Z; + m_current_node_pos.Z += m_step_directions.Z; + } + + m_has_next = (m_next_intersection_multi.X <= 1) + || (m_next_intersection_multi.Y <= 1) + || (m_next_intersection_multi.Z <= 1); +} + } // namespace voxalgo diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index 3632546dd..5eff8f7ac 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -73,7 +73,68 @@ void update_lighting_nodes( std::vector > &oldnodes, std::map &modified_blocks); +/*! + * This class iterates trough voxels that intersect with + * a line. The collision detection does not see nodeboxes, + * every voxel is a cube and is returned. + * This iterator steps to all nodes exactly once. + */ +struct VoxelLineIterator +{ +public: + //! Starting position of the line in world coordinates. + v3f m_start_position; + //! Direction and length of the line in world coordinates. + v3f m_line_vector; + /*! + * Each component stores the next smallest positive number, by + * which multiplying the line's vector gives a vector that ends + * on the intersection of two nodes. + */ + v3f m_next_intersection_multi; + /*! + * Each component stores the smallest positive number, by which + * m_next_intersection_multi's components can be increased. + */ + v3f m_intersection_multi_inc; + /*! + * Direction of the line. Each component can be -1 or 1 (if a + * component of the line's vector is 0, then there will be 1). + */ + v3s16 m_step_directions; + //! Position of the current node. + v3s16 m_current_node_pos; + //! If true, the next node will intersect the line, too. + bool m_has_next; + + /*! + * Creates a voxel line iterator with the given line. + * @param start_position starting point of the line + * in voxel coordinates + * @param line_vector length and direction of the + * line in voxel coordinates. start_position+line_vector + * is the end of the line + */ + VoxelLineIterator(const v3f &start_position,const v3f &line_vector); + + /*! + * Steps to the next voxel. + * Updates m_current_node_pos and + * m_previous_node_pos. + * Note that it works even if hasNext() is false, + * continuing the line as a ray. + */ + void next(); + + /*! + * Returns true if the next voxel intersects the given line. + */ + inline bool hasNext() const { return m_has_next; } +}; + } // namespace voxalgo + + #endif -- cgit v1.2.3 From ad10b8b762d8097092f307867a36e55049d69546 Mon Sep 17 00:00:00 2001 From: Rogier-5 Date: Tue, 3 Jan 2017 21:23:22 -0800 Subject: Use std::vector instead of std::map in class ABMHandler --- src/environment.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 8c0daf87b..5eb9d9770 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -767,7 +767,7 @@ class ABMHandler { private: ServerEnvironment *m_env; - std::map > m_aabms; + std::vector *> m_aabms; public: ABMHandler(std::vector &abms, float dtime_s, ServerEnvironment *env, @@ -826,18 +826,22 @@ public: k != ids.end(); ++k) { content_t c = *k; - std::map >::iterator j; - j = m_aabms.find(c); - if(j == m_aabms.end()){ - std::vector aabmlist; - m_aabms[c] = aabmlist; - j = m_aabms.find(c); - } - j->second.push_back(aabm); + if (c >= m_aabms.size()) + m_aabms.resize(c + 256, (std::vector *) NULL); + if (!m_aabms[c]) + m_aabms[c] = new std::vector; + m_aabms[c]->push_back(aabm); } } } } + + ~ABMHandler() + { + for (size_t i = 0; i < m_aabms.size(); i++) + delete m_aabms[i]; + } + // Find out how many objects the given block and its neighbours contain. // Returns the number of objects in the block, and also in 'wider' the // number of objects in the block and all its neighbours. The latter @@ -886,13 +890,11 @@ public: content_t c = n.getContent(); v3s16 p = p0 + block->getPosRelative(); - std::map >::iterator j; - j = m_aabms.find(c); - if(j == m_aabms.end()) + if (!m_aabms[c]) continue; for(std::vector::iterator - i = j->second.begin(); i != j->second.end(); ++i) { + i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { if(myrand() % i->chance != 0) continue; -- cgit v1.2.3 From ca3629637cb20cea318b8811e717e2caab579dc0 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Wed, 4 Jan 2017 11:11:55 -0800 Subject: Fixes for using std:vector in ABMHander and further perf improvements --- src/environment.cpp | 10 +++++----- src/mapblock.h | 20 +++++++++++++++++--- src/mapnode.h | 5 ----- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 5eb9d9770..a0cf9dca5 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -827,7 +827,7 @@ public: { content_t c = *k; if (c >= m_aabms.size()) - m_aabms.resize(c + 256, (std::vector *) NULL); + m_aabms.resize(c + 256, NULL); if (!m_aabms[c]) m_aabms[c] = new std::vector; m_aabms[c]->push_back(aabm); @@ -872,7 +872,7 @@ public: } void apply(MapBlock *block) { - if(m_aabms.empty()) + if(m_aabms.empty() || block->isDummy()) return; ServerMap *map = &m_env->getServerMap(); @@ -886,13 +886,13 @@ public: for(p0.Y=0; p0.YgetNodeNoEx(p0); + const MapNode &n = block->getNodeUnsafe(p0); content_t c = n.getContent(); - v3s16 p = p0 + block->getPosRelative(); - if (!m_aabms[c]) + if (c >= m_aabms.size() || !m_aabms[c]) continue; + v3s16 p = p0 + block->getPosRelative(); for(std::vector::iterator i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { if(myrand() % i->chance != 0) diff --git a/src/mapblock.h b/src/mapblock.h index 5adfcf3fb..c737b4c37 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -305,8 +305,7 @@ public: inline MapNode getNodeNoEx(v3s16 p) { bool is_valid; - MapNode node = getNode(p.X, p.Y, p.Z, &is_valid); - return is_valid ? node : MapNode(CONTENT_IGNORE); + return getNode(p.X, p.Y, p.Z, &is_valid); } inline void setNode(s16 x, s16 y, s16 z, MapNode & n) @@ -341,6 +340,22 @@ public: return getNodeNoCheck(p.X, p.Y, p.Z, valid_position); } + //// + //// Non-checking, unsafe variants of the above + //// MapBlock must be loaded by another function in the same scope/function + //// Caller must ensure that this is not a dummy block (by calling isDummy()) + //// + + inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z) + { + return data[z * zstride + y * ystride + x]; + } + + inline const MapNode &getNodeUnsafe(v3s16 &p) + { + return getNodeUnsafe(p.X, p.Y, p.Z); + } + inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n) { if (data == NULL) @@ -512,7 +527,6 @@ public: void serializeNetworkSpecific(std::ostream &os, u16 net_proto_version); void deSerializeNetworkSpecific(std::istream &is); - private: /* Private methods diff --git a/src/mapnode.h b/src/mapnode.h index a3c20e8ff..ae0245cfe 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -143,11 +143,6 @@ struct MapNode MapNode() { } - MapNode(const MapNode & n) - { - *this = n; - } - MapNode(content_t content, u8 a_param1=0, u8 a_param2=0) : param0(content), param1(a_param1), -- cgit v1.2.3 From e8b7179ccd5b1f70eba3f9ac570c5f10474cf7a7 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 28 Dec 2016 13:01:32 +0000 Subject: Expose and document chatcommands as minetest.registered_chatcommands --- builtin/game/chatcommands.lua | 19 ++++++++++--------- doc/lua_api.txt | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 71edeb26a..eb6edc1c8 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -4,14 +4,15 @@ -- Chat command handler -- -core.chatcommands = {} +core.registered_chatcommands = {} +core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY function core.register_chatcommand(cmd, def) def = def or {} def.params = def.params or "" def.description = def.description or "" def.privs = def.privs or {} def.mod_origin = core.get_current_modname() or "??" - core.chatcommands[cmd] = def + core.registered_chatcommands[cmd] = def end core.register_on_chat_message(function(name, message) @@ -19,7 +20,7 @@ core.register_on_chat_message(function(name, message) if not param then param = "" end - local cmd_def = core.chatcommands[cmd] + local cmd_def = core.registered_chatcommands[cmd] if not cmd_def then return false end @@ -107,7 +108,7 @@ core.register_chatcommand("help", { if param == "" then local msg = "" local cmds = {} - for cmd, def in pairs(core.chatcommands) do + for cmd, def in pairs(core.registered_chatcommands) do if core.check_player_privs(name, def.privs) then cmds[#cmds + 1] = cmd end @@ -118,7 +119,7 @@ core.register_chatcommand("help", { .. " or '/help all' to list everything." elseif param == "all" then local cmds = {} - for cmd, def in pairs(core.chatcommands) do + for cmd, def in pairs(core.registered_chatcommands) do if core.check_player_privs(name, def.privs) then cmds[#cmds + 1] = format_help_line(cmd, def) end @@ -134,7 +135,7 @@ core.register_chatcommand("help", { return true, "Available privileges:\n"..table.concat(privs, "\n") else local cmd = param - local def = core.chatcommands[cmd] + local def = core.registered_chatcommands[cmd] if not def then return false, "Command not available: "..cmd else @@ -161,7 +162,7 @@ local function handle_grant_command(caller, grantname, grantprivstr) if not (caller_privs.privs or caller_privs.basic_privs) then return false, "Your privileges are insufficient." end - + if not core.get_auth_handler().get_auth(grantname) then return false, "Player " .. grantname .. " does not exist." end @@ -204,7 +205,7 @@ core.register_chatcommand("grant", { local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") if not grantname or not grantprivstr then return false, "Invalid parameters (see /help grant)" - end + end return handle_grant_command(name, grantname, grantprivstr) end, }) @@ -215,7 +216,7 @@ core.register_chatcommand("grantme", { func = function(name, param) if param == "" then return false, "Invalid parameters (see /help grantme)" - end + end return handle_grant_command(name, name, param) end, }) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6166826af..6f83039c8 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2076,6 +2076,7 @@ Call these functions only at load time! ### Other registration functions * `minetest.register_chatcommand(cmd, chatcommand definition)` + * Adds definition to minetest.registered_chatcommands * `minetest.register_privilege(name, definition)` * `definition`: `"description text"` * `definition`: `{ description = "description text", give_to_singleplayer = boolean}` -- cgit v1.2.3 From 8f22272df5423cfc6cdf4425787d7f148ee4ca6c Mon Sep 17 00:00:00 2001 From: Wayward1 Date: Wed, 4 Jan 2017 19:44:11 -0500 Subject: Add raycast.cpp and tileanimation.cpp to Android.mk --- build/android/jni/Android.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index c2186efaf..2567457b9 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -203,6 +203,7 @@ LOCAL_SRC_FILES := \ jni/src/porting.cpp \ jni/src/profiler.cpp \ jni/src/quicktune.cpp \ + jni/src/raycast.cpp \ jni/src/reflowscan.cpp \ jni/src/remoteplayer.cpp \ jni/src/rollback.cpp \ @@ -218,6 +219,7 @@ LOCAL_SRC_FILES := \ jni/src/sound_openal.cpp \ jni/src/staticobject.cpp \ jni/src/subgame.cpp \ + jni/src/tileanimation.cpp \ jni/src/tool.cpp \ jni/src/treegen.cpp \ jni/src/version.cpp \ -- cgit v1.2.3 From 545c37f6139125c9a2f9cc0ecfd1d3ff1f44b832 Mon Sep 17 00:00:00 2001 From: LNJ Date: Thu, 5 Jan 2017 22:38:43 +0100 Subject: lua_api.txt: Add registered_chatcommands to global tables --- doc/lua_api.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 6f83039c8..d05db9d49 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2729,6 +2729,8 @@ These functions return the leftover itemstack. * Map of object references, indexed by active object id * `minetest.luaentities` * Map of Lua entities, indexed by active object id +* `minetest.registered_chatcommands` + * Map of registered chat command definitions, indexed by name * `minetest.registered_ores` * List of registered ore definitions. * `minetest.registered_biomes` -- cgit v1.2.3 From b387f95ed31e9d1dc61ffd10f19187349705d6c9 Mon Sep 17 00:00:00 2001 From: LNJ Date: Thu, 5 Jan 2017 21:28:58 +0100 Subject: README.txt: Update the License to 2010-2017 --- README.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.txt b/README.txt index 5d4d15263..8bd8554ed 100644 --- a/README.txt +++ b/README.txt @@ -3,7 +3,7 @@ Minetest An InfiniMiner/Minecraft inspired game. -Copyright (c) 2010-2013 Perttu Ahola +Copyright (c) 2010-2017 Perttu Ahola and contributors (see source file comments and the version control log) In case you downloaded the source code: @@ -427,7 +427,7 @@ License of Minetest source code ------------------------------- Minetest -Copyright (C) 2010-2013 celeron55, Perttu Ahola +Copyright (C) 2010-2017 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by -- cgit v1.2.3 From c435eabf3ffb77eab955d5faeb5450da1befc149 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 3 Dec 2016 11:38:07 +0000 Subject: Extend minetest.is_yes() --- src/unittest/test_utilities.cpp | 2 ++ src/util/string.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index d73975b9f..58412dd85 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -147,6 +147,8 @@ void TestUtilities::testIsYes() UASSERT(is_yes("0") == false); UASSERT(is_yes("1") == true); UASSERT(is_yes("2") == true); + UASSERT(is_yes("on") == true); + UASSERT(is_yes("off") == false); } diff --git a/src/util/string.h b/src/util/string.h index 572c37150..ba3c09e51 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -272,7 +272,7 @@ inline bool is_yes(const std::string &str) { std::string s2 = lowercase(trim(str)); - return s2 == "y" || s2 == "yes" || s2 == "true" || atoi(s2.c_str()) != 0; + return s2 == "y" || s2 == "yes" || s2 == "true" || s2 == "on" || atoi(s2.c_str()) != 0; } -- cgit v1.2.3 From ce106a4113de048718ab9f396db3d368c35218fc Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 7 Jan 2017 11:05:05 +0100 Subject: Revert "Extend minetest.is_yes()" This reverts commit c435eabf3ffb77eab955d5faeb5450da1befc149. --- src/unittest/test_utilities.cpp | 2 -- src/util/string.h | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 58412dd85..d73975b9f 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -147,8 +147,6 @@ void TestUtilities::testIsYes() UASSERT(is_yes("0") == false); UASSERT(is_yes("1") == true); UASSERT(is_yes("2") == true); - UASSERT(is_yes("on") == true); - UASSERT(is_yes("off") == false); } diff --git a/src/util/string.h b/src/util/string.h index ba3c09e51..572c37150 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -272,7 +272,7 @@ inline bool is_yes(const std::string &str) { std::string s2 = lowercase(trim(str)); - return s2 == "y" || s2 == "yes" || s2 == "true" || s2 == "on" || atoi(s2.c_str()) != 0; + return s2 == "y" || s2 == "yes" || s2 == "true" || atoi(s2.c_str()) != 0; } -- cgit v1.2.3 From 8f9611bcb20f4eca69abb204bf5dc28f62fab4a2 Mon Sep 17 00:00:00 2001 From: Ezhh Date: Sat, 7 Jan 2017 11:33:38 +0000 Subject: Make column alignment consistent in advanced settings (#5004) --- builtin/mainmenu/dlg_settings_advanced.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index b0d923768..85218c852 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -544,7 +544,7 @@ end local function create_settings_formspec(tabview, name, tabdata) local formspec = "size[12,6.5;true]" .. - "tablecolumns[color;tree;text;text]" .. + "tablecolumns[color;tree;text,width=32;text]" .. "tableoptions[background=#00000000;border=false]" .. "table[0,0;12,5.5;list_settings;" -- cgit v1.2.3 From b0746834cc44af1086c83715d0d6f749f653f29a Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Sat, 7 Jan 2017 23:42:25 -0800 Subject: Get neighbor from same map block if possible in ABMHandler (#4998) --- src/environment.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index a0cf9dca5..50f5d58aa 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -902,14 +902,23 @@ public: if(!i->required_neighbors.empty()) { v3s16 p1; - for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++) - for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++) - for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++) + for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) + for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) + for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) { - if(p1 == p) + if(p1 == p0) continue; - MapNode n = map->getNodeNoEx(p1); - content_t c = n.getContent(); + content_t c; + if (block->isValidPosition(p1)) { + // if the neighbor is found on the same map block + // get it straight from there + const MapNode &n = block->getNodeUnsafe(p1); + c = n.getContent(); + } else { + // otherwise consult the map + MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); + c = n.getContent(); + } std::set::const_iterator k; k = i->required_neighbors.find(c); if(k != i->required_neighbors.end()){ -- cgit v1.2.3 From eb2c19bbedecb5f42c946b0c14466abc0b109825 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 8 Jan 2017 10:49:47 +0100 Subject: Move ClientEnvironment to dedicated cpp/header files --- src/CMakeLists.txt | 1 + src/client.h | 2 +- src/clientenvironment.cpp | 847 ++++++++++++++++++++++++++++++++++++++++++++++ src/clientenvironment.h | 191 +++++++++++ src/collision.cpp | 6 +- src/content_cso.cpp | 3 +- src/environment.cpp | 828 -------------------------------------------- src/environment.h | 167 --------- 8 files changed, 1042 insertions(+), 1003 deletions(-) create mode 100644 src/clientenvironment.cpp create mode 100644 src/clientenvironment.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 507624753..5228eb9ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -508,6 +508,7 @@ set(client_SRCS ${client_irrlicht_changes_SRCS} camera.cpp client.cpp + clientenvironment.cpp clientmap.cpp clientmedia.cpp clientobject.cpp diff --git a/src/client.h b/src/client.h index 891fe62f8..9a09704a6 100644 --- a/src/client.h +++ b/src/client.h @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CLIENT_HEADER #include "network/connection.h" -#include "environment.h" +#include "clientenvironment.h" #include "irrlichttypes_extrabloated.h" #include "threading/mutex.h" #include diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp new file mode 100644 index 000000000..e831de109 --- /dev/null +++ b/src/clientenvironment.cpp @@ -0,0 +1,847 @@ +/* +Minetest +Copyright (C) 2010-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "util/serialize.h" +#include "util/pointedthing.h" +#include "clientenvironment.h" +#include "clientsimpleobject.h" +#include "clientmap.h" +#include "mapblock_mesh.h" +#include "event.h" +#include "collision.h" +#include "profiler.h" +#include "raycast.h" +#include "voxelalgorithms.h" + +/* + ClientEnvironment +*/ + +ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, + ITextureSource *texturesource, IGameDef *gamedef, + IrrlichtDevice *irr): + m_map(map), + m_local_player(NULL), + m_smgr(smgr), + m_texturesource(texturesource), + m_gamedef(gamedef), + m_irr(irr) +{ + char zero = 0; + memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); +} + +ClientEnvironment::~ClientEnvironment() +{ + // delete active objects + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + delete i->second; + } + + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { + delete *i; + } + + // Drop/delete map + m_map->drop(); +} + +Map & ClientEnvironment::getMap() +{ + return *m_map; +} + +ClientMap & ClientEnvironment::getClientMap() +{ + return *m_map; +} + +void ClientEnvironment::setLocalPlayer(LocalPlayer *player) +{ + DSTACK(FUNCTION_NAME); + /* + It is a failure if already is a local player + */ + FATAL_ERROR_IF(m_local_player != NULL, + "Local player already allocated"); + + m_local_player = player; +} + +void ClientEnvironment::step(float dtime) +{ + DSTACK(FUNCTION_NAME); + + /* Step time of day */ + stepTimeOfDay(dtime); + + // Get some settings + bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); + bool free_move = fly_allowed && g_settings->getBool("free_move"); + + // Get local player + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + // collision info queue + std::vector player_collisions; + + /* + Get the speed the player is going + */ + bool is_climbing = lplayer->is_climbing; + + f32 player_speed = lplayer->getSpeed().getLength(); + + /* + Maximum position increment + */ + //f32 position_max_increment = 0.05*BS; + f32 position_max_increment = 0.1*BS; + + // Maximum time increment (for collision detection etc) + // time = distance / speed + f32 dtime_max_increment = 1; + if(player_speed > 0.001) + dtime_max_increment = position_max_increment / player_speed; + + // Maximum time increment is 10ms or lower + if(dtime_max_increment > 0.01) + dtime_max_increment = 0.01; + + // Don't allow overly huge dtime + if(dtime > 0.5) + dtime = 0.5; + + f32 dtime_downcount = dtime; + + /* + Stuff that has a maximum time increment + */ + + u32 loopcount = 0; + do + { + loopcount++; + + f32 dtime_part; + if(dtime_downcount > dtime_max_increment) + { + dtime_part = dtime_max_increment; + dtime_downcount -= dtime_part; + } + else + { + dtime_part = dtime_downcount; + /* + Setting this to 0 (no -=dtime_part) disables an infinite loop + when dtime_part is so small that dtime_downcount -= dtime_part + does nothing + */ + dtime_downcount = 0; + } + + /* + Handle local player + */ + + { + // Apply physics + if(!free_move && !is_climbing) + { + // Gravity + v3f speed = lplayer->getSpeed(); + if(!lplayer->in_liquid) + speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; + + // Liquid floating / sinking + if(lplayer->in_liquid && !lplayer->swimming_vertical) + speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; + + // Liquid resistance + if(lplayer->in_liquid_stable || lplayer->in_liquid) + { + // How much the node's viscosity blocks movement, ranges between 0 and 1 + // Should match the scale at which viscosity increase affects other liquid attributes + const f32 viscosity_factor = 0.3; + + v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; + f32 dl = d_wanted.getLength(); + if(dl > lplayer->movement_liquid_fluidity_smooth) + dl = lplayer->movement_liquid_fluidity_smooth; + dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); + + v3f d = d_wanted.normalize() * dl; + speed += d; + } + + lplayer->setSpeed(speed); + } + + /* + Move the lplayer. + This also does collision detection. + */ + lplayer->move(dtime_part, this, position_max_increment, + &player_collisions); + } + } + while(dtime_downcount > 0.001); + + //std::cout<<"Looped "<::iterator i = player_collisions.begin(); + i != player_collisions.end(); ++i) { + CollisionInfo &info = *i; + v3f speed_diff = info.new_speed - info.old_speed;; + // Handle only fall damage + // (because otherwise walking against something in fast_move kills you) + if(speed_diff.Y < 0 || info.old_speed.Y >= 0) + continue; + // Get rid of other components + speed_diff.X = 0; + speed_diff.Z = 0; + f32 pre_factor = 1; // 1 hp per node/s + f32 tolerance = BS*14; // 5 without damage + f32 post_factor = 1; // 1 hp per node/s + if(info.type == COLLISION_NODE) + { + const ContentFeatures &f = m_gamedef->ndef()-> + get(m_map->getNodeNoEx(info.node_p)); + // Determine fall damage multiplier + int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); + pre_factor = 1.0 + (float)addp/100.0; + } + float speed = pre_factor * speed_diff.getLength(); + if(speed > tolerance) + { + f32 damage_f = (speed - tolerance)/BS * post_factor; + u16 damage = (u16)(damage_f+0.5); + if(damage != 0){ + damageLocalPlayer(damage, true); + MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); + m_gamedef->event()->put(e); + } + } + } + + /* + A quick draft of lava damage + */ + if(m_lava_hurt_interval.step(dtime, 1.0)) + { + v3f pf = lplayer->getPosition(); + + // Feet, middle and head + v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); + MapNode n1 = m_map->getNodeNoEx(p1); + v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); + MapNode n2 = m_map->getNodeNoEx(p2); + v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); + MapNode n3 = m_map->getNodeNoEx(p3); + + u32 damage_per_second = 0; + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n1).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n2).damage_per_second); + damage_per_second = MYMAX(damage_per_second, + m_gamedef->ndef()->get(n3).damage_per_second); + + if(damage_per_second != 0) + { + damageLocalPlayer(damage_per_second, true); + } + } + + // Protocol v29 make this behaviour obsolete + if (((Client*) getGameDef())->getProtoVersion() < 29) { + /* + Drowning + */ + if (m_drowning_interval.step(dtime, 2.0)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNodeNoEx(p); + ContentFeatures c = m_gamedef->ndef()->get(n); + u8 drowning_damage = c.drowning; + if (drowning_damage > 0 && lplayer->hp > 0) { + u16 breath = lplayer->getBreath(); + if (breath > 10) { + breath = 11; + } + if (breath > 0) { + breath -= 1; + } + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + + if (lplayer->getBreath() == 0 && drowning_damage > 0) { + damageLocalPlayer(drowning_damage, true); + } + } + if (m_breathing_interval.step(dtime, 0.5)) { + v3f pf = lplayer->getPosition(); + + // head + v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); + MapNode n = m_map->getNodeNoEx(p); + ContentFeatures c = m_gamedef->ndef()->get(n); + if (!lplayer->hp) { + lplayer->setBreath(11); + } else if (c.drowning == 0) { + u16 breath = lplayer->getBreath(); + if (breath <= 10) { + breath += 1; + lplayer->setBreath(breath); + updateLocalPlayerBreath(breath); + } + } + } + } + + // Update lighting on local player (used for wield item) + u32 day_night_ratio = getDayNightRatio(); + { + // Get node at head + + // On InvalidPositionException, use this as default + // (day: LIGHT_SUN, night: 0) + MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); + + v3s16 p = lplayer->getLightPosition(); + node_at_lplayer = m_map->getNodeNoEx(p); + + u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u8 day = light & 0xff; + u8 night = (light >> 8) & 0xff; + finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + } + + /* + Step active objects and update lighting of them + */ + + g_profiler->avg("CEnv: num of objects", m_active_objects.size()); + bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ClientActiveObject* obj = i->second; + // Step object + obj->step(dtime, this); + + if(update_lighting) + { + // Update lighting + u8 light = 0; + bool pos_ok; + + // Get node at head + v3s16 p = obj->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) + light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + else + light = blend_light(day_night_ratio, LIGHT_SUN, 0); + + obj->updateLight(light); + } + } + + /* + Step and handle simple objects + */ + g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); + for(std::vector::iterator + i = m_simple_objects.begin(); i != m_simple_objects.end();) { + std::vector::iterator cur = i; + ClientSimpleObject *simple = *cur; + + simple->step(dtime); + if(simple->m_to_be_removed) { + delete simple; + i = m_simple_objects.erase(cur); + } + else { + ++i; + } + } +} + +void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) +{ + m_simple_objects.push_back(simple); +} + +GenericCAO* ClientEnvironment::getGenericCAO(u16 id) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC) + return (GenericCAO*) obj; + else + return NULL; +} + +ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) +{ + UNORDERED_MAP::iterator n = m_active_objects.find(id); + if (n == m_active_objects.end()) + return NULL; + return n->second; +} + +bool isFreeClientActiveObjectId(const u16 id, + UNORDERED_MAP &objects) +{ + if(id == 0) + return false; + + return objects.find(id) == objects.end(); +} + +u16 getFreeClientActiveObjectId(UNORDERED_MAP &objects) +{ + //try to reuse id's as late as possible + static u16 last_used_id = 0; + u16 startid = last_used_id; + for(;;) { + last_used_id ++; + if (isFreeClientActiveObjectId(last_used_id, objects)) + return last_used_id; + + if (last_used_id == startid) + return 0; + } +} + +u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) +{ + assert(object); // Pre-condition + if(object->getId() == 0) + { + u16 new_id = getFreeClientActiveObjectId(m_active_objects); + if(new_id == 0) + { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"no free ids available"<setId(new_id); + } + if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"id is not free ("<getId()<<")"<getId()] = object; + object->addToScene(m_smgr, m_texturesource, m_irr); + { // Update lighting immediately + u8 light = 0; + bool pos_ok; + + // Get node at head + v3s16 p = object->getLightPosition(); + MapNode n = m_map->getNodeNoEx(p, &pos_ok); + if (pos_ok) + light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); + else + light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); + + object->updateLight(light); + } + return object->getId(); +} + +void ClientEnvironment::addActiveObject(u16 id, u8 type, + const std::string &init_data) +{ + ClientActiveObject* obj = + ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); + if(obj == NULL) + { + infostream<<"ClientEnvironment::addActiveObject(): " + <<"id="<setId(id); + + try + { + obj->initialize(init_data); + } + catch(SerializationError &e) + { + errorstream<<"ClientEnvironment::addActiveObject():" + <<" id="<removeFromScene(true); + delete obj; + m_active_objects.erase(id); +} + +void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) +{ + ClientActiveObject *obj = getActiveObject(id); + if (obj == NULL) { + infostream << "ClientEnvironment::processActiveObjectMessage():" + << " got message for id=" << id << ", which doesn't exist." + << std::endl; + return; + } + + try { + obj->processMessage(data); + } catch (SerializationError &e) { + errorstream<<"ClientEnvironment::processActiveObjectMessage():" + << " id=" << id << " type=" << obj->getType() + << " SerializationError in processMessage(): " << e.what() + << std::endl; + } +} + +/* + Callbacks for activeobjects +*/ + +void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) +{ + LocalPlayer *lplayer = getLocalPlayer(); + assert(lplayer); + + if (handle_hp) { + if (lplayer->hp > damage) + lplayer->hp -= damage; + else + lplayer->hp = 0; + } + + ClientEnvEvent event; + event.type = CEE_PLAYER_DAMAGE; + event.player_damage.amount = damage; + event.player_damage.send_to_server = handle_hp; + m_client_event_queue.push(event); +} + +void ClientEnvironment::updateLocalPlayerBreath(u16 breath) +{ + ClientEnvEvent event; + event.type = CEE_PLAYER_BREATH; + event.player_breath.amount = breath; + m_client_event_queue.push(event); +} + +/* + Client likes to call these +*/ + +void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, + std::vector &dest) +{ + for (UNORDERED_MAP::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ClientActiveObject* obj = i->second; + + f32 d = (obj->getPosition() - origin).getLength(); + + if(d > max_d) + continue; + + DistanceSortedActiveObject dso(obj, d); + + dest.push_back(dso); + } +} + +ClientEnvEvent ClientEnvironment::getClientEvent() +{ + ClientEnvEvent event; + if(m_client_event_queue.empty()) + event.type = CEE_NONE; + else { + event = m_client_event_queue.front(); + m_client_event_queue.pop(); + } + return event; +} + +ClientActiveObject * ClientEnvironment::getSelectedActiveObject( + const core::line3d &shootline_on_map, v3f *intersection_point, + v3s16 *intersection_normal) +{ + std::vector objects; + getActiveObjects(shootline_on_map.start, + shootline_on_map.getLength() + 3, objects); + const v3f line_vector = shootline_on_map.getVector(); + + // Sort them. + // After this, the closest object is the first in the array. + std::sort(objects.begin(), objects.end()); + + /* Because objects can have different nodebox sizes, + * the object whose center is the nearest isn't necessarily + * the closest one. If an object is found, don't stop + * immediately. */ + + f32 d_min = shootline_on_map.getLength(); + ClientActiveObject *nearest_obj = NULL; + for (u32 i = 0; i < objects.size(); i++) { + ClientActiveObject *obj = objects[i].obj; + + aabb3f *selection_box = obj->getSelectionBox(); + if (selection_box == NULL) + continue; + + v3f pos = obj->getPosition(); + + aabb3f offsetted_box(selection_box->MinEdge + pos, + selection_box->MaxEdge + pos); + + if (offsetted_box.getCenter().getDistanceFrom( + shootline_on_map.start) > d_min + 9.6f*BS) { + // Probably there is no active object that has bigger nodebox than + // (-5.5,-5.5,-5.5,5.5,5.5,5.5) + // 9.6 > 5.5*sqrt(3) + break; + } + + v3f current_intersection; + v3s16 current_normal; + if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, + ¤t_intersection, ¤t_normal)) { + f32 d_current = current_intersection.getDistanceFrom( + shootline_on_map.start); + if (d_current <= d_min) { + d_min = d_current; + nearest_obj = obj; + *intersection_point = current_intersection; + *intersection_normal = current_normal; + } + } + } + + return nearest_obj; +} + +/* + Check if a node is pointable +*/ +static inline bool isPointableNode(const MapNode &n, + INodeDefManager *ndef, bool liquids_pointable) +{ + const ContentFeatures &features = ndef->get(n); + return features.pointable || + (liquids_pointable && features.isLiquid()); +} + +PointedThing ClientEnvironment::getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object) +{ + PointedThing result; + + INodeDefManager *nodedef = m_map->getNodeDefManager(); + + core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); + // The code needs to search these nodes + core::aabbox3d search_range(-maximal_exceed.MaxEdge, + -maximal_exceed.MinEdge); + // If a node is found, there might be a larger node behind. + // To find it, we have to go further. + s16 maximal_overcheck = + std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) + + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) + + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); + + const v3f original_vector = shootline.getVector(); + const f32 original_length = original_vector.getLength(); + + f32 min_distance = original_length; + + // First try to find an active object + if (look_for_object) { + ClientActiveObject *selected_object = getSelectedActiveObject( + shootline, &result.intersection_point, + &result.intersection_normal); + + if (selected_object != NULL) { + min_distance = + (result.intersection_point - shootline.start).getLength(); + + result.type = POINTEDTHING_OBJECT; + result.object_id = selected_object->getId(); + } + } + + // Reduce shootline + if (original_length > 0) { + shootline.end = shootline.start + + shootline.getVector() / original_length * min_distance; + } + + // Try to find a node that is closer than the selected active + // object (if it exists). + + voxalgo::VoxelLineIterator iterator(shootline.start / BS, + shootline.getVector() / BS); + v3s16 oldnode = iterator.m_current_node_pos; + // Indicates that a node was found. + bool is_node_found = false; + // If a node is found, it is possible that there's a node + // behind it with a large nodebox, so continue the search. + u16 node_foundcounter = 0; + // If a node is found, this is the center of the + // first nodebox the shootline meets. + v3f found_boxcenter(0, 0, 0); + // The untested nodes are in this range. + core::aabbox3d new_nodes; + while (true) { + // Test the nodes around the current node in search_range. + new_nodes = search_range; + new_nodes.MinEdge += iterator.m_current_node_pos; + new_nodes.MaxEdge += iterator.m_current_node_pos; + + // Only check new nodes + v3s16 delta = iterator.m_current_node_pos - oldnode; + if (delta.X > 0) + new_nodes.MinEdge.X = new_nodes.MaxEdge.X; + else if (delta.X < 0) + new_nodes.MaxEdge.X = new_nodes.MinEdge.X; + else if (delta.Y > 0) + new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; + else if (delta.Y < 0) + new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; + else if (delta.Z > 0) + new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; + else if (delta.Z < 0) + new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; + + // For each untested node + for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { + for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { + for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { + MapNode n; + v3s16 np(x, y, z); + bool is_valid_position; + + n = m_map->getNodeNoEx(np, &is_valid_position); + if (!(is_valid_position && + isPointableNode(n, nodedef, liquids_pointable))) { + continue; + } + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(np, m_map)); + + v3f npf = intToFloat(np, BS); + for (std::vector::const_iterator i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += npf; + box.MaxEdge += npf; + v3f intersection_point; + v3s16 intersection_normal; + if (!boxLineCollision(box, shootline.start, shootline.getVector(), + &intersection_point, &intersection_normal)) { + continue; + } + f32 distance = (intersection_point - shootline.start).getLength(); + if (distance >= min_distance) { + continue; + } + result.type = POINTEDTHING_NODE; + result.node_undersurface = np; + result.intersection_point = intersection_point; + result.intersection_normal = intersection_normal; + found_boxcenter = box.getCenter(); + min_distance = distance; + is_node_found = true; + } + } + } + } + if (is_node_found) { + node_foundcounter++; + if (node_foundcounter > maximal_overcheck) { + break; + } + } + // Next node + if (iterator.hasNext()) { + oldnode = iterator.m_current_node_pos; + iterator.next(); + } else { + break; + } + } + + if (is_node_found) { + // Set undersurface and abovesurface nodes + f32 d = 0.002 * BS; + v3f fake_intersection = result.intersection_point; + // Move intersection towards its source block. + if (fake_intersection.X < found_boxcenter.X) + fake_intersection.X += d; + else + fake_intersection.X -= d; + + if (fake_intersection.Y < found_boxcenter.Y) + fake_intersection.Y += d; + else + fake_intersection.Y -= d; + + if (fake_intersection.Z < found_boxcenter.Z) + fake_intersection.Z += d; + else + fake_intersection.Z -= d; + + result.node_real_undersurface = floatToInt(fake_intersection, BS); + result.node_abovesurface = result.node_real_undersurface + + result.intersection_normal; + } + return result; +} diff --git a/src/clientenvironment.h b/src/clientenvironment.h new file mode 100644 index 000000000..e6292b5b7 --- /dev/null +++ b/src/clientenvironment.h @@ -0,0 +1,191 @@ +/* +Minetest +Copyright (C) 2010-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CLIENT_ENVIRONMENT_HEADER +#define CLIENT_ENVIRONMENT_HEADER + +#include +#include +#include "environment.h" +#include "clientobject.h" + +class ClientSimpleObject; +class ClientMap; +class ClientActiveObject; +class GenericCAO; +class LocalPlayer; + +/* + The client-side environment. + + This is not thread-safe. + Must be called from main (irrlicht) thread (uses the SceneManager) + Client uses an environment mutex. +*/ + +enum ClientEnvEventType +{ + CEE_NONE, + CEE_PLAYER_DAMAGE, + CEE_PLAYER_BREATH +}; + +struct ClientEnvEvent +{ + ClientEnvEventType type; + union { + //struct{ + //} none; + struct{ + u8 amount; + bool send_to_server; + } player_damage; + struct{ + u16 amount; + } player_breath; + }; +}; + +class ClientEnvironment : public Environment +{ +public: + ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, + ITextureSource *texturesource, IGameDef *gamedef, + IrrlichtDevice *device); + ~ClientEnvironment(); + + Map & getMap(); + ClientMap & getClientMap(); + + IGameDef *getGameDef() + { return m_gamedef; } + + void step(f32 dtime); + + virtual void setLocalPlayer(LocalPlayer *player); + LocalPlayer *getLocalPlayer() { return m_local_player; } + + /* + ClientSimpleObjects + */ + + void addSimpleObject(ClientSimpleObject *simple); + + /* + ActiveObjects + */ + + GenericCAO* getGenericCAO(u16 id); + ClientActiveObject* getActiveObject(u16 id); + + /* + Adds an active object to the environment. + Environment handles deletion of object. + Object may be deleted by environment immediately. + If id of object is 0, assigns a free id to it. + Returns the id of the object. + Returns 0 if not added and thus deleted. + */ + u16 addActiveObject(ClientActiveObject *object); + + void addActiveObject(u16 id, u8 type, const std::string &init_data); + void removeActiveObject(u16 id); + + void processActiveObjectMessage(u16 id, const std::string &data); + + /* + Callbacks for activeobjects + */ + + void damageLocalPlayer(u8 damage, bool handle_hp=true); + void updateLocalPlayerBreath(u16 breath); + + /* + Client likes to call these + */ + + // Get all nearby objects + void getActiveObjects(v3f origin, f32 max_d, + std::vector &dest); + + // Get event from queue. CEE_NONE is returned if queue is empty. + ClientEnvEvent getClientEvent(); + + /*! + * Gets closest object pointed by the shootline. + * Returns NULL if not found. + * + * \param[in] shootline_on_map the shootline for + * the test in world coordinates + * \param[out] intersection_point the first point where + * the shootline meets the object. Valid only if + * not NULL is returned. + * \param[out] intersection_normal the normal vector of + * the intersection, pointing outwards. Zero vector if + * the shootline starts in an active object. + * Valid only if not NULL is returned. + */ + ClientActiveObject * getSelectedActiveObject( + const core::line3d &shootline_on_map, + v3f *intersection_point, + v3s16 *intersection_normal + ); + + /*! + * Performs a raycast on the world. + * Returns the first thing the shootline meets. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + */ + PointedThing getPointedThing( + core::line3d shootline, + bool liquids_pointable, + bool look_for_object); + + u16 attachement_parent_ids[USHRT_MAX + 1]; + + const std::list &getPlayerNames() { return m_player_names; } + void addPlayerName(const std::string &name) { m_player_names.push_back(name); } + void removePlayerName(const std::string &name) { m_player_names.remove(name); } + void updateCameraOffset(v3s16 camera_offset) + { m_camera_offset = camera_offset; } + v3s16 getCameraOffset() const { return m_camera_offset; } +private: + ClientMap *m_map; + LocalPlayer *m_local_player; + scene::ISceneManager *m_smgr; + ITextureSource *m_texturesource; + IGameDef *m_gamedef; + IrrlichtDevice *m_irr; + UNORDERED_MAP m_active_objects; + std::vector m_simple_objects; + std::queue m_client_event_queue; + IntervalLimiter m_active_object_light_update_interval; + IntervalLimiter m_lava_hurt_interval; + IntervalLimiter m_drowning_interval; + IntervalLimiter m_breathing_interval; + std::list m_player_names; + v3s16 m_camera_offset; +}; + +#endif diff --git a/src/collision.cpp b/src/collision.cpp index 8e5dbcc9b..21f14bec1 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,12 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" -#include "log.h" -#include "environment.h" +#include "clientenvironment.h" #include "serverobject.h" -#include -#include -#include "util/timetaker.h" #include "profiler.h" // float error is 10 - 9.96875 = 0.03125 diff --git a/src/content_cso.cpp b/src/content_cso.cpp index 0790024fc..c0407f460 100644 --- a/src/content_cso.cpp +++ b/src/content_cso.cpp @@ -20,9 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_cso.h" #include #include "client/tile.h" -#include "environment.h" +#include "clientenvironment.h" #include "gamedef.h" -#include "log.h" #include "map.h" /* diff --git a/src/environment.cpp b/src/environment.cpp index 50f5d58aa..6f6e20238 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -33,12 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "nodemetadata.h" #include "gamedef.h" -#ifndef SERVER -#include "clientmap.h" -#include "localplayer.h" -#include "mapblock_mesh.h" -#include "event.h" -#endif #include "server.h" #include "daynightratio.h" #include "map.h" @@ -2279,825 +2273,3 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) } } -#ifndef SERVER - -#include "clientsimpleobject.h" - -/* - ClientEnvironment -*/ - -ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, - IrrlichtDevice *irr): - m_map(map), - m_local_player(NULL), - m_smgr(smgr), - m_texturesource(texturesource), - m_gamedef(gamedef), - m_irr(irr) -{ - char zero = 0; - memset(attachement_parent_ids, zero, sizeof(attachement_parent_ids)); -} - -ClientEnvironment::~ClientEnvironment() -{ - // delete active objects - for (UNORDERED_MAP::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - delete i->second; - } - - for(std::vector::iterator - i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i) { - delete *i; - } - - // Drop/delete map - m_map->drop(); -} - -Map & ClientEnvironment::getMap() -{ - return *m_map; -} - -ClientMap & ClientEnvironment::getClientMap() -{ - return *m_map; -} - -void ClientEnvironment::setLocalPlayer(LocalPlayer *player) -{ - DSTACK(FUNCTION_NAME); - /* - It is a failure if already is a local player - */ - FATAL_ERROR_IF(m_local_player != NULL, - "Local player already allocated"); - - m_local_player = player; -} - -void ClientEnvironment::step(float dtime) -{ - DSTACK(FUNCTION_NAME); - - /* Step time of day */ - stepTimeOfDay(dtime); - - // Get some settings - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool free_move = fly_allowed && g_settings->getBool("free_move"); - - // Get local player - LocalPlayer *lplayer = getLocalPlayer(); - assert(lplayer); - // collision info queue - std::vector player_collisions; - - /* - Get the speed the player is going - */ - bool is_climbing = lplayer->is_climbing; - - f32 player_speed = lplayer->getSpeed().getLength(); - - /* - Maximum position increment - */ - //f32 position_max_increment = 0.05*BS; - f32 position_max_increment = 0.1*BS; - - // Maximum time increment (for collision detection etc) - // time = distance / speed - f32 dtime_max_increment = 1; - if(player_speed > 0.001) - dtime_max_increment = position_max_increment / player_speed; - - // Maximum time increment is 10ms or lower - if(dtime_max_increment > 0.01) - dtime_max_increment = 0.01; - - // Don't allow overly huge dtime - if(dtime > 0.5) - dtime = 0.5; - - f32 dtime_downcount = dtime; - - /* - Stuff that has a maximum time increment - */ - - u32 loopcount = 0; - do - { - loopcount++; - - f32 dtime_part; - if(dtime_downcount > dtime_max_increment) - { - dtime_part = dtime_max_increment; - dtime_downcount -= dtime_part; - } - else - { - dtime_part = dtime_downcount; - /* - Setting this to 0 (no -=dtime_part) disables an infinite loop - when dtime_part is so small that dtime_downcount -= dtime_part - does nothing - */ - dtime_downcount = 0; - } - - /* - Handle local player - */ - - { - // Apply physics - if(!free_move && !is_climbing) - { - // Gravity - v3f speed = lplayer->getSpeed(); - if(!lplayer->in_liquid) - speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2; - - // Liquid floating / sinking - if(lplayer->in_liquid && !lplayer->swimming_vertical) - speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2; - - // Liquid resistance - if(lplayer->in_liquid_stable || lplayer->in_liquid) - { - // How much the node's viscosity blocks movement, ranges between 0 and 1 - // Should match the scale at which viscosity increase affects other liquid attributes - const f32 viscosity_factor = 0.3; - - v3f d_wanted = -speed / lplayer->movement_liquid_fluidity; - f32 dl = d_wanted.getLength(); - if(dl > lplayer->movement_liquid_fluidity_smooth) - dl = lplayer->movement_liquid_fluidity_smooth; - dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor); - - v3f d = d_wanted.normalize() * dl; - speed += d; - } - - lplayer->setSpeed(speed); - } - - /* - Move the lplayer. - This also does collision detection. - */ - lplayer->move(dtime_part, this, position_max_increment, - &player_collisions); - } - } - while(dtime_downcount > 0.001); - - //std::cout<<"Looped "<::iterator i = player_collisions.begin(); - i != player_collisions.end(); ++i) { - CollisionInfo &info = *i; - v3f speed_diff = info.new_speed - info.old_speed;; - // Handle only fall damage - // (because otherwise walking against something in fast_move kills you) - if(speed_diff.Y < 0 || info.old_speed.Y >= 0) - continue; - // Get rid of other components - speed_diff.X = 0; - speed_diff.Z = 0; - f32 pre_factor = 1; // 1 hp per node/s - f32 tolerance = BS*14; // 5 without damage - f32 post_factor = 1; // 1 hp per node/s - if(info.type == COLLISION_NODE) - { - const ContentFeatures &f = m_gamedef->ndef()-> - get(m_map->getNodeNoEx(info.node_p)); - // Determine fall damage multiplier - int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); - pre_factor = 1.0 + (float)addp/100.0; - } - float speed = pre_factor * speed_diff.getLength(); - if(speed > tolerance) - { - f32 damage_f = (speed - tolerance)/BS * post_factor; - u16 damage = (u16)(damage_f+0.5); - if(damage != 0){ - damageLocalPlayer(damage, true); - MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); - m_gamedef->event()->put(e); - } - } - } - - /* - A quick draft of lava damage - */ - if(m_lava_hurt_interval.step(dtime, 1.0)) - { - v3f pf = lplayer->getPosition(); - - // Feet, middle and head - v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS); - MapNode n1 = m_map->getNodeNoEx(p1); - v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS); - MapNode n2 = m_map->getNodeNoEx(p2); - v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS); - MapNode n3 = m_map->getNodeNoEx(p3); - - u32 damage_per_second = 0; - damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n1).damage_per_second); - damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n2).damage_per_second); - damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n3).damage_per_second); - - if(damage_per_second != 0) - { - damageLocalPlayer(damage_per_second, true); - } - } - - // Protocol v29 make this behaviour obsolete - if (((Client*) getGameDef())->getProtoVersion() < 29) { - /* - Drowning - */ - if (m_drowning_interval.step(dtime, 2.0)) { - v3f pf = lplayer->getPosition(); - - // head - v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); - MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); - u8 drowning_damage = c.drowning; - if (drowning_damage > 0 && lplayer->hp > 0) { - u16 breath = lplayer->getBreath(); - if (breath > 10) { - breath = 11; - } - if (breath > 0) { - breath -= 1; - } - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); - } - - if (lplayer->getBreath() == 0 && drowning_damage > 0) { - damageLocalPlayer(drowning_damage, true); - } - } - if (m_breathing_interval.step(dtime, 0.5)) { - v3f pf = lplayer->getPosition(); - - // head - v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); - MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); - if (!lplayer->hp) { - lplayer->setBreath(11); - } else if (c.drowning == 0) { - u16 breath = lplayer->getBreath(); - if (breath <= 10) { - breath += 1; - lplayer->setBreath(breath); - updateLocalPlayerBreath(breath); - } - } - } - } - - // Update lighting on local player (used for wield item) - u32 day_night_ratio = getDayNightRatio(); - { - // Get node at head - - // On InvalidPositionException, use this as default - // (day: LIGHT_SUN, night: 0) - MapNode node_at_lplayer(CONTENT_AIR, 0x0f, 0); - - v3s16 p = lplayer->getLightPosition(); - node_at_lplayer = m_map->getNodeNoEx(p); - - u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); - u8 day = light & 0xff; - u8 night = (light >> 8) & 0xff; - finalColorBlend(lplayer->light_color, day, night, day_night_ratio); - } - - /* - Step active objects and update lighting of them - */ - - g_profiler->avg("CEnv: num of objects", m_active_objects.size()); - bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21); - for (UNORDERED_MAP::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ClientActiveObject* obj = i->second; - // Step object - obj->step(dtime, this); - - if(update_lighting) - { - // Update lighting - u8 light = 0; - bool pos_ok; - - // Get node at head - v3s16 p = obj->getLightPosition(); - MapNode n = m_map->getNodeNoEx(p, &pos_ok); - if (pos_ok) - light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); - else - light = blend_light(day_night_ratio, LIGHT_SUN, 0); - - obj->updateLight(light); - } - } - - /* - Step and handle simple objects - */ - g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size()); - for(std::vector::iterator - i = m_simple_objects.begin(); i != m_simple_objects.end();) { - std::vector::iterator cur = i; - ClientSimpleObject *simple = *cur; - - simple->step(dtime); - if(simple->m_to_be_removed) { - delete simple; - i = m_simple_objects.erase(cur); - } - else { - ++i; - } - } -} - -void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) -{ - m_simple_objects.push_back(simple); -} - -GenericCAO* ClientEnvironment::getGenericCAO(u16 id) -{ - ClientActiveObject *obj = getActiveObject(id); - if (obj && obj->getType() == ACTIVEOBJECT_TYPE_GENERIC) - return (GenericCAO*) obj; - else - return NULL; -} - -ClientActiveObject* ClientEnvironment::getActiveObject(u16 id) -{ - UNORDERED_MAP::iterator n = m_active_objects.find(id); - if (n == m_active_objects.end()) - return NULL; - return n->second; -} - -bool isFreeClientActiveObjectId(const u16 id, - UNORDERED_MAP &objects) -{ - if(id == 0) - return false; - - return objects.find(id) == objects.end(); -} - -u16 getFreeClientActiveObjectId(UNORDERED_MAP &objects) -{ - //try to reuse id's as late as possible - static u16 last_used_id = 0; - u16 startid = last_used_id; - for(;;) { - last_used_id ++; - if (isFreeClientActiveObjectId(last_used_id, objects)) - return last_used_id; - - if (last_used_id == startid) - return 0; - } -} - -u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) -{ - assert(object); // Pre-condition - if(object->getId() == 0) - { - u16 new_id = getFreeClientActiveObjectId(m_active_objects); - if(new_id == 0) - { - infostream<<"ClientEnvironment::addActiveObject(): " - <<"no free ids available"<setId(new_id); - } - if (!isFreeClientActiveObjectId(object->getId(), m_active_objects)) { - infostream<<"ClientEnvironment::addActiveObject(): " - <<"id is not free ("<getId()<<")"<getId()] = object; - object->addToScene(m_smgr, m_texturesource, m_irr); - { // Update lighting immediately - u8 light = 0; - bool pos_ok; - - // Get node at head - v3s16 p = object->getLightPosition(); - MapNode n = m_map->getNodeNoEx(p, &pos_ok); - if (pos_ok) - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); - else - light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); - - object->updateLight(light); - } - return object->getId(); -} - -void ClientEnvironment::addActiveObject(u16 id, u8 type, - const std::string &init_data) -{ - ClientActiveObject* obj = - ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); - if(obj == NULL) - { - infostream<<"ClientEnvironment::addActiveObject(): " - <<"id="<setId(id); - - try - { - obj->initialize(init_data); - } - catch(SerializationError &e) - { - errorstream<<"ClientEnvironment::addActiveObject():" - <<" id="<removeFromScene(true); - delete obj; - m_active_objects.erase(id); -} - -void ClientEnvironment::processActiveObjectMessage(u16 id, const std::string &data) -{ - ClientActiveObject *obj = getActiveObject(id); - if (obj == NULL) { - infostream << "ClientEnvironment::processActiveObjectMessage():" - << " got message for id=" << id << ", which doesn't exist." - << std::endl; - return; - } - - try { - obj->processMessage(data); - } catch (SerializationError &e) { - errorstream<<"ClientEnvironment::processActiveObjectMessage():" - << " id=" << id << " type=" << obj->getType() - << " SerializationError in processMessage(): " << e.what() - << std::endl; - } -} - -/* - Callbacks for activeobjects -*/ - -void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp) -{ - LocalPlayer *lplayer = getLocalPlayer(); - assert(lplayer); - - if (handle_hp) { - if (lplayer->hp > damage) - lplayer->hp -= damage; - else - lplayer->hp = 0; - } - - ClientEnvEvent event; - event.type = CEE_PLAYER_DAMAGE; - event.player_damage.amount = damage; - event.player_damage.send_to_server = handle_hp; - m_client_event_queue.push(event); -} - -void ClientEnvironment::updateLocalPlayerBreath(u16 breath) -{ - ClientEnvEvent event; - event.type = CEE_PLAYER_BREATH; - event.player_breath.amount = breath; - m_client_event_queue.push(event); -} - -/* - Client likes to call these -*/ - -void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d, - std::vector &dest) -{ - for (UNORDERED_MAP::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ClientActiveObject* obj = i->second; - - f32 d = (obj->getPosition() - origin).getLength(); - - if(d > max_d) - continue; - - DistanceSortedActiveObject dso(obj, d); - - dest.push_back(dso); - } -} - -ClientEnvEvent ClientEnvironment::getClientEvent() -{ - ClientEnvEvent event; - if(m_client_event_queue.empty()) - event.type = CEE_NONE; - else { - event = m_client_event_queue.front(); - m_client_event_queue.pop(); - } - return event; -} - -ClientActiveObject * ClientEnvironment::getSelectedActiveObject( - const core::line3d &shootline_on_map, v3f *intersection_point, - v3s16 *intersection_normal) -{ - std::vector objects; - getActiveObjects(shootline_on_map.start, - shootline_on_map.getLength() + 3, objects); - const v3f line_vector = shootline_on_map.getVector(); - - // Sort them. - // After this, the closest object is the first in the array. - std::sort(objects.begin(), objects.end()); - - /* Because objects can have different nodebox sizes, - * the object whose center is the nearest isn't necessarily - * the closest one. If an object is found, don't stop - * immediately. */ - - f32 d_min = shootline_on_map.getLength(); - ClientActiveObject *nearest_obj = NULL; - for (u32 i = 0; i < objects.size(); i++) { - ClientActiveObject *obj = objects[i].obj; - - aabb3f *selection_box = obj->getSelectionBox(); - if (selection_box == NULL) - continue; - - v3f pos = obj->getPosition(); - - aabb3f offsetted_box(selection_box->MinEdge + pos, - selection_box->MaxEdge + pos); - - if (offsetted_box.getCenter().getDistanceFrom( - shootline_on_map.start) > d_min + 9.6f*BS) { - // Probably there is no active object that has bigger nodebox than - // (-5.5,-5.5,-5.5,5.5,5.5,5.5) - // 9.6 > 5.5*sqrt(3) - break; - } - - v3f current_intersection; - v3s16 current_normal; - if (boxLineCollision(offsetted_box, shootline_on_map.start, line_vector, - ¤t_intersection, ¤t_normal)) { - f32 d_current = current_intersection.getDistanceFrom( - shootline_on_map.start); - if (d_current <= d_min) { - d_min = d_current; - nearest_obj = obj; - *intersection_point = current_intersection; - *intersection_normal = current_normal; - } - } - } - - return nearest_obj; -} - -/* - Check if a node is pointable -*/ -static inline bool isPointableNode(const MapNode &n, - INodeDefManager *ndef, bool liquids_pointable) -{ - const ContentFeatures &features = ndef->get(n); - return features.pointable || - (liquids_pointable && features.isLiquid()); -} - -PointedThing ClientEnvironment::getPointedThing( - core::line3d shootline, - bool liquids_pointable, - bool look_for_object) -{ - PointedThing result; - - INodeDefManager *nodedef = m_map->getNodeDefManager(); - - core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); - // The code needs to search these nodes - core::aabbox3d search_range(-maximal_exceed.MaxEdge, - -maximal_exceed.MinEdge); - // If a node is found, there might be a larger node behind. - // To find it, we have to go further. - s16 maximal_overcheck = - std::max(abs(search_range.MinEdge.X), abs(search_range.MaxEdge.X)) - + std::max(abs(search_range.MinEdge.Y), abs(search_range.MaxEdge.Y)) - + std::max(abs(search_range.MinEdge.Z), abs(search_range.MaxEdge.Z)); - - const v3f original_vector = shootline.getVector(); - const f32 original_length = original_vector.getLength(); - - f32 min_distance = original_length; - - // First try to find an active object - if (look_for_object) { - ClientActiveObject *selected_object = getSelectedActiveObject( - shootline, &result.intersection_point, - &result.intersection_normal); - - if (selected_object != NULL) { - min_distance = - (result.intersection_point - shootline.start).getLength(); - - result.type = POINTEDTHING_OBJECT; - result.object_id = selected_object->getId(); - } - } - - // Reduce shootline - if (original_length > 0) { - shootline.end = shootline.start - + shootline.getVector() / original_length * min_distance; - } - - // Try to find a node that is closer than the selected active - // object (if it exists). - - voxalgo::VoxelLineIterator iterator(shootline.start / BS, - shootline.getVector() / BS); - v3s16 oldnode = iterator.m_current_node_pos; - // Indicates that a node was found. - bool is_node_found = false; - // If a node is found, it is possible that there's a node - // behind it with a large nodebox, so continue the search. - u16 node_foundcounter = 0; - // If a node is found, this is the center of the - // first nodebox the shootline meets. - v3f found_boxcenter(0, 0, 0); - // The untested nodes are in this range. - core::aabbox3d new_nodes; - while (true) { - // Test the nodes around the current node in search_range. - new_nodes = search_range; - new_nodes.MinEdge += iterator.m_current_node_pos; - new_nodes.MaxEdge += iterator.m_current_node_pos; - - // Only check new nodes - v3s16 delta = iterator.m_current_node_pos - oldnode; - if (delta.X > 0) - new_nodes.MinEdge.X = new_nodes.MaxEdge.X; - else if (delta.X < 0) - new_nodes.MaxEdge.X = new_nodes.MinEdge.X; - else if (delta.Y > 0) - new_nodes.MinEdge.Y = new_nodes.MaxEdge.Y; - else if (delta.Y < 0) - new_nodes.MaxEdge.Y = new_nodes.MinEdge.Y; - else if (delta.Z > 0) - new_nodes.MinEdge.Z = new_nodes.MaxEdge.Z; - else if (delta.Z < 0) - new_nodes.MaxEdge.Z = new_nodes.MinEdge.Z; - - // For each untested node - for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { - for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) { - for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { - MapNode n; - v3s16 np(x, y, z); - bool is_valid_position; - - n = m_map->getNodeNoEx(np, &is_valid_position); - if (!(is_valid_position && - isPointableNode(n, nodedef, liquids_pointable))) { - continue; - } - std::vector boxes; - n.getSelectionBoxes(nodedef, &boxes, - n.getNeighbors(np, m_map)); - - v3f npf = intToFloat(np, BS); - for (std::vector::const_iterator i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - box.MinEdge += npf; - box.MaxEdge += npf; - v3f intersection_point; - v3s16 intersection_normal; - if (!boxLineCollision(box, shootline.start, shootline.getVector(), - &intersection_point, &intersection_normal)) { - continue; - } - f32 distance = (intersection_point - shootline.start).getLength(); - if (distance >= min_distance) { - continue; - } - result.type = POINTEDTHING_NODE; - result.node_undersurface = np; - result.intersection_point = intersection_point; - result.intersection_normal = intersection_normal; - found_boxcenter = box.getCenter(); - min_distance = distance; - is_node_found = true; - } - } - } - } - if (is_node_found) { - node_foundcounter++; - if (node_foundcounter > maximal_overcheck) { - break; - } - } - // Next node - if (iterator.hasNext()) { - oldnode = iterator.m_current_node_pos; - iterator.next(); - } else { - break; - } - } - - if (is_node_found) { - // Set undersurface and abovesurface nodes - f32 d = 0.002 * BS; - v3f fake_intersection = result.intersection_point; - // Move intersection towards its source block. - if (fake_intersection.X < found_boxcenter.X) - fake_intersection.X += d; - else - fake_intersection.X -= d; - - if (fake_intersection.Y < found_boxcenter.Y) - fake_intersection.Y += d; - else - fake_intersection.Y -= d; - - if (fake_intersection.Z < found_boxcenter.Z) - fake_intersection.Z += d; - else - fake_intersection.Z -= d; - - result.node_real_undersurface = floatToInt(fake_intersection, BS); - result.node_abovesurface = result.node_real_undersurface - + result.intersection_normal; - } - return result; -} - -#endif // #ifndef SERVER diff --git a/src/environment.h b/src/environment.h index 84805b462..209d795d8 100644 --- a/src/environment.h +++ b/src/environment.h @@ -50,7 +50,6 @@ class ITextureSource; class IGameDef; class Map; class ServerMap; -class ClientMap; class GameScripting; class Player; class RemotePlayer; @@ -525,171 +524,5 @@ private: UNORDERED_MAP m_particle_spawner_attachments; }; -#ifndef SERVER - -#include "clientobject.h" -#include "content_cao.h" - -class ClientSimpleObject; - -/* - The client-side environment. - - This is not thread-safe. - Must be called from main (irrlicht) thread (uses the SceneManager) - Client uses an environment mutex. -*/ - -enum ClientEnvEventType -{ - CEE_NONE, - CEE_PLAYER_DAMAGE, - CEE_PLAYER_BREATH -}; - -struct ClientEnvEvent -{ - ClientEnvEventType type; - union { - //struct{ - //} none; - struct{ - u8 amount; - bool send_to_server; - } player_damage; - struct{ - u16 amount; - } player_breath; - }; -}; - -class ClientEnvironment : public Environment -{ -public: - ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, - IrrlichtDevice *device); - ~ClientEnvironment(); - - Map & getMap(); - ClientMap & getClientMap(); - - IGameDef *getGameDef() - { return m_gamedef; } - - void step(f32 dtime); - - virtual void setLocalPlayer(LocalPlayer *player); - LocalPlayer *getLocalPlayer() { return m_local_player; } - - /* - ClientSimpleObjects - */ - - void addSimpleObject(ClientSimpleObject *simple); - - /* - ActiveObjects - */ - - GenericCAO* getGenericCAO(u16 id); - ClientActiveObject* getActiveObject(u16 id); - - /* - Adds an active object to the environment. - Environment handles deletion of object. - Object may be deleted by environment immediately. - If id of object is 0, assigns a free id to it. - Returns the id of the object. - Returns 0 if not added and thus deleted. - */ - u16 addActiveObject(ClientActiveObject *object); - - void addActiveObject(u16 id, u8 type, const std::string &init_data); - void removeActiveObject(u16 id); - - void processActiveObjectMessage(u16 id, const std::string &data); - - /* - Callbacks for activeobjects - */ - - void damageLocalPlayer(u8 damage, bool handle_hp=true); - void updateLocalPlayerBreath(u16 breath); - - /* - Client likes to call these - */ - - // Get all nearby objects - void getActiveObjects(v3f origin, f32 max_d, - std::vector &dest); - - // Get event from queue. CEE_NONE is returned if queue is empty. - ClientEnvEvent getClientEvent(); - - /*! - * Gets closest object pointed by the shootline. - * Returns NULL if not found. - * - * \param[in] shootline_on_map the shootline for - * the test in world coordinates - * \param[out] intersection_point the first point where - * the shootline meets the object. Valid only if - * not NULL is returned. - * \param[out] intersection_normal the normal vector of - * the intersection, pointing outwards. Zero vector if - * the shootline starts in an active object. - * Valid only if not NULL is returned. - */ - ClientActiveObject * getSelectedActiveObject( - const core::line3d &shootline_on_map, - v3f *intersection_point, - v3s16 *intersection_normal - ); - - /*! - * Performs a raycast on the world. - * Returns the first thing the shootline meets. - * - * @param[in] shootline the shootline, starting from - * the camera position. This also gives the maximal distance - * of the search. - * @param[in] liquids_pointable if false, liquids are ignored - * @param[in] look_for_object if false, objects are ignored - */ - PointedThing getPointedThing( - core::line3d shootline, - bool liquids_pointable, - bool look_for_object); - - u16 attachement_parent_ids[USHRT_MAX + 1]; - - const std::list &getPlayerNames() { return m_player_names; } - void addPlayerName(const std::string &name) { m_player_names.push_back(name); } - void removePlayerName(const std::string &name) { m_player_names.remove(name); } - void updateCameraOffset(v3s16 camera_offset) - { m_camera_offset = camera_offset; } - v3s16 getCameraOffset() const { return m_camera_offset; } -private: - ClientMap *m_map; - LocalPlayer *m_local_player; - scene::ISceneManager *m_smgr; - ITextureSource *m_texturesource; - IGameDef *m_gamedef; - IrrlichtDevice *m_irr; - UNORDERED_MAP m_active_objects; - std::vector m_simple_objects; - std::queue m_client_event_queue; - IntervalLimiter m_active_object_light_update_interval; - IntervalLimiter m_lava_hurt_interval; - IntervalLimiter m_drowning_interval; - IntervalLimiter m_breathing_interval; - std::list m_player_names; - v3s16 m_camera_offset; -}; - -#endif - #endif -- cgit v1.2.3 From 98e36d7d681abc508aa81b6f9df0c8c87c7cfe17 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sun, 8 Jan 2017 11:01:35 +0100 Subject: Move ServerEnvironment to dedicated cpp/header files * also cleanup some unneeded inclusions --- src/CMakeLists.txt | 1 + src/clientiface.cpp | 2 +- src/collision.cpp | 1 + src/environment.cpp | 2149 ----------------------------------- src/environment.h | 397 ------- src/inventorymanager.cpp | 2 +- src/pathfinder.cpp | 2 +- src/script/lua_api/l_env.h | 2 +- src/script/lua_api/l_nodemeta.cpp | 2 +- src/script/lua_api/l_nodetimer.cpp | 2 +- src/server.h | 2 +- src/serverenvironment.cpp | 2167 ++++++++++++++++++++++++++++++++++++ src/serverenvironment.h | 423 +++++++ src/treegen.cpp | 3 +- 14 files changed, 2600 insertions(+), 2555 deletions(-) create mode 100644 src/serverenvironment.cpp create mode 100644 src/serverenvironment.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5228eb9ac..f90542be9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -452,6 +452,7 @@ set(common_SRCS rollback_interface.cpp serialization.cpp server.cpp + serverenvironment.cpp serverlist.cpp serverobject.cpp settings.cpp diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 0390cf0ff..1610c21fd 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "mapblock.h" #include "network/connection.h" -#include "environment.h" +#include "serverenvironment.h" #include "map.h" #include "emerge.h" #include "content_sao.h" // TODO this is used for cleanup of only diff --git a/src/collision.cpp b/src/collision.cpp index 21f14bec1..595fa8059 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "gamedef.h" #include "clientenvironment.h" +#include "serverenvironment.h" #include "serverobject.h" #include "profiler.h" diff --git a/src/environment.cpp b/src/environment.cpp index 6f6e20238..8c1aad9d3 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -19,35 +19,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "environment.h" -#include "filesys.h" -#include "porting.h" #include "collision.h" -#include "content_mapnode.h" -#include "mapblock.h" #include "serverobject.h" -#include "content_sao.h" -#include "settings.h" -#include "log.h" -#include "profiler.h" #include "scripting_game.h" -#include "nodedef.h" -#include "nodemetadata.h" -#include "gamedef.h" #include "server.h" #include "daynightratio.h" -#include "map.h" #include "emerge.h" -#include "raycast.h" -#include "voxelalgorithms.h" -#include "util/serialize.h" -#include "util/basic_macros.h" -#include "util/pointedthing.h" -#include "threading/mutex_auto_lock.h" - -#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" - -// A number that is much smaller than the timeout for particle spawners should/could ever be -#define PARTICLE_SPAWNER_NO_EXPIRY -1024.f Environment::Environment(): m_time_of_day_speed(0), @@ -147,2129 +124,3 @@ u32 Environment::getDayCount() // Atomic counter return m_day_count; } - - -/* - ABMWithState -*/ - -ABMWithState::ABMWithState(ActiveBlockModifier *abm_): - abm(abm_), - timer(0) -{ - // Initialize timer to random value to spread processing - float itv = abm->getTriggerInterval(); - itv = MYMAX(0.001, itv); // No less than 1ms - int minval = MYMAX(-0.51*itv, -60); // Clamp to - int maxval = MYMIN(0.51*itv, 60); // +-60 seconds - timer = myrand_range(minval, maxval); -} - -/* - LBMManager -*/ - -void LBMContentMapping::deleteContents() -{ - for (std::vector::iterator it = lbm_list.begin(); - it != lbm_list.end(); ++it) { - delete *it; - } -} - -void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) -{ - // Add the lbm_def to the LBMContentMapping. - // Unknown names get added to the global NameIdMapping. - INodeDefManager *nodedef = gamedef->ndef(); - - lbm_list.push_back(lbm_def); - - for (std::set::const_iterator it = lbm_def->trigger_contents.begin(); - it != lbm_def->trigger_contents.end(); ++it) { - std::set c_ids; - bool found = nodedef->getIds(*it, c_ids); - if (!found) { - content_t c_id = gamedef->allocateUnknownNodeId(*it); - if (c_id == CONTENT_IGNORE) { - // Seems it can't be allocated. - warningstream << "Could not internalize node name \"" << *it - << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; - continue; - } - c_ids.insert(c_id); - } - - for (std::set::const_iterator iit = - c_ids.begin(); iit != c_ids.end(); ++iit) { - content_t c_id = *iit; - map[c_id].push_back(lbm_def); - } - } -} - -const std::vector * - LBMContentMapping::lookup(content_t c) const -{ - container_map::const_iterator it = map.find(c); - if (it == map.end()) - return NULL; - // This first dereferences the iterator, returning - // a std::vector - // reference, then we convert it to a pointer. - return &(it->second); -} - -LBMManager::~LBMManager() -{ - for (std::map::iterator it = - m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { - delete it->second; - } - for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); - it != m_lbm_lookup.end(); ++it) { - (it->second).deleteContents(); - } -} - -void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) -{ - // Precondition, in query mode the map isn't used anymore - FATAL_ERROR_IF(m_query_mode == true, - "attempted to modify LBMManager in query mode"); - - if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { - throw ModError("Error adding LBM \"" + lbm_def->name + - "\": Does not follow naming conventions: " - "Only chararacters [a-z0-9_:] are allowed."); - } - - m_lbm_defs[lbm_def->name] = lbm_def; -} - -void LBMManager::loadIntroductionTimes(const std::string ×, - IGameDef *gamedef, u32 now) -{ - m_query_mode = true; - - // name -> time map. - // Storing it in a map first instead of - // handling the stuff directly in the loop - // removes all duplicate entries. - // TODO make this std::unordered_map - std::map introduction_times; - - /* - The introduction times string consists of name~time entries, - with each entry terminated by a semicolon. The time is decimal. - */ - - size_t idx = 0; - size_t idx_new; - while ((idx_new = times.find(";", idx)) != std::string::npos) { - std::string entry = times.substr(idx, idx_new - idx); - std::vector components = str_split(entry, '~'); - if (components.size() != 2) - throw SerializationError("Introduction times entry \"" - + entry + "\" requires exactly one '~'!"); - const std::string &name = components[0]; - u32 time = from_string(components[1]); - introduction_times[name] = time; - idx = idx_new + 1; - } - - // Put stuff from introduction_times into m_lbm_lookup - for (std::map::const_iterator it = introduction_times.begin(); - it != introduction_times.end(); ++it) { - const std::string &name = it->first; - u32 time = it->second; - - std::map::iterator def_it = - m_lbm_defs.find(name); - if (def_it == m_lbm_defs.end()) { - // This seems to be an LBM entry for - // an LBM we haven't loaded. Discard it. - continue; - } - LoadingBlockModifierDef *lbm_def = def_it->second; - if (lbm_def->run_at_every_load) { - // This seems to be an LBM entry for - // an LBM that runs at every load. - // Don't add it just yet. - continue; - } - - m_lbm_lookup[time].addLBM(lbm_def, gamedef); - - // Erase the entry so that we know later - // what elements didn't get put into m_lbm_lookup - m_lbm_defs.erase(name); - } - - // Now also add the elements from m_lbm_defs to m_lbm_lookup - // that weren't added in the previous step. - // They are introduced first time to this world, - // or are run at every load (introducement time hardcoded to U32_MAX). - - LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now]; - LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX]; - - for (std::map::iterator it = - m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { - if (it->second->run_at_every_load) { - lbms_running_always.addLBM(it->second, gamedef); - } else { - lbms_we_introduce_now.addLBM(it->second, gamedef); - } - } - - // Clear the list, so that we don't delete remaining elements - // twice in the destructor - m_lbm_defs.clear(); -} - -std::string LBMManager::createIntroductionTimesString() -{ - // Precondition, we must be in query mode - FATAL_ERROR_IF(m_query_mode == false, - "attempted to query on non fully set up LBMManager"); - - std::ostringstream oss; - for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); - it != m_lbm_lookup.end(); ++it) { - u32 time = it->first; - std::vector &lbm_list = it->second.lbm_list; - for (std::vector::iterator iit = lbm_list.begin(); - iit != lbm_list.end(); ++iit) { - // Don't add if the LBM runs at every load, - // then introducement time is hardcoded - // and doesn't need to be stored - if ((*iit)->run_at_every_load) - continue; - oss << (*iit)->name << "~" << time << ";"; - } - } - return oss.str(); -} - -void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) -{ - // Precondition, we need m_lbm_lookup to be initialized - FATAL_ERROR_IF(m_query_mode == false, - "attempted to query on non fully set up LBMManager"); - v3s16 pos_of_block = block->getPosRelative(); - v3s16 pos; - MapNode n; - content_t c; - lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp); - for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) - for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) - for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) - { - n = block->getNodeNoEx(pos); - c = n.getContent(); - for (LBMManager::lbm_lookup_map::const_iterator iit = it; - iit != m_lbm_lookup.end(); ++iit) { - const std::vector *lbm_list = - iit->second.lookup(c); - if (!lbm_list) - continue; - for (std::vector::const_iterator iit = - lbm_list->begin(); iit != lbm_list->end(); ++iit) { - (*iit)->trigger(env, pos + pos_of_block, n); - } - } - } -} - -/* - ActiveBlockList -*/ - -void fillRadiusBlock(v3s16 p0, s16 r, std::set &list) -{ - v3s16 p; - for(p.X=p0.X-r; p.X<=p0.X+r; p.X++) - for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++) - for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++) - { - // limit to a sphere - if (p.getDistanceFrom(p0) <= r) { - // Set in list - list.insert(p); - } - } -} - -void ActiveBlockList::update(std::vector &active_positions, - s16 radius, - std::set &blocks_removed, - std::set &blocks_added) -{ - /* - Create the new list - */ - std::set newlist = m_forceloaded_list; - for(std::vector::iterator i = active_positions.begin(); - i != active_positions.end(); ++i) - { - fillRadiusBlock(*i, radius, newlist); - } - - /* - Find out which blocks on the old list are not on the new list - */ - // Go through old list - for(std::set::iterator i = m_list.begin(); - i != m_list.end(); ++i) - { - v3s16 p = *i; - // If not on new list, it's been removed - 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(std::set::iterator i = newlist.begin(); - i != newlist.end(); ++i) - { - v3s16 p = *i; - // If not on old list, it's been added - if(m_list.find(p) == m_list.end()) - blocks_added.insert(p); - } - - /* - Update m_list - */ - m_list.clear(); - for(std::set::iterator i = newlist.begin(); - i != newlist.end(); ++i) - { - v3s16 p = *i; - m_list.insert(p); - } -} - -/* - ServerEnvironment -*/ - -ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, IGameDef *gamedef, - const std::string &path_world) : - m_map(map), - m_script(scriptIface), - m_gamedef(gamedef), - m_path_world(path_world), - m_send_recommended_timer(0), - m_active_block_interval_overload_skip(0), - m_game_time(0), - m_game_time_fraction_counter(0), - m_last_clear_objects_time(0), - m_recommended_send_interval(0.1), - m_max_lag_estimate(0.1) -{ -} - -ServerEnvironment::~ServerEnvironment() -{ - // Clear active block list. - // This makes the next one delete all active objects. - m_active_blocks.clear(); - - // Convert all objects to static and delete the active objects - deactivateFarObjects(true); - - // Drop/delete map - m_map->drop(); - - // Delete ActiveBlockModifiers - for (std::vector::iterator - i = m_abms.begin(); i != m_abms.end(); ++i){ - delete i->abm; - } - - // Deallocate players - for (std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) { - delete (*i); - } -} - -Map & ServerEnvironment::getMap() -{ - return *m_map; -} - -ServerMap & ServerEnvironment::getServerMap() -{ - return *m_map; -} - -RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id) -{ - for (std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) { - RemotePlayer *player = *i; - if (player->peer_id == peer_id) - return player; - } - return NULL; -} - -RemotePlayer *ServerEnvironment::getPlayer(const char* name) -{ - for (std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) { - RemotePlayer *player = *i; - if (strcmp(player->getName(), name) == 0) - return player; - } - return NULL; -} - -void ServerEnvironment::addPlayer(RemotePlayer *player) -{ - DSTACK(FUNCTION_NAME); - /* - Check that peer_ids are unique. - Also check that names are unique. - Exception: there can be multiple players with peer_id=0 - */ - // If peer id is non-zero, it has to be unique. - if (player->peer_id != 0) - FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique"); - // Name has to be unique. - FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique"); - // Add. - m_players.push_back(player); -} - -void ServerEnvironment::removePlayer(RemotePlayer *player) -{ - for (std::vector::iterator it = m_players.begin(); - it != m_players.end(); ++it) { - if ((*it) == player) { - delete *it; - m_players.erase(it); - return; - } - } -} - -bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p) -{ - float distance = pos1.getDistanceFrom(pos2); - - //calculate normalized direction vector - v3f normalized_vector = v3f((pos2.X - pos1.X)/distance, - (pos2.Y - pos1.Y)/distance, - (pos2.Z - pos1.Z)/distance); - - //find out if there's a node on path between pos1 and pos2 - for (float i = 1; i < distance; i += stepsize) { - v3s16 pos = floatToInt(v3f(normalized_vector.X * i, - normalized_vector.Y * i, - normalized_vector.Z * i) +pos1,BS); - - MapNode n = getMap().getNodeNoEx(pos); - - if(n.param0 != CONTENT_AIR) { - if (p) { - *p = pos; - } - return false; - } - } - return true; -} - -void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, - const std::string &str_reason, bool reconnect) -{ - for (std::vector::iterator it = m_players.begin(); - it != m_players.end(); ++it) { - RemotePlayer *player = dynamic_cast(*it); - ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id, - player->protocol_version, reason, str_reason, reconnect); - } -} - -void ServerEnvironment::saveLoadedPlayers() -{ - std::string players_path = m_path_world + DIR_DELIM "players"; - fs::CreateDir(players_path); - - for (std::vector::iterator it = m_players.begin(); - it != m_players.end(); - ++it) { - if ((*it)->checkModified()) { - (*it)->save(players_path, m_gamedef); - } - } -} - -void ServerEnvironment::savePlayer(RemotePlayer *player) -{ - std::string players_path = m_path_world + DIR_DELIM "players"; - fs::CreateDir(players_path); - - player->save(players_path, m_gamedef); -} - -RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) -{ - bool newplayer = false; - bool found = false; - std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM; - std::string path = players_path + playername; - - RemotePlayer *player = getPlayer(playername.c_str()); - if (!player) { - player = new RemotePlayer("", m_gamedef->idef()); - newplayer = true; - } - - for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { - //// Open file and deserialize - std::ifstream is(path.c_str(), std::ios_base::binary); - if (!is.good()) - continue; - - player->deSerialize(is, path, sao); - is.close(); - - if (player->getName() == playername) { - found = true; - break; - } - - path = players_path + playername + itos(i); - } - - if (!found) { - infostream << "Player file for player " << playername - << " not found" << std::endl; - if (newplayer) - delete player; - - return NULL; - } - - if (newplayer) { - addPlayer(player); - } - player->setModified(false); - return player; -} - -void ServerEnvironment::saveMeta() -{ - std::string path = m_path_world + DIR_DELIM "env_meta.txt"; - - // Open file and serialize - std::ostringstream ss(std::ios_base::binary); - - Settings args; - args.setU64("game_time", m_game_time); - args.setU64("time_of_day", getTimeOfDay()); - args.setU64("last_clear_objects_time", m_last_clear_objects_time); - args.setU64("lbm_introduction_times_version", 1); - args.set("lbm_introduction_times", - m_lbm_mgr.createIntroductionTimesString()); - args.setU64("day_count", m_day_count); - args.writeLines(ss); - ss<<"EnvArgsEnd\n"; - - if(!fs::safeWriteToFile(path, ss.str())) - { - infostream<<"ServerEnvironment::saveMeta(): Failed to write " - < required_neighbors; -}; - -class ABMHandler -{ -private: - ServerEnvironment *m_env; - std::vector *> m_aabms; -public: - ABMHandler(std::vector &abms, - float dtime_s, ServerEnvironment *env, - bool use_timers): - m_env(env) - { - if(dtime_s < 0.001) - return; - INodeDefManager *ndef = env->getGameDef()->ndef(); - for(std::vector::iterator - i = abms.begin(); i != abms.end(); ++i) { - ActiveBlockModifier *abm = i->abm; - float trigger_interval = abm->getTriggerInterval(); - if(trigger_interval < 0.001) - trigger_interval = 0.001; - float actual_interval = dtime_s; - if(use_timers){ - i->timer += dtime_s; - if(i->timer < trigger_interval) - continue; - i->timer -= trigger_interval; - actual_interval = trigger_interval; - } - float chance = abm->getTriggerChance(); - if(chance == 0) - chance = 1; - ActiveABM aabm; - aabm.abm = abm; - if(abm->getSimpleCatchUp()) { - float intervals = actual_interval / trigger_interval; - if(intervals == 0) - continue; - aabm.chance = chance / intervals; - if(aabm.chance == 0) - aabm.chance = 1; - } else { - aabm.chance = chance; - } - // Trigger neighbors - std::set required_neighbors_s - = abm->getRequiredNeighbors(); - for(std::set::iterator - i = required_neighbors_s.begin(); - i != required_neighbors_s.end(); ++i) - { - ndef->getIds(*i, aabm.required_neighbors); - } - // Trigger contents - std::set contents_s = abm->getTriggerContents(); - for(std::set::iterator - i = contents_s.begin(); i != contents_s.end(); ++i) - { - std::set ids; - ndef->getIds(*i, ids); - for(std::set::const_iterator k = ids.begin(); - k != ids.end(); ++k) - { - content_t c = *k; - if (c >= m_aabms.size()) - m_aabms.resize(c + 256, NULL); - if (!m_aabms[c]) - m_aabms[c] = new std::vector; - m_aabms[c]->push_back(aabm); - } - } - } - } - - ~ABMHandler() - { - for (size_t i = 0; i < m_aabms.size(); i++) - delete m_aabms[i]; - } - - // Find out how many objects the given block and its neighbours contain. - // Returns the number of objects in the block, and also in 'wider' the - // number of objects in the block and all its neighbours. The latter - // may an estimate if any neighbours are unloaded. - u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider) - { - wider = 0; - u32 wider_unknown_count = 0; - for(s16 x=-1; x<=1; x++) - for(s16 y=-1; y<=1; y++) - for(s16 z=-1; z<=1; z++) - { - MapBlock *block2 = map->getBlockNoCreateNoEx( - block->getPos() + v3s16(x,y,z)); - if(block2==NULL){ - wider_unknown_count++; - continue; - } - wider += block2->m_static_objects.m_active.size() - + block2->m_static_objects.m_stored.size(); - } - // Extrapolate - u32 active_object_count = block->m_static_objects.m_active.size(); - u32 wider_known_count = 3*3*3 - wider_unknown_count; - wider += wider_unknown_count * wider / wider_known_count; - return active_object_count; - - } - void apply(MapBlock *block) - { - if(m_aabms.empty() || block->isDummy()) - return; - - ServerMap *map = &m_env->getServerMap(); - - u32 active_object_count_wider; - u32 active_object_count = this->countObjects(block, map, active_object_count_wider); - m_env->m_added_objects = 0; - - v3s16 p0; - for(p0.X=0; p0.XgetNodeUnsafe(p0); - content_t c = n.getContent(); - - if (c >= m_aabms.size() || !m_aabms[c]) - continue; - - v3s16 p = p0 + block->getPosRelative(); - for(std::vector::iterator - i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { - if(myrand() % i->chance != 0) - continue; - - // Check neighbors - if(!i->required_neighbors.empty()) - { - v3s16 p1; - for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) - for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) - for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) - { - if(p1 == p0) - continue; - content_t c; - if (block->isValidPosition(p1)) { - // if the neighbor is found on the same map block - // get it straight from there - const MapNode &n = block->getNodeUnsafe(p1); - c = n.getContent(); - } else { - // otherwise consult the map - MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); - c = n.getContent(); - } - std::set::const_iterator k; - k = i->required_neighbors.find(c); - if(k != i->required_neighbors.end()){ - goto neighbor_found; - } - } - // No required neighbor found - continue; - } -neighbor_found: - - // Call all the trigger variations - i->abm->trigger(m_env, p, n); - i->abm->trigger(m_env, p, n, - active_object_count, active_object_count_wider); - - // Count surrounding objects again if the abms added any - if(m_env->m_added_objects > 0) { - active_object_count = countObjects(block, map, active_object_count_wider); - m_env->m_added_objects = 0; - } - } - } - } -}; - -void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) -{ - // Reset usage timer immediately, otherwise a block that becomes active - // again at around the same time as it would normally be unloaded will - // get unloaded incorrectly. (I think this still leaves a small possibility - // of a race condition between this and server::AsyncRunStep, which only - // some kind of synchronisation will fix, but it at least reduces the window - // of opportunity for it to break from seconds to nanoseconds) - block->resetUsageTimer(); - - // Get time difference - u32 dtime_s = 0; - u32 stamp = block->getTimestamp(); - if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) - dtime_s = m_game_time - stamp; - dtime_s += additional_dtime; - - /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: " - <m_static_objects.m_stored.clear(); - // do not set changed flag to avoid unnecessary mapblock writes - } - - // Set current time as timestamp - block->setTimestampNoChangedFlag(m_game_time); - - /*infostream<<"ServerEnvironment::activateBlock(): block is " - < elapsed_timers = - block->m_node_timers.step((float)dtime_s); - if (!elapsed_timers.empty()) { - MapNode n; - for (std::vector::iterator - i = elapsed_timers.begin(); - i != elapsed_timers.end(); ++i){ - n = block->getNodeNoEx(i->position); - v3s16 p = i->position + block->getPosRelative(); - if (m_script->node_on_timer(p, n, i->elapsed)) - block->setNodeTimer(NodeTimer(i->timeout, 0, i->position)); - } - } - - /* Handle ActiveBlockModifiers */ - ABMHandler abmhandler(m_abms, dtime_s, this, false); - abmhandler.apply(block); -} - -void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) -{ - m_abms.push_back(ABMWithState(abm)); -} - -void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) -{ - m_lbm_mgr.addLBMDef(lbm); -} - -bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) -{ - INodeDefManager *ndef = m_gamedef->ndef(); - MapNode n_old = m_map->getNodeNoEx(p); - - // Call destructor - if (ndef->get(n_old).has_on_destruct) - m_script->node_on_destruct(p, n_old); - - // Replace node - if (!m_map->addNodeWithEvent(p, n)) - return false; - - // Update active VoxelManipulator if a mapgen thread - m_map->updateVManip(p); - - // Call post-destructor - if (ndef->get(n_old).has_after_destruct) - m_script->node_after_destruct(p, n_old); - - // Call constructor - if (ndef->get(n).has_on_construct) - m_script->node_on_construct(p, n); - - return true; -} - -bool ServerEnvironment::removeNode(v3s16 p) -{ - INodeDefManager *ndef = m_gamedef->ndef(); - MapNode n_old = m_map->getNodeNoEx(p); - - // Call destructor - if (ndef->get(n_old).has_on_destruct) - m_script->node_on_destruct(p, n_old); - - // Replace with air - // This is slightly optimized compared to addNodeWithEvent(air) - if (!m_map->removeNodeWithEvent(p)) - return false; - - // Update active VoxelManipulator if a mapgen thread - m_map->updateVManip(p); - - // Call post-destructor - if (ndef->get(n_old).has_after_destruct) - m_script->node_after_destruct(p, n_old); - - // Air doesn't require constructor - return true; -} - -bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n) -{ - if (!m_map->addNodeWithEvent(p, n, false)) - return false; - - // Update active VoxelManipulator if a mapgen thread - m_map->updateVManip(p); - - return true; -} - -void ServerEnvironment::getObjectsInsideRadius(std::vector &objects, v3f pos, float radius) -{ - for (ActiveObjectMap::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i->second; - u16 id = i->first; - v3f objectpos = obj->getBasePosition(); - if (objectpos.getDistanceFrom(pos) > radius) - continue; - objects.push_back(id); - } -} - -void ServerEnvironment::clearObjects(ClearObjectsMode mode) -{ - infostream << "ServerEnvironment::clearObjects(): " - << "Removing all active objects" << std::endl; - std::vector objects_to_remove; - for (ActiveObjectMap::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i->second; - if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) - continue; - u16 id = i->first; - // Delete static object if block is loaded - if (obj->m_static_exists) { - MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); - if (block) { - block->m_static_objects.remove(id); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_CLEAR_ALL_OBJECTS); - obj->m_static_exists = false; - } - } - // If known by some client, don't delete immediately - if (obj->m_known_by_count > 0) { - obj->m_pending_deactivation = true; - obj->m_removed = true; - continue; - } - - // Tell the object about removal - obj->removingFromEnvironment(); - // Deregister in scripting api - m_script->removeObjectReference(obj); - - // Delete active object - if (obj->environmentDeletes()) - delete obj; - // Id to be removed from m_active_objects - objects_to_remove.push_back(id); - } - - // Remove references from m_active_objects - for (std::vector::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) { - m_active_objects.erase(*i); - } - - // Get list of loaded blocks - std::vector loaded_blocks; - infostream << "ServerEnvironment::clearObjects(): " - << "Listing all loaded blocks" << std::endl; - m_map->listAllLoadedBlocks(loaded_blocks); - infostream << "ServerEnvironment::clearObjects(): " - << "Done listing all loaded blocks: " - << loaded_blocks.size()< loadable_blocks; - if (mode == CLEAR_OBJECTS_MODE_FULL) { - infostream << "ServerEnvironment::clearObjects(): " - << "Listing all loadable blocks" << std::endl; - m_map->listAllLoadableBlocks(loadable_blocks); - infostream << "ServerEnvironment::clearObjects(): " - << "Done listing all loadable blocks: " - << loadable_blocks.size() << std::endl; - } else { - loadable_blocks = loaded_blocks; - } - - infostream << "ServerEnvironment::clearObjects(): " - << "Now clearing objects in " << loadable_blocks.size() - << " blocks" << std::endl; - - // Grab a reference on each loaded block to avoid unloading it - for (std::vector::iterator i = loaded_blocks.begin(); - i != loaded_blocks.end(); ++i) { - v3s16 p = *i; - MapBlock *block = m_map->getBlockNoCreateNoEx(p); - assert(block != NULL); - block->refGrab(); - } - - // Remove objects in all loadable blocks - u32 unload_interval = U32_MAX; - if (mode == CLEAR_OBJECTS_MODE_FULL) { - unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks"); - unload_interval = MYMAX(unload_interval, 1); - } - u32 report_interval = loadable_blocks.size() / 10; - u32 num_blocks_checked = 0; - u32 num_blocks_cleared = 0; - u32 num_objs_cleared = 0; - for (std::vector::iterator i = loadable_blocks.begin(); - i != loadable_blocks.end(); ++i) { - v3s16 p = *i; - MapBlock *block = m_map->emergeBlock(p, false); - if (!block) { - errorstream << "ServerEnvironment::clearObjects(): " - << "Failed to emerge block " << PP(p) << std::endl; - continue; - } - u32 num_stored = block->m_static_objects.m_stored.size(); - u32 num_active = block->m_static_objects.m_active.size(); - if (num_stored != 0 || num_active != 0) { - block->m_static_objects.m_stored.clear(); - block->m_static_objects.m_active.clear(); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_CLEAR_ALL_OBJECTS); - num_objs_cleared += num_stored + num_active; - num_blocks_cleared++; - } - num_blocks_checked++; - - if (report_interval != 0 && - num_blocks_checked % report_interval == 0) { - float percent = 100.0 * (float)num_blocks_checked / - loadable_blocks.size(); - infostream << "ServerEnvironment::clearObjects(): " - << "Cleared " << num_objs_cleared << " objects" - << " in " << num_blocks_cleared << " blocks (" - << percent << "%)" << std::endl; - } - if (num_blocks_checked % unload_interval == 0) { - m_map->unloadUnreferencedBlocks(); - } - } - m_map->unloadUnreferencedBlocks(); - - // Drop references that were added above - for (std::vector::iterator i = loaded_blocks.begin(); - i != loaded_blocks.end(); ++i) { - v3s16 p = *i; - MapBlock *block = m_map->getBlockNoCreateNoEx(p); - assert(block); - block->refDrop(); - } - - m_last_clear_objects_time = m_game_time; - - infostream << "ServerEnvironment::clearObjects(): " - << "Finished: Cleared " << num_objs_cleared << " objects" - << " in " << num_blocks_cleared << " blocks" << std::endl; -} - -void ServerEnvironment::step(float dtime) -{ - DSTACK(FUNCTION_NAME); - - //TimeTaker timer("ServerEnv step"); - - /* Step time of day */ - stepTimeOfDay(dtime); - - // Update this one - // NOTE: This is kind of funny on a singleplayer game, but doesn't - // really matter that much. - static const float server_step = g_settings->getFloat("dedicated_server_step"); - m_recommended_send_interval = server_step; - - /* - Increment game time - */ - { - m_game_time_fraction_counter += dtime; - u32 inc_i = (u32)m_game_time_fraction_counter; - m_game_time += inc_i; - m_game_time_fraction_counter -= (float)inc_i; - } - - /* - Handle players - */ - { - ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG); - for (std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) { - RemotePlayer *player = dynamic_cast(*i); - assert(player); - - // Ignore disconnected players - if(player->peer_id == 0) - continue; - - // Move - player->move(dtime, this, 100*BS); - } - } - - /* - Manage active block list - */ - if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) { - ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG); - /* - Get player block positions - */ - std::vector players_blockpos; - for (std::vector::iterator i = m_players.begin(); - i != m_players.end(); ++i) { - RemotePlayer *player = dynamic_cast(*i); - assert(player); - - // Ignore disconnected players - if (player->peer_id == 0) - continue; - - PlayerSAO *playersao = player->getPlayerSAO(); - assert(playersao); - - v3s16 blockpos = getNodeBlockPos( - floatToInt(playersao->getBasePosition(), BS)); - players_blockpos.push_back(blockpos); - } - - /* - Update list of active blocks, collecting changes - */ - static const s16 active_block_range = g_settings->getS16("active_block_range"); - std::set blocks_removed; - std::set blocks_added; - m_active_blocks.update(players_blockpos, active_block_range, - blocks_removed, blocks_added); - - /* - Handle removed blocks - */ - - // Convert active objects that are no more in active blocks to static - deactivateFarObjects(false); - - for(std::set::iterator - i = blocks_removed.begin(); - i != blocks_removed.end(); ++i) { - v3s16 p = *i; - - /* infostream<<"Server: Block " << PP(p) - << " became inactive"<getBlockNoCreateNoEx(p); - if(block==NULL) - continue; - - // Set current time as timestamp (and let it set ChangedFlag) - block->setTimestamp(m_game_time); - } - - /* - Handle added blocks - */ - - for(std::set::iterator - i = blocks_added.begin(); - i != blocks_added.end(); ++i) - { - v3s16 p = *i; - - MapBlock *block = m_map->getBlockOrEmerge(p); - if(block==NULL){ - m_active_blocks.m_list.erase(p); - continue; - } - - activateBlock(block); - /* infostream<<"Server: Block " << PP(p) - << " became active"<::iterator - i = m_active_blocks.m_list.begin(); - i != m_active_blocks.m_list.end(); ++i) - { - v3s16 p = *i; - - /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); - if(block==NULL) - continue; - - // Reset block usage timer - block->resetUsageTimer(); - - // Set current time as timestamp - block->setTimestampNoChangedFlag(m_game_time); - // If time has changed much from the one on disk, - // set block to be saved when it is unloaded - if(block->getTimestamp() > block->getDiskTimestamp() + 60) - block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD, - MOD_REASON_BLOCK_EXPIRED); - - // Run node timers - std::vector elapsed_timers = - block->m_node_timers.step((float)dtime); - if (!elapsed_timers.empty()) { - MapNode n; - for (std::vector::iterator i = elapsed_timers.begin(); - i != elapsed_timers.end(); ++i) { - n = block->getNodeNoEx(i->position); - p = i->position + block->getPosRelative(); - if (m_script->node_on_timer(p, n, i->elapsed)) { - block->setNodeTimer(NodeTimer( - i->timeout, 0, i->position)); - } - } - } - } - } - - if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) - do{ // breakable - if(m_active_block_interval_overload_skip > 0){ - ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips"); - m_active_block_interval_overload_skip--; - break; - } - ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG); - TimeTaker timer("modify in active blocks per interval"); - - // Initialize handling of ActiveBlockModifiers - ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true); - - for(std::set::iterator - i = m_active_blocks.m_list.begin(); - i != m_active_blocks.m_list.end(); ++i) - { - v3s16 p = *i; - - /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); - if(block == NULL) - continue; - - // Set current time as timestamp - block->setTimestampNoChangedFlag(m_game_time); - - /* Handle ActiveBlockModifiers */ - abmhandler.apply(block); - } - - u32 time_ms = timer.stop(true); - u32 max_time_ms = 200; - if(time_ms > max_time_ms){ - warningstream<<"active block modifiers took " - <environment_Step(dtime); - - /* - Step active objects - */ - { - ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG); - //TimeTaker timer("Step active objects"); - - g_profiler->avg("SEnv: num of objects", m_active_objects.size()); - - // This helps the objects to send data at the same time - bool send_recommended = false; - m_send_recommended_timer += dtime; - if(m_send_recommended_timer > getSendRecommendedInterval()) - { - m_send_recommended_timer -= getSendRecommendedInterval(); - send_recommended = true; - } - - for(ActiveObjectMap::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - ServerActiveObject* obj = i->second; - // Don't step if is to be removed or stored statically - if(obj->m_removed || obj->m_pending_deactivation) - continue; - // Step object - obj->step(dtime, send_recommended); - // Read messages from object - while(!obj->m_messages_out.empty()) - { - m_active_object_messages.push( - obj->m_messages_out.front()); - obj->m_messages_out.pop(); - } - } - } - - /* - Manage active objects - */ - if(m_object_management_interval.step(dtime, 0.5)) - { - ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG); - /* - Remove objects that satisfy (m_removed && m_known_by_count==0) - */ - removeRemovedObjects(); - } - - /* - Manage particle spawner expiration - */ - if (m_particle_management_interval.step(dtime, 1.0)) { - for (UNORDERED_MAP::iterator i = m_particle_spawners.begin(); - i != m_particle_spawners.end(); ) { - //non expiring spawners - if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) { - ++i; - continue; - } - - i->second -= 1.0f; - if (i->second <= 0.f) - m_particle_spawners.erase(i++); - else - ++i; - } - } -} - -u32 ServerEnvironment::addParticleSpawner(float exptime) -{ - // Timers with lifetime 0 do not expire - float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY; - - u32 id = 0; - for (;;) { // look for unused particlespawner id - id++; - UNORDERED_MAP::iterator f = m_particle_spawners.find(id); - if (f == m_particle_spawners.end()) { - m_particle_spawners[id] = time; - break; - } - } - return id; -} - -u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id) -{ - u32 id = addParticleSpawner(exptime); - m_particle_spawner_attachments[id] = attached_id; - if (ServerActiveObject *obj = getActiveObject(attached_id)) { - obj->attachParticleSpawner(id); - } - return id; -} - -void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object) -{ - m_particle_spawners.erase(id); - UNORDERED_MAP::iterator it = m_particle_spawner_attachments.find(id); - if (it != m_particle_spawner_attachments.end()) { - u16 obj_id = (*it).second; - ServerActiveObject *sao = getActiveObject(obj_id); - if (sao != NULL && remove_from_object) { - sao->detachParticleSpawner(id); - } - m_particle_spawner_attachments.erase(id); - } -} - -ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) -{ - ActiveObjectMap::iterator n = m_active_objects.find(id); - return (n != m_active_objects.end() ? n->second : NULL); -} - -bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects) -{ - if (id == 0) - return false; - - return objects.find(id) == objects.end(); -} - -u16 getFreeServerActiveObjectId(ActiveObjectMap &objects) -{ - //try to reuse id's as late as possible - static u16 last_used_id = 0; - u16 startid = last_used_id; - for(;;) - { - last_used_id ++; - if(isFreeServerActiveObjectId(last_used_id, objects)) - return last_used_id; - - if(last_used_id == startid) - return 0; - } -} - -u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) -{ - assert(object); // Pre-condition - m_added_objects++; - u16 id = addActiveObjectRaw(object, true, 0); - return id; -} - -/* - Finds out what new objects have been added to - inside a radius around a position -*/ -void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius, - s16 player_radius, - std::set ¤t_objects, - std::queue &added_objects) -{ - f32 radius_f = radius * BS; - f32 player_radius_f = player_radius * BS; - - if (player_radius_f < 0) - player_radius_f = 0; - /* - Go through the object list, - - discard m_removed objects, - - discard objects that are too far away, - - discard objects that are found in current_objects. - - add remaining objects to added_objects - */ - for (ActiveObjectMap::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - u16 id = i->first; - - // Get object - ServerActiveObject *object = i->second; - if (object == NULL) - continue; - - // Discard if removed or deactivating - if(object->m_removed || object->m_pending_deactivation) - continue; - - f32 distance_f = object->getBasePosition(). - getDistanceFrom(playersao->getBasePosition()); - if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - // Discard if too far - if (distance_f > player_radius_f && player_radius_f != 0) - continue; - } else if (distance_f > radius_f) - continue; - - // Discard if already on current_objects - std::set::iterator n; - n = current_objects.find(id); - if(n != current_objects.end()) - continue; - // Add to added_objects - added_objects.push(id); - } -} - -/* - Finds out what objects have been removed from - inside a radius around a position -*/ -void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, - s16 player_radius, - std::set ¤t_objects, - std::queue &removed_objects) -{ - f32 radius_f = radius * BS; - f32 player_radius_f = player_radius * BS; - - if (player_radius_f < 0) - player_radius_f = 0; - /* - Go through current_objects; object is removed if: - - object is not found in m_active_objects (this is actually an - error condition; objects should be set m_removed=true and removed - only after all clients have been informed about removal), or - - object has m_removed=true, or - - object is too far away - */ - for(std::set::iterator - i = current_objects.begin(); - i != current_objects.end(); ++i) - { - u16 id = *i; - ServerActiveObject *object = getActiveObject(id); - - if (object == NULL) { - infostream << "ServerEnvironment::getRemovedActiveObjects():" - << " object in current_objects is NULL" << std::endl; - removed_objects.push(id); - continue; - } - - if (object->m_removed || object->m_pending_deactivation) { - removed_objects.push(id); - continue; - } - - f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition()); - if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - if (distance_f <= player_radius_f || player_radius_f == 0) - continue; - } else if (distance_f <= radius_f) - continue; - - // Object is no longer visible - removed_objects.push(id); - } -} - -void ServerEnvironment::setStaticForActiveObjectsInBlock( - v3s16 blockpos, bool static_exists, v3s16 static_block) -{ - MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); - if (!block) - return; - - for (std::map::iterator - so_it = block->m_static_objects.m_active.begin(); - so_it != block->m_static_objects.m_active.end(); ++so_it) { - // Get the ServerActiveObject counterpart to this StaticObject - ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first); - if (ao_it == m_active_objects.end()) { - // If this ever happens, there must be some kind of nasty bug. - errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): " - "Object from MapBlock::m_static_objects::m_active not found " - "in m_active_objects"; - continue; - } - - ServerActiveObject *sao = ao_it->second; - sao->m_static_exists = static_exists; - sao->m_static_block = static_block; - } -} - -ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() -{ - if(m_active_object_messages.empty()) - return ActiveObjectMessage(0); - - ActiveObjectMessage message = m_active_object_messages.front(); - m_active_object_messages.pop(); - return message; -} - -/* - ************ Private methods ************* -*/ - -u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, - bool set_changed, u32 dtime_s) -{ - assert(object); // Pre-condition - if(object->getId() == 0){ - u16 new_id = getFreeServerActiveObjectId(m_active_objects); - if(new_id == 0) - { - errorstream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"no free ids available"<environmentDeletes()) - delete object; - return 0; - } - object->setId(new_id); - } - else{ - verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"supplied with id "<getId()<getId(), m_active_objects)) { - errorstream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"id is not free ("<getId()<<")"<environmentDeletes()) - delete object; - return 0; - } - - if (objectpos_over_limit(object->getBasePosition())) { - v3f p = object->getBasePosition(); - errorstream << "ServerEnvironment::addActiveObjectRaw(): " - << "object position (" << p.X << "," << p.Y << "," << p.Z - << ") outside maximum range" << std::endl; - if (object->environmentDeletes()) - delete object; - return 0; - } - - /*infostream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"added (id="<getId()<<")"<getId()] = object; - - verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"Added id="<getId()<<"; there are now " - <addObjectReference(object); - // Post-initialize object - object->addedToEnvironment(dtime_s); - - // Add static data to block - if(object->isStaticAllowed()) - { - // Add static object to active static list of the block - v3f objectpos = object->getBasePosition(); - std::string staticdata = object->getStaticData(); - StaticObject s_obj(object->getType(), objectpos, staticdata); - // Add to the block where the object is located in - v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); - MapBlock *block = m_map->emergeBlock(blockpos); - if(block){ - block->m_static_objects.m_active[object->getId()] = s_obj; - object->m_static_exists = true; - object->m_static_block = blockpos; - - if(set_changed) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_ADD_ACTIVE_OBJECT_RAW); - } else { - v3s16 p = floatToInt(objectpos, BS); - errorstream<<"ServerEnvironment::addActiveObjectRaw(): " - <<"could not emerge block for storing id="<getId() - <<" statically (pos="<getId(); -} - -/* - Remove objects that satisfy (m_removed && m_known_by_count==0) -*/ -void ServerEnvironment::removeRemovedObjects() -{ - std::vector objects_to_remove; - for(ActiveObjectMap::iterator i = m_active_objects.begin(); - i != m_active_objects.end(); ++i) { - u16 id = i->first; - ServerActiveObject* obj = i->second; - // This shouldn't happen but check it - if(obj == NULL) - { - infostream<<"NULL object found in ServerEnvironment" - <<" while finding removed objects. id="<m_removed && !obj->m_pending_deactivation) - continue; - - /* - Delete static data from block if is marked as removed - */ - if(obj->m_static_exists && obj->m_removed) - { - MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - if (block) { - block->m_static_objects.remove(id); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_REMOVE_OBJECTS_REMOVE); - obj->m_static_exists = false; - } else { - infostream<<"Failed to emerge block from which an object to " - <<"be removed was loaded from. id="< 0, don't actually remove. On some future - // invocation this will be 0, which is when removal will continue. - if(obj->m_known_by_count > 0) - continue; - - /* - Move static data from active to stored if not marked as removed - */ - if(obj->m_static_exists && !obj->m_removed){ - MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - if (block) { - std::map::iterator i = - block->m_static_objects.m_active.find(id); - if(i != block->m_static_objects.m_active.end()){ - block->m_static_objects.m_stored.push_back(i->second); - block->m_static_objects.m_active.erase(id); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_REMOVE_OBJECTS_DEACTIVATE); - } - } else { - infostream<<"Failed to emerge block from which an object to " - <<"be deactivated was loaded from. id="<removingFromEnvironment(); - // Deregister in scripting api - m_script->removeObjectReference(obj); - - // Delete - if(obj->environmentDeletes()) - delete obj; - - // Id to be removed from m_active_objects - objects_to_remove.push_back(id); - } - // Remove references from m_active_objects - for(std::vector::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) { - m_active_objects.erase(*i); - } -} - -static void print_hexdump(std::ostream &o, const std::string &data) -{ - const int linelength = 16; - for(int l=0; ; l++){ - int i0 = linelength * l; - bool at_end = false; - int thislinelength = linelength; - if(i0 + thislinelength > (int)data.size()){ - thislinelength = data.size() - i0; - at_end = true; - } - for(int di=0; di= 32) - o<m_static_objects.m_stored.empty()) - return; - - verbosestream<<"ServerEnvironment::activateObjects(): " - <<"activating objects of block "<getPos()) - <<" ("<m_static_objects.m_stored.size() - <<" objects)"<m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block")); - if (large_amount) { - errorstream<<"suspiciously large amount of objects detected: " - <m_static_objects.m_stored.size()<<" in " - <getPos()) - <<"; removing all of them."<m_static_objects.m_stored.clear(); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_TOO_MANY_OBJECTS); - return; - } - - // Activate stored objects - std::vector new_stored; - for (std::vector::iterator - i = block->m_static_objects.m_stored.begin(); - i != block->m_static_objects.m_stored.end(); ++i) { - StaticObject &s_obj = *i; - - // Create an active object from the data - ServerActiveObject *obj = ServerActiveObject::create - ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data); - // If couldn't create object, store static data back. - if(obj == NULL) { - errorstream<<"ServerEnvironment::activateObjects(): " - <<"failed to create active object from static object " - <<"in block "<getStaticData(); - StaticObject s_obj(obj->getType(), objectpos, staticdata_new); - block->m_static_objects.insert(id, s_obj); - obj->m_static_block = blockpos_o; - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_ADDED); - - // Delete from block where object was located - block = m_map->emergeBlock(old_static_block, false); - if(!block){ - errorstream<<"ServerEnvironment::deactivateFarObjects(): " - <<"Could not delete object id="<m_static_objects.remove(id); - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_REMOVED); - continue; - } - - // If block is active, don't remove - if(!force_delete && m_active_blocks.contains(blockpos_o)) - continue; - - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " - <<"deactivating object id="<m_known_by_count > 0 && !force_delete); - - /* - Update the static data - */ - - if(obj->isStaticAllowed()) - { - // Create new static object - std::string staticdata_new = obj->getStaticData(); - StaticObject s_obj(obj->getType(), objectpos, staticdata_new); - - bool stays_in_same_block = false; - bool data_changed = true; - - if (obj->m_static_exists) { - if (obj->m_static_block == blockpos_o) - stays_in_same_block = true; - - MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - - if (block) { - std::map::iterator n = - block->m_static_objects.m_active.find(id); - if (n != block->m_static_objects.m_active.end()) { - StaticObject static_old = n->second; - - float save_movem = obj->getMinimumSavedMovement(); - - if (static_old.data == staticdata_new && - (static_old.pos - objectpos).getLength() < save_movem) - data_changed = false; - } else { - errorstream<<"ServerEnvironment::deactivateFarObjects(): " - <<"id="<m_static_block)<m_static_exists) - { - MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); - if(block) - { - block->m_static_objects.remove(id); - obj->m_static_exists = false; - // Only mark block as modified if data changed considerably - if(shall_be_written) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_CHANGED); - } - } - - // Add to the block where the object is located in - v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); - // Get or generate the block - MapBlock *block = NULL; - try{ - block = m_map->emergeBlock(blockpos); - } catch(InvalidPositionException &e){ - // Handled via NULL pointer - // NOTE: emergeBlock's failure is usually determined by it - // actually returning NULL - } - - if(block) - { - if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { - warningstream << "ServerEnv: Trying to store id = " << obj->getId() - << " statically but block " << PP(blockpos) - << " already contains " - << block->m_static_objects.m_stored.size() - << " objects." - << " Forcing delete." << std::endl; - force_delete = true; - } else { - // If static counterpart already exists in target block, - // remove it first. - // This shouldn't happen because the object is removed from - // the previous block before this according to - // obj->m_static_block, but happens rarely for some unknown - // reason. Unsuccessful attempts have been made to find - // said reason. - if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){ - warningstream<<"ServerEnv: Performing hack #83274" - <m_static_objects.remove(id); - } - // Store static data - u16 store_id = pending_delete ? id : 0; - block->m_static_objects.insert(store_id, s_obj); - - // Only mark block as modified if data changed considerably - if(shall_be_written) - block->raiseModified(MOD_STATE_WRITE_NEEDED, - MOD_REASON_STATIC_DATA_CHANGED); - - obj->m_static_exists = true; - obj->m_static_block = block->getPos(); - } - } - else{ - if(!force_delete){ - v3s16 p = floatToInt(objectpos, BS); - errorstream<<"ServerEnv: Could not find or generate " - <<"a block for storing id="<getId() - <<" statically (pos="<m_pending_deactivation = true; - continue; - } - - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " - <<"object id="<removingFromEnvironment(); - // Deregister in scripting api - m_script->removeObjectReference(obj); - - // Delete active object - if(obj->environmentDeletes()) - delete obj; - // Id to be removed from m_active_objects - objects_to_remove.push_back(id); - } - - // Remove references from m_active_objects - for(std::vector::iterator i = objects_to_remove.begin(); - i != objects_to_remove.end(); ++i) { - m_active_objects.erase(*i); - } -} - diff --git a/src/environment.h b/src/environment.h index 209d795d8..14a18421b 100644 --- a/src/environment.h +++ b/src/environment.h @@ -30,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc., - etc. */ -#include #include #include #include @@ -43,17 +42,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode -class ServerEnvironment; -class ActiveBlockModifier; -class ServerActiveObject; class ITextureSource; class IGameDef; class Map; -class ServerMap; class GameScripting; class Player; -class RemotePlayer; -class PlayerSAO; class PointedThing; class Environment @@ -134,395 +127,5 @@ private: DISABLE_CLASS_COPY(Environment); }; -/* - {Active, Loading} block modifier interface. - - These are fed into ServerEnvironment at initialization time; - ServerEnvironment handles deleting them. -*/ - -class ActiveBlockModifier -{ -public: - ActiveBlockModifier(){}; - virtual ~ActiveBlockModifier(){}; - - // Set of contents to trigger on - virtual std::set getTriggerContents()=0; - // Set of required neighbors (trigger doesn't happen if none are found) - // Empty = do not check neighbors - virtual std::set getRequiredNeighbors() - { return std::set(); } - // Trigger interval in seconds - virtual float getTriggerInterval() = 0; - // Random chance of (1 / return value), 0 is disallowed - virtual u32 getTriggerChance() = 0; - // Whether to modify chance to simulate time lost by an unnattended block - virtual bool getSimpleCatchUp() = 0; - // This is called usually at interval for 1/chance of the nodes - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, - u32 active_object_count, u32 active_object_count_wider){}; -}; - -struct ABMWithState -{ - ActiveBlockModifier *abm; - float timer; - - ABMWithState(ActiveBlockModifier *abm_); -}; - -struct LoadingBlockModifierDef -{ - // Set of contents to trigger on - std::set trigger_contents; - std::string name; - bool run_at_every_load; - - virtual ~LoadingBlockModifierDef() {} - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; -}; - -struct LBMContentMapping -{ - typedef std::map > container_map; - container_map map; - - std::vector lbm_list; - - // Needs to be separate method (not inside destructor), - // because the LBMContentMapping may be copied and destructed - // many times during operation in the lbm_lookup_map. - void deleteContents(); - void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef); - const std::vector *lookup(content_t c) const; -}; - -class LBMManager -{ -public: - LBMManager(): - m_query_mode(false) - {} - - ~LBMManager(); - - // Don't call this after loadIntroductionTimes() ran. - void addLBMDef(LoadingBlockModifierDef *lbm_def); - - void loadIntroductionTimes(const std::string ×, - IGameDef *gamedef, u32 now); - - // Don't call this before loadIntroductionTimes() ran. - std::string createIntroductionTimesString(); - - // Don't call this before loadIntroductionTimes() ran. - void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp); - - // Warning: do not make this std::unordered_map, order is relevant here - typedef std::map lbm_lookup_map; - -private: - // Once we set this to true, we can only query, - // not modify - bool m_query_mode; - - // For m_query_mode == false: - // The key of the map is the LBM def's name. - // TODO make this std::unordered_map - std::map m_lbm_defs; - - // For m_query_mode == true: - // The key of the map is the LBM def's first introduction time. - lbm_lookup_map m_lbm_lookup; - - // Returns an iterator to the LBMs that were introduced - // after the given time. This is guaranteed to return - // valid values for everything - lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time) - { return m_lbm_lookup.lower_bound(time); } -}; - -/* - List of active blocks, used by ServerEnvironment -*/ - -class ActiveBlockList -{ -public: - void update(std::vector &active_positions, - s16 radius, - std::set &blocks_removed, - std::set &blocks_added); - - bool contains(v3s16 p){ - return (m_list.find(p) != m_list.end()); - } - - void clear(){ - m_list.clear(); - } - - std::set m_list; - std::set m_forceloaded_list; - -private: -}; - -/* - Operation mode for ServerEnvironment::clearObjects() -*/ -enum ClearObjectsMode { - // Load and go through every mapblock, clearing objects - CLEAR_OBJECTS_MODE_FULL, - - // Clear objects immediately in loaded mapblocks; - // clear objects in unloaded mapblocks only when the mapblocks are next activated. - CLEAR_OBJECTS_MODE_QUICK, -}; - -/* - The server-side environment. - - This is not thread-safe. Server uses an environment mutex. -*/ - -typedef UNORDERED_MAP ActiveObjectMap; - -class ServerEnvironment : public Environment -{ -public: - ServerEnvironment(ServerMap *map, GameScripting *scriptIface, - IGameDef *gamedef, const std::string &path_world); - ~ServerEnvironment(); - - Map & getMap(); - - ServerMap & getServerMap(); - - //TODO find way to remove this fct! - GameScripting* getScriptIface() - { return m_script; } - - IGameDef *getGameDef() - { return m_gamedef; } - - float getSendRecommendedInterval() - { return m_recommended_send_interval; } - - void kickAllPlayers(AccessDeniedCode reason, - const std::string &str_reason, bool reconnect); - // Save players - void saveLoadedPlayers(); - void savePlayer(RemotePlayer *player); - RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao); - void addPlayer(RemotePlayer *player); - void removePlayer(RemotePlayer *player); - - /* - Save and load time of day and game timer - */ - void saveMeta(); - void loadMeta(); - // to be called instead of loadMeta if - // env_meta.txt doesn't exist (e.g. new world) - void loadDefaultMeta(); - - u32 addParticleSpawner(float exptime); - u32 addParticleSpawner(float exptime, u16 attached_id); - void deleteParticleSpawner(u32 id, bool remove_from_object = true); - - /* - External ActiveObject interface - ------------------------------------------- - */ - - ServerActiveObject* getActiveObject(u16 id); - - /* - Add an active object to the environment. - Environment handles deletion of object. - Object may be deleted by environment immediately. - If id of object is 0, assigns a free id to it. - Returns the id of the object. - Returns 0 if not added and thus deleted. - */ - u16 addActiveObject(ServerActiveObject *object); - - /* - Add an active object as a static object to the corresponding - MapBlock. - Caller allocates memory, ServerEnvironment frees memory. - Return value: true if succeeded, false if failed. - (note: not used, pending removal from engine) - */ - //bool addActiveObjectAsStatic(ServerActiveObject *object); - - /* - Find out what new objects have been added to - inside a radius around a position - */ - void getAddedActiveObjects(PlayerSAO *playersao, s16 radius, - s16 player_radius, - std::set ¤t_objects, - std::queue &added_objects); - - /* - Find out what new objects have been removed from - inside a radius around a position - */ - void getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, - s16 player_radius, - std::set ¤t_objects, - std::queue &removed_objects); - - /* - Get the next message emitted by some active object. - Returns a message with id=0 if no messages are available. - */ - ActiveObjectMessage getActiveObjectMessage(); - - /* - Activate objects and dynamically modify for the dtime determined - from timestamp and additional_dtime - */ - void activateBlock(MapBlock *block, u32 additional_dtime=0); - - /* - {Active,Loading}BlockModifiers - ------------------------------------------- - */ - - void addActiveBlockModifier(ActiveBlockModifier *abm); - void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm); - - /* - Other stuff - ------------------------------------------- - */ - - // Script-aware node setters - bool setNode(v3s16 p, const MapNode &n); - bool removeNode(v3s16 p); - bool swapNode(v3s16 p, const MapNode &n); - - // Find all active objects inside a radius around a point - void getObjectsInsideRadius(std::vector &objects, v3f pos, float radius); - - // Clear objects, loading and going through every MapBlock - void clearObjects(ClearObjectsMode mode); - - // This makes stuff happen - void step(f32 dtime); - - //check if there's a line of sight between two positions - bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0, v3s16 *p=NULL); - - u32 getGameTime() { return m_game_time; } - - void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } - float getMaxLagEstimate() { return m_max_lag_estimate; } - - std::set* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; }; - - // Sets the static object status all the active objects in the specified block - // This is only really needed for deleting blocks from the map - void setStaticForActiveObjectsInBlock(v3s16 blockpos, - bool static_exists, v3s16 static_block=v3s16(0,0,0)); - - RemotePlayer *getPlayer(const u16 peer_id); - RemotePlayer *getPlayer(const char* name); -private: - - /* - Internal ActiveObject interface - ------------------------------------------- - */ - - /* - Add an active object to the environment. - - Called by addActiveObject. - - Object may be deleted by environment immediately. - If id of object is 0, assigns a free id to it. - Returns the id of the object. - Returns 0 if not added and thus deleted. - */ - u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s); - - /* - Remove all objects that satisfy (m_removed && m_known_by_count==0) - */ - void removeRemovedObjects(); - - /* - Convert stored objects from block to active - */ - void activateObjects(MapBlock *block, u32 dtime_s); - - /* - Convert objects that are not in active blocks to static. - - If m_known_by_count != 0, active object is not deleted, but static - data is still updated. - - If force_delete is set, active object is deleted nevertheless. It - shall only be set so in the destructor of the environment. - */ - void deactivateFarObjects(bool force_delete); - - /* - Member variables - */ - - // The map - ServerMap *m_map; - // Lua state - GameScripting* m_script; - // Game definition - IGameDef *m_gamedef; - // World path - const std::string m_path_world; - // Active object list - ActiveObjectMap m_active_objects; - // Outgoing network message buffer for active objects - std::queue m_active_object_messages; - // Some timers - float m_send_recommended_timer; - IntervalLimiter m_object_management_interval; - // List of active blocks - ActiveBlockList m_active_blocks; - IntervalLimiter m_active_blocks_management_interval; - IntervalLimiter m_active_block_modifier_interval; - IntervalLimiter m_active_blocks_nodemetadata_interval; - int m_active_block_interval_overload_skip; - // Time from the beginning of the game in seconds. - // Incremented in step(). - u32 m_game_time; - // A helper variable for incrementing the latter - float m_game_time_fraction_counter; - // Time of last clearObjects call (game time). - // When a mapblock older than this is loaded, its objects are cleared. - u32 m_last_clear_objects_time; - // Active block modifiers - std::vector m_abms; - LBMManager m_lbm_mgr; - // An interval for generally sending object positions and stuff - float m_recommended_send_interval; - // Estimate for general maximum lag as determined by server. - // Can raise to high values like 15s with eg. map generation mods. - float m_max_lag_estimate; - - // peer_ids in here should be unique, except that there may be many 0s - std::vector m_players; - - // Particles - IntervalLimiter m_particle_management_interval; - UNORDERED_MAP m_particle_spawners; - UNORDERED_MAP m_particle_spawner_attachments; -}; - #endif diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index f36a2fa4e..469e7396b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "log.h" -#include "environment.h" +#include "serverenvironment.h" #include "scripting_game.h" #include "serverobject.h" #include "settings.h" diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 073670c6d..84aa9252c 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., /******************************************************************************/ #include "pathfinder.h" -#include "environment.h" +#include "serverenvironment.h" #include "gamedef.h" #include "nodedef.h" #include "map.h" diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 89dd7978f..21b235f84 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define L_ENV_H_ #include "lua_api/l_base.h" -#include "environment.h" +#include "serverenvironment.h" class ModApiEnvMod : public ModApiBase { private: diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index c8bc7d558..3cdd3cbfe 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_inventory.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "environment.h" +#include "serverenvironment.h" #include "map.h" #include "gamedef.h" #include "nodemetadata.h" diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp index 3242d6ea5..ed11cc58e 100644 --- a/src/script/lua_api/l_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodetimer.h" #include "lua_api/l_internal.h" -#include "environment.h" +#include "serverenvironment.h" #include "map.h" diff --git a/src/server.h b/src/server.h index f0df0f9ec..fe7b50b77 100644 --- a/src/server.h +++ b/src/server.h @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "util/thread.h" #include "util/basic_macros.h" -#include "environment.h" +#include "serverenvironment.h" #include "chat_interface.h" #include "clientiface.h" #include "remoteplayer.h" diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp new file mode 100644 index 000000000..6229e4cf1 --- /dev/null +++ b/src/serverenvironment.cpp @@ -0,0 +1,2167 @@ +/* +Minetest +Copyright (C) 2010-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "serverenvironment.h" +#include "content_sao.h" +#include "settings.h" +#include "log.h" +#include "nodedef.h" +#include "nodemetadata.h" +#include "gamedef.h" +#include "map.h" +#include "profiler.h" +#include "raycast.h" +#include "remoteplayer.h" +#include "scripting_game.h" +#include "server.h" +#include "voxelalgorithms.h" +#include "util/serialize.h" +#include "util/basic_macros.h" +#include "util/pointedthing.h" +#include "threading/mutex_auto_lock.h" +#include "filesys.h" + +#define LBM_NAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_:" + +// A number that is much smaller than the timeout for particle spawners should/could ever be +#define PARTICLE_SPAWNER_NO_EXPIRY -1024.f + +/* + ABMWithState +*/ + +ABMWithState::ABMWithState(ActiveBlockModifier *abm_): + abm(abm_), + timer(0) +{ + // Initialize timer to random value to spread processing + float itv = abm->getTriggerInterval(); + itv = MYMAX(0.001, itv); // No less than 1ms + int minval = MYMAX(-0.51*itv, -60); // Clamp to + int maxval = MYMIN(0.51*itv, 60); // +-60 seconds + timer = myrand_range(minval, maxval); +} + +/* + LBMManager +*/ + +void LBMContentMapping::deleteContents() +{ + for (std::vector::iterator it = lbm_list.begin(); + it != lbm_list.end(); ++it) { + delete *it; + } +} + +void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) +{ + // Add the lbm_def to the LBMContentMapping. + // Unknown names get added to the global NameIdMapping. + INodeDefManager *nodedef = gamedef->ndef(); + + lbm_list.push_back(lbm_def); + + for (std::set::const_iterator it = lbm_def->trigger_contents.begin(); + it != lbm_def->trigger_contents.end(); ++it) { + std::set c_ids; + bool found = nodedef->getIds(*it, c_ids); + if (!found) { + content_t c_id = gamedef->allocateUnknownNodeId(*it); + if (c_id == CONTENT_IGNORE) { + // Seems it can't be allocated. + warningstream << "Could not internalize node name \"" << *it + << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; + continue; + } + c_ids.insert(c_id); + } + + for (std::set::const_iterator iit = + c_ids.begin(); iit != c_ids.end(); ++iit) { + content_t c_id = *iit; + map[c_id].push_back(lbm_def); + } + } +} + +const std::vector * +LBMContentMapping::lookup(content_t c) const +{ + container_map::const_iterator it = map.find(c); + if (it == map.end()) + return NULL; + // This first dereferences the iterator, returning + // a std::vector + // reference, then we convert it to a pointer. + return &(it->second); +} + +LBMManager::~LBMManager() +{ + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + delete it->second; + } + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + (it->second).deleteContents(); + } +} + +void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) +{ + // Precondition, in query mode the map isn't used anymore + FATAL_ERROR_IF(m_query_mode == true, + "attempted to modify LBMManager in query mode"); + + if (!string_allowed(lbm_def->name, LBM_NAME_ALLOWED_CHARS)) { + throw ModError("Error adding LBM \"" + lbm_def->name + + "\": Does not follow naming conventions: " + "Only chararacters [a-z0-9_:] are allowed."); + } + + m_lbm_defs[lbm_def->name] = lbm_def; +} + +void LBMManager::loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now) +{ + m_query_mode = true; + + // name -> time map. + // Storing it in a map first instead of + // handling the stuff directly in the loop + // removes all duplicate entries. + // TODO make this std::unordered_map + std::map introduction_times; + + /* + The introduction times string consists of name~time entries, + with each entry terminated by a semicolon. The time is decimal. + */ + + size_t idx = 0; + size_t idx_new; + while ((idx_new = times.find(";", idx)) != std::string::npos) { + std::string entry = times.substr(idx, idx_new - idx); + std::vector components = str_split(entry, '~'); + if (components.size() != 2) + throw SerializationError("Introduction times entry \"" + + entry + "\" requires exactly one '~'!"); + const std::string &name = components[0]; + u32 time = from_string(components[1]); + introduction_times[name] = time; + idx = idx_new + 1; + } + + // Put stuff from introduction_times into m_lbm_lookup + for (std::map::const_iterator it = introduction_times.begin(); + it != introduction_times.end(); ++it) { + const std::string &name = it->first; + u32 time = it->second; + + std::map::iterator def_it = + m_lbm_defs.find(name); + if (def_it == m_lbm_defs.end()) { + // This seems to be an LBM entry for + // an LBM we haven't loaded. Discard it. + continue; + } + LoadingBlockModifierDef *lbm_def = def_it->second; + if (lbm_def->run_at_every_load) { + // This seems to be an LBM entry for + // an LBM that runs at every load. + // Don't add it just yet. + continue; + } + + m_lbm_lookup[time].addLBM(lbm_def, gamedef); + + // Erase the entry so that we know later + // what elements didn't get put into m_lbm_lookup + m_lbm_defs.erase(name); + } + + // Now also add the elements from m_lbm_defs to m_lbm_lookup + // that weren't added in the previous step. + // They are introduced first time to this world, + // or are run at every load (introducement time hardcoded to U32_MAX). + + LBMContentMapping &lbms_we_introduce_now = m_lbm_lookup[now]; + LBMContentMapping &lbms_running_always = m_lbm_lookup[U32_MAX]; + + for (std::map::iterator it = + m_lbm_defs.begin(); it != m_lbm_defs.end(); ++it) { + if (it->second->run_at_every_load) { + lbms_running_always.addLBM(it->second, gamedef); + } else { + lbms_we_introduce_now.addLBM(it->second, gamedef); + } + } + + // Clear the list, so that we don't delete remaining elements + // twice in the destructor + m_lbm_defs.clear(); +} + +std::string LBMManager::createIntroductionTimesString() +{ + // Precondition, we must be in query mode + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + + std::ostringstream oss; + for (lbm_lookup_map::iterator it = m_lbm_lookup.begin(); + it != m_lbm_lookup.end(); ++it) { + u32 time = it->first; + std::vector &lbm_list = it->second.lbm_list; + for (std::vector::iterator iit = lbm_list.begin(); + iit != lbm_list.end(); ++iit) { + // Don't add if the LBM runs at every load, + // then introducement time is hardcoded + // and doesn't need to be stored + if ((*iit)->run_at_every_load) + continue; + oss << (*iit)->name << "~" << time << ";"; + } + } + return oss.str(); +} + +void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp) +{ + // Precondition, we need m_lbm_lookup to be initialized + FATAL_ERROR_IF(m_query_mode == false, + "attempted to query on non fully set up LBMManager"); + v3s16 pos_of_block = block->getPosRelative(); + v3s16 pos; + MapNode n; + content_t c; + lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp); + for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) + for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) + for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) + { + n = block->getNodeNoEx(pos); + c = n.getContent(); + for (LBMManager::lbm_lookup_map::const_iterator iit = it; + iit != m_lbm_lookup.end(); ++iit) { + const std::vector *lbm_list = + iit->second.lookup(c); + if (!lbm_list) + continue; + for (std::vector::const_iterator iit = + lbm_list->begin(); iit != lbm_list->end(); ++iit) { + (*iit)->trigger(env, pos + pos_of_block, n); + } + } + } +} + +/* + ActiveBlockList +*/ + +void fillRadiusBlock(v3s16 p0, s16 r, std::set &list) +{ + v3s16 p; + for(p.X=p0.X-r; p.X<=p0.X+r; p.X++) + for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++) + for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++) + { + // limit to a sphere + if (p.getDistanceFrom(p0) <= r) { + // Set in list + list.insert(p); + } + } +} + +void ActiveBlockList::update(std::vector &active_positions, + s16 radius, + std::set &blocks_removed, + std::set &blocks_added) +{ + /* + Create the new list + */ + std::set newlist = m_forceloaded_list; + for(std::vector::iterator i = active_positions.begin(); + i != active_positions.end(); ++i) + { + fillRadiusBlock(*i, radius, newlist); + } + + /* + Find out which blocks on the old list are not on the new list + */ + // Go through old list + for(std::set::iterator i = m_list.begin(); + i != m_list.end(); ++i) + { + v3s16 p = *i; + // If not on new list, it's been removed + 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(std::set::iterator i = newlist.begin(); + i != newlist.end(); ++i) + { + v3s16 p = *i; + // If not on old list, it's been added + if(m_list.find(p) == m_list.end()) + blocks_added.insert(p); + } + + /* + Update m_list + */ + m_list.clear(); + for(std::set::iterator i = newlist.begin(); + i != newlist.end(); ++i) + { + v3s16 p = *i; + m_list.insert(p); + } +} + +/* + ServerEnvironment +*/ + +ServerEnvironment::ServerEnvironment(ServerMap *map, + GameScripting *scriptIface, IGameDef *gamedef, + const std::string &path_world) : + m_map(map), + m_script(scriptIface), + m_gamedef(gamedef), + m_path_world(path_world), + m_send_recommended_timer(0), + m_active_block_interval_overload_skip(0), + m_game_time(0), + m_game_time_fraction_counter(0), + m_last_clear_objects_time(0), + m_recommended_send_interval(0.1), + m_max_lag_estimate(0.1) +{ +} + +ServerEnvironment::~ServerEnvironment() +{ + // Clear active block list. + // This makes the next one delete all active objects. + m_active_blocks.clear(); + + // Convert all objects to static and delete the active objects + deactivateFarObjects(true); + + // Drop/delete map + m_map->drop(); + + // Delete ActiveBlockModifiers + for (std::vector::iterator + i = m_abms.begin(); i != m_abms.end(); ++i){ + delete i->abm; + } + + // Deallocate players + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + delete (*i); + } +} + +Map & ServerEnvironment::getMap() +{ + return *m_map; +} + +ServerMap & ServerEnvironment::getServerMap() +{ + return *m_map; +} + +RemotePlayer *ServerEnvironment::getPlayer(const u16 peer_id) +{ + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = *i; + if (player->peer_id == peer_id) + return player; + } + return NULL; +} + +RemotePlayer *ServerEnvironment::getPlayer(const char* name) +{ + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = *i; + if (strcmp(player->getName(), name) == 0) + return player; + } + return NULL; +} + +void ServerEnvironment::addPlayer(RemotePlayer *player) +{ + DSTACK(FUNCTION_NAME); + /* + Check that peer_ids are unique. + Also check that names are unique. + Exception: there can be multiple players with peer_id=0 + */ + // If peer id is non-zero, it has to be unique. + if (player->peer_id != 0) + FATAL_ERROR_IF(getPlayer(player->peer_id) != NULL, "Peer id not unique"); + // Name has to be unique. + FATAL_ERROR_IF(getPlayer(player->getName()) != NULL, "Player name not unique"); + // Add. + m_players.push_back(player); +} + +void ServerEnvironment::removePlayer(RemotePlayer *player) +{ + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); ++it) { + if ((*it) == player) { + delete *it; + m_players.erase(it); + return; + } + } +} + +bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p) +{ + float distance = pos1.getDistanceFrom(pos2); + + //calculate normalized direction vector + v3f normalized_vector = v3f((pos2.X - pos1.X)/distance, + (pos2.Y - pos1.Y)/distance, + (pos2.Z - pos1.Z)/distance); + + //find out if there's a node on path between pos1 and pos2 + for (float i = 1; i < distance; i += stepsize) { + v3s16 pos = floatToInt(v3f(normalized_vector.X * i, + normalized_vector.Y * i, + normalized_vector.Z * i) +pos1,BS); + + MapNode n = getMap().getNodeNoEx(pos); + + if(n.param0 != CONTENT_AIR) { + if (p) { + *p = pos; + } + return false; + } + } + return true; +} + +void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, + const std::string &str_reason, bool reconnect) +{ + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); ++it) { + RemotePlayer *player = dynamic_cast(*it); + ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id, + player->protocol_version, reason, str_reason, reconnect); + } +} + +void ServerEnvironment::saveLoadedPlayers() +{ + std::string players_path = m_path_world + DIR_DELIM "players"; + fs::CreateDir(players_path); + + for (std::vector::iterator it = m_players.begin(); + it != m_players.end(); + ++it) { + if ((*it)->checkModified()) { + (*it)->save(players_path, m_gamedef); + } + } +} + +void ServerEnvironment::savePlayer(RemotePlayer *player) +{ + std::string players_path = m_path_world + DIR_DELIM "players"; + fs::CreateDir(players_path); + + player->save(players_path, m_gamedef); +} + +RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) +{ + bool newplayer = false; + bool found = false; + std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM; + std::string path = players_path + playername; + + RemotePlayer *player = getPlayer(playername.c_str()); + if (!player) { + player = new RemotePlayer("", m_gamedef->idef()); + newplayer = true; + } + + for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { + //// Open file and deserialize + std::ifstream is(path.c_str(), std::ios_base::binary); + if (!is.good()) + continue; + + player->deSerialize(is, path, sao); + is.close(); + + if (player->getName() == playername) { + found = true; + break; + } + + path = players_path + playername + itos(i); + } + + if (!found) { + infostream << "Player file for player " << playername + << " not found" << std::endl; + if (newplayer) + delete player; + + return NULL; + } + + if (newplayer) { + addPlayer(player); + } + player->setModified(false); + return player; +} + +void ServerEnvironment::saveMeta() +{ + std::string path = m_path_world + DIR_DELIM "env_meta.txt"; + + // Open file and serialize + std::ostringstream ss(std::ios_base::binary); + + Settings args; + args.setU64("game_time", m_game_time); + args.setU64("time_of_day", getTimeOfDay()); + args.setU64("last_clear_objects_time", m_last_clear_objects_time); + args.setU64("lbm_introduction_times_version", 1); + args.set("lbm_introduction_times", + m_lbm_mgr.createIntroductionTimesString()); + args.setU64("day_count", m_day_count); + args.writeLines(ss); + ss<<"EnvArgsEnd\n"; + + if(!fs::safeWriteToFile(path, ss.str())) + { + infostream<<"ServerEnvironment::saveMeta(): Failed to write " + < required_neighbors; +}; + +class ABMHandler +{ +private: + ServerEnvironment *m_env; + std::vector *> m_aabms; +public: + ABMHandler(std::vector &abms, + float dtime_s, ServerEnvironment *env, + bool use_timers): + m_env(env) + { + if(dtime_s < 0.001) + return; + INodeDefManager *ndef = env->getGameDef()->ndef(); + for(std::vector::iterator + i = abms.begin(); i != abms.end(); ++i) { + ActiveBlockModifier *abm = i->abm; + float trigger_interval = abm->getTriggerInterval(); + if(trigger_interval < 0.001) + trigger_interval = 0.001; + float actual_interval = dtime_s; + if(use_timers){ + i->timer += dtime_s; + if(i->timer < trigger_interval) + continue; + i->timer -= trigger_interval; + actual_interval = trigger_interval; + } + float chance = abm->getTriggerChance(); + if(chance == 0) + chance = 1; + ActiveABM aabm; + aabm.abm = abm; + if(abm->getSimpleCatchUp()) { + float intervals = actual_interval / trigger_interval; + if(intervals == 0) + continue; + aabm.chance = chance / intervals; + if(aabm.chance == 0) + aabm.chance = 1; + } else { + aabm.chance = chance; + } + // Trigger neighbors + std::set required_neighbors_s + = abm->getRequiredNeighbors(); + for(std::set::iterator + i = required_neighbors_s.begin(); + i != required_neighbors_s.end(); ++i) + { + ndef->getIds(*i, aabm.required_neighbors); + } + // Trigger contents + std::set contents_s = abm->getTriggerContents(); + for(std::set::iterator + i = contents_s.begin(); i != contents_s.end(); ++i) + { + std::set ids; + ndef->getIds(*i, ids); + for(std::set::const_iterator k = ids.begin(); + k != ids.end(); ++k) + { + content_t c = *k; + if (c >= m_aabms.size()) + m_aabms.resize(c + 256, NULL); + if (!m_aabms[c]) + m_aabms[c] = new std::vector; + m_aabms[c]->push_back(aabm); + } + } + } + } + + ~ABMHandler() + { + for (size_t i = 0; i < m_aabms.size(); i++) + delete m_aabms[i]; + } + + // Find out how many objects the given block and its neighbours contain. + // Returns the number of objects in the block, and also in 'wider' the + // number of objects in the block and all its neighbours. The latter + // may an estimate if any neighbours are unloaded. + u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider) + { + wider = 0; + u32 wider_unknown_count = 0; + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) + { + MapBlock *block2 = map->getBlockNoCreateNoEx( + block->getPos() + v3s16(x,y,z)); + if(block2==NULL){ + wider_unknown_count++; + continue; + } + wider += block2->m_static_objects.m_active.size() + + block2->m_static_objects.m_stored.size(); + } + // Extrapolate + u32 active_object_count = block->m_static_objects.m_active.size(); + u32 wider_known_count = 3*3*3 - wider_unknown_count; + wider += wider_unknown_count * wider / wider_known_count; + return active_object_count; + + } + void apply(MapBlock *block) + { + if(m_aabms.empty() || block->isDummy()) + return; + + ServerMap *map = &m_env->getServerMap(); + + u32 active_object_count_wider; + u32 active_object_count = this->countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + + v3s16 p0; + for(p0.X=0; p0.XgetNodeUnsafe(p0); + content_t c = n.getContent(); + + if (c >= m_aabms.size() || !m_aabms[c]) + continue; + + v3s16 p = p0 + block->getPosRelative(); + for(std::vector::iterator + i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { + if(myrand() % i->chance != 0) + continue; + + // Check neighbors + if(!i->required_neighbors.empty()) + { + v3s16 p1; + for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) + for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) + for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) + { + if(p1 == p0) + continue; + content_t c; + if (block->isValidPosition(p1)) { + // if the neighbor is found on the same map block + // get it straight from there + const MapNode &n = block->getNodeUnsafe(p1); + c = n.getContent(); + } else { + // otherwise consult the map + MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); + c = n.getContent(); + } + std::set::const_iterator k; + k = i->required_neighbors.find(c); + if(k != i->required_neighbors.end()){ + goto neighbor_found; + } + } + // No required neighbor found + continue; + } + neighbor_found: + + // Call all the trigger variations + i->abm->trigger(m_env, p, n); + i->abm->trigger(m_env, p, n, + active_object_count, active_object_count_wider); + + // Count surrounding objects again if the abms added any + if(m_env->m_added_objects > 0) { + active_object_count = countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + } + } + } + } +}; + +void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) +{ + // Reset usage timer immediately, otherwise a block that becomes active + // again at around the same time as it would normally be unloaded will + // get unloaded incorrectly. (I think this still leaves a small possibility + // of a race condition between this and server::AsyncRunStep, which only + // some kind of synchronisation will fix, but it at least reduces the window + // of opportunity for it to break from seconds to nanoseconds) + block->resetUsageTimer(); + + // Get time difference + u32 dtime_s = 0; + u32 stamp = block->getTimestamp(); + if (m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) + dtime_s = m_game_time - stamp; + dtime_s += additional_dtime; + + /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: " + <m_static_objects.m_stored.clear(); + // do not set changed flag to avoid unnecessary mapblock writes + } + + // Set current time as timestamp + block->setTimestampNoChangedFlag(m_game_time); + + /*infostream<<"ServerEnvironment::activateBlock(): block is " + < elapsed_timers = + block->m_node_timers.step((float)dtime_s); + if (!elapsed_timers.empty()) { + MapNode n; + for (std::vector::iterator + i = elapsed_timers.begin(); + i != elapsed_timers.end(); ++i){ + n = block->getNodeNoEx(i->position); + v3s16 p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) + block->setNodeTimer(NodeTimer(i->timeout, 0, i->position)); + } + } + + /* Handle ActiveBlockModifiers */ + ABMHandler abmhandler(m_abms, dtime_s, this, false); + abmhandler.apply(block); +} + +void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm) +{ + m_abms.push_back(ABMWithState(abm)); +} + +void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) +{ + m_lbm_mgr.addLBMDef(lbm); +} + +bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) +{ + INodeDefManager *ndef = m_gamedef->ndef(); + MapNode n_old = m_map->getNodeNoEx(p); + + // Call destructor + if (ndef->get(n_old).has_on_destruct) + m_script->node_on_destruct(p, n_old); + + // Replace node + if (!m_map->addNodeWithEvent(p, n)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + // Call post-destructor + if (ndef->get(n_old).has_after_destruct) + m_script->node_after_destruct(p, n_old); + + // Call constructor + if (ndef->get(n).has_on_construct) + m_script->node_on_construct(p, n); + + return true; +} + +bool ServerEnvironment::removeNode(v3s16 p) +{ + INodeDefManager *ndef = m_gamedef->ndef(); + MapNode n_old = m_map->getNodeNoEx(p); + + // Call destructor + if (ndef->get(n_old).has_on_destruct) + m_script->node_on_destruct(p, n_old); + + // Replace with air + // This is slightly optimized compared to addNodeWithEvent(air) + if (!m_map->removeNodeWithEvent(p)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + // Call post-destructor + if (ndef->get(n_old).has_after_destruct) + m_script->node_after_destruct(p, n_old); + + // Air doesn't require constructor + return true; +} + +bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n) +{ + if (!m_map->addNodeWithEvent(p, n, false)) + return false; + + // Update active VoxelManipulator if a mapgen thread + m_map->updateVManip(p); + + return true; +} + +void ServerEnvironment::getObjectsInsideRadius(std::vector &objects, v3f pos, float radius) +{ + for (ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ServerActiveObject* obj = i->second; + u16 id = i->first; + v3f objectpos = obj->getBasePosition(); + if (objectpos.getDistanceFrom(pos) > radius) + continue; + objects.push_back(id); + } +} + +void ServerEnvironment::clearObjects(ClearObjectsMode mode) +{ + infostream << "ServerEnvironment::clearObjects(): " + << "Removing all active objects" << std::endl; + std::vector objects_to_remove; + for (ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ServerActiveObject* obj = i->second; + if (obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) + continue; + u16 id = i->first; + // Delete static object if block is loaded + if (obj->m_static_exists) { + MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block); + if (block) { + block->m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_CLEAR_ALL_OBJECTS); + obj->m_static_exists = false; + } + } + // If known by some client, don't delete immediately + if (obj->m_known_by_count > 0) { + obj->m_pending_deactivation = true; + obj->m_removed = true; + continue; + } + + // Tell the object about removal + obj->removingFromEnvironment(); + // Deregister in scripting api + m_script->removeObjectReference(obj); + + // Delete active object + if (obj->environmentDeletes()) + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + + // Remove references from m_active_objects + for (std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { + m_active_objects.erase(*i); + } + + // Get list of loaded blocks + std::vector loaded_blocks; + infostream << "ServerEnvironment::clearObjects(): " + << "Listing all loaded blocks" << std::endl; + m_map->listAllLoadedBlocks(loaded_blocks); + infostream << "ServerEnvironment::clearObjects(): " + << "Done listing all loaded blocks: " + << loaded_blocks.size()< loadable_blocks; + if (mode == CLEAR_OBJECTS_MODE_FULL) { + infostream << "ServerEnvironment::clearObjects(): " + << "Listing all loadable blocks" << std::endl; + m_map->listAllLoadableBlocks(loadable_blocks); + infostream << "ServerEnvironment::clearObjects(): " + << "Done listing all loadable blocks: " + << loadable_blocks.size() << std::endl; + } else { + loadable_blocks = loaded_blocks; + } + + infostream << "ServerEnvironment::clearObjects(): " + << "Now clearing objects in " << loadable_blocks.size() + << " blocks" << std::endl; + + // Grab a reference on each loaded block to avoid unloading it + for (std::vector::iterator i = loaded_blocks.begin(); + i != loaded_blocks.end(); ++i) { + v3s16 p = *i; + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + assert(block != NULL); + block->refGrab(); + } + + // Remove objects in all loadable blocks + u32 unload_interval = U32_MAX; + if (mode == CLEAR_OBJECTS_MODE_FULL) { + unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks"); + unload_interval = MYMAX(unload_interval, 1); + } + u32 report_interval = loadable_blocks.size() / 10; + u32 num_blocks_checked = 0; + u32 num_blocks_cleared = 0; + u32 num_objs_cleared = 0; + for (std::vector::iterator i = loadable_blocks.begin(); + i != loadable_blocks.end(); ++i) { + v3s16 p = *i; + MapBlock *block = m_map->emergeBlock(p, false); + if (!block) { + errorstream << "ServerEnvironment::clearObjects(): " + << "Failed to emerge block " << PP(p) << std::endl; + continue; + } + u32 num_stored = block->m_static_objects.m_stored.size(); + u32 num_active = block->m_static_objects.m_active.size(); + if (num_stored != 0 || num_active != 0) { + block->m_static_objects.m_stored.clear(); + block->m_static_objects.m_active.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_CLEAR_ALL_OBJECTS); + num_objs_cleared += num_stored + num_active; + num_blocks_cleared++; + } + num_blocks_checked++; + + if (report_interval != 0 && + num_blocks_checked % report_interval == 0) { + float percent = 100.0 * (float)num_blocks_checked / + loadable_blocks.size(); + infostream << "ServerEnvironment::clearObjects(): " + << "Cleared " << num_objs_cleared << " objects" + << " in " << num_blocks_cleared << " blocks (" + << percent << "%)" << std::endl; + } + if (num_blocks_checked % unload_interval == 0) { + m_map->unloadUnreferencedBlocks(); + } + } + m_map->unloadUnreferencedBlocks(); + + // Drop references that were added above + for (std::vector::iterator i = loaded_blocks.begin(); + i != loaded_blocks.end(); ++i) { + v3s16 p = *i; + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + assert(block); + block->refDrop(); + } + + m_last_clear_objects_time = m_game_time; + + infostream << "ServerEnvironment::clearObjects(): " + << "Finished: Cleared " << num_objs_cleared << " objects" + << " in " << num_blocks_cleared << " blocks" << std::endl; +} + +void ServerEnvironment::step(float dtime) +{ + DSTACK(FUNCTION_NAME); + + //TimeTaker timer("ServerEnv step"); + + /* Step time of day */ + stepTimeOfDay(dtime); + + // Update this one + // NOTE: This is kind of funny on a singleplayer game, but doesn't + // really matter that much. + static const float server_step = g_settings->getFloat("dedicated_server_step"); + m_recommended_send_interval = server_step; + + /* + Increment game time + */ + { + m_game_time_fraction_counter += dtime; + u32 inc_i = (u32)m_game_time_fraction_counter; + m_game_time += inc_i; + m_game_time_fraction_counter -= (float)inc_i; + } + + /* + Handle players + */ + { + ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG); + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = dynamic_cast(*i); + assert(player); + + // Ignore disconnected players + if(player->peer_id == 0) + continue; + + // Move + player->move(dtime, this, 100*BS); + } + } + + /* + Manage active block list + */ + if (m_active_blocks_management_interval.step(dtime, m_cache_active_block_mgmt_interval)) { + ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg per interval", SPT_AVG); + /* + Get player block positions + */ + std::vector players_blockpos; + for (std::vector::iterator i = m_players.begin(); + i != m_players.end(); ++i) { + RemotePlayer *player = dynamic_cast(*i); + assert(player); + + // Ignore disconnected players + if (player->peer_id == 0) + continue; + + PlayerSAO *playersao = player->getPlayerSAO(); + assert(playersao); + + v3s16 blockpos = getNodeBlockPos( + floatToInt(playersao->getBasePosition(), BS)); + players_blockpos.push_back(blockpos); + } + + /* + Update list of active blocks, collecting changes + */ + static const s16 active_block_range = g_settings->getS16("active_block_range"); + std::set blocks_removed; + std::set blocks_added; + m_active_blocks.update(players_blockpos, active_block_range, + blocks_removed, blocks_added); + + /* + Handle removed blocks + */ + + // Convert active objects that are no more in active blocks to static + deactivateFarObjects(false); + + for(std::set::iterator + i = blocks_removed.begin(); + i != blocks_removed.end(); ++i) { + v3s16 p = *i; + + /* infostream<<"Server: Block " << PP(p) + << " became inactive"<getBlockNoCreateNoEx(p); + if(block==NULL) + continue; + + // Set current time as timestamp (and let it set ChangedFlag) + block->setTimestamp(m_game_time); + } + + /* + Handle added blocks + */ + + for(std::set::iterator + i = blocks_added.begin(); + i != blocks_added.end(); ++i) + { + v3s16 p = *i; + + MapBlock *block = m_map->getBlockOrEmerge(p); + if(block==NULL){ + m_active_blocks.m_list.erase(p); + continue; + } + + activateBlock(block); + /* infostream<<"Server: Block " << PP(p) + << " became active"<::iterator + i = m_active_blocks.m_list.begin(); + i != m_active_blocks.m_list.end(); ++i) + { + v3s16 p = *i; + + /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); + if(block==NULL) + continue; + + // Reset block usage timer + block->resetUsageTimer(); + + // Set current time as timestamp + block->setTimestampNoChangedFlag(m_game_time); + // If time has changed much from the one on disk, + // set block to be saved when it is unloaded + if(block->getTimestamp() > block->getDiskTimestamp() + 60) + block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD, + MOD_REASON_BLOCK_EXPIRED); + + // Run node timers + std::vector elapsed_timers = + block->m_node_timers.step((float)dtime); + if (!elapsed_timers.empty()) { + MapNode n; + for (std::vector::iterator i = elapsed_timers.begin(); + i != elapsed_timers.end(); ++i) { + n = block->getNodeNoEx(i->position); + p = i->position + block->getPosRelative(); + if (m_script->node_on_timer(p, n, i->elapsed)) { + block->setNodeTimer(NodeTimer( + i->timeout, 0, i->position)); + } + } + } + } + } + + if (m_active_block_modifier_interval.step(dtime, m_cache_abm_interval)) + do{ // breakable + if(m_active_block_interval_overload_skip > 0){ + ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips"); + m_active_block_interval_overload_skip--; + break; + } + ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg per interval", SPT_AVG); + TimeTaker timer("modify in active blocks per interval"); + + // Initialize handling of ActiveBlockModifiers + ABMHandler abmhandler(m_abms, m_cache_abm_interval, this, true); + + for(std::set::iterator + i = m_active_blocks.m_list.begin(); + i != m_active_blocks.m_list.end(); ++i) + { + v3s16 p = *i; + + /*infostream<<"Server: Block ("<getBlockNoCreateNoEx(p); + if(block == NULL) + continue; + + // Set current time as timestamp + block->setTimestampNoChangedFlag(m_game_time); + + /* Handle ActiveBlockModifiers */ + abmhandler.apply(block); + } + + u32 time_ms = timer.stop(true); + u32 max_time_ms = 200; + if(time_ms > max_time_ms){ + warningstream<<"active block modifiers took " + <environment_Step(dtime); + + /* + Step active objects + */ + { + ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG); + //TimeTaker timer("Step active objects"); + + g_profiler->avg("SEnv: num of objects", m_active_objects.size()); + + // This helps the objects to send data at the same time + bool send_recommended = false; + m_send_recommended_timer += dtime; + if(m_send_recommended_timer > getSendRecommendedInterval()) + { + m_send_recommended_timer -= getSendRecommendedInterval(); + send_recommended = true; + } + + for(ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + ServerActiveObject* obj = i->second; + // Don't step if is to be removed or stored statically + if(obj->m_removed || obj->m_pending_deactivation) + continue; + // Step object + obj->step(dtime, send_recommended); + // Read messages from object + while(!obj->m_messages_out.empty()) + { + m_active_object_messages.push( + obj->m_messages_out.front()); + obj->m_messages_out.pop(); + } + } + } + + /* + Manage active objects + */ + if(m_object_management_interval.step(dtime, 0.5)) + { + ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG); + /* + Remove objects that satisfy (m_removed && m_known_by_count==0) + */ + removeRemovedObjects(); + } + + /* + Manage particle spawner expiration + */ + if (m_particle_management_interval.step(dtime, 1.0)) { + for (UNORDERED_MAP::iterator i = m_particle_spawners.begin(); + i != m_particle_spawners.end(); ) { + //non expiring spawners + if (i->second == PARTICLE_SPAWNER_NO_EXPIRY) { + ++i; + continue; + } + + i->second -= 1.0f; + if (i->second <= 0.f) + m_particle_spawners.erase(i++); + else + ++i; + } + } +} + +u32 ServerEnvironment::addParticleSpawner(float exptime) +{ + // Timers with lifetime 0 do not expire + float time = exptime > 0.f ? exptime : PARTICLE_SPAWNER_NO_EXPIRY; + + u32 id = 0; + for (;;) { // look for unused particlespawner id + id++; + UNORDERED_MAP::iterator f = m_particle_spawners.find(id); + if (f == m_particle_spawners.end()) { + m_particle_spawners[id] = time; + break; + } + } + return id; +} + +u32 ServerEnvironment::addParticleSpawner(float exptime, u16 attached_id) +{ + u32 id = addParticleSpawner(exptime); + m_particle_spawner_attachments[id] = attached_id; + if (ServerActiveObject *obj = getActiveObject(attached_id)) { + obj->attachParticleSpawner(id); + } + return id; +} + +void ServerEnvironment::deleteParticleSpawner(u32 id, bool remove_from_object) +{ + m_particle_spawners.erase(id); + UNORDERED_MAP::iterator it = m_particle_spawner_attachments.find(id); + if (it != m_particle_spawner_attachments.end()) { + u16 obj_id = (*it).second; + ServerActiveObject *sao = getActiveObject(obj_id); + if (sao != NULL && remove_from_object) { + sao->detachParticleSpawner(id); + } + m_particle_spawner_attachments.erase(id); + } +} + +ServerActiveObject* ServerEnvironment::getActiveObject(u16 id) +{ + ActiveObjectMap::iterator n = m_active_objects.find(id); + return (n != m_active_objects.end() ? n->second : NULL); +} + +bool isFreeServerActiveObjectId(u16 id, ActiveObjectMap &objects) +{ + if (id == 0) + return false; + + return objects.find(id) == objects.end(); +} + +u16 getFreeServerActiveObjectId(ActiveObjectMap &objects) +{ + //try to reuse id's as late as possible + static u16 last_used_id = 0; + u16 startid = last_used_id; + for(;;) + { + last_used_id ++; + if(isFreeServerActiveObjectId(last_used_id, objects)) + return last_used_id; + + if(last_used_id == startid) + return 0; + } +} + +u16 ServerEnvironment::addActiveObject(ServerActiveObject *object) +{ + assert(object); // Pre-condition + m_added_objects++; + u16 id = addActiveObjectRaw(object, true, 0); + return id; +} + +/* + Finds out what new objects have been added to + inside a radius around a position +*/ +void ServerEnvironment::getAddedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &added_objects) +{ + f32 radius_f = radius * BS; + f32 player_radius_f = player_radius * BS; + + if (player_radius_f < 0) + player_radius_f = 0; + /* + Go through the object list, + - discard m_removed objects, + - discard objects that are too far away, + - discard objects that are found in current_objects. + - add remaining objects to added_objects + */ + for (ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + u16 id = i->first; + + // Get object + ServerActiveObject *object = i->second; + if (object == NULL) + continue; + + // Discard if removed or deactivating + if(object->m_removed || object->m_pending_deactivation) + continue; + + f32 distance_f = object->getBasePosition(). + getDistanceFrom(playersao->getBasePosition()); + if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + // Discard if too far + if (distance_f > player_radius_f && player_radius_f != 0) + continue; + } else if (distance_f > radius_f) + continue; + + // Discard if already on current_objects + std::set::iterator n; + n = current_objects.find(id); + if(n != current_objects.end()) + continue; + // Add to added_objects + added_objects.push(id); + } +} + +/* + Finds out what objects have been removed from + inside a radius around a position +*/ +void ServerEnvironment::getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &removed_objects) +{ + f32 radius_f = radius * BS; + f32 player_radius_f = player_radius * BS; + + if (player_radius_f < 0) + player_radius_f = 0; + /* + Go through current_objects; object is removed if: + - object is not found in m_active_objects (this is actually an + error condition; objects should be set m_removed=true and removed + only after all clients have been informed about removal), or + - object has m_removed=true, or + - object is too far away + */ + for(std::set::iterator + i = current_objects.begin(); + i != current_objects.end(); ++i) + { + u16 id = *i; + ServerActiveObject *object = getActiveObject(id); + + if (object == NULL) { + infostream << "ServerEnvironment::getRemovedActiveObjects():" + << " object in current_objects is NULL" << std::endl; + removed_objects.push(id); + continue; + } + + if (object->m_removed || object->m_pending_deactivation) { + removed_objects.push(id); + continue; + } + + f32 distance_f = object->getBasePosition().getDistanceFrom(playersao->getBasePosition()); + if (object->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + if (distance_f <= player_radius_f || player_radius_f == 0) + continue; + } else if (distance_f <= radius_f) + continue; + + // Object is no longer visible + removed_objects.push(id); + } +} + +void ServerEnvironment::setStaticForActiveObjectsInBlock( + v3s16 blockpos, bool static_exists, v3s16 static_block) +{ + MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos); + if (!block) + return; + + for (std::map::iterator + so_it = block->m_static_objects.m_active.begin(); + so_it != block->m_static_objects.m_active.end(); ++so_it) { + // Get the ServerActiveObject counterpart to this StaticObject + ActiveObjectMap::iterator ao_it = m_active_objects.find(so_it->first); + if (ao_it == m_active_objects.end()) { + // If this ever happens, there must be some kind of nasty bug. + errorstream << "ServerEnvironment::setStaticForObjectsInBlock(): " + "Object from MapBlock::m_static_objects::m_active not found " + "in m_active_objects"; + continue; + } + + ServerActiveObject *sao = ao_it->second; + sao->m_static_exists = static_exists; + sao->m_static_block = static_block; + } +} + +ActiveObjectMessage ServerEnvironment::getActiveObjectMessage() +{ + if(m_active_object_messages.empty()) + return ActiveObjectMessage(0); + + ActiveObjectMessage message = m_active_object_messages.front(); + m_active_object_messages.pop(); + return message; +} + +/* + ************ Private methods ************* +*/ + +u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, + bool set_changed, u32 dtime_s) +{ + assert(object); // Pre-condition + if(object->getId() == 0){ + u16 new_id = getFreeServerActiveObjectId(m_active_objects); + if(new_id == 0) + { + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"no free ids available"<environmentDeletes()) + delete object; + return 0; + } + object->setId(new_id); + } + else{ + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"supplied with id "<getId()<getId(), m_active_objects)) { + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"id is not free ("<getId()<<")"<environmentDeletes()) + delete object; + return 0; + } + + if (objectpos_over_limit(object->getBasePosition())) { + v3f p = object->getBasePosition(); + errorstream << "ServerEnvironment::addActiveObjectRaw(): " + << "object position (" << p.X << "," << p.Y << "," << p.Z + << ") outside maximum range" << std::endl; + if (object->environmentDeletes()) + delete object; + return 0; + } + + /*infostream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"added (id="<getId()<<")"<getId()] = object; + + verbosestream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"Added id="<getId()<<"; there are now " + <addObjectReference(object); + // Post-initialize object + object->addedToEnvironment(dtime_s); + + // Add static data to block + if(object->isStaticAllowed()) + { + // Add static object to active static list of the block + v3f objectpos = object->getBasePosition(); + std::string staticdata = object->getStaticData(); + StaticObject s_obj(object->getType(), objectpos, staticdata); + // Add to the block where the object is located in + v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); + MapBlock *block = m_map->emergeBlock(blockpos); + if(block){ + block->m_static_objects.m_active[object->getId()] = s_obj; + object->m_static_exists = true; + object->m_static_block = blockpos; + + if(set_changed) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_ADD_ACTIVE_OBJECT_RAW); + } else { + v3s16 p = floatToInt(objectpos, BS); + errorstream<<"ServerEnvironment::addActiveObjectRaw(): " + <<"could not emerge block for storing id="<getId() + <<" statically (pos="<getId(); +} + +/* + Remove objects that satisfy (m_removed && m_known_by_count==0) +*/ +void ServerEnvironment::removeRemovedObjects() +{ + std::vector objects_to_remove; + for(ActiveObjectMap::iterator i = m_active_objects.begin(); + i != m_active_objects.end(); ++i) { + u16 id = i->first; + ServerActiveObject* obj = i->second; + // This shouldn't happen but check it + if(obj == NULL) + { + infostream<<"NULL object found in ServerEnvironment" + <<" while finding removed objects. id="<m_removed && !obj->m_pending_deactivation) + continue; + + /* + Delete static data from block if is marked as removed + */ + if(obj->m_static_exists && obj->m_removed) + { + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + if (block) { + block->m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_REMOVE_OBJECTS_REMOVE); + obj->m_static_exists = false; + } else { + infostream<<"Failed to emerge block from which an object to " + <<"be removed was loaded from. id="< 0, don't actually remove. On some future + // invocation this will be 0, which is when removal will continue. + if(obj->m_known_by_count > 0) + continue; + + /* + Move static data from active to stored if not marked as removed + */ + if(obj->m_static_exists && !obj->m_removed){ + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + if (block) { + std::map::iterator i = + block->m_static_objects.m_active.find(id); + if(i != block->m_static_objects.m_active.end()){ + block->m_static_objects.m_stored.push_back(i->second); + block->m_static_objects.m_active.erase(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_REMOVE_OBJECTS_DEACTIVATE); + } + } else { + infostream<<"Failed to emerge block from which an object to " + <<"be deactivated was loaded from. id="<removingFromEnvironment(); + // Deregister in scripting api + m_script->removeObjectReference(obj); + + // Delete + if(obj->environmentDeletes()) + delete obj; + + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + // Remove references from m_active_objects + for(std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { + m_active_objects.erase(*i); + } +} + +static void print_hexdump(std::ostream &o, const std::string &data) +{ + const int linelength = 16; + for(int l=0; ; l++){ + int i0 = linelength * l; + bool at_end = false; + int thislinelength = linelength; + if(i0 + thislinelength > (int)data.size()){ + thislinelength = data.size() - i0; + at_end = true; + } + for(int di=0; di= 32) + o<m_static_objects.m_stored.empty()) + return; + + verbosestream<<"ServerEnvironment::activateObjects(): " + <<"activating objects of block "<getPos()) + <<" ("<m_static_objects.m_stored.size() + <<" objects)"<m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block")); + if (large_amount) { + errorstream<<"suspiciously large amount of objects detected: " + <m_static_objects.m_stored.size()<<" in " + <getPos()) + <<"; removing all of them."<m_static_objects.m_stored.clear(); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_TOO_MANY_OBJECTS); + return; + } + + // Activate stored objects + std::vector new_stored; + for (std::vector::iterator + i = block->m_static_objects.m_stored.begin(); + i != block->m_static_objects.m_stored.end(); ++i) { + StaticObject &s_obj = *i; + + // Create an active object from the data + ServerActiveObject *obj = ServerActiveObject::create + ((ActiveObjectType) s_obj.type, this, 0, s_obj.pos, s_obj.data); + // If couldn't create object, store static data back. + if(obj == NULL) { + errorstream<<"ServerEnvironment::activateObjects(): " + <<"failed to create active object from static object " + <<"in block "<getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + block->m_static_objects.insert(id, s_obj); + obj->m_static_block = blockpos_o; + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_ADDED); + + // Delete from block where object was located + block = m_map->emergeBlock(old_static_block, false); + if(!block){ + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"Could not delete object id="<m_static_objects.remove(id); + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_REMOVED); + continue; + } + + // If block is active, don't remove + if(!force_delete && m_active_blocks.contains(blockpos_o)) + continue; + + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"deactivating object id="<m_known_by_count > 0 && !force_delete); + + /* + Update the static data + */ + + if(obj->isStaticAllowed()) + { + // Create new static object + std::string staticdata_new = obj->getStaticData(); + StaticObject s_obj(obj->getType(), objectpos, staticdata_new); + + bool stays_in_same_block = false; + bool data_changed = true; + + if (obj->m_static_exists) { + if (obj->m_static_block == blockpos_o) + stays_in_same_block = true; + + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + + if (block) { + std::map::iterator n = + block->m_static_objects.m_active.find(id); + if (n != block->m_static_objects.m_active.end()) { + StaticObject static_old = n->second; + + float save_movem = obj->getMinimumSavedMovement(); + + if (static_old.data == staticdata_new && + (static_old.pos - objectpos).getLength() < save_movem) + data_changed = false; + } else { + errorstream<<"ServerEnvironment::deactivateFarObjects(): " + <<"id="<m_static_block)<m_static_exists) + { + MapBlock *block = m_map->emergeBlock(obj->m_static_block, false); + if(block) + { + block->m_static_objects.remove(id); + obj->m_static_exists = false; + // Only mark block as modified if data changed considerably + if(shall_be_written) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_CHANGED); + } + } + + // Add to the block where the object is located in + v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); + // Get or generate the block + MapBlock *block = NULL; + try{ + block = m_map->emergeBlock(blockpos); + } catch(InvalidPositionException &e){ + // Handled via NULL pointer + // NOTE: emergeBlock's failure is usually determined by it + // actually returning NULL + } + + if(block) + { + if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + warningstream << "ServerEnv: Trying to store id = " << obj->getId() + << " statically but block " << PP(blockpos) + << " already contains " + << block->m_static_objects.m_stored.size() + << " objects." + << " Forcing delete." << std::endl; + force_delete = true; + } else { + // If static counterpart already exists in target block, + // remove it first. + // This shouldn't happen because the object is removed from + // the previous block before this according to + // obj->m_static_block, but happens rarely for some unknown + // reason. Unsuccessful attempts have been made to find + // said reason. + if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){ + warningstream<<"ServerEnv: Performing hack #83274" + <m_static_objects.remove(id); + } + // Store static data + u16 store_id = pending_delete ? id : 0; + block->m_static_objects.insert(store_id, s_obj); + + // Only mark block as modified if data changed considerably + if(shall_be_written) + block->raiseModified(MOD_STATE_WRITE_NEEDED, + MOD_REASON_STATIC_DATA_CHANGED); + + obj->m_static_exists = true; + obj->m_static_block = block->getPos(); + } + } + else{ + if(!force_delete){ + v3s16 p = floatToInt(objectpos, BS); + errorstream<<"ServerEnv: Could not find or generate " + <<"a block for storing id="<getId() + <<" statically (pos="<m_pending_deactivation = true; + continue; + } + + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " + <<"object id="<removingFromEnvironment(); + // Deregister in scripting api + m_script->removeObjectReference(obj); + + // Delete active object + if(obj->environmentDeletes()) + delete obj; + // Id to be removed from m_active_objects + objects_to_remove.push_back(id); + } + + // Remove references from m_active_objects + for(std::vector::iterator i = objects_to_remove.begin(); + i != objects_to_remove.end(); ++i) { + m_active_objects.erase(*i); + } +} diff --git a/src/serverenvironment.h b/src/serverenvironment.h new file mode 100644 index 000000000..20a783ea5 --- /dev/null +++ b/src/serverenvironment.h @@ -0,0 +1,423 @@ +/* +Minetest +Copyright (C) 2010-2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef SERVER_ENVIRONMENT_HEADER +#define SERVER_ENVIRONMENT_HEADER + +#include "environment.h" + +class IGameDef; +class ServerMap; +class RemotePlayer; +class PlayerSAO; +class ServerEnvironment; +class ActiveBlockModifier; +class ServerActiveObject; + +/* + {Active, Loading} block modifier interface. + + These are fed into ServerEnvironment at initialization time; + ServerEnvironment handles deleting them. +*/ + +class ActiveBlockModifier +{ +public: + ActiveBlockModifier(){}; + virtual ~ActiveBlockModifier(){}; + + // Set of contents to trigger on + virtual std::set getTriggerContents()=0; + // Set of required neighbors (trigger doesn't happen if none are found) + // Empty = do not check neighbors + virtual std::set getRequiredNeighbors() + { return std::set(); } + // Trigger interval in seconds + virtual float getTriggerInterval() = 0; + // Random chance of (1 / return value), 0 is disallowed + virtual u32 getTriggerChance() = 0; + // Whether to modify chance to simulate time lost by an unnattended block + virtual bool getSimpleCatchUp() = 0; + // This is called usually at interval for 1/chance of the nodes + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider){}; +}; + +struct ABMWithState +{ + ActiveBlockModifier *abm; + float timer; + + ABMWithState(ActiveBlockModifier *abm_); +}; + +struct LoadingBlockModifierDef +{ + // Set of contents to trigger on + std::set trigger_contents; + std::string name; + bool run_at_every_load; + + virtual ~LoadingBlockModifierDef() {} + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n){}; +}; + +struct LBMContentMapping +{ + typedef std::map > container_map; + container_map map; + + std::vector lbm_list; + + // Needs to be separate method (not inside destructor), + // because the LBMContentMapping may be copied and destructed + // many times during operation in the lbm_lookup_map. + void deleteContents(); + void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef); + const std::vector *lookup(content_t c) const; +}; + +class LBMManager +{ +public: + LBMManager(): + m_query_mode(false) + {} + + ~LBMManager(); + + // Don't call this after loadIntroductionTimes() ran. + void addLBMDef(LoadingBlockModifierDef *lbm_def); + + void loadIntroductionTimes(const std::string ×, + IGameDef *gamedef, u32 now); + + // Don't call this before loadIntroductionTimes() ran. + std::string createIntroductionTimesString(); + + // Don't call this before loadIntroductionTimes() ran. + void applyLBMs(ServerEnvironment *env, MapBlock *block, u32 stamp); + + // Warning: do not make this std::unordered_map, order is relevant here + typedef std::map lbm_lookup_map; + +private: + // Once we set this to true, we can only query, + // not modify + bool m_query_mode; + + // For m_query_mode == false: + // The key of the map is the LBM def's name. + // TODO make this std::unordered_map + std::map m_lbm_defs; + + // For m_query_mode == true: + // The key of the map is the LBM def's first introduction time. + lbm_lookup_map m_lbm_lookup; + + // Returns an iterator to the LBMs that were introduced + // after the given time. This is guaranteed to return + // valid values for everything + lbm_lookup_map::const_iterator getLBMsIntroducedAfter(u32 time) + { return m_lbm_lookup.lower_bound(time); } +}; + +/* + List of active blocks, used by ServerEnvironment +*/ + +class ActiveBlockList +{ +public: + void update(std::vector &active_positions, + s16 radius, + std::set &blocks_removed, + std::set &blocks_added); + + bool contains(v3s16 p){ + return (m_list.find(p) != m_list.end()); + } + + void clear(){ + m_list.clear(); + } + + std::set m_list; + std::set m_forceloaded_list; + +private: +}; + +/* + Operation mode for ServerEnvironment::clearObjects() +*/ +enum ClearObjectsMode { + // Load and go through every mapblock, clearing objects + CLEAR_OBJECTS_MODE_FULL, + + // Clear objects immediately in loaded mapblocks; + // clear objects in unloaded mapblocks only when the mapblocks are next activated. + CLEAR_OBJECTS_MODE_QUICK, +}; + +/* + The server-side environment. + + This is not thread-safe. Server uses an environment mutex. +*/ + +typedef UNORDERED_MAP ActiveObjectMap; + +class ServerEnvironment : public Environment +{ +public: + ServerEnvironment(ServerMap *map, GameScripting *scriptIface, + IGameDef *gamedef, const std::string &path_world); + ~ServerEnvironment(); + + Map & getMap(); + + ServerMap & getServerMap(); + + //TODO find way to remove this fct! + GameScripting* getScriptIface() + { return m_script; } + + IGameDef *getGameDef() + { return m_gamedef; } + + float getSendRecommendedInterval() + { return m_recommended_send_interval; } + + void kickAllPlayers(AccessDeniedCode reason, + const std::string &str_reason, bool reconnect); + // Save players + void saveLoadedPlayers(); + void savePlayer(RemotePlayer *player); + RemotePlayer *loadPlayer(const std::string &playername, PlayerSAO *sao); + void addPlayer(RemotePlayer *player); + void removePlayer(RemotePlayer *player); + + /* + Save and load time of day and game timer + */ + void saveMeta(); + void loadMeta(); + // to be called instead of loadMeta if + // env_meta.txt doesn't exist (e.g. new world) + void loadDefaultMeta(); + + u32 addParticleSpawner(float exptime); + u32 addParticleSpawner(float exptime, u16 attached_id); + void deleteParticleSpawner(u32 id, bool remove_from_object = true); + + /* + External ActiveObject interface + ------------------------------------------- + */ + + ServerActiveObject* getActiveObject(u16 id); + + /* + Add an active object to the environment. + Environment handles deletion of object. + Object may be deleted by environment immediately. + If id of object is 0, assigns a free id to it. + Returns the id of the object. + Returns 0 if not added and thus deleted. + */ + u16 addActiveObject(ServerActiveObject *object); + + /* + Add an active object as a static object to the corresponding + MapBlock. + Caller allocates memory, ServerEnvironment frees memory. + Return value: true if succeeded, false if failed. + (note: not used, pending removal from engine) + */ + //bool addActiveObjectAsStatic(ServerActiveObject *object); + + /* + Find out what new objects have been added to + inside a radius around a position + */ + void getAddedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &added_objects); + + /* + Find out what new objects have been removed from + inside a radius around a position + */ + void getRemovedActiveObjects(PlayerSAO *playersao, s16 radius, + s16 player_radius, + std::set ¤t_objects, + std::queue &removed_objects); + + /* + Get the next message emitted by some active object. + Returns a message with id=0 if no messages are available. + */ + ActiveObjectMessage getActiveObjectMessage(); + + /* + Activate objects and dynamically modify for the dtime determined + from timestamp and additional_dtime + */ + void activateBlock(MapBlock *block, u32 additional_dtime=0); + + /* + {Active,Loading}BlockModifiers + ------------------------------------------- + */ + + void addActiveBlockModifier(ActiveBlockModifier *abm); + void addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm); + + /* + Other stuff + ------------------------------------------- + */ + + // Script-aware node setters + bool setNode(v3s16 p, const MapNode &n); + bool removeNode(v3s16 p); + bool swapNode(v3s16 p, const MapNode &n); + + // Find all active objects inside a radius around a point + void getObjectsInsideRadius(std::vector &objects, v3f pos, float radius); + + // Clear objects, loading and going through every MapBlock + void clearObjects(ClearObjectsMode mode); + + // This makes stuff happen + void step(f32 dtime); + + //check if there's a line of sight between two positions + bool line_of_sight(v3f pos1, v3f pos2, float stepsize=1.0, v3s16 *p=NULL); + + u32 getGameTime() { return m_game_time; } + + void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; } + float getMaxLagEstimate() { return m_max_lag_estimate; } + + std::set* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; }; + + // Sets the static object status all the active objects in the specified block + // This is only really needed for deleting blocks from the map + void setStaticForActiveObjectsInBlock(v3s16 blockpos, + bool static_exists, v3s16 static_block=v3s16(0,0,0)); + + RemotePlayer *getPlayer(const u16 peer_id); + RemotePlayer *getPlayer(const char* name); +private: + + /* + Internal ActiveObject interface + ------------------------------------------- + */ + + /* + Add an active object to the environment. + + Called by addActiveObject. + + Object may be deleted by environment immediately. + If id of object is 0, assigns a free id to it. + Returns the id of the object. + Returns 0 if not added and thus deleted. + */ + u16 addActiveObjectRaw(ServerActiveObject *object, bool set_changed, u32 dtime_s); + + /* + Remove all objects that satisfy (m_removed && m_known_by_count==0) + */ + void removeRemovedObjects(); + + /* + Convert stored objects from block to active + */ + void activateObjects(MapBlock *block, u32 dtime_s); + + /* + Convert objects that are not in active blocks to static. + + If m_known_by_count != 0, active object is not deleted, but static + data is still updated. + + If force_delete is set, active object is deleted nevertheless. It + shall only be set so in the destructor of the environment. + */ + void deactivateFarObjects(bool force_delete); + + /* + Member variables + */ + + // The map + ServerMap *m_map; + // Lua state + GameScripting* m_script; + // Game definition + IGameDef *m_gamedef; + // World path + const std::string m_path_world; + // Active object list + ActiveObjectMap m_active_objects; + // Outgoing network message buffer for active objects + std::queue m_active_object_messages; + // Some timers + float m_send_recommended_timer; + IntervalLimiter m_object_management_interval; + // List of active blocks + ActiveBlockList m_active_blocks; + IntervalLimiter m_active_blocks_management_interval; + IntervalLimiter m_active_block_modifier_interval; + IntervalLimiter m_active_blocks_nodemetadata_interval; + int m_active_block_interval_overload_skip; + // Time from the beginning of the game in seconds. + // Incremented in step(). + u32 m_game_time; + // A helper variable for incrementing the latter + float m_game_time_fraction_counter; + // Time of last clearObjects call (game time). + // When a mapblock older than this is loaded, its objects are cleared. + u32 m_last_clear_objects_time; + // Active block modifiers + std::vector m_abms; + LBMManager m_lbm_mgr; + // An interval for generally sending object positions and stuff + float m_recommended_send_interval; + // Estimate for general maximum lag as determined by server. + // Can raise to high values like 15s with eg. map generation mods. + float m_max_lag_estimate; + + // peer_ids in here should be unique, except that there may be many 0s + std::vector m_players; + + // Particles + IntervalLimiter m_particle_management_interval; + UNORDERED_MAP m_particle_spawners; + UNORDERED_MAP m_particle_spawner_attachments; +}; + +#endif diff --git a/src/treegen.cpp b/src/treegen.cpp index f37bf0e86..4df574f34 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -21,9 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/pointer.h" #include "util/numeric.h" -#include "util/mathconstants.h" #include "map.h" -#include "environment.h" +#include "serverenvironment.h" #include "nodedef.h" #include "treegen.h" -- cgit v1.2.3 From 1fee649f1586c39510169f4dbc84b6c7aed12cfd Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sun, 8 Jan 2017 09:32:16 -0800 Subject: Minor: Fix indentation in serverenvironment.cpp --- src/serverenvironment.cpp | 102 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 6229e4cf1..c9fa64ec5 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -770,65 +770,65 @@ public: v3s16 p0; for(p0.X=0; p0.XgetNodeUnsafe(p0); - content_t c = n.getContent(); + for(p0.Y=0; p0.YgetNodeUnsafe(p0); + content_t c = n.getContent(); - if (c >= m_aabms.size() || !m_aabms[c]) - continue; + if (c >= m_aabms.size() || !m_aabms[c]) + continue; - v3s16 p = p0 + block->getPosRelative(); - for(std::vector::iterator - i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { - if(myrand() % i->chance != 0) - continue; + v3s16 p = p0 + block->getPosRelative(); + for(std::vector::iterator + i = m_aabms[c]->begin(); i != m_aabms[c]->end(); ++i) { + if(myrand() % i->chance != 0) + continue; - // Check neighbors - if(!i->required_neighbors.empty()) - { - v3s16 p1; - for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) - for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) - for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) - { - if(p1 == p0) - continue; - content_t c; - if (block->isValidPosition(p1)) { - // if the neighbor is found on the same map block - // get it straight from there - const MapNode &n = block->getNodeUnsafe(p1); - c = n.getContent(); - } else { - // otherwise consult the map - MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); - c = n.getContent(); - } - std::set::const_iterator k; - k = i->required_neighbors.find(c); - if(k != i->required_neighbors.end()){ - goto neighbor_found; - } - } - // No required neighbor found + // Check neighbors + if(!i->required_neighbors.empty()) + { + v3s16 p1; + for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) + for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) + for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) + { + if(p1 == p0) continue; + content_t c; + if (block->isValidPosition(p1)) { + // if the neighbor is found on the same map block + // get it straight from there + const MapNode &n = block->getNodeUnsafe(p1); + c = n.getContent(); + } else { + // otherwise consult the map + MapNode n = map->getNodeNoEx(p1 + block->getPosRelative()); + c = n.getContent(); } - neighbor_found: - - // Call all the trigger variations - i->abm->trigger(m_env, p, n); - i->abm->trigger(m_env, p, n, - active_object_count, active_object_count_wider); - - // Count surrounding objects again if the abms added any - if(m_env->m_added_objects > 0) { - active_object_count = countObjects(block, map, active_object_count_wider); - m_env->m_added_objects = 0; + std::set::const_iterator k; + k = i->required_neighbors.find(c); + if(k != i->required_neighbors.end()){ + goto neighbor_found; } } + // No required neighbor found + continue; } + neighbor_found: + + // Call all the trigger variations + i->abm->trigger(m_env, p, n); + i->abm->trigger(m_env, p, n, + active_object_count, active_object_count_wider); + + // Count surrounding objects again if the abms added any + if(m_env->m_added_objects > 0) { + active_object_count = countObjects(block, map, active_object_count_wider); + m_env->m_added_objects = 0; + } + } + } } }; -- cgit v1.2.3 From ddcf8422a229c4965233177f6315847a6773a20c Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 27 Dec 2016 17:00:47 +0000 Subject: Map generation limit: Fix checks for block/sector over-limit Fix the maths that check if any part of a mapblock or sector is over the set map_generation_limit. Therefore avoid the loading of any over-limit blocks that were previously generated when map_generation_limit was larger. The set limit can vary for a world because it is not yet a per-world mapgen parameter, even when it is sometimes it will be changed deliberately. Therefore avoid a player being returned to world centre if they re-enter a world while being over-limit. Fix the createSector() crash caused by a mob spawning over-limit in an over-limit mapblock --- src/map.cpp | 21 ++++++++++++++++----- src/mapblock.h | 29 ++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 53657ce6b..d4115887f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2064,15 +2064,26 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d) return sector; } #endif + /* - Do not create over-limit + Do not create over-limit. + We are checking for any nodes of the mapblocks of the sector being beyond the limit. + A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos. + + At the negative limit we are checking for + block minimum nodepos < -mapgenlimit. + At the positive limit we are checking for + block maximum nodepos > mapgenlimit. + + Block minimum nodepos = blockpos * mapblocksize. + Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. */ const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); - if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE - || p2d.X > map_gen_limit / MAP_BLOCKSIZE - || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE - || p2d.Y > map_gen_limit / MAP_BLOCKSIZE) + if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit + || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit + || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit + || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit) throw InvalidPositionException("createSector(): pos. over limit"); /* diff --git a/src/mapblock.h b/src/mapblock.h index c737b4c37..d46b7b880 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -656,23 +656,34 @@ inline bool objectpos_over_limit(v3f p) const static float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")) * BS; return (p.X < -map_gen_limit_bs - || p.X > map_gen_limit_bs + || p.X > map_gen_limit_bs || p.Y < -map_gen_limit_bs - || p.Y > map_gen_limit_bs + || p.Y > map_gen_limit_bs || p.Z < -map_gen_limit_bs - || p.Z > map_gen_limit_bs); + || p.Z > map_gen_limit_bs); } +/* + We are checking for any node of the mapblock being beyond the limit. + + At the negative limit we are checking for + block minimum nodepos < -mapgenlimit. + At the positive limit we are checking for + block maximum nodepos > mapgenlimit. + + Block minimum nodepos = blockpos * mapblocksize. + Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. +*/ inline bool blockpos_over_limit(v3s16 p) { const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); - return (p.X < -map_gen_limit / MAP_BLOCKSIZE - || p.X > map_gen_limit / MAP_BLOCKSIZE - || p.Y < -map_gen_limit / MAP_BLOCKSIZE - || p.Y > map_gen_limit / MAP_BLOCKSIZE - || p.Z < -map_gen_limit / MAP_BLOCKSIZE - || p.Z > map_gen_limit / MAP_BLOCKSIZE); + return (p.X * MAP_BLOCKSIZE < -map_gen_limit + || (p.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit + || p.Y * MAP_BLOCKSIZE < -map_gen_limit + || (p.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit + || p.Z * MAP_BLOCKSIZE < -map_gen_limit + || (p.Z + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit); } /* -- cgit v1.2.3 From 8c1b4f298e2812598b0350e8c86b6a0788062ed7 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 7 Jan 2017 21:24:31 +0000 Subject: Map generation limit: Cache as 'const' not 'const static' --- src/map.cpp | 2 +- src/mapblock.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index d4115887f..0659f66aa 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2078,7 +2078,7 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d) Block minimum nodepos = blockpos * mapblocksize. Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. */ - const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, + const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit diff --git a/src/mapblock.h b/src/mapblock.h index d46b7b880..f80800109 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -653,7 +653,7 @@ typedef std::vector MapBlockVect; inline bool objectpos_over_limit(v3f p) { - const static float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, + const float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")) * BS; return (p.X < -map_gen_limit_bs || p.X > map_gen_limit_bs @@ -676,7 +676,7 @@ inline bool objectpos_over_limit(v3f p) */ inline bool blockpos_over_limit(v3s16 p) { - const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, + const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); return (p.X * MAP_BLOCKSIZE < -map_gen_limit || (p.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit -- cgit v1.2.3 From 73fdb635974cb11521d80f15261b97d6fac53cd0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 9 Jan 2017 16:39:40 +0100 Subject: builtin/.../falling.lua: Avoid crash when hitting unknown nodes --- builtin/game/falling.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 4696ce481..39a74008c 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -83,7 +83,7 @@ core.register_entity(":__builtin:falling_node", { -- it's drops if n2.name ~= "air" and (not nd or nd.liquidtype == "none") then core.remove_node(np) - if nd.buildable_to == false then + if nd and nd.buildable_to == false then -- Add dropped items local drops = core.get_node_drops(n2.name, "") for _, dropped_item in pairs(drops) do -- cgit v1.2.3 From 11df7e886a674e280e3ec6f895c11fc1e70eb7b3 Mon Sep 17 00:00:00 2001 From: zeuner Date: Mon, 9 Jan 2017 17:03:13 +0100 Subject: support older PostGreSQL versions (#4999) * support older PostGreSQL versions * documentation accuracy * improve performance by affecting less rows in UPDATE queries --- README.txt | 2 +- src/database-postgresql.cpp | 31 +++++++++++++++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/README.txt b/README.txt index 8bd8554ed..4c02763ca 100644 --- a/README.txt +++ b/README.txt @@ -178,7 +178,7 @@ ENABLE_FREETYPE - Build with FreeType2; Allows using TTF fonts ENABLE_GETTEXT - Build with Gettext; Allows using translations ENABLE_GLES - Search for Open GLES headers & libraries and use them ENABLE_LEVELDB - Build with LevelDB; Enables use of LevelDB map backend -ENABLE_POSTGRESQL - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater required) +ENABLE_POSTGRESQL - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended) ENABLE_REDIS - Build with libhiredis; Enables use of Redis map backend ENABLE_SPATIAL - Build with LibSpatial; Speeds up AreaStores ENABLE_SOUND - Build with OpenAL, libogg & libvorbis; in-game Sounds diff --git a/src/database-postgresql.cpp b/src/database-postgresql.cpp index 3b6b42aea..03a69566b 100644 --- a/src/database-postgresql.cpp +++ b/src/database-postgresql.cpp @@ -80,12 +80,12 @@ void Database_PostgreSQL::connectToDatabase() /* * We are using UPSERT feature from PostgreSQL 9.5 - * to have the better performance, - * set the minimum version to 90500 + * to have the better performance where possible. */ if (m_pgversion < 90500) { - throw DatabaseException("PostgreSQL database error: " - "Server version 9.5 or greater required."); + warningstream << "Your PostgreSQL server lacks UPSERT " + << "support. Use version 9.5 or better if possible." + << std::endl; } infostream << "PostgreSQL Database: Version " << m_pgversion @@ -125,11 +125,25 @@ void Database_PostgreSQL::initStatements() "WHERE posX = $1::int4 AND posY = $2::int4 AND " "posZ = $3::int4"); - prepareStatement("write_block", + if (m_pgversion < 90500) { + prepareStatement("write_block_insert", + "INSERT INTO blocks (posX, posY, posZ, data) SELECT " + "$1::int4, $2::int4, $3::int4, $4::bytea " + "WHERE NOT EXISTS (SELECT true FROM blocks " + "WHERE posX = $1::int4 AND posY = $2::int4 AND " + "posZ = $3::int4)"); + + prepareStatement("write_block_update", + "UPDATE blocks SET data = $4::bytea " + "WHERE posX = $1::int4 AND posY = $2::int4 AND " + "posZ = $3::int4"); + } else { + prepareStatement("write_block", "INSERT INTO blocks (posX, posY, posZ, data) VALUES " "($1::int4, $2::int4, $3::int4, $4::bytea) " "ON CONFLICT ON CONSTRAINT blocks_pkey DO " "UPDATE SET data = $4::bytea"); + } prepareStatement("delete_block", "DELETE FROM blocks WHERE " "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4"); @@ -218,7 +232,12 @@ bool Database_PostgreSQL::saveBlock(const v3s16 &pos, }; const int argFmt[] = { 1, 1, 1, 1 }; - execPrepared("write_block", ARRLEN(args), args, argLen, argFmt); + if (m_pgversion < 90500) { + execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt); + execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt); + } else { + execPrepared("write_block", ARRLEN(args), args, argLen, argFmt); + } return true; } -- cgit v1.2.3 From 8e7449e09253e138716d8dbad6a2ab5c6e089e28 Mon Sep 17 00:00:00 2001 From: Ner'zhul Date: Mon, 9 Jan 2017 20:39:22 +0100 Subject: Environment & IGameDef code refactoring (#4985) * Environment code refactoring * Cleanup includes & class declarations in client & server environment to improve build speed * ServerEnvironment::m_gamedef is now a pointer to Server instead of IGameDef, permitting to cleanup many casts. * Cleanup IGameDef * Move ITextureSource* IGameDef::getTextureSource() to Client only. * Also move ITextureSource *IGameDef::tsrc() helper * drop getShaderSource, getSceneManager, getSoundManager & getCamera abstract call * drop unused emerge() call * cleanup server unused functions (mentionned before) * Drop one unused parameter from ContentFeatures::updateTextures * move checkLocalPrivilege to Client * Remove some unnecessary casts * create_formspec_menu: remove IWritableTextureSource pointer, as client already knows it * Fix some comments * Change required IGameDef to Server/Client pointers * Previous change that game.cpp sometimes calls functions with Client + InventoryManager + IGameDef in same functions but it's the same objects * Remove duplicate Client pointer in GUIFormSpecMenu::GUIFormSpecMenu * drop ClientMap::sectorWasDrawn which is unused --- build/android/jni/Android.mk | 2 ++ src/camera.cpp | 21 ++++++------ src/camera.h | 6 ++-- src/client.cpp | 2 +- src/client.h | 17 +++++----- src/clientenvironment.cpp | 30 +++++++++--------- src/clientenvironment.h | 8 ++--- src/clientmap.cpp | 7 ++-- src/clientmap.h | 21 ++++-------- src/clientobject.cpp | 9 +++--- src/clientobject.h | 21 +++--------- src/collision.cpp | 3 ++ src/content_cao.cpp | 67 +++++++++++++++++++-------------------- src/content_cao.h | 9 +++--- src/content_cso.cpp | 13 +------- src/content_mapblock.cpp | 6 ++-- src/content_sao.cpp | 20 ++++++------ src/emerge.cpp | 14 ++++---- src/emerge.h | 3 +- src/environment.h | 7 ---- src/game.cpp | 59 +++++++++++++++------------------- src/gamedef.h | 29 ++--------------- src/guiEngine.cpp | 2 -- src/guiFormSpecMenu.cpp | 39 +++++++++++------------ src/guiFormSpecMenu.h | 6 +--- src/hud.cpp | 20 ++++++------ src/hud.h | 8 ++--- src/itemdef.cpp | 22 ++++++------- src/itemdef.h | 9 +++--- src/localplayer.cpp | 23 +++++++------- src/localplayer.h | 4 +-- src/mapblock_mesh.cpp | 25 +++++++-------- src/mapblock_mesh.h | 8 ++--- src/mg_biome.cpp | 11 +++---- src/mg_biome.h | 5 +-- src/mg_schematic.cpp | 10 +++--- src/mg_schematic.h | 6 ++-- src/nodedef.cpp | 16 ++++++---- src/nodedef.h | 4 +-- src/particles.cpp | 16 ++++------ src/particles.h | 2 +- src/pathfinder.cpp | 6 +--- src/script/lua_api/l_nodemeta.cpp | 6 +--- src/server.cpp | 18 ----------- src/server.h | 6 +--- src/serverenvironment.cpp | 20 ++++++------ src/serverenvironment.h | 12 ++++--- src/wieldmesh.cpp | 26 +++++++-------- src/wieldmesh.h | 6 ++-- 49 files changed, 301 insertions(+), 409 deletions(-) diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 2567457b9..3be856770 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -117,6 +117,7 @@ LOCAL_SRC_FILES := \ jni/src/cavegen.cpp \ jni/src/chat.cpp \ jni/src/client.cpp \ + jni/src/clientenvironment.cpp \ jni/src/clientiface.cpp \ jni/src/clientmap.cpp \ jni/src/clientmedia.cpp \ @@ -210,6 +211,7 @@ LOCAL_SRC_FILES := \ jni/src/rollback_interface.cpp \ jni/src/serialization.cpp \ jni/src/server.cpp \ + jni/src/serverenvironment.cpp \ jni/src/serverlist.cpp \ jni/src/serverobject.cpp \ jni/src/shader.cpp \ diff --git a/src/camera.cpp b/src/camera.cpp index 43980db1c..4768e8778 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "wieldmesh.h" #include "noise.h" // easeCurve -#include "gamedef.h" #include "sound.h" #include "event.h" #include "profiler.h" @@ -41,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, - IGameDef *gamedef): + Client *client): m_playernode(NULL), m_headnode(NULL), m_cameranode(NULL), @@ -50,7 +49,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldnode(NULL), m_draw_control(draw_control), - m_gamedef(gamedef), + m_client(client), m_camera_position(0,0,0), m_camera_direction(0,0,0), @@ -88,7 +87,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); m_wieldnode = new WieldMeshSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr, -1, false); - m_wieldnode->setItem(ItemStack(), m_gamedef); + m_wieldnode->setItem(ItemStack(), m_client); m_wieldnode->drop(); // m_wieldmgr grabbed it /* TODO: Add a callback function so these can be updated when a setting @@ -151,7 +150,7 @@ void Camera::step(f32 dtime) m_wield_change_timer = MYMIN(m_wield_change_timer + dtime, 0.125); if (m_wield_change_timer >= 0 && was_under_zero) - m_wieldnode->setItem(m_wield_item_next, m_gamedef); + m_wieldnode->setItem(m_wield_item_next, m_client); if (m_view_bobbing_state != 0) { @@ -189,7 +188,7 @@ void Camera::step(f32 dtime) (was > 0.5f && m_view_bobbing_anim <= 0.5f)); if(step) { MtEvent *e = new SimpleTriggerEvent("ViewBobbingStep"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -210,10 +209,10 @@ void Camera::step(f32 dtime) if(m_digging_button == 0) { MtEvent *e = new SimpleTriggerEvent("CameraPunchLeft"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } else if(m_digging_button == 1) { MtEvent *e = new SimpleTriggerEvent("CameraPunchRight"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -352,7 +351,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, my_cp.Y = m_camera_position.Y + (m_camera_direction.Y*-i); // Prevent camera positioned inside nodes - INodeDefManager *nodemgr = m_gamedef->ndef(); + INodeDefManager *nodemgr = m_client->ndef(); MapNode n = c_env.getClientMap().getNodeNoEx(floatToInt(my_cp, BS)); const ContentFeatures& features = nodemgr->get(n); if(features.walkable) @@ -390,7 +389,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, // Get FOV f32 fov_degrees; - if (player->getPlayerControl().zoom && m_gamedef->checkLocalPrivilege("zoom")) { + if (player->getPlayerControl().zoom && m_client->checkLocalPrivilege("zoom")) { fov_degrees = m_cache_zoom_fov; } else { fov_degrees = m_cache_fov; @@ -468,7 +467,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, const bool climbing = movement_Y && player->is_climbing; if ((walking || swimming || climbing) && m_cache_view_bobbing && - (!g_settings->getBool("free_move") || !m_gamedef->checkLocalPrivilege("fly"))) + (!g_settings->getBool("free_move") || !m_client->checkLocalPrivilege("fly"))) { // Start animation m_view_bobbing_state = 1; diff --git a/src/camera.h b/src/camera.h index cb0e9686d..b5be26718 100644 --- a/src/camera.h +++ b/src/camera.h @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class LocalPlayer; struct MapDrawControl; -class IGameDef; +class Client; class WieldMeshSceneNode; struct Nametag { @@ -61,7 +61,7 @@ class Camera { public: Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control, - IGameDef *gamedef); + Client *client); ~Camera(); // Get player scene node. @@ -189,7 +189,7 @@ private: // draw control MapDrawControl& m_draw_control; - IGameDef *m_gamedef; + Client *m_client; video::IVideoDriver *m_driver; // Absolute camera position diff --git a/src/client.cpp b/src/client.cpp index 693a90604..c2471dbd7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -221,7 +221,7 @@ Client::Client( m_event(event), m_mesh_update_thread(), m_env( - new ClientMap(this, this, control, + new ClientMap(this, control, device->getSceneManager()->getRootSceneNode(), device->getSceneManager(), 666), device->getSceneManager(), diff --git a/src/client.h b/src/client.h index 9a09704a6..df3e7e605 100644 --- a/src/client.h +++ b/src/client.h @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include "hud.h" #include "particles.h" -#include "network/networkpacket.h" struct MeshMakeData; class MapBlockMesh; @@ -51,6 +50,7 @@ class Database; class Mapper; struct MinimapMapblock; class Camera; +class NetworkPacket; struct QueuedMeshUpdate { @@ -402,9 +402,6 @@ public: void ProcessData(NetworkPacket *pkt); - // Returns true if something was received - bool AsyncProcessPacket(); - bool AsyncProcessData(); void Send(NetworkPacket* pkt); void interact(u8 action, const PointedThing& pointed); @@ -422,8 +419,9 @@ public: void sendRespawn(); void sendReady(); - ClientEnvironment& getEnv() - { return m_env; } + ClientEnvironment& getEnv() { return m_env; } + ITextureSource *tsrc() { return getTextureSource(); } + ISoundManager *sound() { return getSoundManager(); } // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); @@ -521,14 +519,15 @@ public: virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ITextureSource* getTextureSource(); + ITextureSource* getTextureSource(); virtual IShaderSource* getShaderSource(); - virtual scene::ISceneManager* getSceneManager(); + IShaderSource *shsrc() { return getShaderSource(); } + scene::ISceneManager* getSceneManager(); virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); virtual ParticleManager* getParticleManager(); - virtual bool checkLocalPrivilege(const std::string &priv) + bool checkLocalPrivilege(const std::string &priv) { return checkPrivilege(priv); } virtual scene::IAnimatedMesh* getMesh(const std::string &filename); diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index e831de109..65646c6b4 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -34,13 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc., */ ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, + ITextureSource *texturesource, Client *client, IrrlichtDevice *irr): m_map(map), m_local_player(NULL), m_smgr(smgr), m_texturesource(texturesource), - m_gamedef(gamedef), + m_client(client), m_irr(irr) { char zero = 0; @@ -94,7 +94,7 @@ void ClientEnvironment::step(float dtime) stepTimeOfDay(dtime); // Get some settings - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); + bool fly_allowed = m_client->checkLocalPrivilege("fly"); bool free_move = fly_allowed && g_settings->getBool("free_move"); // Get local player @@ -223,7 +223,7 @@ void ClientEnvironment::step(float dtime) f32 post_factor = 1; // 1 hp per node/s if(info.type == COLLISION_NODE) { - const ContentFeatures &f = m_gamedef->ndef()-> + const ContentFeatures &f = m_client->ndef()-> get(m_map->getNodeNoEx(info.node_p)); // Determine fall damage multiplier int addp = itemgroup_get(f.groups, "fall_damage_add_percent"); @@ -237,7 +237,7 @@ void ClientEnvironment::step(float dtime) if(damage != 0){ damageLocalPlayer(damage, true); MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } } @@ -259,11 +259,11 @@ void ClientEnvironment::step(float dtime) u32 damage_per_second = 0; damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n1).damage_per_second); + m_client->ndef()->get(n1).damage_per_second); damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n2).damage_per_second); + m_client->ndef()->get(n2).damage_per_second); damage_per_second = MYMAX(damage_per_second, - m_gamedef->ndef()->get(n3).damage_per_second); + m_client->ndef()->get(n3).damage_per_second); if(damage_per_second != 0) { @@ -272,7 +272,7 @@ void ClientEnvironment::step(float dtime) } // Protocol v29 make this behaviour obsolete - if (((Client*) getGameDef())->getProtoVersion() < 29) { + if (getGameDef()->getProtoVersion() < 29) { /* Drowning */ @@ -282,7 +282,7 @@ void ClientEnvironment::step(float dtime) // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); + ContentFeatures c = m_client->ndef()->get(n); u8 drowning_damage = c.drowning; if (drowning_damage > 0 && lplayer->hp > 0) { u16 breath = lplayer->getBreath(); @@ -306,7 +306,7 @@ void ClientEnvironment::step(float dtime) // head v3s16 p = floatToInt(pf + v3f(0, BS * 1.6, 0), BS); MapNode n = m_map->getNodeNoEx(p); - ContentFeatures c = m_gamedef->ndef()->get(n); + ContentFeatures c = m_client->ndef()->get(n); if (!lplayer->hp) { lplayer->setBreath(11); } else if (c.drowning == 0) { @@ -332,7 +332,7 @@ void ClientEnvironment::step(float dtime) v3s16 p = lplayer->getLightPosition(); node_at_lplayer = m_map->getNodeNoEx(p); - u16 light = getInteriorLight(node_at_lplayer, 0, m_gamedef->ndef()); + u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); u8 day = light & 0xff; u8 night = (light >> 8) & 0xff; finalColorBlend(lplayer->light_color, day, night, day_night_ratio); @@ -360,7 +360,7 @@ void ClientEnvironment::step(float dtime) v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) - light = n.getLightBlend(day_night_ratio, m_gamedef->ndef()); + light = n.getLightBlend(day_night_ratio, m_client->ndef()); else light = blend_light(day_night_ratio, LIGHT_SUN, 0); @@ -467,7 +467,7 @@ u16 ClientEnvironment::addActiveObject(ClientActiveObject *object) v3s16 p = object->getLightPosition(); MapNode n = m_map->getNodeNoEx(p, &pos_ok); if (pos_ok) - light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef()); + light = n.getLightBlend(getDayNightRatio(), m_client->ndef()); else light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); @@ -480,7 +480,7 @@ void ClientEnvironment::addActiveObject(u16 id, u8 type, const std::string &init_data) { ClientActiveObject* obj = - ClientActiveObject::create((ActiveObjectType) type, m_gamedef, this); + ClientActiveObject::create((ActiveObjectType) type, m_client, this); if(obj == NULL) { infostream<<"ClientEnvironment::addActiveObject(): " diff --git a/src/clientenvironment.h b/src/clientenvironment.h index e6292b5b7..b30a7a6d7 100644 --- a/src/clientenvironment.h +++ b/src/clientenvironment.h @@ -30,6 +30,7 @@ class ClientMap; class ClientActiveObject; class GenericCAO; class LocalPlayer; +struct PointedThing; /* The client-side environment. @@ -66,15 +67,14 @@ class ClientEnvironment : public Environment { public: ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr, - ITextureSource *texturesource, IGameDef *gamedef, + ITextureSource *texturesource, Client *client, IrrlichtDevice *device); ~ClientEnvironment(); Map & getMap(); ClientMap & getClientMap(); - IGameDef *getGameDef() - { return m_gamedef; } + Client *getGameDef() { return m_client; } void step(f32 dtime); @@ -175,7 +175,7 @@ private: LocalPlayer *m_local_player; scene::ISceneManager *m_smgr; ITextureSource *m_texturesource; - IGameDef *m_gamedef; + Client *m_client; IrrlichtDevice *m_irr; UNORDERED_MAP m_active_objects; std::vector m_simple_objects; diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 542eb03e8..7e688daad 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -35,13 +35,12 @@ with this program; if not, write to the Free Software Foundation, Inc., ClientMap::ClientMap( Client *client, - IGameDef *gamedef, MapDrawControl &control, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id ): - Map(dout_client, gamedef), + Map(dout_client, client), scene::ISceneNode(parent, mgr, id), m_client(client), m_control(control), @@ -140,7 +139,7 @@ static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac, return false; } -void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, +void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max) { v3s16 box_nodes_d = m_control.wanted_range * v3s16(1, 1, 1); @@ -766,7 +765,7 @@ void ClientMap::renderPostFx(CameraMode cam_mode) const ContentFeatures& features = m_nodedef->get(n); video::SColor post_effect_color = features.post_effect_color; if(features.solidness == 2 && !(g_settings->getBool("noclip") && - m_gamedef->checkLocalPrivilege("noclip")) && + m_client->checkLocalPrivilege("noclip")) && cam_mode == CAMERA_MODE_FIRST) { post_effect_color = video::SColor(255, 0, 0, 0); diff --git a/src/clientmap.h b/src/clientmap.h index cb686ff33..84228f4ca 100644 --- a/src/clientmap.h +++ b/src/clientmap.h @@ -59,7 +59,7 @@ class ITextureSource; /* ClientMap - + This is the only map class that is able to render itself on screen. */ @@ -68,7 +68,6 @@ class ClientMap : public Map, public scene::ISceneNode public: ClientMap( Client *client, - IGameDef *gamedef, MapDrawControl &control, scene::ISceneNode* parent, scene::ISceneManager* mgr, @@ -114,13 +113,13 @@ public: driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); renderMap(driver, SceneManager->getSceneNodeRenderPass()); } - + virtual const aabb3f &getBoundingBox() const { return m_box; } - - void getBlocksInViewRange(v3s16 cam_pos_nodes, + + void getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max); void updateDrawList(video::IVideoDriver* driver); void renderMap(video::IVideoDriver* driver, s32 pass); @@ -132,20 +131,14 @@ public: // For debug printing virtual void PrintInfo(std::ostream &out); - - // Check if sector was drawn on last render() - bool sectorWasDrawn(v2s16 p) - { - return (m_last_drawn_sectors.find(p) != m_last_drawn_sectors.end()); - } const MapDrawControl & getControl() const { return m_control; } f32 getCameraFov() const { return m_camera_fov; } private: Client *m_client; - + aabb3f m_box; - + MapDrawControl &m_control; v3f m_camera_position; @@ -154,7 +147,7 @@ private: v3s16 m_camera_offset; std::map m_drawlist; - + std::set m_last_drawn_sectors; bool m_cache_trilinear_filter; diff --git a/src/clientobject.cpp b/src/clientobject.cpp index ff3f47187..89a0474a4 100644 --- a/src/clientobject.cpp +++ b/src/clientobject.cpp @@ -20,16 +20,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientobject.h" #include "debug.h" #include "porting.h" -#include "constants.h" /* ClientActiveObject */ -ClientActiveObject::ClientActiveObject(u16 id, IGameDef *gamedef, +ClientActiveObject::ClientActiveObject(u16 id, Client *client, ClientEnvironment *env): ActiveObject(id), - m_gamedef(gamedef), + m_client(client), m_env(env) { } @@ -40,7 +39,7 @@ ClientActiveObject::~ClientActiveObject() } ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, - IGameDef *gamedef, ClientEnvironment *env) + Client *client, ClientEnvironment *env) { // Find factory function UNORDERED_MAP::iterator n = m_types.find(type); @@ -52,7 +51,7 @@ ClientActiveObject* ClientActiveObject::create(ActiveObjectType type, } Factory f = n->second; - ClientActiveObject *object = (*f)(gamedef, env); + ClientActiveObject *object = (*f)(client, env); return object; } diff --git a/src/clientobject.h b/src/clientobject.h index 83931e438..f0bde0adc 100644 --- a/src/clientobject.h +++ b/src/clientobject.h @@ -25,20 +25,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/cpp11_container.h" -/* - -Some planning -------------- - -* Client receives a network packet with information of added objects - in it -* Client supplies the information to its ClientEnvironment -* The environment adds the specified objects to itself - -*/ - class ClientEnvironment; class ITextureSource; +class Client; class IGameDef; class LocalPlayer; struct ItemStack; @@ -47,7 +36,7 @@ class WieldMeshSceneNode; class ClientActiveObject : public ActiveObject { public: - ClientActiveObject(u16 id, IGameDef *gamedef, ClientEnvironment *env); + ClientActiveObject(u16 id, Client *client, ClientEnvironment *env); virtual ~ClientActiveObject(); virtual void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -89,7 +78,7 @@ public: virtual void initialize(const std::string &data){} // Create a certain type of ClientActiveObject - static ClientActiveObject* create(ActiveObjectType type, IGameDef *gamedef, + static ClientActiveObject* create(ActiveObjectType type, Client *client, ClientEnvironment *env); // If returns true, punch will not be sent to the server @@ -99,9 +88,9 @@ public: protected: // Used for creating objects based on type - typedef ClientActiveObject* (*Factory)(IGameDef *gamedef, ClientEnvironment *env); + typedef ClientActiveObject* (*Factory)(Client *client, ClientEnvironment *env); static void registerType(u16 type, Factory f); - IGameDef *m_gamedef; + Client *m_client; ClientEnvironment *m_env; private: // Used for creating objects based on type diff --git a/src/collision.cpp b/src/collision.cpp index 595fa8059..c0891c152 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -22,9 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "nodedef.h" #include "gamedef.h" +#ifndef SERVER #include "clientenvironment.h" +#endif #include "serverenvironment.h" #include "serverobject.h" +#include "util/timetaker.h" #include "profiler.h" // float error is 10 - 9.96875 = 0.03125 diff --git a/src/content_cao.cpp b/src/content_cao.cpp index a02d5168e..a4c0bf14d 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -33,7 +33,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "collision.h" #include "settings.h" #include "serialization.h" // For decompressZlib -#include "gamedef.h" #include "clientobject.h" #include "mesh.h" #include "itemdef.h" @@ -139,7 +138,7 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, class TestCAO : public ClientActiveObject { public: - TestCAO(IGameDef *gamedef, ClientEnvironment *env); + TestCAO(Client *client, ClientEnvironment *env); virtual ~TestCAO(); ActiveObjectType getType() const @@ -147,7 +146,7 @@ public: return ACTIVEOBJECT_TYPE_TEST; } - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env); + static ClientActiveObject* create(Client *client, ClientEnvironment *env); void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr); @@ -169,8 +168,8 @@ private: // Prototype TestCAO proto_TestCAO(NULL, NULL); -TestCAO::TestCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +TestCAO::TestCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), m_node(NULL), m_position(v3f(0,10*BS,0)) { @@ -181,9 +180,9 @@ TestCAO::~TestCAO() { } -ClientActiveObject* TestCAO::create(IGameDef *gamedef, ClientEnvironment *env) +ClientActiveObject* TestCAO::create(Client *client, ClientEnvironment *env) { - return new TestCAO(gamedef, env); + return new TestCAO(client, env); } void TestCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -283,7 +282,7 @@ void TestCAO::processMessage(const std::string &data) class ItemCAO : public ClientActiveObject { public: - ItemCAO(IGameDef *gamedef, ClientEnvironment *env); + ItemCAO(Client *client, ClientEnvironment *env); virtual ~ItemCAO(); ActiveObjectType getType() const @@ -291,7 +290,7 @@ public: return ACTIVEOBJECT_TYPE_ITEM; } - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env); + static ClientActiveObject* create(Client *client, ClientEnvironment *env); void addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, IrrlichtDevice *irr); @@ -331,13 +330,13 @@ private: // Prototype ItemCAO proto_ItemCAO(NULL, NULL); -ItemCAO::ItemCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +ItemCAO::ItemCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), m_selection_box(-BS/3.,0.0,-BS/3., BS/3.,BS*2./3.,BS/3.), m_node(NULL), m_position(v3f(0,10*BS,0)) { - if(!gamedef && !env) + if(!client && !env) { ClientActiveObject::registerType(getType(), create); } @@ -347,9 +346,9 @@ ItemCAO::~ItemCAO() { } -ClientActiveObject* ItemCAO::create(IGameDef *gamedef, ClientEnvironment *env) +ClientActiveObject* ItemCAO::create(Client *client, ClientEnvironment *env) { - return new ItemCAO(gamedef, env); + return new ItemCAO(client, env); } void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, @@ -433,7 +432,7 @@ void ItemCAO::updateNodePos() void ItemCAO::updateInfoText() { try{ - IItemDefManager *idef = m_gamedef->idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(m_itemstring, idef); if(item.isKnown(idef)) @@ -458,10 +457,10 @@ void ItemCAO::updateTexture() std::istringstream is(m_itemstring, std::ios_base::binary); video::ITexture *texture = NULL; try{ - IItemDefManager *idef = m_gamedef->idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(is, idef); - texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); + texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_client); } catch(SerializationError &e) { @@ -538,15 +537,15 @@ void ItemCAO::initialize(const std::string &data) #include "genericobject.h" -GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): - ClientActiveObject(0, gamedef, env), +GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): + ClientActiveObject(0, client, env), // m_is_player(false), m_is_local_player(false), // m_smgr(NULL), m_irr(NULL), - m_gamedef(NULL), + m_client(NULL), m_selection_box(-BS/3.,-BS/3.,-BS/3., BS/3.,BS/3.,BS/3.), m_meshnode(NULL), m_animated_meshnode(NULL), @@ -581,10 +580,10 @@ GenericCAO::GenericCAO(IGameDef *gamedef, ClientEnvironment *env): m_last_light(255), m_is_visible(false) { - if (gamedef == NULL) { + if (client == NULL) { ClientActiveObject::registerType(getType(), create); } else { - m_gamedef = gamedef; + m_client = client; } } @@ -793,7 +792,7 @@ void GenericCAO::removeFromScene(bool permanent) } if (m_nametag) { - m_gamedef->getCamera()->removeNametag(m_nametag); + m_client->getCamera()->removeNametag(m_nametag); m_nametag = NULL; } } @@ -906,7 +905,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, } else if(m_prop.visual == "mesh") { infostream<<"GenericCAO::addToScene(): mesh"<getMesh(m_prop.mesh); + scene::IAnimatedMesh *mesh = m_client->getMesh(m_prop.mesh); if(mesh) { m_animated_meshnode = smgr->addAnimatedMeshSceneNode(mesh, NULL); @@ -937,12 +936,12 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, infostream<<"textures: "<= 1){ infostream<<"textures[0]: "<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item(m_prop.textures[0], 1, 0, "", idef); m_wield_meshnode = new WieldMeshSceneNode( smgr->getRootSceneNode(), smgr, -1); - m_wield_meshnode->setItem(item, m_gamedef); + m_wield_meshnode->setItem(item, m_client); m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2, m_prop.visual_size.Y/2, @@ -959,7 +958,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, scene::ISceneNode *node = getSceneNode(); if (node && m_prop.nametag != "" && !m_is_local_player) { // Add nametag - m_nametag = m_gamedef->getCamera()->addNametag(node, + m_nametag = m_client->getCamera()->addNametag(node, m_prop.nametag, m_prop.nametag_color); } @@ -1058,11 +1057,11 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) // increase speed if using fast or flying fast if((g_settings->getBool("fast_move") && - m_gamedef->checkLocalPrivilege("fast")) && + m_client->checkLocalPrivilege("fast")) && (controls.aux1 || (!player->touching_ground && g_settings->getBool("free_move") && - m_gamedef->checkLocalPrivilege("fly")))) + m_client->checkLocalPrivilege("fly")))) new_speed *= 1.5; // slowdown speed if sneeking if(controls.sneak && walking) @@ -1129,7 +1128,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) } removeFromScene(false); - addToScene(m_smgr, m_gamedef->tsrc(), m_irr); + addToScene(m_smgr, m_client->tsrc(), m_irr); // Attachments, part 2: Now that the parent has been refreshed, put its attachments back for (std::vector::size_type i = 0; i < m_children.size(); i++) { @@ -1199,12 +1198,12 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) m_step_distance_counter = 0; if(!m_is_local_player && m_prop.makes_footstep_sound) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_client->ndef(); v3s16 p = floatToInt(getPosition() + v3f(0, (m_prop.collisionbox.MinEdge.Y-0.5)*BS, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); SimpleSoundSpec spec = ndef->get(n).sound_footstep; - m_gamedef->sound()->playSoundAt(spec, false, getPosition()); + m_client->sound()->playSoundAt(spec, false, getPosition()); } } } @@ -1305,7 +1304,7 @@ void GenericCAO::updateTexturePos() void GenericCAO::updateTextures(const std::string &mod) { - ITextureSource *tsrc = m_gamedef->tsrc(); + ITextureSource *tsrc = m_client->tsrc(); bool use_trilinear_filter = g_settings->getBool("trilinear_filter"); bool use_bilinear_filter = g_settings->getBool("bilinear_filter"); @@ -1778,7 +1777,7 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem, { assert(punchitem); // pre-condition const ToolCapabilities *toolcap = - &punchitem->getToolCapabilities(m_gamedef->idef()); + &punchitem->getToolCapabilities(m_client->idef()); PunchDamageResult result = getPunchDamage( m_armor_groups, toolcap, diff --git a/src/content_cao.h b/src/content_cao.h index a158e8296..96a160055 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" class Camera; +class Client; struct Nametag; /* @@ -68,7 +69,7 @@ private: // scene::ISceneManager *m_smgr; IrrlichtDevice *m_irr; - IGameDef *m_gamedef; + Client *m_client; aabb3f m_selection_box; scene::IMeshSceneNode *m_meshnode; scene::IAnimatedMeshSceneNode *m_animated_meshnode; @@ -109,13 +110,13 @@ private: std::vector m_children; public: - GenericCAO(IGameDef *gamedef, ClientEnvironment *env); + GenericCAO(Client *client, ClientEnvironment *env); ~GenericCAO(); - static ClientActiveObject* create(IGameDef *gamedef, ClientEnvironment *env) + static ClientActiveObject* create(Client *client, ClientEnvironment *env) { - return new GenericCAO(gamedef, env); + return new GenericCAO(client, env); } inline ActiveObjectType getType() const diff --git a/src/content_cso.cpp b/src/content_cso.cpp index c0407f460..aca71212b 100644 --- a/src/content_cso.cpp +++ b/src/content_cso.cpp @@ -21,20 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "client/tile.h" #include "clientenvironment.h" -#include "gamedef.h" +#include "client.h" #include "map.h" -/* -static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, - float txs, float tys, int col, int row) -{ - video::SMaterial& material = bill->getMaterial(0); - core::matrix4& matrix = material.getTextureMatrix(0); - matrix.setTextureTranslate(txs*col, tys*row); - matrix.setTextureScale(txs, tys); -} -*/ - class SmokePuffCSO: public ClientSimpleObject { float m_age; diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 8ce0f1e0a..a7134590b 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" #include "mesh.h" #include -#include "gamedef.h" +#include "client.h" #include "log.h" #include "noise.h" @@ -188,8 +188,8 @@ static inline int NeighborToIndex(const v3s16 &pos) void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector) { - INodeDefManager *nodedef = data->m_gamedef->ndef(); - scene::ISceneManager* smgr = data->m_gamedef->getSceneManager(); + INodeDefManager *nodedef = data->m_client->ndef(); + scene::ISceneManager* smgr = data->m_client->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); // 0ms diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f866d4372..dd8bdc592 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -273,7 +273,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; - moveresult = collisionMoveSimple(m_env,m_env->getGameDef(), + moveresult = collisionMoveSimple(m_env, m_env->getGameDef(), pos_max_d, box, m_prop.stepheight, dtime, &p_pos, &p_velocity, p_acceleration, this, m_prop.collideWithObjects); @@ -945,7 +945,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // get head position v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); - const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If node generates drown if (c.drowning > 0) { if (m_hp > 0 && m_breath > 0) @@ -954,7 +954,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // No more breath, damage player if (m_breath == 0) { setHP(m_hp - c.drowning); - ((Server*) m_env->getGameDef())->SendPlayerHPOrDie(this); + m_env->getGameDef()->SendPlayerHPOrDie(this); } } } @@ -963,7 +963,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // get head position v3s16 p = floatToInt(m_base_position + v3f(0, BS * 1.6, 0), BS); MapNode n = m_env->getMap().getNodeNoEx(p); - const ContentFeatures &c = ((Server*) m_env->getGameDef())->ndef()->get(n); + const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If player is alive & no drowning, breath if (m_hp > 0 && c.drowning == 0) setBreath(m_breath + 1); @@ -985,7 +985,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) m_attachment_position = v3f(0,0,0); m_attachment_rotation = v3f(0,0,0); setBasePosition(m_last_good_position); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } //dstream<<"PlayerSAO::step: dtime: "<getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::moveTo(v3f pos, bool continuous) @@ -1118,7 +1118,7 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) setBasePosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::setYaw(const float yaw) @@ -1148,7 +1148,7 @@ void PlayerSAO::setWantedRange(const s16 range) void PlayerSAO::setYawAndSend(const float yaw) { setYaw(yaw); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } void PlayerSAO::setPitch(const float pitch) @@ -1162,7 +1162,7 @@ void PlayerSAO::setPitch(const float pitch) void PlayerSAO::setPitchAndSend(const float pitch) { setPitch(pitch); - ((Server*)m_env->getGameDef())->SendMovePlayer(m_peer_id); + m_env->getGameDef()->SendMovePlayer(m_peer_id); } int PlayerSAO::punch(v3f dir, @@ -1273,7 +1273,7 @@ void PlayerSAO::setBreath(const u16 breath, bool send) m_breath = MYMIN(breath, PLAYER_MAX_BREATH); if (send) - ((Server *) m_env->getGameDef())->SendPlayerBreath(this); + m_env->getGameDef()->SendPlayerBreath(this); } void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) diff --git a/src/emerge.cpp b/src/emerge.cpp index 25b2e924b..3f0a46010 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -89,13 +89,13 @@ private: //// EmergeManager //// -EmergeManager::EmergeManager(IGameDef *gamedef) +EmergeManager::EmergeManager(Server *server) { - this->ndef = gamedef->getNodeDefManager(); - this->biomemgr = new BiomeManager(gamedef); - this->oremgr = new OreManager(gamedef); - this->decomgr = new DecorationManager(gamedef); - this->schemmgr = new SchematicManager(gamedef); + this->ndef = server->getNodeDefManager(); + this->biomemgr = new BiomeManager(server); + this->oremgr = new OreManager(server); + this->decomgr = new DecorationManager(server); + this->schemmgr = new SchematicManager(server); this->gen_notify_on = 0; // Note that accesses to this variable are not synchronized. @@ -128,7 +128,7 @@ EmergeManager::EmergeManager(IGameDef *gamedef) m_qlimit_generate = 1; for (s16 i = 0; i < nthreads; i++) - m_threads.push_back(new EmergeThread((Server *)gamedef, i)); + m_threads.push_back(new EmergeThread(server, i)); infostream << "EmergeManager: using " << nthreads << " threads" << std::endl; } diff --git a/src/emerge.h b/src/emerge.h index 71ad97da3..76653e6cd 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -42,6 +42,7 @@ class BiomeManager; class OreManager; class DecorationManager; class SchematicManager; +class Server; // Structure containing inputs/outputs for chunk generation struct BlockMakeData { @@ -115,7 +116,7 @@ public: SchematicManager *schemmgr; // Methods - EmergeManager(IGameDef *gamedef); + EmergeManager(Server *server); ~EmergeManager(); bool initMapgens(MapgenParams *mgparams); diff --git a/src/environment.h b/src/environment.h index 14a18421b..0cc3222f9 100644 --- a/src/environment.h +++ b/src/environment.h @@ -42,13 +42,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode -class ITextureSource; -class IGameDef; -class Map; -class GameScripting; -class Player; -class PointedThing; - class Environment { public: diff --git a/src/game.cpp b/src/game.cpp index cfa6234ff..1070cb1b2 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -916,16 +916,14 @@ bool nodePlacementPrediction(Client &client, } static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, - IWritableTextureSource *tsrc, IrrlichtDevice *device, - JoystickController *joystick, - IFormSource *fs_src, TextDest *txt_dest, Client *client) + Client *client, IrrlichtDevice *device, JoystickController *joystick, + IFormSource *fs_src, TextDest *txt_dest) { if (*cur_formspec == 0) { *cur_formspec = new GUIFormSpecMenu(device, joystick, - guiroot, -1, &g_menumgr, invmgr, gamedef, tsrc, - fs_src, txt_dest, client); + guiroot, -1, &g_menumgr, client, client->getTextureSource(), + fs_src, txt_dest); (*cur_formspec)->doPause = false; /* @@ -950,9 +948,9 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, #endif static void show_deathscreen(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, - JoystickController *joystick, Client *client) + JoystickController *joystick) { std::string formspec = std::string(FORMSPEC_VERSION_STRING) + @@ -968,13 +966,12 @@ static void show_deathscreen(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(formspec); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, - joystick, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); } /******************************************************************************/ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, JoystickController *joystick, bool singleplayermode) { @@ -1041,8 +1038,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, - joystick, fs_src, txt_dst, NULL); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); std::string con("btn_continue"); (*cur_formspec)->setFocus(con); (*cur_formspec)->doPause = true; @@ -1534,7 +1530,6 @@ private: bool *kill; std::string *error_message; bool *reconnect_requested; - IGameDef *gamedef; // Convenience (same as *client) scene::ISceneNode *skybox; bool random_input; @@ -2011,7 +2006,7 @@ bool Game::createClient(const std::string &playername, /* Camera */ - camera = new Camera(smgr, *draw_control, gamedef); + camera = new Camera(smgr, *draw_control, client); if (!camera || !camera->successfullyCreated(*error_message)) return false; client->setCamera(camera); @@ -2068,7 +2063,7 @@ bool Game::createClient(const std::string &playername, player->hurt_tilt_timer = 0; player->hurt_tilt_strength = 0; - hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory); + hud = new Hud(driver, smgr, guienv, client, player, local_inventory); if (!hud) { *error_message = "Memory error: could not create HUD"; @@ -2198,8 +2193,6 @@ bool Game::connectToServer(const std::string &playername, if (!client) return false; - gamedef = client; // Client acts as our GameDef - infostream << "Connecting to server at "; connect_address.print(&infostream); infostream << std::endl; @@ -2445,7 +2438,7 @@ inline bool Game::handleCallbacks() void Game::processQueues() { texture_src->processQueue(); - itemdef_manager->processQueue(gamedef); + itemdef_manager->processQueue(client); shader_src->processQueue(); } @@ -2617,7 +2610,7 @@ void Game::processKeyInput(VolatileRunFlags *flags, openInventory(); } else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { if (!gui_chat_console->isOpenInhibited()) { - show_pause_menu(¤t_formspec, client, gamedef, + show_pause_menu(¤t_formspec, client, texture_src, device, &input->joystick, simple_singleplayer_mode); } @@ -2769,8 +2762,7 @@ void Game::openInventory() PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); TextDest *txt_dst = new TextDestPlayerInventory(client); - create_formspec_menu(¤t_formspec, client, gamedef, texture_src, - device, &input->joystick, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, fs_src, txt_dst); cur_formname = ""; InventoryLocation inventoryloc; @@ -3245,13 +3237,13 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) rangelim(event.player_damage.amount / 4, 1.0, 4.0); MtEvent *e = new SimpleTriggerEvent("PlayerDamage"); - gamedef->event()->put(e); + client->event()->put(e); } else if (event.type == CE_PLAYER_FORCE_MOVE) { cam->camera_yaw = event.player_force_move.yaw; cam->camera_pitch = event.player_force_move.pitch; } else if (event.type == CE_DEATHSCREEN) { - show_deathscreen(¤t_formspec, client, gamedef, texture_src, - device, &input->joystick, client); + show_deathscreen(¤t_formspec, client, texture_src, + device, &input->joystick); chat_backend->addMessage(L"", L"You died."); @@ -3271,9 +3263,8 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) TextDestPlayerInventory *txt_dst = new TextDestPlayerInventory(client, *(event.show_formspec.formname)); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, &input->joystick, - fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, + fs_src, txt_dst); cur_formname = *(event.show_formspec.formname); } @@ -3282,7 +3273,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) } else if ((event.type == CE_SPAWN_PARTICLE) || (event.type == CE_ADD_PARTICLESPAWNER) || (event.type == CE_DELETE_PARTICLESPAWNER)) { - client->getParticleManager()->handleParticleEvent(&event, gamedef, + client->getParticleManager()->handleParticleEvent(&event, client, smgr, player); } else if (event.type == CE_HUDADD) { u32 id = event.hudadd.id; @@ -3840,8 +3831,8 @@ void Game::handlePointingAtNode(GameRunData *runData, &client->getEnv().getClientMap(), nodepos); TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, &input->joystick, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, + device, &input->joystick, fs_src, txt_dst); cur_formname = ""; current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); @@ -3972,7 +3963,7 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(n); - client->getParticleManager()->addPunchingParticles(gamedef, smgr, + client->getParticleManager()->addPunchingParticles(client, smgr, player, nodepos, features.tiles); } } @@ -4019,7 +4010,7 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(wasnode); - client->getParticleManager()->addDiggingParticles(gamedef, smgr, + client->getParticleManager()->addDiggingParticles(client, smgr, player, nodepos, features.tiles); } @@ -4043,7 +4034,7 @@ void Game::handleDigging(GameRunData *runData, // Send event to trigger sound MtEvent *e = new NodeDugEvent(nodepos, wasnode); - gamedef->event()->put(e); + client->event()->put(e); } if (runData->dig_time_complete < 100000.0) { diff --git a/src/gamedef.h b/src/gamedef.h index 7e3da4cac..cb624bd6a 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -53,47 +53,22 @@ public: virtual INodeDefManager* getNodeDefManager()=0; virtual ICraftDefManager* getCraftDefManager()=0; - // This is always thread-safe, but referencing the irrlicht texture - // pointers in other threads than main thread will make things explode. - virtual ITextureSource* getTextureSource()=0; - - virtual IShaderSource* getShaderSource()=0; - // Used for keeping track of names/ids of unknown nodes virtual u16 allocateUnknownNodeId(const std::string &name)=0; - // Only usable on the client - virtual ISoundManager* getSoundManager()=0; virtual MtEventManager* getEventManager()=0; - virtual scene::IAnimatedMesh* getMesh(const std::string &filename) - { return NULL; } - virtual scene::ISceneManager* getSceneManager()=0; - - virtual Camera* getCamera() - { return NULL; } - virtual void setCamera(Camera *camera) {} // Only usable on the server, and NOT thread-safe. It is usable from the // environment thread. - virtual IRollbackManager* getRollbackManager(){return NULL;} - - // Only usable on the server. Thread safe if not written while running threads. - virtual EmergeManager *getEmergeManager() { return NULL; } - - // Used on the client - virtual bool checkLocalPrivilege(const std::string &priv) - { return false; } + virtual IRollbackManager* getRollbackManager() { return NULL; } // Shorthands IItemDefManager *idef() { return getItemDefManager(); } INodeDefManager *ndef() { return getNodeDefManager(); } ICraftDefManager *cdef() { return getCraftDefManager(); } - ITextureSource *tsrc() { return getTextureSource(); } - ISoundManager *sound() { return getSoundManager(); } - IShaderSource *shsrc() { return getShaderSource(); } + MtEventManager *event() { return getEventManager(); } IRollbackManager *rollback() { return getRollbackManager();} - EmergeManager *emerge() { return getEmergeManager(); } }; #endif diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index a3c35f68d..6d66ed08d 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -194,11 +194,9 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, -1, m_menumanager, NULL /* &client */, - NULL /* gamedef */, m_texture_source, m_formspecgui, m_buttonhandler, - NULL, false); m_menu->allowClose(false); diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index bfc7a9b79..45b0e9c11 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -81,13 +81,12 @@ static unsigned int font_line_height(gui::IGUIFont *font) GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst, - Client* client, bool remap_dbl_click) : + bool remap_dbl_click) : GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr), m_device(dev), - m_invmgr(invmgr), - m_gamedef(gamedef), + m_invmgr(client), m_tsrc(tsrc), m_client(client), m_selected_item(NULL), @@ -307,8 +306,8 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data) void GUIFormSpecMenu::parseList(parserData* data,std::string element) { - if (m_gamedef == 0) { - warningstream<<"invalid use of 'list' with m_gamedef==0"<explicit_size) warningstream<<"invalid use of item_image_button without a size[] element"<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(item_name, idef); @@ -2297,14 +2296,14 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, if(!item.empty()) { drawItemStack(driver, m_font, item, - rect, &AbsoluteClippingRect, m_gamedef, + rect, &AbsoluteClippingRect, m_client, rotation_kind); } // Draw tooltip std::wstring tooltip_text = L""; if (hovering && !m_selected_item) { - tooltip_text = utf8_to_wide(item.getDefinition(m_gamedef->idef()).description); + tooltip_text = utf8_to_wide(item.getDefinition(m_client->idef()).description); } if (tooltip_text != L"") { std::vector tt_rows = str_split(tooltip_text, L'\n'); @@ -2349,7 +2348,7 @@ void GUIFormSpecMenu::drawSelectedItem() if (!m_selected_item) { drawItemStack(driver, m_font, ItemStack(), core::rect(v2s32(0, 0), v2s32(0, 0)), - NULL, m_gamedef, IT_ROT_DRAGGED); + NULL, m_client, IT_ROT_DRAGGED); return; } @@ -2363,7 +2362,7 @@ void GUIFormSpecMenu::drawSelectedItem() core::rect imgrect(0,0,imgsize.X,imgsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); rect.constrainTo(driver->getViewPort()); - drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef, IT_ROT_DRAGGED); + drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED); } void GUIFormSpecMenu::drawMenu() @@ -2488,11 +2487,11 @@ void GUIFormSpecMenu::drawMenu() */ for(u32 i=0; iidef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(spec.item_name, idef); core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); @@ -2509,7 +2508,7 @@ void GUIFormSpecMenu::drawMenu() #endif } drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect, - m_gamedef, IT_ROT_NONE); + m_client, IT_ROT_NONE); } /* @@ -2527,7 +2526,7 @@ void GUIFormSpecMenu::drawMenu() if (!item_hovered) { drawItemStack(driver, m_font, ItemStack(), core::rect(v2s32(0, 0), v2s32(0, 0)), - NULL, m_gamedef, IT_ROT_HOVERED); + NULL, m_client, IT_ROT_HOVERED); } /* TODO find way to show tooltips on touchscreen */ @@ -3470,7 +3469,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Check how many items can be moved move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); - ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); + ItemStack leftover = stack_to.addItem(stack_from, m_client->idef()); // If source stack cannot be added to destination stack at all, // they are swapped if ((leftover.count == stack_from.count) && diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 95df11e6a..94b52e6f0 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/enriched_string.h" -class IGameDef; class InventoryManager; class ISimpleTextureSource; class Client; @@ -289,12 +288,10 @@ public: JoystickController *joystick, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, - IGameDef *gamedef, + Client *client, ISimpleTextureSource *tsrc, IFormSource* fs_src, TextDest* txt_dst, - Client* client, bool remap_dbl_click = true); ~GUIFormSpecMenu(); @@ -384,7 +381,6 @@ protected: irr::IrrlichtDevice* m_device; InventoryManager *m_invmgr; - IGameDef *m_gamedef; ISimpleTextureSource *m_tsrc; Client *m_client; diff --git a/src/hud.cpp b/src/hud.cpp index 43d957380..a602125e3 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -22,10 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "settings.h" #include "util/numeric.h" -#include "util/string.h" #include "log.h" -#include "gamedef.h" -#include "itemdef.h" +#include "client.h" #include "inventory.h" #include "client/tile.h" #include "localplayer.h" @@ -41,13 +39,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, - gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player, + gui::IGUIEnvironment* guienv, Client *client, LocalPlayer *player, Inventory *inventory) { this->driver = driver; this->smgr = smgr; this->guienv = guienv; - this->gamedef = gamedef; + this->client = client; this->player = player; this->inventory = inventory; @@ -61,7 +59,7 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, for (unsigned int i = 0; i < 4; i++) hbar_colors[i] = video::SColor(255, 255, 255, 255); - tsrc = gamedef->getTextureSource(); + tsrc = client->getTextureSource(); v3f crosshair_color = g_settings->getV3F("crosshair_color"); u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255); @@ -92,7 +90,7 @@ Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr, m_selection_material.Lighting = false; if (g_settings->getBool("enable_shaders")) { - IShaderSource *shdrsrc = gamedef->getShaderSource(); + IShaderSource *shdrsrc = client->getShaderSource(); u16 shader_id = shdrsrc->getShader( mode == "halo" ? "selection_shader" : "default_shader", 1, 1); m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; @@ -193,7 +191,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect& rect, if (!use_hotbar_image) driver->draw2DRectangle(bgcolor2, rect, NULL); drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL, - gamedef, selected ? IT_ROT_SELECTED : IT_ROT_NONE); + client, selected ? IT_ROT_SELECTED : IT_ROT_NONE); } //NOTE: selectitem = 0 -> no selected; selectitem 1-based @@ -629,7 +627,7 @@ void drawItemStack(video::IVideoDriver *driver, const ItemStack &item, const core::rect &rect, const core::rect *clip, - IGameDef *gamedef, + Client *client, ItemRotationKind rotation_kind) { static MeshTimeInfo rotation_time_infos[IT_ROT_NONE]; @@ -643,8 +641,8 @@ void drawItemStack(video::IVideoDriver *driver, return; } - const ItemDefinition &def = item.getDefinition(gamedef->idef()); - scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef); + const ItemDefinition &def = item.getDefinition(client->idef()); + scene::IMesh* mesh = client->idef()->getWieldMesh(def.name, client); if (mesh) { driver->clearZBuffer(); diff --git a/src/hud.h b/src/hud.h index a4d7990e9..efa0c3648 100644 --- a/src/hud.h +++ b/src/hud.h @@ -95,7 +95,7 @@ struct HudElement { #include #include "irr_aabb3d.h" -class IGameDef; +class Client; class ITextureSource; class Inventory; class InventoryList; @@ -107,7 +107,7 @@ public: video::IVideoDriver *driver; scene::ISceneManager* smgr; gui::IGUIEnvironment *guienv; - IGameDef *gamedef; + Client *client; LocalPlayer *player; Inventory *inventory; ITextureSource *tsrc; @@ -121,7 +121,7 @@ public: bool use_hotbar_selected_image; Hud(video::IVideoDriver *driver,scene::ISceneManager* smgr, - gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player, + gui::IGUIEnvironment* guienv, Client *client, LocalPlayer *player, Inventory *inventory); ~Hud(); @@ -190,7 +190,7 @@ void drawItemStack(video::IVideoDriver *driver, const ItemStack &item, const core::rect &rect, const core::rect *clip, - IGameDef *gamedef, + Client *client, ItemRotationKind rotation_kind); #endif diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 1aa6331dc..5ba9d8f9a 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" -#include "gamedef.h" #include "nodedef.h" #include "tool.h" #include "inventory.h" @@ -29,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mesh.h" #include "wieldmesh.h" #include "client/tile.h" +#include "client.h" #endif #include "log.h" #include "settings.h" @@ -317,7 +317,7 @@ public: #ifndef SERVER public: ClientCached* createClientCachedDirect(const std::string &name, - IGameDef *gamedef) const + Client *client) const { infostream<<"Lazily creating item texture and mesh for \"" <getTextureSource(); + ITextureSource *tsrc = client->getTextureSource(); const ItemDefinition &def = get(name); // Create new ClientCached @@ -345,7 +345,7 @@ public: ItemStack item = ItemStack(); item.name = def.name; - scene::IMesh *mesh = getItemMesh(gamedef, item); + scene::IMesh *mesh = getItemMesh(client, item); cc->wield_mesh = mesh; // Put in cache @@ -354,7 +354,7 @@ public: return cc; } ClientCached* getClientCached(const std::string &name, - IGameDef *gamedef) const + Client *client) const { ClientCached *cc = NULL; m_clientcached.get(name, &cc); @@ -363,7 +363,7 @@ public: if(thr_is_current_thread(m_main_thread)) { - return createClientCachedDirect(name, gamedef); + return createClientCachedDirect(name, client); } else { @@ -392,18 +392,18 @@ public: } // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const + Client *client) const { - ClientCached *cc = getClientCached(name, gamedef); + ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; return cc->inventory_texture; } // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const + Client *client) const { - ClientCached *cc = getClientCached(name, gamedef); + ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; return cc->wield_mesh; @@ -543,7 +543,7 @@ public: request = m_get_clientcached_queue.pop(); m_get_clientcached_queue.pushResult(request, - createClientCachedDirect(request.key, gamedef)); + createClientCachedDirect(request.key, (Client *)gamedef)); } #endif } diff --git a/src/itemdef.h b/src/itemdef.h index dcb98e8a9..2ade6116a 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemgroup.h" #include "sound.h" class IGameDef; +class Client; struct ToolCapabilities; /* @@ -107,10 +108,10 @@ public: #ifndef SERVER // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; #endif virtual void serialize(std::ostream &os, u16 protocol_version)=0; @@ -133,10 +134,10 @@ public: #ifndef SERVER // Get item inventory texture virtual video::ITexture* getInventoryTexture(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; // Get item wield mesh virtual scene::IMesh* getWieldMesh(const std::string &name, - IGameDef *gamedef) const=0; + Client *client) const=0; #endif // Remove all registered item and node definitions and aliases diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 4d0ca0600..b859c6455 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "event.h" #include "collision.h" -#include "gamedef.h" #include "nodedef.h" #include "settings.h" #include "environment.h" @@ -32,8 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., LocalPlayer */ -LocalPlayer::LocalPlayer(Client *gamedef, const char *name): - Player(name, gamedef->idef()), +LocalPlayer::LocalPlayer(Client *client, const char *name): + Player(name, client->idef()), parent(0), hp(PLAYER_MAX_HP), got_teleported(false), @@ -79,7 +78,7 @@ LocalPlayer::LocalPlayer(Client *gamedef, const char *name): camera_barely_in_ceiling(false), m_collisionbox(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30), m_cao(NULL), - m_gamedef(gamedef) + m_client(client) { // Initialize hp to 0, so that no hearts will be shown if server // doesn't support health points @@ -96,7 +95,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, std::vector *collision_info) { Map *map = &env->getMap(); - INodeDefManager *nodemgr = m_gamedef->ndef(); + INodeDefManager *nodemgr = m_client->ndef(); v3f position = getPosition(); @@ -109,8 +108,8 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, } // Skip collision detection if noclip mode is used - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool noclip = m_gamedef->checkLocalPrivilege("noclip") && + bool fly_allowed = m_client->checkLocalPrivilege("fly"); + bool noclip = m_client->checkLocalPrivilege("noclip") && g_settings->getBool("noclip"); bool free_move = noclip && fly_allowed && g_settings->getBool("free_move"); if (free_move) { @@ -241,7 +240,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, v3f accel_f = v3f(0,0,0); - collisionMoveResult result = collisionMoveSimple(env, m_gamedef, + collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, &position, &m_speed, accel_f); @@ -376,7 +375,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, if(!result.standing_on_object && !touching_ground_was && touching_ground) { MtEvent *e = new SimpleTriggerEvent("PlayerRegainGround"); - m_gamedef->event()->put(e); + m_client->event()->put(e); // Set camera impact value to be used for view bobbing camera_impact = getSpeed().Y * -1; @@ -448,8 +447,8 @@ void LocalPlayer::applyControl(float dtime) v3f speedH = v3f(0,0,0); // Horizontal (X, Z) v3f speedV = v3f(0,0,0); // Vertical (Y) - bool fly_allowed = m_gamedef->checkLocalPrivilege("fly"); - bool fast_allowed = m_gamedef->checkLocalPrivilege("fast"); + bool fly_allowed = m_client->checkLocalPrivilege("fly"); + bool fast_allowed = m_client->checkLocalPrivilege("fast"); bool free_move = fly_allowed && g_settings->getBool("free_move"); bool fast_move = fast_allowed && g_settings->getBool("fast_move"); @@ -599,7 +598,7 @@ void LocalPlayer::applyControl(float dtime) setSpeed(speedJ); MtEvent *e = new SimpleTriggerEvent("PlayerJump"); - m_gamedef->event()->put(e); + m_client->event()->put(e); } } else if(in_liquid) diff --git a/src/localplayer.h b/src/localplayer.h index 7a1cb7466..cbdcb9867 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -35,7 +35,7 @@ enum LocalPlayerAnimations {NO_ANIM, WALK_ANIM, DIG_ANIM, WD_ANIM}; // no local class LocalPlayer : public Player { public: - LocalPlayer(Client *gamedef, const char *name); + LocalPlayer(Client *client, const char *name); virtual ~LocalPlayer(); ClientActiveObject *parent; @@ -162,7 +162,7 @@ private: aabb3f m_collisionbox; GenericCAO* m_cao; - Client *m_gamedef; + Client *m_client; }; #endif diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 977eabb6e..143adb410 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "profiler.h" #include "nodedef.h" -#include "gamedef.h" #include "mesh.h" #include "minimap.h" #include "content_mapblock.h" @@ -43,14 +42,14 @@ static void applyFacesShading(video::SColor &color, const float factor) MeshMakeData */ -MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders, +MeshMakeData::MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices): m_vmanip(), m_blockpos(-1337,-1337,-1337), m_crack_pos_relative(-1337, -1337, -1337), m_smooth_lighting(false), m_show_hud(false), - m_gamedef(gamedef), + m_client(client), m_use_shaders(use_shaders), m_use_tangent_vertices(use_tangent_vertices) {} @@ -233,7 +232,7 @@ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) v3s16(1,1,1), }; - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); u16 ambient_occlusion = 0; u16 light_count = 0; @@ -664,7 +663,7 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, */ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) { - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); TileSpec spec = ndef->get(mn).tiles[tileindex]; // Apply temporary crack if (p == data->m_crack_pos_relative) @@ -677,7 +676,7 @@ TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) */ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) { - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0), // (0,0,1), (0,0,-1) or (0,0,0) @@ -734,7 +733,7 @@ TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data) 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]; - spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id); + spec.texture = data->m_client->tsrc()->getTexture(spec.texture_id); return spec; } @@ -753,7 +752,7 @@ static void getTileInfo( ) { VoxelManipulator &vmanip = data->m_vmanip; - INodeDefManager *ndef = data->m_gamedef->ndef(); + INodeDefManager *ndef = data->m_client->ndef(); v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p); @@ -1020,10 +1019,10 @@ static void updateAllFastFaceRows(MeshMakeData *data, MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): m_mesh(new scene::SMesh()), m_minimap_mapblock(NULL), - m_gamedef(data->m_gamedef), - m_driver(m_gamedef->tsrc()->getDevice()->getVideoDriver()), - m_tsrc(m_gamedef->getTextureSource()), - m_shdrsrc(m_gamedef->getShaderSource()), + m_client(data->m_client), + m_driver(m_client->tsrc()->getDevice()->getVideoDriver()), + m_tsrc(m_client->getTextureSource()), + m_shdrsrc(m_client->getShaderSource()), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_crack_materials(), @@ -1243,7 +1242,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): if (m_use_tangent_vertices) { scene::IMeshManipulator* meshmanip = - m_gamedef->getSceneManager()->getMeshManipulator(); + m_client->getSceneManager()->getMeshManipulator(); meshmanip->recalculateTangents(m_mesh, true, false, false); } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 8376468da..5adb7df3f 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/cpp11_container.h" #include -class IGameDef; +class Client; class IShaderSource; /* @@ -45,11 +45,11 @@ struct MeshMakeData bool m_smooth_lighting; bool m_show_hud; - IGameDef *m_gamedef; + Client *m_client; bool m_use_shaders; bool m_use_tangent_vertices; - MeshMakeData(IGameDef *gamedef, bool use_shaders, + MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices = false); /* @@ -128,7 +128,7 @@ public: private: scene::IMesh *m_mesh; MinimapMapblock *m_minimap_mapblock; - IGameDef *m_gamedef; + Client *m_client; video::IVideoDriver *m_driver; ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index 78034bf6c..d564e9415 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -20,10 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_biome.h" #include "mg_decoration.h" #include "emerge.h" -#include "gamedef.h" +#include "server.h" #include "nodedef.h" #include "map.h" //for MMVManip -#include "log.h" #include "util/numeric.h" #include "util/mathconstants.h" #include "porting.h" @@ -33,10 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc., /////////////////////////////////////////////////////////////////////////////// -BiomeManager::BiomeManager(IGameDef *gamedef) : - ObjDefManager(gamedef, OBJDEF_BIOME) +BiomeManager::BiomeManager(Server *server) : + ObjDefManager(server, OBJDEF_BIOME) { - m_gamedef = gamedef; + m_server = server; // Create default biome to be used in case none exist Biome *b = new Biome; @@ -73,7 +72,7 @@ BiomeManager::~BiomeManager() void BiomeManager::clear() { - EmergeManager *emerge = m_gamedef->getEmergeManager(); + EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations DecorationManager *decomgr = emerge->decomgr; diff --git a/src/mg_biome.h b/src/mg_biome.h index a10193bc3..15088f7dd 100644 --- a/src/mg_biome.h +++ b/src/mg_biome.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "noise.h" +class Server; class Settings; class BiomeManager; @@ -186,7 +187,7 @@ private: class BiomeManager : public ObjDefManager { public: - BiomeManager(IGameDef *gamedef); + BiomeManager(Server *server); virtual ~BiomeManager(); const char *getObjectTitle() const @@ -223,7 +224,7 @@ public: virtual void clear(); private: - IGameDef *m_gamedef; + Server *m_server; }; diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp index e028215dc..3d08d86fa 100644 --- a/src/mg_schematic.cpp +++ b/src/mg_schematic.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "mg_schematic.h" -#include "gamedef.h" +#include "server.h" #include "mapgen.h" #include "emerge.h" #include "map.h" @@ -34,16 +34,16 @@ with this program; if not, write to the Free Software Foundation, Inc., /////////////////////////////////////////////////////////////////////////////// -SchematicManager::SchematicManager(IGameDef *gamedef) : - ObjDefManager(gamedef, OBJDEF_SCHEMATIC) +SchematicManager::SchematicManager(Server *server) : + ObjDefManager(server, OBJDEF_SCHEMATIC) { - m_gamedef = gamedef; + m_server = server; } void SchematicManager::clear() { - EmergeManager *emerge = m_gamedef->getEmergeManager(); + EmergeManager *emerge = m_server->getEmergeManager(); // Remove all dangling references in Decorations DecorationManager *decomgr = emerge->decomgr; diff --git a/src/mg_schematic.h b/src/mg_schematic.h index da8859540..1d46e6ac4 100644 --- a/src/mg_schematic.h +++ b/src/mg_schematic.h @@ -29,7 +29,7 @@ class Mapgen; class MMVManip; class PseudoRandom; class NodeResolver; -class IGameDef; +class Server; /* Minetest Schematic File Format @@ -123,7 +123,7 @@ public: class SchematicManager : public ObjDefManager { public: - SchematicManager(IGameDef *gamedef); + SchematicManager(Server *server); virtual ~SchematicManager() {} virtual void clear(); @@ -139,7 +139,7 @@ public: } private: - IGameDef *m_gamedef; + Server *m_server; }; void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount, diff --git a/src/nodedef.cpp b/src/nodedef.cpp index dbbdf95d2..b7d023897 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "client/tile.h" #include "mesh.h" +#include "client.h" #include #endif #include "log.h" @@ -572,8 +573,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, #ifndef SERVER void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, - scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, - IGameDef *gamedef, const TextureSettings &tsettings) + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings) { // minimap pixel color - the average color of a texture if (tsettings.enable_minimap && tiledef[0].name != "") @@ -709,7 +709,7 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc if ((drawtype == NDT_MESH) && (mesh != "")) { // Meshnode drawtype // Read the mesh and apply scale - mesh_ptr[0] = gamedef->getMesh(mesh); + mesh_ptr[0] = client->getMesh(mesh); if (mesh_ptr[0]){ v3f scale = v3f(1.0, 1.0, 1.0) * BS * visual_scale; scaleMesh(mesh_ptr[0], scale); @@ -1316,9 +1316,11 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, #ifndef SERVER infostream << "CNodeDefManager::updateTextures(): Updating " "textures in node definitions" << std::endl; - ITextureSource *tsrc = gamedef->tsrc(); - IShaderSource *shdsrc = gamedef->getShaderSource(); - scene::ISceneManager* smgr = gamedef->getSceneManager(); + + Client *client = (Client *)gamedef; + ITextureSource *tsrc = client->tsrc(); + IShaderSource *shdsrc = client->getShaderSource(); + scene::ISceneManager* smgr = client->getSceneManager(); scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); TextureSettings tsettings; tsettings.readSettings(); @@ -1326,7 +1328,7 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - m_content_features[i].updateTextures(tsrc, shdsrc, smgr, meshmanip, gamedef, tsettings); + m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } #endif diff --git a/src/nodedef.h b/src/nodedef.h index 284c4a198..183b95d87 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include "client/tile.h" #include "shader.h" +class Client; #endif #include "itemgroup.h" #include "sound.h" // SimpleSoundSpec @@ -322,8 +323,7 @@ struct ContentFeatures u32 shader_id, bool use_normal_texture, bool backface_culling, u8 alpha, u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, - scene::ISceneManager *smgr, scene::IMeshManipulator *meshmanip, - IGameDef *gamedef, const TextureSettings &tsettings); + scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif }; diff --git a/src/particles.cpp b/src/particles.cpp index 97f42e2c4..d9eb3cfa5 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -18,11 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "particles.h" -#include "constants.h" -#include "debug.h" -#include "settings.h" -#include "client/tile.h" -#include "gamedef.h" +#include "client.h" #include "collision.h" #include #include "util/numeric.h" @@ -452,7 +448,7 @@ void ParticleManager::clearAll () } } -void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, +void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, scene::ISceneManager* smgr, LocalPlayer *player) { switch (event->type) { @@ -477,9 +473,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, } video::ITexture *texture = - gamedef->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); + client->tsrc()->getTextureForMesh(*(event->add_particlespawner.texture)); - ParticleSpawner* toadd = new ParticleSpawner(gamedef, smgr, player, + ParticleSpawner* toadd = new ParticleSpawner(client, smgr, player, event->add_particlespawner.amount, event->add_particlespawner.spawntime, *event->add_particlespawner.minpos, @@ -520,9 +516,9 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, IGameDef *gamedef, } case CE_SPAWN_PARTICLE: { video::ITexture *texture = - gamedef->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); + client->tsrc()->getTextureForMesh(*(event->spawn_particle.texture)); - Particle* toadd = new Particle(gamedef, smgr, player, m_env, + Particle* toadd = new Particle(client, smgr, player, m_env, *event->spawn_particle.pos, *event->spawn_particle.vel, *event->spawn_particle.acc, diff --git a/src/particles.h b/src/particles.h index eb8c6665d..00cb2c08e 100644 --- a/src/particles.h +++ b/src/particles.h @@ -170,7 +170,7 @@ public: void step (float dtime); - void handleParticleEvent(ClientEvent *event,IGameDef *gamedef, + void handleParticleEvent(ClientEvent *event, Client *client, scene::ISceneManager* smgr, LocalPlayer *player); void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, diff --git a/src/pathfinder.cpp b/src/pathfinder.cpp index 84aa9252c..b240ec21f 100644 --- a/src/pathfinder.cpp +++ b/src/pathfinder.cpp @@ -24,12 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "pathfinder.h" #include "serverenvironment.h" -#include "gamedef.h" +#include "server.h" #include "nodedef.h" -#include "map.h" -#include "log.h" -#include "irr_aabb3d.h" -#include "util/basic_macros.h" //#define PATHFINDER_DEBUG //#define PATHFINDER_CALC_TIME diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 3cdd3cbfe..3d03c0c41 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -20,14 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodemeta.h" #include "lua_api/l_internal.h" #include "lua_api/l_inventory.h" -#include "common/c_converter.h" #include "common/c_content.h" #include "serverenvironment.h" #include "map.h" -#include "gamedef.h" -#include "nodemetadata.h" - - +#include "server.h" /* NodeMetaRef diff --git a/src/server.cpp b/src/server.cpp index 60dbef0d2..7380d37c2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -50,7 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_abm.h" #include "content_sao.h" #include "mods.h" -#include "sound.h" // dummySoundManager #include "event_manager.h" #include "serverlist.h" #include "util/string.h" @@ -3310,29 +3309,12 @@ ICraftDefManager *Server::getCraftDefManager() { return m_craftdef; } -ITextureSource *Server::getTextureSource() -{ - return NULL; -} -IShaderSource *Server::getShaderSource() -{ - return NULL; -} -scene::ISceneManager *Server::getSceneManager() -{ - return NULL; -} u16 Server::allocateUnknownNodeId(const std::string &name) { return m_nodedef->allocateDummy(name); } -ISoundManager *Server::getSoundManager() -{ - return &dummySoundManager; -} - MtEventManager *Server::getEventManager() { return m_event; diff --git a/src/server.h b/src/server.h index fe7b50b77..a86f75f1d 100644 --- a/src/server.h +++ b/src/server.h @@ -283,13 +283,9 @@ public: virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ITextureSource* getTextureSource(); - virtual IShaderSource* getShaderSource(); virtual u16 allocateUnknownNodeId(const std::string &name); - virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); - virtual scene::ISceneManager* getSceneManager(); - virtual IRollbackManager *getRollbackManager() { return m_rollback; } + IRollbackManager *getRollbackManager() { return m_rollback; } virtual EmergeManager *getEmergeManager() { return m_emerge; } IWritableItemDefManager* getWritableItemDefManager(); diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index c9fa64ec5..e1962bcff 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -352,11 +352,11 @@ void ActiveBlockList::update(std::vector &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, IGameDef *gamedef, + GameScripting *scriptIface, Server *server, const std::string &path_world) : m_map(map), m_script(scriptIface), - m_gamedef(gamedef), + m_server(server), m_path_world(path_world), m_send_recommended_timer(0), m_active_block_interval_overload_skip(0), @@ -487,7 +487,7 @@ void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason, for (std::vector::iterator it = m_players.begin(); it != m_players.end(); ++it) { RemotePlayer *player = dynamic_cast(*it); - ((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id, + m_server->DenyAccessVerCompliant(player->peer_id, player->protocol_version, reason, str_reason, reconnect); } } @@ -501,7 +501,7 @@ void ServerEnvironment::saveLoadedPlayers() it != m_players.end(); ++it) { if ((*it)->checkModified()) { - (*it)->save(players_path, m_gamedef); + (*it)->save(players_path, m_server); } } } @@ -511,7 +511,7 @@ void ServerEnvironment::savePlayer(RemotePlayer *player) std::string players_path = m_path_world + DIR_DELIM "players"; fs::CreateDir(players_path); - player->save(players_path, m_gamedef); + player->save(players_path, m_server); } RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, PlayerSAO *sao) @@ -523,7 +523,7 @@ RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername, Playe RemotePlayer *player = getPlayer(playername.c_str()); if (!player) { - player = new RemotePlayer("", m_gamedef->idef()); + player = new RemotePlayer("", m_server->idef()); newplayer = true; } @@ -632,7 +632,7 @@ void ServerEnvironment::loadMeta() } catch (SettingNotFoundException &e) { // No problem, this is expected. Just continue with an empty string } - m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time); + m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_server, m_game_time); m_day_count = args.exists("day_count") ? args.getU64("day_count") : 0; @@ -640,7 +640,7 @@ void ServerEnvironment::loadMeta() void ServerEnvironment::loadDefaultMeta() { - m_lbm_mgr.loadIntroductionTimes("", m_gamedef, m_game_time); + m_lbm_mgr.loadIntroductionTimes("", m_server, m_game_time); } struct ActiveABM @@ -902,7 +902,7 @@ void ServerEnvironment::addLoadingBlockModifierDef(LoadingBlockModifierDef *lbm) bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_server->ndef(); MapNode n_old = m_map->getNodeNoEx(p); // Call destructor @@ -929,7 +929,7 @@ bool ServerEnvironment::setNode(v3s16 p, const MapNode &n) bool ServerEnvironment::removeNode(v3s16 p) { - INodeDefManager *ndef = m_gamedef->ndef(); + INodeDefManager *ndef = m_server->ndef(); MapNode n_old = m_map->getNodeNoEx(p); // Call destructor diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 20a783ea5..d71d29a9c 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -29,6 +29,8 @@ class PlayerSAO; class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; +class Server; +class GameScripting; /* {Active, Loading} block modifier interface. @@ -190,7 +192,7 @@ class ServerEnvironment : public Environment { public: ServerEnvironment(ServerMap *map, GameScripting *scriptIface, - IGameDef *gamedef, const std::string &path_world); + Server *server, const std::string &path_world); ~ServerEnvironment(); Map & getMap(); @@ -201,8 +203,8 @@ public: GameScripting* getScriptIface() { return m_script; } - IGameDef *getGameDef() - { return m_gamedef; } + Server *getGameDef() + { return m_server; } float getSendRecommendedInterval() { return m_recommended_send_interval; } @@ -377,8 +379,8 @@ private: ServerMap *m_map; // Lua state GameScripting* m_script; - // Game definition - IGameDef *m_gamedef; + // Server definition + Server *m_server; // World path const std::string m_path_world; // Active object list diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 9c4d5b642..c305238fe 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "wieldmesh.h" #include "inventory.h" -#include "gamedef.h" +#include "client.h" #include "itemdef.h" #include "nodedef.h" #include "mesh.h" @@ -283,7 +283,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, video::SMaterial &material = m_meshnode->getMaterial(0); material.setTexture(0, tsrc->getTextureForMesh(imagename)); material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; - material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; material.MaterialType = m_material_type; material.setFlag(video::EMF_BACK_FACE_CULLING, true); // Enable bi/trilinear filtering only for high resolution textures @@ -304,12 +304,12 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) +void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) { - ITextureSource *tsrc = gamedef->getTextureSource(); - IItemDefManager *idef = gamedef->getItemDefManager(); - IShaderSource *shdrsrc = gamedef->getShaderSource(); - INodeDefManager *ndef = gamedef->getNodeDefManager(); + ITextureSource *tsrc = client->getTextureSource(); + IItemDefManager *idef = client->getItemDefManager(); + IShaderSource *shdrsrc = client->getShaderSource(); + INodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); @@ -341,7 +341,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, IGameDef *gamedef) } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES) { setCube(f.tiles, def.wield_scale, tsrc); } else { - MeshMakeData mesh_make_data(gamedef, false); + MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); @@ -435,11 +435,11 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) m_meshnode->setVisible(true); } -scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item) +scene::IMesh *getItemMesh(Client *client, const ItemStack &item) { - ITextureSource *tsrc = gamedef->getTextureSource(); - IItemDefManager *idef = gamedef->getItemDefManager(); - INodeDefManager *ndef = gamedef->getNodeDefManager(); + ITextureSource *tsrc = client->getTextureSource(); + IItemDefManager *idef = client->getItemDefManager(); + INodeDefManager *ndef = client->getNodeDefManager(); const ItemDefinition &def = item.getDefinition(idef); const ContentFeatures &f = ndef->get(def.name); content_t id = ndef->getId(def.name); @@ -470,7 +470,7 @@ scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item) mesh = cloneMesh(g_extrusion_mesh_cache->createCube()); scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); } else { - MeshMakeData mesh_make_data(gamedef, false); + MeshMakeData mesh_make_data(client, false); MapNode mesh_make_node(id, 255, 0); mesh_make_data.fillSingleNode(&mesh_make_node); MapBlockMesh mapblock_mesh(&mesh_make_data, v3s16(0, 0, 0)); diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 0b3136bc1..0162c5e5a 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include struct ItemStack; -class IGameDef; +class Client; class ITextureSource; struct TileSpec; @@ -42,7 +42,7 @@ public: v3f wield_scale, ITextureSource *tsrc); void setExtruded(const std::string &imagename, v3f wield_scale, ITextureSource *tsrc, u8 num_frames); - void setItem(const ItemStack &item, IGameDef *gamedef); + void setItem(const ItemStack &item, Client *client); // Sets the vertex color of the wield mesh. // Must only be used if the constructor was called with lighting = false @@ -77,7 +77,7 @@ private: aabb3f m_bounding_box; }; -scene::IMesh *getItemMesh(IGameDef *gamedef, const ItemStack &item); +scene::IMesh *getItemMesh(Client *client, const ItemStack &item); scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); -- cgit v1.2.3 From ec30d49e026af2d0cb8329eb66aec48d12e79839 Mon Sep 17 00:00:00 2001 From: Rui Date: Tue, 10 Jan 2017 04:39:45 +0900 Subject: Add staticdata parameter to add_entity (#5009) * Add staticdata parameter to add_entity * Add add_entity_with_staticdata to core.features --- builtin/game/features.lua | 1 + doc/lua_api.txt | 2 +- src/script/lua_api/l_env.cpp | 6 ++++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 2aad458da..646b254ea 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -9,6 +9,7 @@ core.features = { no_legacy_abms = true, texture_names_parens = true, area_store_custom_ids = true, + add_entity_with_staticdata = true, } function core.has_feature(arg) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d05db9d49..fc0f8e1fc 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2173,7 +2173,7 @@ and `minetest.auth_reload` call the authetification handler. * `minetest.get_node_timer(pos)` * Get `NodeTimerRef` -* `minetest.add_entity(pos, name)`: Spawn Lua-defined entity at position +* `minetest.add_entity(pos, name, [staticdata])`: Spawn Lua-defined entity at position * Returns `ObjectRef`, or `nil` if failed * `minetest.add_item(pos, item)`: Spawn item * Returns `ObjectRef`, or `nil` if failed diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 68d10308c..3d9db7917 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -440,7 +440,7 @@ int ModApiEnvMod::l_get_node_timer(lua_State *L) return 1; } -// add_entity(pos, entityname) -> ObjectRef or nil +// add_entity(pos, entityname, [staticdata]) -> ObjectRef or nil // pos = {x=num, y=num, z=num} int ModApiEnvMod::l_add_entity(lua_State *L) { @@ -450,8 +450,10 @@ int ModApiEnvMod::l_add_entity(lua_State *L) v3f pos = checkFloatPos(L, 1); // content const char *name = luaL_checkstring(L, 2); + // staticdata + const char *staticdata = luaL_optstring(L, 3, ""); // Do it - ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, ""); + ServerActiveObject *obj = new LuaEntitySAO(env, pos, name, staticdata); int objectid = env->addActiveObject(obj); // If failed to add, return nothing (reads as nil) if(objectid == 0) -- cgit v1.2.3 From 66479394037baa941cb06d75d3afc79ff4c717a2 Mon Sep 17 00:00:00 2001 From: Rogier Date: Tue, 10 Jan 2017 04:39:45 +0900 Subject: Performance fix + SAO factorization Original credits goes to @Rogier-5 * Merge common attributes between LuaEntitySAO & PlayerSAO to UnitSAO * Make some functions const * Improve some lists performance by returning const ref Signed-off-by: Loic Blot --- src/activeobject.h | 6 +-- src/clientobject.h | 4 +- src/content_cao.cpp | 6 +-- src/content_cao.h | 2 +- src/content_sao.cpp | 85 ++++++++++++++++++---------------------- src/content_sao.h | 87 ++++++++++++++++------------------------- src/script/lua_api/l_object.cpp | 7 ++-- src/serverobject.h | 8 ++-- 8 files changed, 88 insertions(+), 117 deletions(-) diff --git a/src/activeobject.h b/src/activeobject.h index 48f078d3f..fe6c08514 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -64,7 +64,7 @@ public: m_id(id) { } - + u16 getId() { return m_id; @@ -76,8 +76,8 @@ public: } virtual ActiveObjectType getType() const = 0; - virtual bool getCollisionBox(aabb3f *toset) = 0; - virtual bool collideWithObjects() = 0; + virtual bool getCollisionBox(aabb3f *toset) const = 0; + virtual bool collideWithObjects() const = 0; protected: u16 m_id; // 0 is invalid, "no id" }; diff --git a/src/clientobject.h b/src/clientobject.h index f0bde0adc..1db5bcf24 100644 --- a/src/clientobject.h +++ b/src/clientobject.h @@ -47,8 +47,8 @@ public: virtual void updateLightNoCheck(u8 light_at_pos){} virtual v3s16 getLightPosition(){return v3s16(0,0,0);} virtual aabb3f *getSelectionBox() { return NULL; } - virtual bool getCollisionBox(aabb3f *toset){return false;} - virtual bool collideWithObjects(){return false;} + virtual bool getCollisionBox(aabb3f *toset) const { return false; } + virtual bool collideWithObjects() const { return false; } virtual v3f getPosition(){return v3f(0,0,0);} virtual float getYaw() const {return 0;} virtual scene::ISceneNode *getSceneNode(){return NULL;} diff --git a/src/content_cao.cpp b/src/content_cao.cpp index a4c0bf14d..5ddbd27c9 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -159,7 +159,7 @@ public: void processMessage(const std::string &data); - bool getCollisionBox(aabb3f *toset) { return false; } + bool getCollisionBox(aabb3f *toset) const { return false; } private: scene::IMeshSceneNode *m_node; v3f m_position; @@ -316,7 +316,7 @@ public: std::string infoText() {return m_infotext;} - bool getCollisionBox(aabb3f *toset) { return false; } + bool getCollisionBox(aabb3f *toset) const { return false; } private: aabb3f m_selection_box; scene::IMeshSceneNode *m_node; @@ -587,7 +587,7 @@ GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): } } -bool GenericCAO::getCollisionBox(aabb3f *toset) +bool GenericCAO::getCollisionBox(aabb3f *toset) const { if (m_prop.physical) { diff --git a/src/content_cao.h b/src/content_cao.h index 96a160055..19fecdde5 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -130,7 +130,7 @@ public: ClientActiveObject *getParent(); - bool getCollisionBox(aabb3f *toset); + bool getCollisionBox(aabb3f *toset) const; bool collideWithObjects(); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index dd8bdc592..c403a3673 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -19,11 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "util/serialize.h" -#include "util/mathconstants.h" #include "collision.h" #include "environment.h" -#include "settings.h" -#include "serialization.h" // For compressZlib #include "tool.h" // For ToolCapabilities #include "gamedef.h" #include "nodedef.h" @@ -31,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "scripting_game.h" #include "genericobject.h" -#include "log.h" std::map ServerActiveObject::m_types; @@ -94,13 +90,8 @@ public: } } - bool getCollisionBox(aabb3f *toset) { - return false; - } - - bool collideWithObjects() { - return false; - } + bool getCollisionBox(aabb3f *toset) const { return false; } + bool collideWithObjects() const { return false; } private: float m_timer1; @@ -110,6 +101,28 @@ private: // Prototype (registers item for deserialization) TestSAO proto_TestSAO(NULL, v3f(0,0,0)); +/* + UnitSAO + */ + +UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): + ServerActiveObject(env, pos), + m_hp(-1), + m_yaw(0), + m_properties_sent(true), + m_armor_groups_sent(false), + m_animation_range(0,0), + m_animation_speed(0), + m_animation_blend(0), + m_animation_loop(true), + m_animation_sent(false), + m_bone_position_sent(false), + m_attachment_parent_id(0), + m_attachment_sent(false) +{ + // Initialize something to armor groups + m_armor_groups["fleshy"] = 100; +} /* LuaEntitySAO */ @@ -125,29 +138,17 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, m_registered(false), m_velocity(0,0,0), m_acceleration(0,0,0), - m_properties_sent(true), m_last_sent_yaw(0), m_last_sent_position(0,0,0), m_last_sent_velocity(0,0,0), m_last_sent_position_timer(0), - m_last_sent_move_precision(0), - m_armor_groups_sent(false), - m_animation_speed(0), - m_animation_blend(0), - m_animation_loop(true), - m_animation_sent(false), - m_bone_position_sent(false), - m_attachment_parent_id(0), - m_attachment_sent(false) + m_last_sent_move_precision(0) { // Only register type if no environment supplied if(env == NULL){ ServerActiveObject::registerType(getType(), create); return; } - - // Initialize something to armor groups - m_armor_groups["fleshy"] = 100; } LuaEntitySAO::~LuaEntitySAO() @@ -565,7 +566,7 @@ void LuaEntitySAO::setArmorGroups(const ItemGroupList &armor_groups) m_armor_groups_sent = false; } -ItemGroupList LuaEntitySAO::getArmorGroups() +const ItemGroupList &LuaEntitySAO::getArmorGroups() { return m_armor_groups; } @@ -635,7 +636,7 @@ void LuaEntitySAO::removeAttachmentChild(int child_id) m_attachment_child_ids.erase(child_id); } -UNORDERED_SET LuaEntitySAO::getAttachmentChildIds() +const UNORDERED_SET &LuaEntitySAO::getAttachmentChildIds() { return m_attachment_child_ids; } @@ -732,7 +733,8 @@ void LuaEntitySAO::sendPosition(bool do_interpolate, bool is_movement_end) m_messages_out.push(aom); } -bool LuaEntitySAO::getCollisionBox(aabb3f *toset) { +bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const +{ if (m_prop.physical) { //update collision box @@ -748,7 +750,8 @@ bool LuaEntitySAO::getCollisionBox(aabb3f *toset) { return false; } -bool LuaEntitySAO::collideWithObjects(){ +bool LuaEntitySAO::collideWithObjects() const +{ return m_prop.collideWithObjects; } @@ -770,16 +773,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer m_nocheat_dig_time(0), m_wield_index(0), m_position_not_sent(false), - m_armor_groups_sent(false), - m_properties_sent(true), m_is_singleplayer(is_singleplayer), - m_animation_speed(0), - m_animation_blend(0), - m_animation_loop(true), - m_animation_sent(false), - m_bone_position_sent(false), - m_attachment_parent_id(0), - m_attachment_sent(false), m_breath(PLAYER_MAX_BREATH), m_pitch(0), m_fov(0), @@ -793,7 +787,6 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer m_physics_override_sent(false) { assert(m_peer_id != 0); // pre-condition - m_armor_groups["fleshy"] = 100; m_prop.hp_max = PLAYER_MAX_HP; m_prop.physical = false; @@ -828,6 +821,11 @@ void PlayerSAO::initialize(RemotePlayer *player, const std::set &pr m_inventory = &m_player->inventory; } +v3f PlayerSAO::getEyeOffset() const +{ + return v3f(0, BS * 1.625f, 0); +} + std::string PlayerSAO::getDescription() { return std::string("player ") + m_player->getName(); @@ -1282,7 +1280,7 @@ void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) m_armor_groups_sent = false; } -ItemGroupList PlayerSAO::getArmorGroups() +const ItemGroupList &PlayerSAO::getArmorGroups() { return m_armor_groups; } @@ -1354,7 +1352,7 @@ void PlayerSAO::removeAttachmentChild(int child_id) m_attachment_child_ids.erase(child_id); } -UNORDERED_SET PlayerSAO::getAttachmentChildIds() +const UNORDERED_SET &PlayerSAO::getAttachmentChildIds() { return m_attachment_child_ids; } @@ -1512,15 +1510,10 @@ bool PlayerSAO::checkMovementCheat() return cheated; } -bool PlayerSAO::getCollisionBox(aabb3f *toset) +bool PlayerSAO::getCollisionBox(aabb3f *toset) const { *toset = aabb3f(-BS * 0.30, 0.0, -BS * 0.30, BS * 0.30, BS * 1.75, BS * 0.30); toset->MinEdge += m_base_position; toset->MaxEdge += m_base_position; return true; } - -bool PlayerSAO::collideWithObjects() -{ - return true; -} diff --git a/src/content_sao.h b/src/content_sao.h index 9c66068b3..d5e9b2cbf 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -24,14 +24,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverobject.h" #include "itemgroup.h" #include "object_properties.h" -#include "constants.h" class UnitSAO: public ServerActiveObject { public: - UnitSAO(ServerEnvironment *env, v3f pos): - ServerActiveObject(env, pos), - m_hp(-1), m_yaw(0) {} + UnitSAO(ServerEnvironment *env, v3f pos); virtual ~UnitSAO() {} virtual void setYaw(const float yaw) { m_yaw = yaw; } @@ -46,6 +43,29 @@ public: protected: s16 m_hp; float m_yaw; + + bool m_properties_sent; + struct ObjectProperties m_prop; + + ItemGroupList m_armor_groups; + bool m_armor_groups_sent; + + v2f m_animation_range; + float m_animation_speed; + float m_animation_blend; + bool m_animation_loop; + bool m_animation_sent; + + // Stores position and rotation for each bone name + UNORDERED_MAP > m_bone_position; + bool m_bone_position_sent; + + int m_attachment_parent_id; + UNORDERED_SET m_attachment_child_ids; + std::string m_attachment_bone; + v3f m_attachment_position; + v3f m_attachment_rotation; + bool m_attachment_sent; }; /* @@ -81,7 +101,7 @@ public: void setHP(s16 hp); s16 getHP() const; void setArmorGroups(const ItemGroupList &armor_groups); - ItemGroupList getArmorGroups(); + const ItemGroupList &getArmorGroups(); void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); void setBonePosition(const std::string &bone, v3f position, v3f rotation); @@ -90,7 +110,7 @@ public: void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); - UNORDERED_SET getAttachmentChildIds(); + const UNORDERED_SET &getAttachmentChildIds(); ObjectProperties* accessObjectProperties(); void notifyObjectPropertiesModified(); /* LuaEntitySAO-specific */ @@ -103,8 +123,8 @@ public: void setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch); std::string getName(); - bool getCollisionBox(aabb3f *toset); - bool collideWithObjects(); + bool getCollisionBox(aabb3f *toset) const; + bool collideWithObjects() const; private: std::string getPropertyPacket(); void sendPosition(bool do_interpolate, bool is_movement_end); @@ -112,36 +132,15 @@ private: std::string m_init_name; std::string m_init_state; bool m_registered; - struct ObjectProperties m_prop; v3f m_velocity; v3f m_acceleration; - ItemGroupList m_armor_groups; - - bool m_properties_sent; float m_last_sent_yaw; v3f m_last_sent_position; v3f m_last_sent_velocity; float m_last_sent_position_timer; float m_last_sent_move_precision; - bool m_armor_groups_sent; - - v2f m_animation_range; - float m_animation_speed; - float m_animation_blend; - bool m_animation_loop; - bool m_animation_sent; - - UNORDERED_MAP > m_bone_position; - bool m_bone_position_sent; - - int m_attachment_parent_id; - UNORDERED_SET m_attachment_child_ids; - std::string m_attachment_bone; - v3f m_attachment_position; - v3f m_attachment_rotation; - bool m_attachment_sent; }; /* @@ -235,7 +234,7 @@ public: u16 getBreath() const { return m_breath; } void setBreath(const u16 breath, bool send = true); void setArmorGroups(const ItemGroupList &armor_groups); - ItemGroupList getArmorGroups(); + const ItemGroupList &getArmorGroups(); void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); void setBonePosition(const std::string &bone, v3f position, v3f rotation); @@ -244,7 +243,7 @@ public: void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); - UNORDERED_SET getAttachmentChildIds(); + const UNORDERED_SET &getAttachmentChildIds(); ObjectProperties* accessObjectProperties(); void notifyObjectPropertiesModified(); @@ -315,13 +314,13 @@ public: m_is_singleplayer = is_singleplayer; } - bool getCollisionBox(aabb3f *toset); - bool collideWithObjects(); + bool getCollisionBox(aabb3f *toset) const; + bool collideWithObjects() const { return true; } void initialize(RemotePlayer *player, const std::set &privs); v3f getEyePosition() const { return m_base_position + getEyeOffset(); } - v3f getEyeOffset() const { return v3f(0, BS * 1.625f, 0); } + v3f getEyeOffset() const; private: std::string getPropertyPacket(); @@ -346,31 +345,11 @@ private: int m_wield_index; bool m_position_not_sent; - ItemGroupList m_armor_groups; - bool m_armor_groups_sent; - bool m_properties_sent; - struct ObjectProperties m_prop; // Cached privileges for enforcement std::set m_privs; bool m_is_singleplayer; - v2f m_animation_range; - float m_animation_speed; - float m_animation_blend; - bool m_animation_loop; - bool m_animation_sent; - - // Stores position and rotation for each bone name - UNORDERED_MAP > m_bone_position; - bool m_bone_position_sent; - - int m_attachment_parent_id; - UNORDERED_SET m_attachment_child_ids; - std::string m_attachment_bone; - v3f m_attachment_position; - v3f m_attachment_rotation; - bool m_attachment_sent; u16 m_breath; f32 m_pitch; f32 m_fov; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index cfdceb28e..84d14d521 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -137,8 +137,8 @@ int ObjectRef::l_remove(lua_State *L) if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) return 0; - UNORDERED_SET child_ids = co->getAttachmentChildIds(); - UNORDERED_SET::iterator it; + const UNORDERED_SET &child_ids = co->getAttachmentChildIds(); + UNORDERED_SET::const_iterator it; for (it = child_ids.begin(); it != child_ids.end(); ++it) { // Child can be NULL if it was deleted earlier if (ServerActiveObject *child = env->getActiveObject(*it)) @@ -396,8 +396,7 @@ int ObjectRef::l_get_armor_groups(lua_State *L) if (co == NULL) return 0; // Do it - ItemGroupList groups = co->getArmorGroups(); - push_groups(L, groups); + push_groups(L, co->getArmorGroups()); return 1; } diff --git a/src/serverobject.h b/src/serverobject.h index 9884eb0a1..9e8b5a779 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -146,8 +146,8 @@ public: virtual void setArmorGroups(const ItemGroupList &armor_groups) {} - virtual ItemGroupList getArmorGroups() - { return ItemGroupList(); } + virtual const ItemGroupList &getArmorGroups() + { static const ItemGroupList rv; return rv; } virtual void setPhysicsOverride(float physics_override_speed, float physics_override_jump, float physics_override_gravity) {} virtual void setAnimation(v2f frames, float frame_speed, float frame_blend, bool frame_loop) @@ -166,8 +166,8 @@ public: {} virtual void removeAttachmentChild(int child_id) {} - virtual UNORDERED_SET getAttachmentChildIds() - { return UNORDERED_SET(); } + virtual const UNORDERED_SET &getAttachmentChildIds() + { static const UNORDERED_SET rv; return rv; } virtual ObjectProperties* accessObjectProperties() { return NULL; } virtual void notifyObjectPropertiesModified() -- cgit v1.2.3 From 430d3b28e46b16e1411ec0ecb6c0d0ab9d6feb63 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 11 Jan 2017 09:03:07 +0100 Subject: Cleanup some header inclusions to improve compilation times --- src/client.h | 1 + src/clientenvironment.cpp | 1 + src/environment.h | 4 ++-- src/guiFormSpecMenu.h | 1 - src/localplayer.cpp | 11 +++++++++++ src/localplayer.h | 11 ++--------- src/minimap.cpp | 1 + src/network/serverpackethandler.cpp | 1 + src/player.h | 1 - src/serverenvironment.h | 3 +++ 10 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/client.h b/src/client.h index df3e7e605..f84246deb 100644 --- a/src/client.h +++ b/src/client.h @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "localplayer.h" #include "hud.h" #include "particles.h" +#include "mapnode.h" struct MeshMakeData; class MapBlockMesh; diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index 65646c6b4..b32a02f2d 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "raycast.h" #include "voxelalgorithms.h" +#include "settings.h" /* ClientEnvironment diff --git a/src/environment.h b/src/environment.h index 0cc3222f9..5154bbdcb 100644 --- a/src/environment.h +++ b/src/environment.h @@ -36,12 +36,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "activeobject.h" #include "util/numeric.h" -#include "mapnode.h" -#include "mapblock.h" #include "threading/mutex.h" #include "threading/atomic.h" #include "network/networkprotocol.h" // for AccessDeniedCode +class Map; + class Environment { public: diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 94b52e6f0..2ab7db4f1 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "irrlichttypes_extrabloated.h" -#include "inventory.h" #include "inventorymanager.h" #include "modalMenu.h" #include "guiTable.h" diff --git a/src/localplayer.cpp b/src/localplayer.cpp index b859c6455..857d95d8b 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -655,6 +655,17 @@ v3s16 LocalPlayer::getStandingNodePos() return floatToInt(getPosition() - v3f(0, BS, 0), BS); } +v3s16 LocalPlayer::getLightPosition() const +{ + return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); +} + +v3f LocalPlayer::getEyeOffset() const +{ + float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f; + return v3f(0, BS * eye_height, 0); +} + // Horizontal acceleration (X and Z), Y direction is ignored void LocalPlayer::accelerateHorizontal(const v3f &target_speed, const f32 max_increase) { diff --git a/src/localplayer.h b/src/localplayer.h index cbdcb9867..685a78cb3 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -105,10 +105,7 @@ public: u16 getBreath() const { return m_breath; } void setBreath(u16 breath) { m_breath = breath; } - v3s16 getLightPosition() const - { - return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); - } + v3s16 getLightPosition() const; void setYaw(f32 yaw) { @@ -131,11 +128,7 @@ public: v3f getPosition() const { return m_position; } v3f getEyePosition() const { return m_position + getEyeOffset(); } - v3f getEyeOffset() const - { - float eye_height = camera_barely_in_ceiling ? 1.5f : 1.625f; - return v3f(0, BS * eye_height, 0); - } + v3f getEyeOffset() const; private: void accelerateHorizontal(const v3f &target_speed, const f32 max_increase); void accelerateVertical(const v3f &target_speed, const f32 max_increase); diff --git a/src/minimap.cpp b/src/minimap.cpp index 8cd0a7beb..f49adb517 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "util/numeric.h" #include "util/string.h" +#include "mapblock.h" #include diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index eeabcca71..408fe7706 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_abm.h" #include "content_sao.h" #include "emerge.h" +#include "mapblock.h" #include "nodedef.h" #include "player.h" #include "rollback_interface.h" diff --git a/src/player.h b/src/player.h index 5f9bb7ec9..3432069c0 100644 --- a/src/player.h +++ b/src/player.h @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "inventory.h" -#include "constants.h" // BS #include "threading/mutex.h" #include diff --git a/src/serverenvironment.h b/src/serverenvironment.h index d71d29a9c..b7056c00c 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -21,6 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define SERVER_ENVIRONMENT_HEADER #include "environment.h" +#include "mapnode.h" +#include "mapblock.h" +#include class IGameDef; class ServerMap; -- cgit v1.2.3 From 5d60a6c533efb572a4004d110536da63bb62c58a Mon Sep 17 00:00:00 2001 From: Rui Date: Thu, 12 Jan 2017 04:25:25 +0900 Subject: Make nametag removable with set_nametag_attributes (#5021) --- src/script/lua_api/l_object.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 84d14d521..506e56df9 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -789,8 +789,7 @@ int ObjectRef::l_set_nametag_attributes(lua_State *L) lua_pop(L, 1); std::string nametag = getstringfield_default(L, 2, "text", ""); - if (nametag != "") - prop->nametag = nametag; + prop->nametag = nametag; co->notifyObjectPropertiesModified(); lua_pushboolean(L, true); -- cgit v1.2.3 From 25ba96fcac9fce7bfa9dd6e9c2c3e96e2d5e147e Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 24 Dec 2016 06:40:57 +0000 Subject: Meshes: Make object mesh face shading consistent Previously, object meshes had their North and South faces darker than East and West faces, the opposite of nodes and meshnodes. This commit corrects this. State constants as float-literals not double-literals. Simplify code. Add comment. --- src/mesh.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/mesh.cpp b/src/mesh.cpp index b68862d22..50465748c 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -186,17 +186,14 @@ void shadeMeshFaces(scene::IMesh *mesh) for (u32 i = 0; i < vertex_count; i++) { video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride); video::SColor &vc = vertex->Color; - if (vertex->Normal.Y < -0.5) { - applyFacesShading (vc, 0.447213); - } else if (vertex->Normal.Z > 0.5) { - applyFacesShading (vc, 0.670820); - } else if (vertex->Normal.Z < -0.5) { - applyFacesShading (vc, 0.670820); - } else if (vertex->Normal.X > 0.5) { - applyFacesShading (vc, 0.836660); - } else if (vertex->Normal.X < -0.5) { - applyFacesShading (vc, 0.836660); - } + // Many special drawtypes have normals set to 0,0,0 and this + // must result in maximum brightness (no face shadng). + if (vertex->Normal.Y < -0.5f) + applyFacesShading (vc, 0.447213f); + else if (vertex->Normal.X > 0.5f || vertex->Normal.X < -0.5f) + applyFacesShading (vc, 0.670820f); + else if (vertex->Normal.Z > 0.5f || vertex->Normal.Z < -0.5f) + applyFacesShading (vc, 0.836660f); } } } -- cgit v1.2.3 From 7ae7f1ea4cbbda7d7849a1240ce30d867e850bfb Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sun, 9 Oct 2016 14:36:22 -0400 Subject: Enable mod security by default --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 8668e1472..779224be4 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1186,7 +1186,7 @@ mgvalleys_np_inter_valley_slope (Valley Slope) noise_params 0.5, 0.5, (128, 128, [*Security] # Prevent mods from doing insecure things like running shell commands. -secure.enable_security (Enable mod security) bool false +secure.enable_security (Enable mod security) bool true # Comma-separated list of trusted mods that are allowed to access insecure # functions even when mod security is on (via request_insecure_environment()). diff --git a/minetest.conf.example b/minetest.conf.example index bf70f5e51..13b2dc7a7 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1530,7 +1530,7 @@ # Prevent mods from doing insecure things like running shell commands. # type: bool -# secure.enable_security = false +# secure.enable_security = true # Comma-separated list of trusted mods that are allowed to access insecure # functions even when mod security is on (via request_insecure_environment()). diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 52f4cfec8..1096b8904 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -303,7 +303,7 @@ void set_default_settings(Settings *settings) settings->setDefault("emergequeue_limit_diskonly", "32"); settings->setDefault("emergequeue_limit_generate", "32"); settings->setDefault("num_emerge_threads", "1"); - settings->setDefault("secure.enable_security", "false"); + settings->setDefault("secure.enable_security", "true"); settings->setDefault("secure.trusted_mods", ""); settings->setDefault("secure.http_mods", ""); -- cgit v1.2.3 From e2dd96b432f057cd8a3886314c78a79138a99c5e Mon Sep 17 00:00:00 2001 From: Rogier Date: Thu, 12 Jan 2017 04:25:25 +0900 Subject: Cleanup content_sao by factorizing similar code parts Signed-off-by: Loic Blot --- src/content_sao.cpp | 324 +++++++++++++++++----------------------------------- src/content_sao.h | 51 +++------ 2 files changed, 126 insertions(+), 249 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index c403a3673..852e2d788 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -123,6 +123,111 @@ UnitSAO::UnitSAO(ServerEnvironment *env, v3f pos): // Initialize something to armor groups m_armor_groups["fleshy"] = 100; } + +bool UnitSAO::isAttached() const +{ + if (!m_attachment_parent_id) + return false; + // Check if the parent still exists + ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); + if (obj) + return true; + return false; +} + +void UnitSAO::setArmorGroups(const ItemGroupList &armor_groups) +{ + m_armor_groups = armor_groups; + m_armor_groups_sent = false; +} + +const ItemGroupList &UnitSAO::getArmorGroups() +{ + return m_armor_groups; +} + +void UnitSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) +{ + // store these so they can be updated to clients + m_animation_range = frame_range; + m_animation_speed = frame_speed; + m_animation_blend = frame_blend; + m_animation_loop = frame_loop; + m_animation_sent = false; +} + +void UnitSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) +{ + *frame_range = m_animation_range; + *frame_speed = m_animation_speed; + *frame_blend = m_animation_blend; + *frame_loop = m_animation_loop; +} + +void UnitSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation) +{ + // store these so they can be updated to clients + m_bone_position[bone] = core::vector2d(position, rotation); + m_bone_position_sent = false; +} + +void UnitSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) +{ + *position = m_bone_position[bone].X; + *rotation = m_bone_position[bone].Y; +} + +void UnitSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) +{ + // Attachments need to be handled on both the server and client. + // If we just attach on the server, we can only copy the position of the parent. Attachments + // are still sent to clients at an interval so players might see them lagging, plus we can't + // read and attach to skeletal bones. + // If we just attach on the client, the server still sees the child at its original location. + // This breaks some things so we also give the server the most accurate representation + // even if players only see the client changes. + + m_attachment_parent_id = parent_id; + m_attachment_bone = bone; + m_attachment_position = position; + m_attachment_rotation = rotation; + m_attachment_sent = false; +} + +void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, + v3f *rotation) +{ + *parent_id = m_attachment_parent_id; + *bone = m_attachment_bone; + *position = m_attachment_position; + *rotation = m_attachment_rotation; +} + +void UnitSAO::addAttachmentChild(int child_id) +{ + m_attachment_child_ids.insert(child_id); +} + +void UnitSAO::removeAttachmentChild(int child_id) +{ + m_attachment_child_ids.erase(child_id); +} + +const UNORDERED_SET &UnitSAO::getAttachmentChildIds() +{ + return m_attachment_child_ids; +} + +ObjectProperties* UnitSAO::accessObjectProperties() +{ + return &m_prop; +} + +void UnitSAO::notifyObjectPropertiesModified() +{ + m_properties_sent = false; +} + /* LuaEntitySAO */ @@ -220,17 +325,6 @@ ServerActiveObject* LuaEntitySAO::create(ServerEnvironment *env, v3f pos, return sao; } -bool LuaEntitySAO::isAttached() -{ - if(!m_attachment_parent_id) - return false; - // Check if the parent still exists - ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); - if(obj) - return true; - return false; -} - void LuaEntitySAO::step(float dtime, bool send_recommended) { if(!m_properties_sent) @@ -427,7 +521,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) return os.str(); } -std::string LuaEntitySAO::getStaticData() +std::string LuaEntitySAO::getStaticData() const { verbosestream<(position, rotation); - m_bone_position_sent = false; -} - -void LuaEntitySAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) -{ - *position = m_bone_position[bone].X; - *rotation = m_bone_position[bone].Y; -} - -void LuaEntitySAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) -{ - // Attachments need to be handled on both the server and client. - // If we just attach on the server, we can only copy the position of the parent. Attachments - // are still sent to clients at an interval so players might see them lagging, plus we can't - // read and attach to skeletal bones. - // If we just attach on the client, the server still sees the child at its original location. - // This breaks some things so we also give the server the most accurate representation - // even if players only see the client changes. - - m_attachment_parent_id = parent_id; - m_attachment_bone = bone; - m_attachment_position = position; - m_attachment_rotation = rotation; - m_attachment_sent = false; -} - -void LuaEntitySAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) -{ - *parent_id = m_attachment_parent_id; - *bone = m_attachment_bone; - *position = m_attachment_position; - *rotation = m_attachment_rotation; -} - -void LuaEntitySAO::addAttachmentChild(int child_id) -{ - m_attachment_child_ids.insert(child_id); -} - -void LuaEntitySAO::removeAttachmentChild(int child_id) -{ - m_attachment_child_ids.erase(child_id); -} - -const UNORDERED_SET &LuaEntitySAO::getAttachmentChildIds() -{ - return m_attachment_child_ids; -} - -ObjectProperties* LuaEntitySAO::accessObjectProperties() -{ - return &m_prop; -} - -void LuaEntitySAO::notifyObjectPropertiesModified() -{ - m_properties_sent = false; -} - void LuaEntitySAO::setVelocity(v3f velocity) { m_velocity = velocity; @@ -854,11 +857,6 @@ void PlayerSAO::removingFromEnvironment() } } -bool PlayerSAO::isStaticAllowed() const -{ - return false; -} - std::string PlayerSAO::getClientInitializationData(u16 protocol_version) { std::ostringstream os(std::ios::binary); @@ -920,23 +918,12 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) return os.str(); } -std::string PlayerSAO::getStaticData() +std::string PlayerSAO::getStaticData() const { FATAL_ERROR("Deprecated function (?)"); return ""; } -bool PlayerSAO::isAttached() -{ - if(!m_attachment_parent_id) - return false; - // Check if the parent still exists - ServerActiveObject *obj = m_env->getActiveObject(m_attachment_parent_id); - if(obj) - return true; - return false; -} - void PlayerSAO::step(float dtime, bool send_recommended) { if (m_drowning_interval.step(dtime, 2.0)) { @@ -1224,10 +1211,6 @@ int PlayerSAO::punch(v3f dir, return hitparams.wear; } -void PlayerSAO::rightClick(ServerActiveObject *) -{ -} - s16 PlayerSAO::readDamage() { s16 damage = m_damage; @@ -1274,99 +1257,6 @@ void PlayerSAO::setBreath(const u16 breath, bool send) m_env->getGameDef()->SendPlayerBreath(this); } -void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups) -{ - m_armor_groups = armor_groups; - m_armor_groups_sent = false; -} - -const ItemGroupList &PlayerSAO::getArmorGroups() -{ - return m_armor_groups; -} - -void PlayerSAO::setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) -{ - // store these so they can be updated to clients - m_animation_range = frame_range; - m_animation_speed = frame_speed; - m_animation_blend = frame_blend; - m_animation_loop = frame_loop; - m_animation_sent = false; -} - -void PlayerSAO::getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop) -{ - *frame_range = m_animation_range; - *frame_speed = m_animation_speed; - *frame_blend = m_animation_blend; - *frame_loop = m_animation_loop; -} - -void PlayerSAO::setBonePosition(const std::string &bone, v3f position, v3f rotation) -{ - // store these so they can be updated to clients - m_bone_position[bone] = core::vector2d(position, rotation); - m_bone_position_sent = false; -} - -void PlayerSAO::getBonePosition(const std::string &bone, v3f *position, v3f *rotation) -{ - *position = m_bone_position[bone].X; - *rotation = m_bone_position[bone].Y; -} - -void PlayerSAO::setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation) -{ - // Attachments need to be handled on both the server and client. - // If we just attach on the server, we can only copy the position of the parent. Attachments - // are still sent to clients at an interval so players might see them lagging, plus we can't - // read and attach to skeletal bones. - // If we just attach on the client, the server still sees the child at its original location. - // This breaks some things so we also give the server the most accurate representation - // even if players only see the client changes. - - m_attachment_parent_id = parent_id; - m_attachment_bone = bone; - m_attachment_position = position; - m_attachment_rotation = rotation; - m_attachment_sent = false; -} - -void PlayerSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, - v3f *rotation) -{ - *parent_id = m_attachment_parent_id; - *bone = m_attachment_bone; - *position = m_attachment_position; - *rotation = m_attachment_rotation; -} - -void PlayerSAO::addAttachmentChild(int child_id) -{ - m_attachment_child_ids.insert(child_id); -} - -void PlayerSAO::removeAttachmentChild(int child_id) -{ - m_attachment_child_ids.erase(child_id); -} - -const UNORDERED_SET &PlayerSAO::getAttachmentChildIds() -{ - return m_attachment_child_ids; -} - -ObjectProperties* PlayerSAO::accessObjectProperties() -{ - return &m_prop; -} - -void PlayerSAO::notifyObjectPropertiesModified() -{ - m_properties_sent = false; -} - Inventory* PlayerSAO::getInventory() { return m_inventory; diff --git a/src/content_sao.h b/src/content_sao.h index d5e9b2cbf..4d6cf4dcf 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -40,6 +40,21 @@ public: s16 getHP() const { return m_hp; } // Use a function, if isDead can be defined by other conditions bool isDead() const { return m_hp == 0; } + + bool isAttached() const; + void setArmorGroups(const ItemGroupList &armor_groups); + const ItemGroupList &getArmorGroups(); + void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); + void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); + void setBonePosition(const std::string &bone, v3f position, v3f rotation); + void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); + void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); + void addAttachmentChild(int child_id); + void removeAttachmentChild(int child_id); + const UNORDERED_SET &getAttachmentChildIds(); + ObjectProperties* accessObjectProperties(); + void notifyObjectPropertiesModified(); protected: s16 m_hp; float m_yaw; @@ -85,10 +100,9 @@ public: virtual void addedToEnvironment(u32 dtime_s); static ServerActiveObject* create(ServerEnvironment *env, v3f pos, const std::string &data); - bool isAttached(); void step(float dtime, bool send_recommended); std::string getClientInitializationData(u16 protocol_version); - std::string getStaticData(); + std::string getStaticData() const; int punch(v3f dir, const ToolCapabilities *toolcap=NULL, ServerActiveObject *puncher=NULL, @@ -100,19 +114,6 @@ public: std::string getDescription(); void setHP(s16 hp); s16 getHP() const; - void setArmorGroups(const ItemGroupList &armor_groups); - const ItemGroupList &getArmorGroups(); - void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); - void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); - void setBonePosition(const std::string &bone, v3f position, v3f rotation); - void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); - void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); - void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); - void addAttachmentChild(int child_id); - void removeAttachmentChild(int child_id); - const UNORDERED_SET &getAttachmentChildIds(); - ObjectProperties* accessObjectProperties(); - void notifyObjectPropertiesModified(); /* LuaEntitySAO-specific */ void setVelocity(v3f velocity); v3f getVelocity(); @@ -196,10 +197,9 @@ public: void addedToEnvironment(u32 dtime_s); void removingFromEnvironment(); - bool isStaticAllowed() const; + bool isStaticAllowed() const { return false; } std::string getClientInitializationData(u16 protocol_version); - std::string getStaticData(); - bool isAttached(); + std::string getStaticData() const; void step(float dtime, bool send_recommended); void setBasePosition(const v3f &position); void setPos(const v3f &pos); @@ -227,25 +227,12 @@ public: const ToolCapabilities *toolcap, ServerActiveObject *puncher, float time_from_last_punch); - void rightClick(ServerActiveObject *clicker); + void rightClick(ServerActiveObject *clicker) {} void setHP(s16 hp); void setHPRaw(s16 hp) { m_hp = hp; } s16 readDamage(); u16 getBreath() const { return m_breath; } void setBreath(const u16 breath, bool send = true); - void setArmorGroups(const ItemGroupList &armor_groups); - const ItemGroupList &getArmorGroups(); - void setAnimation(v2f frame_range, float frame_speed, float frame_blend, bool frame_loop); - void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, bool *frame_loop); - void setBonePosition(const std::string &bone, v3f position, v3f rotation); - void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); - void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); - void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); - void addAttachmentChild(int child_id); - void removeAttachmentChild(int child_id); - const UNORDERED_SET &getAttachmentChildIds(); - ObjectProperties* accessObjectProperties(); - void notifyObjectPropertiesModified(); /* Inventory interface -- cgit v1.2.3 From ef0aa7d5b543b6561e1b7292b2d0a0ac43add55d Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 11 Jan 2017 22:48:14 +0100 Subject: Optimize SAO getStaticData by using std::string pointer instead of return copy Signed-off-by: Loic Blot --- src/content_sao.cpp | 9 ++++----- src/content_sao.h | 4 ++-- src/serverenvironment.cpp | 9 ++++++--- src/serverobject.h | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 852e2d788..bf8282af4 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -521,7 +521,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) return os.str(); } -std::string LuaEntitySAO::getStaticData() const +void LuaEntitySAO::getStaticData(std::string *result) const { verbosestream<getBasePosition(); - std::string staticdata = object->getStaticData(); + std::string staticdata = ""; + object->getStaticData(&staticdata); StaticObject s_obj(object->getType(), objectpos, staticdata); // Add to the block where the object is located in v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS)); @@ -1980,7 +1981,8 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) <getStaticData(); + std::string staticdata_new = ""; + obj->getStaticData(&staticdata_new); StaticObject s_obj(obj->getType(), objectpos, staticdata_new); block->m_static_objects.insert(id, s_obj); obj->m_static_block = blockpos_o; @@ -2020,7 +2022,8 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(obj->isStaticAllowed()) { // Create new static object - std::string staticdata_new = obj->getStaticData(); + std::string staticdata_new = ""; + obj->getStaticData(&staticdata_new); StaticObject s_obj(obj->getType(), objectpos, staticdata_new); bool stays_in_same_block = false; diff --git a/src/serverobject.h b/src/serverobject.h index 9e8b5a779..26c8b062d 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -119,10 +119,10 @@ public: when it is created (converted from static to active - actually the data is the static form) */ - virtual std::string getStaticData() + virtual void getStaticData(std::string *result) { assert(isStaticAllowed()); - return ""; + *result = ""; } /* Return false in here to never save and instead remove object -- cgit v1.2.3 From bb154c2e1cf185f181c3e85df9addb79e6d18b4c Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 15 Oct 2015 13:05:33 -0400 Subject: Main menu tweaks --- builtin/init.lua | 7 +++---- builtin/mainmenu/dlg_settings_advanced.lua | 2 +- src/guiEngine.cpp | 10 +++++----- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/builtin/init.lua b/builtin/init.lua index 4400a19d6..b34ad14a0 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -37,9 +37,9 @@ dofile(commonpath .. "misc_helpers.lua") if INIT == "game" then dofile(gamepath .. "init.lua") elseif INIT == "mainmenu" then - local mainmenuscript = core.setting_get("main_menu_script") - if mainmenuscript ~= nil and mainmenuscript ~= "" then - dofile(mainmenuscript) + local mm_script = core.setting_get("main_menu_script") + if mm_script and mm_script ~= "" then + dofile(mm_script) else dofile(core.get_mainmenu_path() .. DIR_DELIM .. "init.lua") end @@ -48,4 +48,3 @@ elseif INIT == "async" then else error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT))) end - diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index 85218c852..60ec1250f 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -667,4 +667,4 @@ end -- The documentation of mapgen noise parameter formats (title plus 16 lines) -- Noise parameter 'mgv5_np_ground' in group format (13 lines) ---assert(loadfile(core.get_mainmenu_path()..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false)) +--assert(loadfile(core.get_builtin_path()..DIR_DELIM.."mainmenu"..DIR_DELIM.."generate_from_settingtypes.lua"))(parse_config_file(true, false)) diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp index 6d66ed08d..03fee6b96 100644 --- a/src/guiEngine.cpp +++ b/src/guiEngine.cpp @@ -213,13 +213,13 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, m_data->script_data.errormessage = ""; if (!loadMainMenuScript()) { - errorstream << "No future without mainmenu" << std::endl; + errorstream << "No future without main menu!" << std::endl; abort(); } run(); } catch (LuaError &e) { - errorstream << "MAINMENU ERROR: " << e.what() << std::endl; + errorstream << "Main menu error: " << e.what() << std::endl; m_data->script_data.errormessage = e.what(); } @@ -231,13 +231,13 @@ GUIEngine::GUIEngine( irr::IrrlichtDevice* dev, /******************************************************************************/ bool GUIEngine::loadMainMenuScript() { - // Try custom menu script (main_menu_path) - + // Set main menu path (for core.get_mainmenu_path()) m_scriptdir = g_settings->get("main_menu_path"); if (m_scriptdir.empty()) { - m_scriptdir = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "mainmenu"; + m_scriptdir = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM + "mainmenu"; } + // Load builtin (which will load the main menu script) std::string script = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "init.lua"; try { m_script->loadScript(script); -- cgit v1.2.3 From 80023660979e19b3607fdfb573adb76cb60da5f1 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Thu, 15 Oct 2015 13:16:26 -0400 Subject: Organize defaultsettings.cpp --- src/defaultsettings.cpp | 219 +++++++++++++++++++++++------------------------- 1 file changed, 107 insertions(+), 112 deletions(-) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 1096b8904..14a88b7a6 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -23,14 +23,39 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "config.h" #include "constants.h" #include "porting.h" +#include "util/string.h" void set_default_settings(Settings *settings) { // Client and server - + settings->setDefault("language", ""); settings->setDefault("name", ""); + settings->setDefault("bind_address", ""); + settings->setDefault("serverlist_url", "servers.minetest.net"); - // Client stuff + // Client + settings->setDefault("address", ""); + settings->setDefault("enable_sound", "true"); + settings->setDefault("sound_volume", "0.8"); + settings->setDefault("enable_mesh_cache", "false"); + settings->setDefault("enable_vbo", "true"); + settings->setDefault("free_move", "false"); + settings->setDefault("fast_move", "false"); + settings->setDefault("noclip", "false"); + settings->setDefault("screenshot_path", "."); + settings->setDefault("screenshot_format", "png"); + settings->setDefault("screenshot_quality", "0"); + settings->setDefault("client_unload_unused_data_timeout", "600"); + settings->setDefault("client_mapblock_limit", "5000"); + settings->setDefault("enable_build_where_you_stand", "false" ); + settings->setDefault("send_pre_v25_init", "false"); + settings->setDefault("curl_timeout", "5000"); + settings->setDefault("curl_parallel_limit", "8"); + settings->setDefault("curl_file_download_timeout", "300000"); + settings->setDefault("curl_verify_cert", "true"); + settings->setDefault("enable_remote_media_server", "true"); + + // Keymap settings->setDefault("remote_port", "30000"); settings->setDefault("keymap_forward", "KEY_KEY_W"); settings->setDefault("keymap_autorun", ""); @@ -52,31 +77,20 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_fastmove", "KEY_KEY_J"); settings->setDefault("keymap_noclip", "KEY_KEY_H"); settings->setDefault("keymap_cinematic", ""); - settings->setDefault("keymap_screenshot", "KEY_F12"); settings->setDefault("keymap_toggle_hud", "KEY_F1"); settings->setDefault("keymap_toggle_chat", "KEY_F2"); settings->setDefault("keymap_toggle_force_fog_off", "KEY_F3"); - settings->setDefault("keymap_toggle_update_camera", #if DEBUG - "KEY_F4"); + settings->setDefault("keymap_toggle_update_camera", "KEY_F4"); #else - ""); + settings->setDefault("keymap_toggle_update_camera", ""); #endif settings->setDefault("keymap_toggle_debug", "KEY_F5"); settings->setDefault("keymap_toggle_profiler", "KEY_F6"); settings->setDefault("keymap_camera_mode", "KEY_F7"); + settings->setDefault("keymap_screenshot", "KEY_F12"); settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_decrease_viewing_range_min", "-"); - settings->setDefault("enable_build_where_you_stand", "false" ); - settings->setDefault("3d_mode", "none"); - settings->setDefault("3d_paralax_strength", "0.025"); - settings->setDefault("aux1_descends", "false"); - settings->setDefault("doubletap_jump", "false"); - settings->setDefault("always_fly_fast", "true"); - settings->setDefault("directional_colored_fog", "true"); - settings->setDefault("tooltip_show_delay", "400"); - settings->setDefault("zoom_fov", "15"); - // Some (temporary) keys for debugging settings->setDefault("keymap_print_debug_stacks", "KEY_KEY_P"); settings->setDefault("keymap_quicktune_prev", "KEY_HOME"); @@ -84,31 +98,28 @@ void set_default_settings(Settings *settings) settings->setDefault("keymap_quicktune_dec", "KEY_NEXT"); settings->setDefault("keymap_quicktune_inc", "KEY_PRIOR"); - // Show debug info by default? - #ifdef NDEBUG + // Visuals +#ifdef NDEBUG settings->setDefault("show_debug", "false"); - #else +#else settings->setDefault("show_debug", "true"); - #endif - +#endif + settings->setDefault("fsaa", "0"); + settings->setDefault("enable_fog", "true"); + settings->setDefault("fog_start", "0.4"); + settings->setDefault("3d_mode", "none"); + settings->setDefault("3d_paralax_strength", "0.025"); + settings->setDefault("tooltip_show_delay", "400"); + settings->setDefault("zoom_fov", "15"); settings->setDefault("fps_max", "60"); settings->setDefault("pause_fps_max", "20"); settings->setDefault("viewing_range", "100"); - settings->setDefault("map_generation_limit", "31000"); settings->setDefault("screenW", "800"); settings->setDefault("screenH", "600"); settings->setDefault("fullscreen", "false"); settings->setDefault("fullscreen_bpp", "24"); - settings->setDefault("fsaa", "0"); settings->setDefault("vsync", "false"); - settings->setDefault("address", ""); - settings->setDefault("random_input", "false"); - settings->setDefault("client_unload_unused_data_timeout", "600"); - settings->setDefault("client_mapblock_limit", "5000"); - settings->setDefault("enable_fog", "true"); - settings->setDefault("fog_start", "0.4"); settings->setDefault("fov", "72"); - settings->setDefault("view_bobbing", "true"); settings->setDefault("leaves_style", "fancy"); settings->setDefault("connected_glass", "false"); settings->setDefault("smooth_lighting", "true"); @@ -116,21 +127,10 @@ void set_default_settings(Settings *settings) settings->setDefault("texture_path", ""); settings->setDefault("shader_path", ""); settings->setDefault("video_driver", "opengl"); - settings->setDefault("free_move", "false"); - settings->setDefault("noclip", "false"); - settings->setDefault("continuous_forward", "false"); - settings->setDefault("enable_joysticks", "false"); - settings->setDefault("repeat_joystick_button_time", "0.17"); - settings->setDefault("joystick_frustum_sensitivity", "170"); settings->setDefault("cinematic", "false"); settings->setDefault("camera_smoothing", "0"); settings->setDefault("cinematic_camera_smoothing", "0.7"); - settings->setDefault("fast_move", "false"); - settings->setDefault("invert_mouse", "false"); settings->setDefault("enable_clouds", "true"); - settings->setDefault("screenshot_path", "."); - settings->setDefault("screenshot_format", "png"); - settings->setDefault("screenshot_quality", "0"); settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.0"); settings->setDefault("enable_3d_clouds", "true"); @@ -142,7 +142,6 @@ void set_default_settings(Settings *settings) settings->setDefault("console_alpha", "200"); settings->setDefault("selectionbox_color", "(0,0,0)"); settings->setDefault("selectionbox_width", "2"); - settings->setDefault("inventory_items_animations", "false"); settings->setDefault("node_highlighting", "box"); settings->setDefault("crosshair_color", "(255,255,255)"); settings->setDefault("crosshair_alpha", "255"); @@ -150,20 +149,28 @@ void set_default_settings(Settings *settings) settings->setDefault("gui_scaling", "1.0"); settings->setDefault("gui_scaling_filter", "false"); settings->setDefault("gui_scaling_filter_txr2img", "true"); - settings->setDefault("mouse_sensitivity", "0.2"); - settings->setDefault("enable_sound", "true"); - settings->setDefault("sound_volume", "0.8"); settings->setDefault("desynchronize_mapblock_texture_animation", "true"); settings->setDefault("hud_hotbar_max_width", "1.0"); settings->setDefault("enable_local_map_saving", "false"); settings->setDefault("show_entity_selectionbox", "true"); + settings->setDefault("texture_clean_transparent", "false"); + settings->setDefault("texture_min_size", "64"); + settings->setDefault("ambient_occlusion_gamma", "2.2"); + settings->setDefault("enable_shaders", "true"); + settings->setDefault("enable_particles", "true"); + + settings->setDefault("enable_minimap", "true"); + settings->setDefault("minimap_shape_round", "true"); + settings->setDefault("minimap_double_scan_height", "true"); + // Effects + settings->setDefault("directional_colored_fog", "true"); + settings->setDefault("view_bobbing", "true"); + settings->setDefault("inventory_items_animations", "false"); settings->setDefault("mip_map", "false"); settings->setDefault("anisotropic_filter", "false"); settings->setDefault("bilinear_filter", "false"); settings->setDefault("trilinear_filter", "false"); - settings->setDefault("texture_clean_transparent", "false"); - settings->setDefault("texture_min_size", "64"); settings->setDefault("tone_mapping", "false"); settings->setDefault("enable_bumpmapping", "false"); settings->setDefault("enable_parallax_occlusion", "false"); @@ -180,35 +187,29 @@ void set_default_settings(Settings *settings) settings->setDefault("water_wave_speed", "5.0"); settings->setDefault("enable_waving_leaves", "false"); settings->setDefault("enable_waving_plants", "false"); - settings->setDefault("ambient_occlusion_gamma", "2.2"); - settings->setDefault("enable_shaders", "true"); - settings->setDefault("repeat_rightclick_time", "0.25"); - settings->setDefault("enable_particles", "true"); - settings->setDefault("enable_mesh_cache", "false"); - settings->setDefault("enable_vbo", "true"); - - settings->setDefault("enable_minimap", "true"); - settings->setDefault("minimap_shape_round", "true"); - settings->setDefault("minimap_double_scan_height", "true"); - settings->setDefault("send_pre_v25_init", "false"); - - settings->setDefault("curl_timeout", "5000"); - settings->setDefault("curl_parallel_limit", "8"); - settings->setDefault("curl_file_download_timeout", "300000"); - settings->setDefault("curl_verify_cert", "true"); - settings->setDefault("enable_remote_media_server", "true"); + // Input + settings->setDefault("invert_mouse", "false"); + settings->setDefault("mouse_sensitivity", "0.2"); + settings->setDefault("repeat_rightclick_time", "0.25"); + settings->setDefault("random_input", "false"); + settings->setDefault("aux1_descends", "false"); + settings->setDefault("doubletap_jump", "false"); + settings->setDefault("always_fly_fast", "true"); + settings->setDefault("continuous_forward", "false"); + settings->setDefault("enable_joysticks", "false"); + settings->setDefault("repeat_joystick_button_time", "0.17"); + settings->setDefault("joystick_frustum_sensitivity", "170"); - settings->setDefault("serverlist_url", "servers.minetest.net"); + // Main menu + settings->setDefault("main_menu_path", ""); + settings->setDefault("main_menu_mod_mgr", "1"); + settings->setDefault("main_menu_game_mgr", "0"); + settings->setDefault("modstore_download_url", "https://forum.minetest.net/media/"); + settings->setDefault("modstore_listmods_url", "https://forum.minetest.net/mmdb/mods/"); + settings->setDefault("modstore_details_url", "https://forum.minetest.net/mmdb/mod/*/"); settings->setDefault("serverlist_file", "favoriteservers.txt"); - settings->setDefault("server_announce", "false"); - settings->setDefault("server_url", ""); - settings->setDefault("server_address", ""); - settings->setDefault("server_name", ""); - settings->setDefault("server_description", ""); - - settings->setDefault("disable_escape_sequences", "false"); #if USE_FREETYPE settings->setDefault("freetype", "true"); @@ -221,41 +222,43 @@ void set_default_settings(Settings *settings) settings->setDefault("fallback_font_shadow", "1"); settings->setDefault("fallback_font_shadow_alpha", "128"); - std::stringstream fontsize; - fontsize << TTF_DEFAULT_FONT_SIZE; + std::string font_size_str = std::to_string(TTF_DEFAULT_FONT_SIZE); - settings->setDefault("font_size", fontsize.str()); - settings->setDefault("mono_font_size", fontsize.str()); - settings->setDefault("fallback_font_size", fontsize.str()); + settings->setDefault("fallback_font_size", font_size_str); #else settings->setDefault("freetype", "false"); settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "lucida_sans")); settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "mono_dejavu_sans")); - std::stringstream fontsize; - fontsize << DEFAULT_FONT_SIZE; - - settings->setDefault("font_size", fontsize.str()); - settings->setDefault("mono_font_size", fontsize.str()); + std::string font_size_str = to_string(DEFAULT_FONT_SIZE); #endif + settings->setDefault("font_size", font_size_str); + settings->setDefault("mono_font_size", font_size_str); + + + // Server + settings->setDefault("disable_escape_sequences", "false"); - // Server stuff - // "map-dir" doesn't exist by default. + // Network + settings->setDefault("enable_ipv6", "true"); + settings->setDefault("ipv6_server", "false"); settings->setDefault("workaround_window_size","5"); settings->setDefault("max_packets_per_iteration","1024"); settings->setDefault("port", "30000"); - settings->setDefault("bind_address", ""); + settings->setDefault("strict_protocol_version_checking", "false"); + settings->setDefault("player_transfer_distance", "0"); + settings->setDefault("max_simultaneous_block_sends_per_client", "10"); + settings->setDefault("max_simultaneous_block_sends_server_total", "40"); + settings->setDefault("time_send_interval", "5"); + settings->setDefault("default_game", "minetest"); settings->setDefault("motd", ""); settings->setDefault("max_users", "15"); - settings->setDefault("strict_protocol_version_checking", "false"); settings->setDefault("creative_mode", "false"); settings->setDefault("enable_damage", "true"); - settings->setDefault("fixed_map_seed", ""); settings->setDefault("give_initial_stuff", "false"); settings->setDefault("default_password", ""); settings->setDefault("default_privs", "interact, shout"); - settings->setDefault("player_transfer_distance", "0"); settings->setDefault("enable_pvp", "true"); settings->setDefault("disallow_empty_password", "false"); settings->setDefault("disable_anticheat", "false"); @@ -271,18 +274,13 @@ void set_default_settings(Settings *settings) settings->setDefault("ask_reconnect_on_crash", "false"); settings->setDefault("profiler_print_interval", "0"); - settings->setDefault("enable_mapgen_debug_info", "false"); settings->setDefault("active_object_send_range_blocks", "3"); settings->setDefault("active_block_range", "3"); //settings->setDefault("max_simultaneous_block_sends_per_client", "1"); // This causes frametime jitter on client side, or does it? - settings->setDefault("max_simultaneous_block_sends_per_client", "10"); - settings->setDefault("max_simultaneous_block_sends_server_total", "40"); settings->setDefault("max_block_send_distance", "9"); - settings->setDefault("max_block_generate_distance", "7"); settings->setDefault("block_send_optimize_distance", "4"); settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096"); - settings->setDefault("time_send_interval", "5"); settings->setDefault("time_speed", "72"); settings->setDefault("server_unload_unused_data_timeout", "29"); settings->setDefault("max_objects_per_block", "64"); @@ -307,7 +305,7 @@ void set_default_settings(Settings *settings) settings->setDefault("secure.trusted_mods", ""); settings->setDefault("secure.http_mods", ""); - // physics stuff + // Physics settings->setDefault("movement_acceleration_default", "3"); settings->setDefault("movement_acceleration_air", "2"); settings->setDefault("movement_acceleration_fast", "10"); @@ -321,32 +319,30 @@ void set_default_settings(Settings *settings) settings->setDefault("movement_liquid_sink", "10"); settings->setDefault("movement_gravity", "9.81"); - //liquid stuff + // Liquids settings->setDefault("liquid_loop_max", "100000"); settings->setDefault("liquid_queue_purge_time", "0"); settings->setDefault("liquid_update", "1.0"); - //mapgen stuff + // Mapgen settings->setDefault("mg_name", "v7"); settings->setDefault("water_level", "1"); settings->setDefault("chunksize", "5"); settings->setDefault("mg_flags", "dungeons"); + settings->setDefault("fixed_map_seed", ""); + settings->setDefault("map_generation_limit", "31000"); + settings->setDefault("max_block_generate_distance", "7"); + settings->setDefault("enable_mapgen_debug_info", "false"); - // IPv6 - settings->setDefault("enable_ipv6", "true"); - settings->setDefault("ipv6_server", "false"); - - settings->setDefault("main_menu_path", ""); - settings->setDefault("main_menu_mod_mgr", "1"); - settings->setDefault("main_menu_game_mgr", "0"); - settings->setDefault("modstore_download_url", "https://forum.minetest.net/media/"); - settings->setDefault("modstore_listmods_url", "https://forum.minetest.net/mmdb/mods/"); - settings->setDefault("modstore_details_url", "https://forum.minetest.net/mmdb/mod/*/"); + // Server list announcing + settings->setDefault("server_announce", "false"); + settings->setDefault("server_url", ""); + settings->setDefault("server_address", ""); + settings->setDefault("server_name", ""); + settings->setDefault("server_description", ""); settings->setDefault("high_precision_fpu", "true"); - settings->setDefault("language", ""); - #ifdef __ANDROID__ settings->setDefault("screenW", "0"); settings->setDefault("screenH", "0"); @@ -375,13 +371,12 @@ void set_default_settings(Settings *settings) settings->setDefault("viewing_range", "25"); settings->setDefault("inventory_image_hack", "false"); - //check for device with small screen + // Check for a device with a small screen float x_inches = ((double) porting::getDisplaySize().X / (160 * porting::getDisplayDensity())); if (x_inches < 3.5) { settings->setDefault("hud_scaling", "0.6"); - } - else if (x_inches < 4.5) { + } else if (x_inches < 4.5) { settings->setDefault("hud_scaling", "0.7"); } settings->setDefault("curl_verify_cert","false"); @@ -393,7 +388,7 @@ void set_default_settings(Settings *settings) void override_default_settings(Settings *settings, Settings *from) { std::vector names = from->getNames(); - for(size_t i=0; isetDefault(name, from->get(name)); } -- cgit v1.2.3 From ee6d8c10ce3f7570a47c6042c36da3c8566364a7 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 14 Jan 2017 12:03:50 +0100 Subject: Fix missing const in ServerActiveObject::getStaticData This fixes #5033 Signed-off-by: Loic Blot --- src/serverobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/serverobject.h b/src/serverobject.h index 26c8b062d..38204980e 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -119,7 +119,7 @@ public: when it is created (converted from static to active - actually the data is the static form) */ - virtual void getStaticData(std::string *result) + virtual void getStaticData(std::string *result) const { assert(isStaticAllowed()); *result = ""; -- cgit v1.2.3 From ee9b59a7fe9a3277ffdd26d29e66ef7a285df5ce Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 14 Jan 2017 12:20:59 +0100 Subject: Fix another missing const reported by clang & @sfan5 Signed-off-by: Loic Blot --- src/content_cao.cpp | 2 +- src/content_cao.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 5ddbd27c9..83756c963 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -604,7 +604,7 @@ bool GenericCAO::getCollisionBox(aabb3f *toset) const return false; } -bool GenericCAO::collideWithObjects() +bool GenericCAO::collideWithObjects() const { return m_prop.collideWithObjects; } diff --git a/src/content_cao.h b/src/content_cao.h index 19fecdde5..846e0690a 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -132,7 +132,7 @@ public: bool getCollisionBox(aabb3f *toset) const; - bool collideWithObjects(); + bool collideWithObjects() const; aabb3f *getSelectionBox(); -- cgit v1.2.3 From f0c6feca97ee412b26c96521724dcd63104185bd Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 14 Jan 2017 12:28:43 +0100 Subject: Fix build with freetype support disabled --- src/defaultsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 14a88b7a6..59cfdd50d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -230,7 +230,7 @@ void set_default_settings(Settings *settings) settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "lucida_sans")); settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "mono_dejavu_sans")); - std::string font_size_str = to_string(DEFAULT_FONT_SIZE); + std::string font_size_str = std::to_string(DEFAULT_FONT_SIZE); #endif settings->setDefault("font_size", font_size_str); settings->setDefault("mono_font_size", font_size_str); -- cgit v1.2.3 From c41352a1c7a393299ea0dce0b4d075849f96dcd9 Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Sat, 14 Jan 2017 13:30:14 -0800 Subject: Only set material flag on rendered meshes (#5023) --- src/clientmap.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 7e688daad..fa326f0b4 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -371,10 +371,10 @@ struct MeshBufListList void add(scene::IMeshBuffer *buf) { + const video::SMaterial &m = buf->getMaterial(); for(std::vector::iterator i = lists.begin(); i != lists.end(); ++i){ MeshBufList &l = *i; - video::SMaterial &m = buf->getMaterial(); // comparing a full material is quite expensive so we don't do it if // not even first texture is equal @@ -387,7 +387,7 @@ struct MeshBufListList } } MeshBufList l; - l.m = buf->getMaterial(); + l.m = m; l.bufs.push_back(buf); lists.push_back(l); } @@ -508,12 +508,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); - buf->getMaterial().setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter); - buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter); - buf->getMaterial().setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter); - buf->getMaterial().setFlag(video::EMF_WIREFRAME, m_control.show_wireframe); - - const video::SMaterial& material = buf->getMaterial(); + video::SMaterial& material = buf->getMaterial(); video::IMaterialRenderer* rnd = driver->getMaterialRenderer(material.MaterialType); bool transparent = (rnd && rnd->isTransparent()); @@ -521,6 +516,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) if (buf->getVertexCount() == 0) errorstream << "Block [" << analyze_block(block) << "] contains an empty meshbuf" << std::endl; + + material.setFlag(video::EMF_TRILINEAR_FILTER, m_cache_trilinear_filter); + material.setFlag(video::EMF_BILINEAR_FILTER, m_cache_bilinear_filter); + material.setFlag(video::EMF_ANISOTROPIC_FILTER, m_cache_anistropic_filter); + material.setFlag(video::EMF_WIREFRAME, m_control.show_wireframe); + drawbufs.add(buf); } } -- cgit v1.2.3 From d03b4fb627b51fd794033452cfa3af77e879d0b5 Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 14 Jan 2017 22:30:03 +0100 Subject: Add color names from web page referenced in luaapi doc --- src/util/string.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/util/string.cpp b/src/util/string.cpp index 94064ef93..141068512 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -561,6 +561,7 @@ ColorContainer::ColorContainer() colors["darkgoldenrod"] = 0xb8860b; colors["darkgray"] = 0xa9a9a9; colors["darkgreen"] = 0x006400; + colors["darkgrey"] = 0xa9a9a9; colors["darkkhaki"] = 0xbdb76b; colors["darkmagenta"] = 0x8b008b; colors["darkolivegreen"] = 0x556b2f; @@ -571,11 +572,13 @@ ColorContainer::ColorContainer() colors["darkseagreen"] = 0x8fbc8f; colors["darkslateblue"] = 0x483d8b; colors["darkslategray"] = 0x2f4f4f; + colors["darkslategrey"] = 0x2f4f4f; colors["darkturquoise"] = 0x00ced1; colors["darkviolet"] = 0x9400d3; colors["deeppink"] = 0xff1493; colors["deepskyblue"] = 0x00bfff; colors["dimgray"] = 0x696969; + colors["dimgrey"] = 0x696969; colors["dodgerblue"] = 0x1e90ff; colors["firebrick"] = 0xb22222; colors["floralwhite"] = 0xfffaf0; @@ -588,6 +591,7 @@ ColorContainer::ColorContainer() colors["gray"] = 0x808080; colors["green"] = 0x008000; colors["greenyellow"] = 0xadff2f; + colors["grey"] = 0x808080; colors["honeydew"] = 0xf0fff0; colors["hotpink"] = 0xff69b4; colors["indianred"] = 0xcd5c5c; @@ -604,11 +608,13 @@ ColorContainer::ColorContainer() colors["lightgoldenrodyellow"] = 0xfafad2; colors["lightgray"] = 0xd3d3d3; colors["lightgreen"] = 0x90ee90; + colors["lightgrey"] = 0xd3d3d3; colors["lightpink"] = 0xffb6c1; colors["lightsalmon"] = 0xffa07a; colors["lightseagreen"] = 0x20b2aa; colors["lightskyblue"] = 0x87cefa; colors["lightslategray"] = 0x778899; + colors["lightslategrey"] = 0x778899; colors["lightsteelblue"] = 0xb0c4de; colors["lightyellow"] = 0xffffe0; colors["lime"] = 0x00ff00; @@ -661,6 +667,7 @@ ColorContainer::ColorContainer() colors["skyblue"] = 0x87ceeb; colors["slateblue"] = 0x6a5acd; colors["slategray"] = 0x708090; + colors["slategrey"] = 0x708090; colors["snow"] = 0xfffafa; colors["springgreen"] = 0x00ff7f; colors["steelblue"] = 0x4682b4; -- cgit v1.2.3 From e12019cfd9ff4e9afa1d7dd326a0094b15fb9b2b Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 15 Jan 2017 01:21:36 +0000 Subject: Documentation: Correct biome heat / humidity noise parameters When the new set of biomes was added in MTGame the 'spread' for heat and humidity noise parameters was increased to 1000, i forgot to update settingtypes.txt and minetest.conf. --- builtin/settingtypes.txt | 4 ++-- minetest.conf.example | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 779224be4..2dfe86e04 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -910,9 +910,9 @@ emergequeue_limit_generate (Limit of emerge queues to generate) int 32 num_emerge_threads (Number of emerge threads) int 1 # Noise parameters for biome API temperature, humidity and biome blend. -mg_biome_np_heat (Mapgen biome heat noise parameters) noise_params 50, 50, (750, 750, 750), 5349, 3, 0.5, 2.0 +mg_biome_np_heat (Mapgen biome heat noise parameters) noise_params 50, 50, (1000, 1000, 1000), 5349, 3, 0.5, 2.0 mg_biome_np_heat_blend (Mapgen heat blend noise parameters) noise_params 0, 1.5, (8, 8, 8), 13, 2, 1.0, 2.0 -mg_biome_np_humidity (Mapgen biome humidity noise parameters) noise_params 50, 50, (750, 750, 750), 842, 3, 0.5, 2.0 +mg_biome_np_humidity (Mapgen biome humidity noise parameters) noise_params 50, 50, (1000, 1000, 1000), 842, 3, 0.5, 2.0 mg_biome_np_humidity_blend (Mapgen biome humidity blend noise parameters) noise_params 0, 1.5, (8, 8, 8), 90003, 2, 1.0, 2.0 [***Mapgen v5] diff --git a/minetest.conf.example b/minetest.conf.example index 13b2dc7a7..3b686aa61 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1131,13 +1131,13 @@ # Noise parameters for biome API temperature, humidity and biome blend. # type: noise_params -# mg_biome_np_heat = 50, 50, (750, 750, 750), 5349, 3, 0.5, 2.0 +# mg_biome_np_heat = 50, 50, (1000, 1000, 1000), 5349, 3, 0.5, 2.0 # type: noise_params # mg_biome_np_heat_blend = 0, 1.5, (8, 8, 8), 13, 2, 1.0, 2.0 # type: noise_params -# mg_biome_np_humidity = 50, 50, (750, 750, 750), 842, 3, 0.5, 2.0 +# mg_biome_np_humidity = 50, 50, (1000, 1000, 1000), 842, 3, 0.5, 2.0 # type: noise_params # mg_biome_np_humidity_blend = 0, 1.5, (8, 8, 8), 90003, 2, 1.0, 2.0 -- cgit v1.2.3 From f5070b44542edba97a20445c3774c221077cc4a2 Mon Sep 17 00:00:00 2001 From: sapier Date: Sun, 15 Jan 2017 13:36:53 +0100 Subject: =?UTF-8?q?Added=20lua=20tracebacks=20to=20some=20errors=20where?= =?UTF-8?q?=20you=20have=20been=20blind=20to=20what=E2=80=A6=20(#5043)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added lua tracebacks to some errors where you have been blind to what actually went wrong --- src/emerge.cpp | 2 +- src/script/common/c_converter.cpp | 4 +++- src/script/cpp_api/s_env.cpp | 12 +++++++++--- src/server.cpp | 5 +++-- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/emerge.cpp b/src/emerge.cpp index 3f0a46010..1c9719c48 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -564,7 +564,7 @@ MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, m_server->getScriptIface()->environment_OnGenerated( minp, maxp, m_mapgen->blockseed); } catch (LuaError &e) { - m_server->setAsyncFatalError("Lua: " + std::string(e.what())); + m_server->setAsyncFatalError("Lua: finishGen" + std::string(e.what())); } EMERGE_DBG_OUT("ended up with: " << analyze_block(block)); diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index f36298915..fc516d56a 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -26,15 +26,17 @@ extern "C" { #include "util/serialize.h" #include "util/string.h" #include "common/c_converter.h" +#include "common/c_internal.h" #include "constants.h" #define CHECK_TYPE(index, name, type) do { \ int t = lua_type(L, (index)); \ if (t != (type)) { \ + std::string traceback = script_get_backtrace(L); \ throw LuaError(std::string("Invalid ") + (name) + \ " (expected " + lua_typename(L, (type)) + \ - " got " + lua_typename(L, t) + ")."); \ + " got " + lua_typename(L, t) + ").\n" + traceback); \ } \ } while(0) #define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER) diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index 913d8539d..b1404bf22 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -54,7 +54,9 @@ void ScriptApiEnv::environment_Step(float dtime) try { runCallbacks(1, RUN_CALLBACKS_MODE_FIRST); } catch (LuaError &e) { - getServer()->setAsyncFatalError(e.what()); + getServer()->setAsyncFatalError( + std::string("environment_Step: ") + e.what() + "\n" + + script_get_backtrace(L)); } } @@ -75,7 +77,9 @@ void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &t try { runCallbacks(2, RUN_CALLBACKS_MODE_FIRST); } catch (LuaError &e) { - getServer()->setAsyncFatalError(e.what()); + getServer()->setAsyncFatalError( + std::string("player_event: ") + e.what() + "\n" + + script_get_backtrace(L) ); } } @@ -237,7 +241,9 @@ void ScriptApiEnv::on_emerge_area_completion( try { PCALL_RES(lua_pcall(L, 4, 0, error_handler)); } catch (LuaError &e) { - server->setAsyncFatalError(e.what()); + server->setAsyncFatalError( + std::string("on_emerge_area_completion: ") + e.what() + "\n" + + script_get_backtrace(L)); } lua_pop(L, 1); // Pop error handler diff --git a/src/server.cpp b/src/server.cpp index 7380d37c2..29dce5a4a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -107,7 +107,8 @@ void *ServerThread::run() } catch (con::ConnectionBindFailed &e) { m_server->setAsyncFatalError(e.what()); } catch (LuaError &e) { - m_server->setAsyncFatalError("Lua: " + std::string(e.what())); + m_server->setAsyncFatalError( + "ServerThread::run Lua: " + std::string(e.what())); } } @@ -487,7 +488,7 @@ void Server::step(float dtime) g_settings->get("kick_msg_crash"), g_settings->getBool("ask_reconnect_on_crash")); } - throw ServerError(async_err); + throw ServerError("AsyncErr: " + async_err); } } -- cgit v1.2.3 From f3bd4c405df6bce64b7a98789d825eeac9c279b2 Mon Sep 17 00:00:00 2001 From: red-001 Date: Fri, 6 Jan 2017 20:29:29 +0000 Subject: Add keyword based search to serverlist --- builtin/mainmenu/common.lua | 2 +- builtin/mainmenu/init.lua | 2 +- builtin/mainmenu/tab_credits.lua | 2 +- builtin/mainmenu/tab_multiplayer.lua | 116 ++++++++++++++++++++++++++++++---- builtin/mainmenu/tab_server.lua | 2 +- builtin/mainmenu/tab_settings.lua | 4 +- builtin/mainmenu/tab_simple_main.lua | 4 +- builtin/mainmenu/tab_singleplayer.lua | 4 +- 8 files changed, 113 insertions(+), 23 deletions(-) diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index da3667828..5e3df0864 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -77,7 +77,7 @@ function order_favorite_list(list) end -------------------------------------------------------------------------------- -function render_favorite(spec, is_favorite) +function render_serverlist_row(spec, is_favorite) local text = "" if spec.name then text = text .. core.formspec_escape(spec.name:trim()) diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 7f0c1e386..1b527a053 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -128,7 +128,7 @@ local function init_globals() end -- Create main tabview - local tv_main = tabview_create("maintab", {x = 12, y = 5.2}, {x = 0, y = 0}) + local tv_main = tabview_create("maintab", {x = 12, y = 5.4}, {x = 0, y = 0}) if PLATFORM == "Android" then tv_main:add(tabs.simple_main) diff --git a/builtin/mainmenu/tab_credits.lua b/builtin/mainmenu/tab_credits.lua index c2ad19183..80e03f84d 100644 --- a/builtin/mainmenu/tab_credits.lua +++ b/builtin/mainmenu/tab_credits.lua @@ -85,7 +85,7 @@ return { "label[0.5,3.5;http://minetest.net]" .. "tablecolumns[color;text]" .. "tableoptions[background=#00000000;highlight=#00000000;border=false]" .. - "table[3.5,-0.25;8.5,5.8;list_credits;" .. + "table[3.5,-0.25;8.5,6.05;list_credits;" .. "#FFFF00," .. fgettext("Core Developers") .. ",," .. table.concat(core_developers, ",,") .. ",,," .. "#FFFF00," .. fgettext("Active Contributors") .. ",," .. diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua index 00150f26d..3c2b2cdbe 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_multiplayer.lua @@ -20,7 +20,16 @@ local function get_formspec(tabview, name, tabdata) -- Update the cached supported proto info, -- it may have changed after a change by the settings menu. common_update_cached_supp_proto() - local fav_selected = menudata.favorites[tabdata.fav_selected] + local fav_selected = nil + if menudata.search_result then + fav_selected = menudata.search_result[tabdata.fav_selected] + else + fav_selected = menudata.favorites[tabdata.fav_selected] + end + + if not tabdata.search_for then + tabdata.search_for = "" + end local retval = "label[7.75,-0.15;" .. fgettext("Address / Port") .. "]" .. @@ -33,7 +42,9 @@ local function get_formspec(tabview, name, tabdata) "field[8,1.95;2.95,0.5;te_name;;" .. core.formspec_escape(core.setting_get("name")) .. "]" .. "pwdfield[10.78,1.95;1.77,0.5;te_pwd;]" .. - "box[7.73,2.35;4.3,2.28;#999999]" + "box[7.73,2.35;4.3,2.28;#999999]".. + "field[0.15,0.25;4.5,0.27;te_search;;"..core.formspec_escape(tabdata.search_for).."]".. + "button[4.8,0;2.7,0.1;btn_mp_search;" .. fgettext("Search") .. "]" if tabdata.fav_selected and fav_selected then if gamedata.fav then @@ -58,9 +69,27 @@ local function get_formspec(tabview, name, tabdata) image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. "color,span=1;" .. "text,padding=1]" .. - "table[-0.15,-0.1;7.75,5.5;favourites;" + "table[-0.15,0.4;7.75,5.35;favourites;" + + if menudata.search_result then + for i = 1, #menudata.search_result do + local favs = core.get_favorites("local") + local server = menudata.search_result[i] - if #menudata.favorites > 0 then + for fav_id = 1, #favs do + if server.address == favs[fav_id].address and + server.port == favs[fav_id].port then + server.is_favorite = true + end + end + + if i ~= 1 then + retval = retval .. "," + end + + retval = retval .. render_serverlist_row(server, server.is_favorite) + end + elseif #menudata.favorites > 0 then local favs = core.get_favorites("local") if #favs > 0 then for i = 1, #favs do @@ -75,9 +104,9 @@ local function get_formspec(tabview, name, tabdata) end end end - retval = retval .. render_favorite(menudata.favorites[1], (#favs > 0)) + retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0)) for i = 2, #menudata.favorites do - retval = retval .. "," .. render_favorite(menudata.favorites[i], (i <= #favs)) + retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs)) end end @@ -92,6 +121,8 @@ end -------------------------------------------------------------------------------- local function main_button_handler(tabview, fields, name, tabdata) + local serverlist = menudata.search_result or menudata.favorites + if fields.te_name then gamedata.playername = fields.te_name core.setting_set("name", fields.te_name) @@ -99,10 +130,10 @@ local function main_button_handler(tabview, fields, name, tabdata) if fields.favourites then local event = core.explode_table_event(fields.favourites) - local fav = menudata.favorites[event.row] + local fav = serverlist[event.row] if event.type == "DCL" then - if event.row <= #menudata.favorites then + if event.row <= #serverlist then if menudata.favorites_is_public and not is_server_protocol_compat_or_error( fav.proto_min, fav.proto_max) then @@ -131,7 +162,7 @@ local function main_button_handler(tabview, fields, name, tabdata) end if event.type == "CHG" then - if event.row <= #menudata.favorites then + if event.row <= #serverlist then gamedata.fav = false local favs = core.get_favorites("local") local address = fav.address @@ -157,7 +188,7 @@ local function main_button_handler(tabview, fields, name, tabdata) if fields.key_up or fields.key_down then local fav_idx = core.get_table_index("favourites") - local fav = menudata.favorites[fav_idx] + local fav = serverlist[fav_idx] if fav_idx then if fields.key_up and fav_idx > 1 then @@ -176,7 +207,7 @@ local function main_button_handler(tabview, fields, name, tabdata) local address = fav.address local port = fav.port - + gamedata.serverdescription = fav.description if address and port then core.setting_set("address", address) core.setting_set("remote_port", port) @@ -199,6 +230,65 @@ local function main_button_handler(tabview, fields, name, tabdata) return true end + if fields.btn_mp_search or fields.key_enter_field == "te_search" then + tabdata.fav_selected = 1 + local input = fields.te_search:lower() + tabdata.search_for = fields.te_search + + if #menudata.favorites < 2 then + return true + end + + menudata.search_result = {} + + -- setup the keyword list + local keywords = {} + for word in input:gmatch("%S+") do + table.insert(keywords, word) + end + + if #keywords == 0 then + menudata.search_result = nil + return true + end + + -- Search the serverlist + local search_result = {} + for i = 1, #menudata.favorites do + local server = menudata.favorites[i] + local found = 0 + for k = 1, #keywords do + local keyword = keywords[k] + if server.name then + local name = server.name:lower() + local _, count = name:gsub(keyword, keyword) + found = found + count * 4 + end + + if server.description then + local desc = server.description:lower() + local _, count = desc:gsub(keyword, keyword) + found = found + count * 2 + end + end + if found > 0 then + local points = (#menudata.favorites - i) / 5 + found + server.points = points + table.insert(search_result, server) + end + end + if #search_result > 0 then + table.sort(search_result, function(a, b) + return a.points > b.points + end) + menudata.search_result = search_result + local first_server = search_result[1] + core.setting_set("address", first_server.address) + core.setting_set("remote_port", first_server.port) + end + return true + end + if (fields.btn_mp_connect or fields.key_enter) and fields.te_address and fields.te_port then gamedata.playername = fields.te_name gamedata.password = fields.te_pwd @@ -206,9 +296,9 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.port = fields.te_port gamedata.selected_world = 0 local fav_idx = core.get_table_index("favourites") - local fav = menudata.favorites[fav_idx] + local fav = serverlist[fav_idx] - if fav_idx and fav_idx <= #menudata.favorites and + if fav_idx and fav_idx <= #serverlist and fav.address == fields.te_address and fav.port == fields.te_port then diff --git a/builtin/mainmenu/tab_server.lua b/builtin/mainmenu/tab_server.lua index 6b96825a0..be57ad7ef 100644 --- a/builtin/mainmenu/tab_server.lua +++ b/builtin/mainmenu/tab_server.lua @@ -26,7 +26,7 @@ local function get_formspec(tabview, name, tabdata) "button[4,4.15;2.6,0.5;world_delete;" .. fgettext("Delete") .. "]" .. "button[6.5,4.15;2.8,0.5;world_create;" .. fgettext("New") .. "]" .. "button[9.2,4.15;2.55,0.5;world_configure;" .. fgettext("Configure") .. "]" .. - "button[8.5,4.95;3.25,0.5;start_server;" .. fgettext("Start Game") .. "]" .. + "button[8.5,5;3.25,0.5;start_server;" .. fgettext("Start Game") .. "]" .. "label[4,-0.25;" .. fgettext("Select World:") .. "]" .. "checkbox[0.25,0.25;cb_creative_mode;" .. fgettext("Creative Mode") .. ";" .. dump(core.setting_getbool("creative_mode")) .. "]" .. diff --git a/builtin/mainmenu/tab_settings.lua b/builtin/mainmenu/tab_settings.lua index af8df0ccb..e59572a41 100644 --- a/builtin/mainmenu/tab_settings.lua +++ b/builtin/mainmenu/tab_settings.lua @@ -209,12 +209,12 @@ local function formspec(tabview, name, tabdata) .. fgettext("Reset singleplayer world") .. "]" else tab_string = tab_string .. - "button[8,4.75;3.75,0.5;btn_change_keys;" + "button[8,4.85;3.75,0.5;btn_change_keys;" .. fgettext("Change keys") .. "]" end tab_string = tab_string .. - "button[0,4.75;3.75,0.5;btn_advanced_settings;" + "button[0,4.85;3.75,0.5;btn_advanced_settings;" .. fgettext("Advanced Settings") .. "]" diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua index 3818f321f..a773a4912 100644 --- a/builtin/mainmenu/tab_simple_main.lua +++ b/builtin/mainmenu/tab_simple_main.lua @@ -70,9 +70,9 @@ local function get_formspec(tabview, name, tabdata) end end end - retval = retval .. render_favorite(menudata.favorites[1], (#favs > 0)) + retval = retval .. render_serverlist_row(menudata.favorites[1], (#favs > 0)) for i = 2, #menudata.favorites do - retval = retval .. "," .. render_favorite(menudata.favorites[i], (i <= #favs)) + retval = retval .. "," .. render_serverlist_row(menudata.favorites[i], (i <= #favs)) end end diff --git a/builtin/mainmenu/tab_singleplayer.lua b/builtin/mainmenu/tab_singleplayer.lua index 05060cbc6..236de763c 100644 --- a/builtin/mainmenu/tab_singleplayer.lua +++ b/builtin/mainmenu/tab_singleplayer.lua @@ -57,7 +57,7 @@ local function singleplayer_refresh_gamebar() local btnbar = buttonbar_create("game_button_bar", game_buttonbar_button_handler, - {x=-0.3,y=5.65}, "horizontal", {x=12.4,y=1.15}) + {x=-0.3,y=5.9}, "horizontal", {x=12.4,y=1.15}) for i=1,#gamemgr.games,1 do local btn_name = "game_btnbar_" .. gamemgr.games[i].id @@ -96,7 +96,7 @@ local function get_formspec(tabview, name, tabdata) "button[4,4.15;2.6,0.5;world_delete;".. fgettext("Delete") .. "]" .. "button[6.5,4.15;2.8,0.5;world_create;".. fgettext("New") .. "]" .. "button[9.2,4.15;2.55,0.5;world_configure;".. fgettext("Configure") .. "]" .. - "button[8.5,4.95;3.25,0.5;play;".. fgettext("Play") .. "]" .. + "button[8.5,5;3.25,0.5;play;".. fgettext("Play") .. "]" .. "label[4,-0.25;".. fgettext("Select World:") .. "]".. "checkbox[0.25,0.25;cb_creative_mode;".. fgettext("Creative Mode") .. ";" .. dump(core.setting_getbool("creative_mode")) .. "]".. -- cgit v1.2.3 From 63c892eedfe21cbca2e2369d28e5e4d4e62ca1bd Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Mon, 16 Jan 2017 13:08:59 +0000 Subject: Rename ObjectRef methods to be consistent and predictable --- doc/lua_api.txt | 22 +++++++------- src/script/lua_api/l_internal.h | 1 + src/script/lua_api/l_object.cpp | 66 ++++++++++++++++++++--------------------- src/script/lua_api/l_object.h | 44 +++++++++++++-------------- 4 files changed, 67 insertions(+), 66 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index fc0f8e1fc..da6a898d9 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2788,9 +2788,9 @@ This is basically a reference to a C++ `ServerActiveObject` #### Methods * `remove()`: remove object (after returning from Lua) * Note: Doesn't work on players, use minetest.kick_player instead -* `getpos()`: returns `{x=num, y=num, z=num}` -* `setpos(pos)`; `pos`=`{x=num, y=num, z=num}` -* `moveto(pos, continuous=false)`: interpolated move +* `get_pos()`: returns `{x=num, y=num, z=num}` +* `set_pos(pos)`; `pos`=`{x=num, y=num, z=num}` +* `move_to(pos, continuous=false)`: interpolated move * `punch(puncher, time_from_last_punch, tool_capabilities, direction)` * `puncher` = another `ObjectRef`, * `time_from_last_punch` = time since last punch action of the puncher @@ -2836,14 +2836,14 @@ This is basically a reference to a C++ `ServerActiveObject` } ##### LuaEntitySAO-only (no-op for other objects) -* `setvelocity({x=num, y=num, z=num})` -* `getvelocity()`: returns `{x=num, y=num, z=num}` -* `setacceleration({x=num, y=num, z=num})` -* `getacceleration()`: returns `{x=num, y=num, z=num}` -* `setyaw(radians)` -* `getyaw()`: returns number in radians -* `settexturemod(mod)` -* `setsprite(p={x=0,y=0}, num_frames=1, framelength=0.2, +* `set_velocity({x=num, y=num, z=num})` +* `get_velocity()`: returns `{x=num, y=num, z=num}` +* `set_acceleration({x=num, y=num, z=num})` +* `get_acceleration()`: returns `{x=num, y=num, z=num}` +* `set_yaw(radians)` +* `get_yaw()`: returns number in radians +* `set_texture_mod(mod)` +* `set_sprite(p={x=0,y=0}, num_frames=1, framelength=0.2, select_horiz_by_yawpitch=false)` * Select sprite from spritesheet with optional animation and DM-style texture selection based on yaw relative to camera diff --git a/src/script/lua_api/l_internal.h b/src/script/lua_api/l_internal.h index 456c8fcce..c610dc5a3 100644 --- a/src/script/lua_api/l_internal.h +++ b/src/script/lua_api/l_internal.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_internal.h" #define luamethod(class, name) {#name, class::l_##name} +#define luamethod_aliased(class, name, alias) {#name, class::l_##name}, {#alias, class::l_##name} #define API_FCT(name) registerFunction(L, #name, l_##name,top) #define ASYNC_API_FCT(name) engine.registerFunction(#name, l_##name) diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 506e56df9..dd29208c5 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -150,9 +150,9 @@ int ObjectRef::l_remove(lua_State *L) return 0; } -// getpos(self) +// get_pos(self) // returns: {x=num, y=num, z=num} -int ObjectRef::l_getpos(lua_State *L) +int ObjectRef::l_get_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -169,8 +169,8 @@ int ObjectRef::l_getpos(lua_State *L) return 1; } -// setpos(self, pos) -int ObjectRef::l_setpos(lua_State *L) +// set_pos(self, pos) +int ObjectRef::l_set_pos(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -184,8 +184,8 @@ int ObjectRef::l_setpos(lua_State *L) return 0; } -// moveto(self, pos, continuous=false) -int ObjectRef::l_moveto(lua_State *L) +// move_to(self, pos, continuous=false) +int ObjectRef::l_move_to(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -821,8 +821,8 @@ int ObjectRef::l_get_nametag_attributes(lua_State *L) /* LuaEntitySAO-only */ -// setvelocity(self, {x=num, y=num, z=num}) -int ObjectRef::l_setvelocity(lua_State *L) +// set_velocity(self, {x=num, y=num, z=num}) +int ObjectRef::l_set_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -834,8 +834,8 @@ int ObjectRef::l_setvelocity(lua_State *L) return 0; } -// getvelocity(self) -int ObjectRef::l_getvelocity(lua_State *L) +// get_velocity(self) +int ObjectRef::l_get_velocity(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -847,8 +847,8 @@ int ObjectRef::l_getvelocity(lua_State *L) return 1; } -// setacceleration(self, {x=num, y=num, z=num}) -int ObjectRef::l_setacceleration(lua_State *L) +// set_acceleration(self, {x=num, y=num, z=num}) +int ObjectRef::l_set_acceleration(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -861,8 +861,8 @@ int ObjectRef::l_setacceleration(lua_State *L) return 0; } -// getacceleration(self) -int ObjectRef::l_getacceleration(lua_State *L) +// get_acceleration(self) +int ObjectRef::l_get_acceleration(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -874,8 +874,8 @@ int ObjectRef::l_getacceleration(lua_State *L) return 1; } -// setyaw(self, radians) -int ObjectRef::l_setyaw(lua_State *L) +// set_yaw(self, radians) +int ObjectRef::l_set_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -887,8 +887,8 @@ int ObjectRef::l_setyaw(lua_State *L) return 0; } -// getyaw(self) -int ObjectRef::l_getyaw(lua_State *L) +// get_yaw(self) +int ObjectRef::l_get_yaw(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -900,8 +900,8 @@ int ObjectRef::l_getyaw(lua_State *L) return 1; } -// settexturemod(self, mod) -int ObjectRef::l_settexturemod(lua_State *L) +// set_texture_mod(self, mod) +int ObjectRef::l_set_texture_mod(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -913,9 +913,9 @@ int ObjectRef::l_settexturemod(lua_State *L) return 0; } -// setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, +// set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, // select_horiz_by_yawpitch=false) -int ObjectRef::l_setsprite(lua_State *L) +int ObjectRef::l_set_sprite(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkobject(L, 1); @@ -1774,9 +1774,9 @@ const char ObjectRef::className[] = "ObjectRef"; const luaL_reg ObjectRef::methods[] = { // ServerActiveObject luamethod(ObjectRef, remove), - luamethod(ObjectRef, getpos), - luamethod(ObjectRef, setpos), - luamethod(ObjectRef, moveto), + luamethod_aliased(ObjectRef, get_pos, getpos), + luamethod_aliased(ObjectRef, set_pos, setpos), + luamethod_aliased(ObjectRef, move_to, moveto), luamethod(ObjectRef, punch), luamethod(ObjectRef, right_click), luamethod(ObjectRef, set_hp), @@ -1800,14 +1800,14 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, set_nametag_attributes), luamethod(ObjectRef, get_nametag_attributes), // 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_aliased(ObjectRef, set_velocity, setvelocity), + luamethod_aliased(ObjectRef, get_velocity, getvelocity), + luamethod_aliased(ObjectRef, set_acceleration, setacceleration), + luamethod_aliased(ObjectRef, get_acceleration, getacceleration), + luamethod_aliased(ObjectRef, set_yaw, setyaw), + luamethod_aliased(ObjectRef, get_yaw, getyaw), + luamethod_aliased(ObjectRef, set_texture_mod, set_texturemod), + luamethod_aliased(ObjectRef, set_sprite, setsprite), luamethod(ObjectRef, get_entity_name), luamethod(ObjectRef, get_luaentity), // Player-only diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 09f10e417..06b1bb79b 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -57,15 +57,15 @@ private: // remove(self) static int l_remove(lua_State *L); - // getpos(self) + // get_pos(self) // returns: {x=num, y=num, z=num} - static int l_getpos(lua_State *L); + static int l_get_pos(lua_State *L); - // setpos(self, pos) - static int l_setpos(lua_State *L); + // set_pos(self, pos) + static int l_set_pos(lua_State *L); - // moveto(self, pos, continuous=false) - static int l_moveto(lua_State *L); + // move_to(self, pos, continuous=false) + static int l_move_to(lua_State *L); // punch(self, puncher, time_from_last_punch, tool_capabilities, dir) static int l_punch(lua_State *L); @@ -143,30 +143,30 @@ private: /* LuaEntitySAO-only */ - // setvelocity(self, {x=num, y=num, z=num}) - static int l_setvelocity(lua_State *L); + // set_velocity(self, {x=num, y=num, z=num}) + static int l_set_velocity(lua_State *L); - // getvelocity(self) - static int l_getvelocity(lua_State *L); + // get_velocity(self) + static int l_get_velocity(lua_State *L); - // setacceleration(self, {x=num, y=num, z=num}) - static int l_setacceleration(lua_State *L); + // set_acceleration(self, {x=num, y=num, z=num}) + static int l_set_acceleration(lua_State *L); - // getacceleration(self) - static int l_getacceleration(lua_State *L); + // get_acceleration(self) + static int l_get_acceleration(lua_State *L); - // setyaw(self, radians) - static int l_setyaw(lua_State *L); + // set_yaw(self, radians) + static int l_set_yaw(lua_State *L); - // getyaw(self) - static int l_getyaw(lua_State *L); + // get_yaw(self) + static int l_get_yaw(lua_State *L); - // settexturemod(self, mod) - static int l_settexturemod(lua_State *L); + // set_texture_mod(self, mod) + static int l_set_texture_mod(lua_State *L); - // setsprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, + // set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, // select_horiz_by_yawpitch=false) - static int l_setsprite(lua_State *L); + static int l_set_sprite(lua_State *L); // DEPRECATED // get_entity_name(self) -- cgit v1.2.3 From d2f5732f89cd58dafc6a4f398b8ebfd122754852 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Mon, 16 Jan 2017 15:15:43 +0000 Subject: Adjust formspec spacing on the Client tab of the mainmenu --- builtin/mainmenu/tab_multiplayer.lua | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua index 3c2b2cdbe..f8edeaddd 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_multiplayer.lua @@ -32,27 +32,36 @@ local function get_formspec(tabview, name, tabdata) end local retval = - "label[7.75,-0.15;" .. fgettext("Address / Port") .. "]" .. - "label[7.75,1.05;" .. fgettext("Name / Password") .. "]" .. - "field[8,0.75;3.3,0.5;te_address;;" .. + -- Search + "field[0.15,0.35;6.05,0.27;te_search;;"..core.formspec_escape(tabdata.search_for).."]".. + "button[5.8,0.1;2,0.1;btn_mp_search;" .. fgettext("Search") .. "]" .. + + -- Address / Port + "label[7.75,-0.25;" .. fgettext("Address / Port") .. "]" .. + "field[8,0.65;3.25,0.5;te_address;;" .. core.formspec_escape(core.setting_get("address")) .. "]" .. - "field[11.15,0.75;1.4,0.5;te_port;;" .. + "field[11.1,0.65;1.4,0.5;te_port;;" .. core.formspec_escape(core.setting_get("remote_port")) .. "]" .. - "button[10.1,4.9;2,0.5;btn_mp_connect;" .. fgettext("Connect") .. "]" .. - "field[8,1.95;2.95,0.5;te_name;;" .. + + -- Name / Password + "label[7.75,0.95;" .. fgettext("Name / Password") .. "]" .. + "field[8,1.85;2.9,0.5;te_name;;" .. core.formspec_escape(core.setting_get("name")) .. "]" .. - "pwdfield[10.78,1.95;1.77,0.5;te_pwd;]" .. - "box[7.73,2.35;4.3,2.28;#999999]".. - "field[0.15,0.25;4.5,0.27;te_search;;"..core.formspec_escape(tabdata.search_for).."]".. - "button[4.8,0;2.7,0.1;btn_mp_search;" .. fgettext("Search") .. "]" + "pwdfield[10.73,1.85;1.77,0.5;te_pwd;]" .. + + -- Description Background + "box[7.73,2.25;4.25,2.6;#999999]".. + + -- Connect + "button[10.1,5.15;2,0.5;btn_mp_connect;" .. fgettext("Connect") .. "]" if tabdata.fav_selected and fav_selected then if gamedata.fav then - retval = retval .. "button[7.85,4.9;2.3,0.5;btn_delete_favorite;" .. + retval = retval .. "button[7.75,5.15;2.3,0.5;btn_delete_favorite;" .. fgettext("Del. Favorite") .. "]" end if fav_selected.description then - retval = retval .. "textarea[8.1,2.4;4.26,2.6;;" .. + retval = retval .. "textarea[8.1,2.3;4.23,2.9;;" .. core.formspec_escape((gamedata.serverdescription or ""), true) .. ";]" end end @@ -69,7 +78,7 @@ local function get_formspec(tabview, name, tabdata) image_column(fgettext("PvP enabled"), "pvp") .. ",padding=0.25;" .. "color,span=1;" .. "text,padding=1]" .. - "table[-0.15,0.4;7.75,5.35;favourites;" + "table[-0.15,0.6;7.75,5.15;favourites;" if menudata.search_result then for i = 1, #menudata.search_result do -- cgit v1.2.3 From 2f56a00d9eef82052614e5854a07b39b087efd0b Mon Sep 17 00:00:00 2001 From: red-001 Date: Mon, 16 Jan 2017 23:09:47 +0000 Subject: Remove client-side chat prediction. (#5055) Network lag isn't really a big issue with chat and chat prediction makes writing mods harder. --- builtin/game/features.lua | 1 + src/client.cpp | 11 +++++++---- src/network/networkprotocol.h | 1 + src/server.cpp | 8 ++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 646b254ea..ef85fbbc3 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -10,6 +10,7 @@ core.features = { texture_names_parens = true, area_store_custom_ids = true, add_entity_with_staticdata = true, + no_chat_message_prediction = true, } function core.has_feature(arg) diff --git a/src/client.cpp b/src/client.cpp index c2471dbd7..30058a2b0 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1563,10 +1563,13 @@ void Client::typeChatMessage(const std::wstring &message) } else { - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - std::wstring name = narrow_to_wide(player->getName()); - m_chat_queue.push((std::wstring)L"<" + name + L"> " + message); + // compatibility code + if (m_proto_ver < 29) { + LocalPlayer *player = m_env.getLocalPlayer(); + assert(player != NULL); + std::wstring name = narrow_to_wide(player->getName()); + m_chat_queue.push((std::wstring)L"<" + name + L"> " + message); + } } } diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 45bf76ff8..23c8a665b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -142,6 +142,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Server doesn't accept TOSERVER_BREATH anymore serialization of TileAnimation params changed TAT_SHEET_2D + Removed client-sided chat perdiction */ #define LATEST_PROTOCOL_VERSION 29 diff --git a/src/server.cpp b/src/server.cpp index 29dce5a4a..74d9541c9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2826,7 +2826,15 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna std::vector clients = m_clients.getClientIDs(); + /* + Send the message back to the inital sender + if they are using protocol version >= 29 + */ + u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT); + if (player->protocol_version >= 29) + peer_id_to_avoid_sending = PEER_ID_INEXISTENT; + for (u16 i = 0; i < clients.size(); i++) { u16 cid = clients[i]; if (cid != peer_id_to_avoid_sending) -- cgit v1.2.3 From d218baa3ace1f40813a346543feeac2aa71c480c Mon Sep 17 00:00:00 2001 From: Ezhh Date: Tue, 17 Jan 2017 14:41:25 +0000 Subject: Improve priv descriptions (#5047) --- builtin/game/chatcommands.lua | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index eb6edc1c8..199b9e964 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -71,7 +71,8 @@ end -- core.register_chatcommand("me", { params = "", - description = "chat action (eg. /me orders a pizza)", + description = "Display chat action (e.g., '/me orders a pizza' displays" + .. " ' orders a pizza')", privs = {shout=true}, func = function(name, param) core.chat_send_all("* " .. name .. " " .. param) @@ -147,7 +148,7 @@ core.register_chatcommand("help", { core.register_chatcommand("privs", { params = "", - description = "print out privileges of player", + description = "Print privileges of player", func = function(caller, param) param = param:trim() local name = (param ~= "" and param or caller) @@ -270,7 +271,7 @@ core.register_chatcommand("revoke", { core.register_chatcommand("setpassword", { params = " ", - description = "set given password", + description = "Set player's password", privs = {password=true}, func = function(name, param) local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$") @@ -308,7 +309,7 @@ core.register_chatcommand("setpassword", { core.register_chatcommand("clearpassword", { params = "", - description = "set empty password", + description = "Set empty password", privs = {password=true}, func = function(name, param) local toname = param @@ -325,7 +326,7 @@ core.register_chatcommand("clearpassword", { core.register_chatcommand("auth_reload", { params = "", - description = "reload authentication data", + description = "Reload authentication data", privs = {server=true}, func = function(name, param) local done = core.auth_reload() @@ -335,7 +336,7 @@ core.register_chatcommand("auth_reload", { core.register_chatcommand("teleport", { params = ",, | | ,, | ", - description = "teleport to given position", + description = "Teleport to player or position", privs = {teleport=true}, func = function(name, param) -- Returns (pos, true) if found, otherwise (pos, false) @@ -443,7 +444,7 @@ core.register_chatcommand("teleport", { core.register_chatcommand("set", { params = "[-n] | ", - description = "set or read server configuration setting", + description = "Set or read server configuration setting", privs = {server=true}, func = function(name, param) local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)") @@ -498,7 +499,7 @@ end core.register_chatcommand("emergeblocks", { params = "(here [radius]) | ( )", - description = "starts loading (or generating, if inexistent) map blocks " + description = "Load (or, if nonexistent, generate) map blocks " .. "contained in area pos1 to pos2", privs = {server=true}, func = function(name, param) @@ -524,7 +525,7 @@ core.register_chatcommand("emergeblocks", { core.register_chatcommand("deleteblocks", { params = "(here [radius]) | ( )", - description = "delete map blocks contained in area pos1 to pos2", + description = "Delete map blocks contained in area pos1 to pos2", privs = {server=true}, func = function(name, param) local p1, p2 = parse_range_str(name, param) @@ -588,7 +589,7 @@ end core.register_chatcommand("give", { params = " ", - description = "give item to player", + description = "Give item to player", privs = {give=true}, func = function(name, param) local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$") @@ -601,7 +602,7 @@ core.register_chatcommand("give", { core.register_chatcommand("giveme", { params = "", - description = "give item to yourself", + description = "Give item to yourself", privs = {give=true}, func = function(name, param) local itemstring = string.match(param, "(.+)$") @@ -672,9 +673,9 @@ end) core.register_chatcommand("rollback_check", { params = "[] [] [limit]", - description = "Check who has last touched a node or near it," - .. " max. ago (default range=0," - .. " seconds=86400=24h, limit=5)", + description = "Check who last touched a node or a node near it" + .. " within the time specified by . Default: range = 0," + .. " seconds = 86400 = 24h, limit = 5", privs = {rollback=true}, func = function(name, param) if not core.setting_getbool("enable_rollback_recording") then @@ -725,7 +726,7 @@ core.register_chatcommand("rollback_check", { core.register_chatcommand("rollback", { params = " [] | : []", - description = "revert actions of a player; default for is 60", + description = "Revert actions of a player. Default for is 60", privs = {rollback=true}, func = function(name, param) if not core.setting_getbool("enable_rollback_recording") then @@ -770,7 +771,7 @@ core.register_chatcommand("status", { core.register_chatcommand("time", { params = "<0..23>:<0..59> | <0..24000>", - description = "set time of day", + description = "Set time of day", privs = {}, func = function(name, param) if param == "" then @@ -816,7 +817,7 @@ core.register_chatcommand("days", { }) core.register_chatcommand("shutdown", { - description = "shutdown server", + description = "Shutdown server", privs = {server=true}, func = function(name, param) core.log("action", name .. " shuts down server") @@ -847,7 +848,7 @@ core.register_chatcommand("ban", { core.register_chatcommand("unban", { params = "", - description = "remove IP ban", + description = "Remove IP ban", privs = {ban=true}, func = function(name, param) if not core.unban_player_or_ip(param) then @@ -860,7 +861,7 @@ core.register_chatcommand("unban", { core.register_chatcommand("kick", { params = " [reason]", - description = "kick a player", + description = "Kick a player", privs = {kick=true}, func = function(name, param) local tokick, reason = param:match("([^ ]+) (.+)") @@ -879,7 +880,7 @@ core.register_chatcommand("kick", { core.register_chatcommand("clearobjects", { params = "[full|quick]", - description = "clear all objects in world", + description = "Clear all objects in world", privs = {server=true}, func = function(name, param) local options = {} -- cgit v1.2.3 From 51746ca910f8bce0837a72110e47ec7de76d1c19 Mon Sep 17 00:00:00 2001 From: sapier Date: Tue, 17 Jan 2017 19:41:52 +0100 Subject: Fix typo in alias for deprecated settexturemod --- src/script/lua_api/l_object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index dd29208c5..59c04f301 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1806,7 +1806,7 @@ const luaL_reg ObjectRef::methods[] = { luamethod_aliased(ObjectRef, get_acceleration, getacceleration), luamethod_aliased(ObjectRef, set_yaw, setyaw), luamethod_aliased(ObjectRef, get_yaw, getyaw), - luamethod_aliased(ObjectRef, set_texture_mod, set_texturemod), + luamethod_aliased(ObjectRef, set_texture_mod, settexturemod), luamethod_aliased(ObjectRef, set_sprite, setsprite), luamethod(ObjectRef, get_entity_name), luamethod(ObjectRef, get_luaentity), -- cgit v1.2.3 From a378e32751127e2ae3708a33f035d1dcf5d400a1 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 18 Jan 2017 06:48:25 +0000 Subject: Add search to advanced settings (#4806) * Add search to advanced settings * Press enter again to go to next result * Use keyword based search, auto select best option --- builtin/mainmenu/dlg_settings_advanced.lua | 120 +++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 6 deletions(-) diff --git a/builtin/mainmenu/dlg_settings_advanced.lua b/builtin/mainmenu/dlg_settings_advanced.lua index 60ec1250f..697babeb6 100644 --- a/builtin/mainmenu/dlg_settings_advanced.lua +++ b/builtin/mainmenu/dlg_settings_advanced.lua @@ -344,7 +344,82 @@ local function parse_config_file(read_all, parse_mods) return settings end -local settings = parse_config_file(false, true) +local function filter_settings(settings, searchstring) + if not searchstring or searchstring == "" then + return settings, -1 + end + + -- Setup the keyword list + local keywords = {} + for word in searchstring:lower():gmatch("%S+") do + table.insert(keywords, word) + end + + local result = {} + local category_stack = {} + local current_level = 0 + local best_setting = nil + for _, entry in pairs(settings) do + if entry.type == "category" then + -- Remove all settingless categories + while #category_stack > 0 and entry.level <= current_level do + table.remove(category_stack, #category_stack) + if #category_stack > 0 then + current_level = category_stack[#category_stack].level + else + current_level = 0 + end + end + + -- Push category onto stack + category_stack[#category_stack + 1] = entry + current_level = entry.level + else + -- See if setting matches keywords + local setting_score = 0 + for k = 1, #keywords do + local keyword = keywords[k] + + if string.find(entry.name:lower(), keyword, 1, true) then + setting_score = setting_score + 1 + end + + if entry.readable_name and + string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then + setting_score = setting_score + 1 + end + + if entry.comment and + string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then + setting_score = setting_score + 1 + end + end + + -- Add setting to results if match + if setting_score > 0 then + -- Add parent categories + for _, category in pairs(category_stack) do + result[#result + 1] = category + end + category_stack = {} + + -- Add setting + result[#result + 1] = entry + entry.score = setting_score + + if not best_setting or + setting_score > result[best_setting].score then + best_setting = #result + end + end + end + end + return result, best_setting or -1 +end + +local full_settings = parse_config_file(false, true) +local search_string = "" +local settings = full_settings local selected_setting = 1 local function get_current_value(setting) @@ -546,7 +621,10 @@ local function create_settings_formspec(tabview, name, tabdata) local formspec = "size[12,6.5;true]" .. "tablecolumns[color;tree;text,width=32;text]" .. "tableoptions[background=#00000000;border=false]" .. - "table[0,0;12,5.5;list_settings;" + "field[0.3,0.1;10.2,1;search_string;;" .. core.formspec_escape(search_string) .. "]" .. + "field_close_on_enter[search_string;false]" .. + "button[10.2,-0.2;2,1;search;" .. fgettext("Search") .. "]" .. + "table[0,0.8;12,4.5;list_settings;" local current_level = 0 for _, entry in ipairs(settings) do @@ -597,10 +675,10 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) local list_enter = false if fields["list_settings"] then selected_setting = core.get_table_index("list_settings") - if core.explode_table_event(fields["list_settings"]).type == "DCL" then + if core.explode_table_event(fields["list_settings"]).type == "DCL" then -- Directly toggle booleans local setting = settings[selected_setting] - if setting.type == "bool" then + if setting and setting.type == "bool" then local current_value = get_current_value(setting) core.setting_setbool(setting.name, not core.is_yes(current_value)) core.setting_save() @@ -613,9 +691,39 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) end end + if fields.search or fields.key_enter_field == "search_string" then + if search_string == fields.search_string then + if selected_setting > 0 then + -- Go to next result on enter press + local i = selected_setting + 1 + local looped = false + while i > #settings or settings[i].type == "category" do + i = i + 1 + if i > #settings then + -- Stop infinte looping + if looped then + return false + end + i = 1 + looped = true + end + end + selected_setting = i + core.update_formspec(this:get_formspec()) + return true + end + else + -- Search for setting + search_string = fields.search_string + settings, selected_setting = filter_settings(full_settings, search_string) + core.update_formspec(this:get_formspec()) + end + return true + end + if fields["btn_edit"] or list_enter then local setting = settings[selected_setting] - if setting.type ~= "category" then + if setting and setting.type ~= "category" then local edit_dialog = dialog_create("change_setting", create_change_setting_formspec, handle_change_setting_buttons) edit_dialog:set_parent(this) @@ -627,7 +735,7 @@ local function handle_settings_buttons(this, fields, tabname, tabdata) if fields["btn_restore"] then local setting = settings[selected_setting] - if setting.type ~= "category" then + if setting and setting.type ~= "category" then core.setting_set(setting.name, setting.default) core.setting_save() core.update_formspec(this:get_formspec()) -- cgit v1.2.3 From c5967f75f0a9827d1b65b384edd6ba07c73ffd2f Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 18 Jan 2017 10:19:57 +0000 Subject: Add minetest.player_exists() (#5064) --- builtin/game/misc.lua | 13 ++++++++----- doc/lua_api.txt | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 7caa9e7ba..3419c1980 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -56,11 +56,11 @@ function core.check_player_privs(name, ...) elseif arg_type ~= "string" then error("Invalid core.check_player_privs argument type: " .. arg_type, 2) end - + local requested_privs = {...} local player_privs = core.get_player_privs(name) local missing_privileges = {} - + if type(requested_privs[1]) == "table" then -- We were provided with a table like { privA = true, privB = true }. for priv, value in pairs(requested_privs[1]) do @@ -76,11 +76,11 @@ function core.check_player_privs(name, ...) end end end - + if #missing_privileges > 0 then return false, missing_privileges end - + return true, "" end @@ -114,6 +114,10 @@ function core.get_connected_players() return temp_table end +function minetest.player_exists(name) + return minetest.get_auth_handler().get_auth(name) ~= nil +end + -- Returns two position vectors representing a box of `radius` in each -- direction centered around the player corresponding to `player_name` function core.get_player_radius_area(player_name, radius) @@ -244,4 +248,3 @@ end function core.close_formspec(player_name, formname) return minetest.show_formspec(player_name, formname, "") end - diff --git a/doc/lua_api.txt b/doc/lua_api.txt index da6a898d9..c96131455 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2583,6 +2583,7 @@ These functions return the leftover itemstack. ### Misc. * `minetest.get_connected_players()`: returns list of `ObjectRefs` +* `minetest.player_exists(name)`: boolean, whether player exists (regardless of online status) * `minetest.hud_replace_builtin(name, hud_definition)` * Replaces definition of a builtin hud element * `name`: `"breath"` or `"health"` -- cgit v1.2.3 From 7279f0b37335396c85f6bdd7dc67ff56e53df0f9 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 14 Jan 2017 16:48:49 +0100 Subject: Add particle animation, glow This is implemented by reusing and extending the TileAnimation code for the methods used by particles. --- doc/lua_api.txt | 7 ++- games/minimal/mods/experimental/init.lua | 37 ++++++++++++++ src/client.h | 5 ++ src/network/clientpackethandler.cpp | 18 +++++++ src/network/networkpacket.cpp | 2 +- src/network/networkpacket.h | 5 +- src/nodedef.cpp | 2 +- src/particles.cpp | 74 ++++++++++++++++++++++----- src/particles.h | 12 ++++- src/script/common/c_content.cpp | 60 +++++++++++++--------- src/script/common/c_content.h | 1 + src/script/lua_api/l_particles.cpp | 27 +++++++++- src/server.cpp | 85 +++++++++++++++++++++++--------- src/server.h | 18 ++++--- src/tileanimation.cpp | 32 ++++++++++-- src/tileanimation.h | 4 +- 16 files changed, 311 insertions(+), 78 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c96131455..9bdc01c07 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -4180,10 +4180,15 @@ The Biome API is still in an experimental phase and subject to change. -- ^ vertical: if true faces player using y axis only texture = "image.png", -- ^ Uses texture (string) - playername = "singleplayer" + playername = "singleplayer", -- ^ optional, if specified spawns particle only on the player's client + animation = {Tile Animation definition}, + -- ^ optional, specifies how to animate the particle texture + glow = 0 + -- ^ optional, specify particle self-luminescence in darkness } + ### `ParticleSpawner` definition (`add_particlespawner`) { diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 142734cda..5e98e1a80 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -523,6 +523,43 @@ minetest.register_craft({ } }) +minetest.register_craftitem("experimental:tester_tool_2", { + description = "Tester Tool 2", + inventory_image = "experimental_tester_tool_1.png^[invert:g", + on_use = function(itemstack, user, pointed_thing) + local pos = minetest.get_pointed_thing_position(pointed_thing, true) + if pos == nil then return end + pos = vector.add(pos, {x=0, y=0.5, z=0}) + local tex, anim + if math.random(0, 1) == 0 then + tex = "default_lava_source_animated.png" + anim = {type="sheet_2d", frames_w=3, frames_h=2, frame_length=0.5} + else + tex = "default_lava_flowing_animated.png" + anim = {type="vertical_frames", aspect_w=16, aspect_h=16, length=3.3} + end + + minetest.add_particle({ + pos = pos, + velocity = {x=0, y=0, z=0}, + acceleration = {x=0, y=0.04, z=0}, + expirationtime = 6, + collisiondetection = true, + texture = tex, + animation = anim, + size = 4, + glow = math.random(0, 5), + }) + end, +}) + +minetest.register_craft({ + output = 'experimental:tester_tool_2', + recipe = { + {'group:crumbly','group:crumbly'}, + } +}) + --[[minetest.register_on_joinplayer(function(player) minetest.after(3, function() player:set_inventory_formspec("size[8,7.5]".. diff --git a/src/client.h b/src/client.h index f84246deb..b33358d94 100644 --- a/src/client.h +++ b/src/client.h @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "particles.h" #include "mapnode.h" +#include "tileanimation.h" struct MeshMakeData; class MapBlockMesh; @@ -186,6 +187,8 @@ struct ClientEvent bool collision_removal; bool vertical; std::string *texture; + struct TileAnimationParams animation; + u8 glow; } spawn_particle; struct{ u16 amount; @@ -206,6 +209,8 @@ struct ClientEvent bool vertical; std::string *texture; u32 id; + struct TileAnimationParams animation; + u8 glow; } add_particlespawner; struct{ u32 id; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 411982f69..b11f73e86 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/clientopcodes.h" #include "util/serialize.h" #include "util/srp.h" +#include "tileanimation.h" void Client::handleCommand_Deprecated(NetworkPacket* pkt) { @@ -896,9 +897,14 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) std::string texture = deSerializeLongString(is); bool vertical = false; bool collision_removal = false; + struct TileAnimationParams animation; + animation.type = TAT_NONE; + u8 glow = 0; try { vertical = readU8(is); collision_removal = readU8(is); + animation.deSerialize(is, m_proto_ver); + glow = readU8(is); } catch (...) {} ClientEvent event; @@ -912,6 +918,8 @@ void Client::handleCommand_SpawnParticle(NetworkPacket* pkt) event.spawn_particle.collision_removal = collision_removal; event.spawn_particle.vertical = vertical; event.spawn_particle.texture = new std::string(texture); + event.spawn_particle.animation = animation; + event.spawn_particle.glow = glow; m_client_event_queue.push(event); } @@ -943,12 +951,20 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) bool vertical = false; bool collision_removal = false; + struct TileAnimationParams animation; + animation.type = TAT_NONE; + u8 glow = 0; u16 attached_id = 0; try { *pkt >> vertical; *pkt >> collision_removal; *pkt >> attached_id; + // This is horrible but required (why are there two ways to deserialize pkts?) + std::string datastring(pkt->getRemainingString(), pkt->getRemainingBytes()); + std::istringstream is(datastring, std::ios_base::binary); + animation.deSerialize(is, m_proto_ver); + glow = readU8(is); } catch (...) {} ClientEvent event; @@ -971,6 +987,8 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) event.add_particlespawner.vertical = vertical; event.add_particlespawner.texture = new std::string(texture); event.add_particlespawner.id = id; + event.add_particlespawner.animation = animation; + event.add_particlespawner.glow = glow; m_client_event_queue.push(event); } diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index 388afc18e..91e6c58e2 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -63,7 +63,7 @@ void NetworkPacket::putRawPacket(u8 *data, u32 datasize, u16 peer_id) m_data = std::vector(&data[2], &data[2 + m_datasize]); } -char* NetworkPacket::getString(u32 from_offset) +const char* NetworkPacket::getString(u32 from_offset) { checkReadOffset(from_offset, 0); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index 524470999..3e436aba9 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -41,12 +41,15 @@ public: u16 getPeerId() { return m_peer_id; } u16 getCommand() { return m_command; } const u32 getRemainingBytes() const { return m_datasize - m_read_offset; } + const char* getRemainingString() { return getString(m_read_offset); } // Returns a c-string without copying. // A better name for this would be getRawString() - char* getString(u32 from_offset); + const char* getString(u32 from_offset); // major difference to putCString(): doesn't write len into the buffer void putRawString(const char* src, u32 len); + void putRawString(const std::string &src) + { putRawString(src.c_str(), src.size()); } NetworkPacket& operator>>(std::string& dst); NetworkPacket& operator<<(std::string src); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index b7d023897..a4af26e87 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -541,7 +541,7 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { int frame_length_ms; tiledef->animation.determineParams(tile->texture->getOriginalSize(), - &frame_count, &frame_length_ms); + &frame_count, &frame_length_ms, NULL); tile->animation_frame_count = frame_count; tile->animation_frame_length_ms = frame_length_ms; } diff --git a/src/particles.cpp b/src/particles.cpp index d9eb3cfa5..5f17763e0 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -54,7 +54,9 @@ Particle::Particle( bool vertical, video::ITexture *texture, v2f texpos, - v2f texsize + v2f texsize, + const struct TileAnimationParams &anim, + u8 glow ): scene::ISceneNode(smgr->getRootSceneNode(), smgr) { @@ -71,7 +73,9 @@ Particle::Particle( m_material.setTexture(0, texture); m_texpos = texpos; m_texsize = texsize; - + m_animation = anim; + m_animation_frame = 0; + m_animation_time = 0.0; // Particle related m_pos = pos; @@ -84,6 +88,7 @@ Particle::Particle( m_collisiondetection = collisiondetection; m_collision_removal = collision_removal; m_vertical = vertical; + m_glow = glow; // Irrlicht stuff m_collisionbox = aabb3f @@ -142,6 +147,18 @@ void Particle::step(float dtime) m_velocity += m_acceleration * dtime; m_pos += m_velocity * dtime; } + if (m_animation.type != TAT_NONE) { + m_animation_time += dtime; + int frame_length_i, frame_count; + m_animation.determineParams( + m_material.getTexture(0)->getSize(), + &frame_count, &frame_length_i, NULL); + float frame_length = frame_length_i / 1000.0; + while (m_animation_time > frame_length) { + m_animation_frame++; + m_animation_time -= frame_length; + } + } // Update lighting updateLight(); @@ -166,16 +183,32 @@ void Particle::updateLight() else light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); - m_light = decode_light(light); + m_light = decode_light(light + m_glow); } void Particle::updateVertices() { video::SColor c(255, m_light, m_light, m_light); - f32 tx0 = m_texpos.X; - f32 tx1 = m_texpos.X + m_texsize.X; - f32 ty0 = m_texpos.Y; - f32 ty1 = m_texpos.Y + m_texsize.Y; + f32 tx0, tx1, ty0, ty1; + + if (m_animation.type != TAT_NONE) { + const v2u32 texsize = m_material.getTexture(0)->getSize(); + v2f texcoord, framesize_f; + v2u32 framesize; + texcoord = m_animation.getTextureCoords(texsize, m_animation_frame); + m_animation.determineParams(texsize, NULL, NULL, &framesize); + framesize_f = v2f(framesize.X / (float) texsize.X, framesize.Y / (float) texsize.Y); + + tx0 = m_texpos.X + texcoord.X; + tx1 = m_texpos.X + texcoord.X + framesize_f.X * m_texsize.X; + ty0 = m_texpos.Y + texcoord.Y; + ty1 = m_texpos.Y + texcoord.Y + framesize_f.Y * m_texsize.Y; + } else { + tx0 = m_texpos.X; + tx1 = m_texpos.X + m_texsize.X; + ty0 = m_texpos.Y; + ty1 = m_texpos.Y + m_texsize.Y; + } m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, c, tx0, ty1); @@ -210,7 +243,9 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, u16 attached_id, bool vertical, - video::ITexture *texture, u32 id, ParticleManager *p_manager) : + video::ITexture *texture, u32 id, const struct TileAnimationParams &anim, + u8 glow, + ParticleManager *p_manager) : m_particlemanager(p_manager) { m_gamedef = gamedef; @@ -234,6 +269,8 @@ ParticleSpawner::ParticleSpawner(IGameDef* gamedef, scene::ISceneManager *smgr, m_vertical = vertical; m_texture = texture; m_time = 0; + m_animation = anim; + m_glow = glow; for (u16 i = 0; i<=m_amount; i++) { @@ -309,7 +346,9 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) m_vertical, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0)); + v2f(1.0, 1.0), + m_animation, + m_glow); m_particlemanager->addParticle(toadd); } i = m_spawntimes.erase(i); @@ -363,7 +402,9 @@ void ParticleSpawner::step(float dtime, ClientEnvironment* env) m_vertical, m_texture, v2f(0.0, 0.0), - v2f(1.0, 1.0)); + v2f(1.0, 1.0), + m_animation, + m_glow); m_particlemanager->addParticle(toadd); } } @@ -494,6 +535,8 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, event->add_particlespawner.vertical, texture, event->add_particlespawner.id, + event->add_particlespawner.animation, + event->add_particlespawner.glow, this); /* delete allocated content of event */ @@ -529,13 +572,16 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, event->spawn_particle.vertical, texture, v2f(0.0, 0.0), - v2f(1.0, 1.0)); + v2f(1.0, 1.0), + event->spawn_particle.animation, + event->spawn_particle.glow); addParticle(toadd); delete event->spawn_particle.pos; delete event->spawn_particle.vel; delete event->spawn_particle.acc; + delete event->spawn_particle.texture; break; } @@ -564,6 +610,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s // Texture u8 texid = myrand_range(0, 5); video::ITexture *texture; + struct TileAnimationParams anim; + anim.type = TAT_NONE; // Only use first frame of animated texture if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) @@ -605,7 +653,9 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s false, texture, texpos, - texsize); + texsize, + anim, + 0); addParticle(toadd); } diff --git a/src/particles.h b/src/particles.h index 00cb2c08e..5464e6672 100644 --- a/src/particles.h +++ b/src/particles.h @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/tile.h" #include "localplayer.h" #include "environment.h" +#include "tileanimation.h" struct ClientEvent; class ParticleManager; @@ -50,7 +51,9 @@ class Particle : public scene::ISceneNode bool vertical, video::ITexture *texture, v2f texpos, - v2f texsize + v2f texsize, + const struct TileAnimationParams &anim, + u8 glow ); ~Particle(); @@ -102,6 +105,10 @@ private: bool m_collision_removal; bool m_vertical; v3s16 m_camera_offset; + struct TileAnimationParams m_animation; + float m_animation_time; + int m_animation_frame; + u8 m_glow; }; class ParticleSpawner @@ -123,6 +130,7 @@ class ParticleSpawner bool vertical, video::ITexture *texture, u32 id, + const struct TileAnimationParams &anim, u8 glow, ParticleManager* p_manager); ~ParticleSpawner(); @@ -156,6 +164,8 @@ class ParticleSpawner bool m_collision_removal; bool m_vertical; u16 m_attached_id; + struct TileAnimationParams m_animation; + u8 m_glow; }; /** diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index b9bcfef69..84af4583b 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -322,7 +322,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) } else if(lua_istable(L, index)) { - // {name="default_lava.png", animation={}} + // name="default_lava.png" tiledef.name = ""; getstringfield(L, index, "name", tiledef.name); getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. @@ -334,28 +334,7 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) L, index, "tileable_vertical", default_tiling); // animation = {} lua_getfield(L, index, "animation"); - if(lua_istable(L, -1)){ - tiledef.animation.type = (TileAnimationType) - getenumfield(L, -1, "type", es_TileAnimationType, - TAT_NONE); - if (tiledef.animation.type == TAT_VERTICAL_FRAMES) { - // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} - tiledef.animation.vertical_frames.aspect_w = - getintfield_default(L, -1, "aspect_w", 16); - tiledef.animation.vertical_frames.aspect_h = - getintfield_default(L, -1, "aspect_h", 16); - tiledef.animation.vertical_frames.length = - getfloatfield_default(L, -1, "length", 1.0); - } else if (tiledef.animation.type == TAT_SHEET_2D) { - // {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5} - getintfield(L, -1, "frames_w", - tiledef.animation.sheet_2d.frames_w); - getintfield(L, -1, "frames_h", - tiledef.animation.sheet_2d.frames_h); - getfloatfield(L, -1, "frame_length", - tiledef.animation.sheet_2d.frame_length); - } - } + tiledef.animation = read_animation_definition(L, -1); lua_pop(L, 1); } @@ -925,6 +904,41 @@ void read_inventory_list(lua_State *L, int tableindex, } } +/******************************************************************************/ +struct TileAnimationParams read_animation_definition(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + struct TileAnimationParams anim; + anim.type = TAT_NONE; + if (!lua_istable(L, index)) + return anim; + + anim.type = (TileAnimationType) + getenumfield(L, index, "type", es_TileAnimationType, + TAT_NONE); + if (anim.type == TAT_VERTICAL_FRAMES) { + // {type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0} + anim.vertical_frames.aspect_w = + getintfield_default(L, index, "aspect_w", 16); + anim.vertical_frames.aspect_h = + getintfield_default(L, index, "aspect_h", 16); + anim.vertical_frames.length = + getfloatfield_default(L, index, "length", 1.0); + } else if (anim.type == TAT_SHEET_2D) { + // {type="sheet_2d", frames_w=5, frames_h=3, frame_length=0.5} + getintfield(L, index, "frames_w", + anim.sheet_2d.frames_w); + getintfield(L, index, "frames_h", + anim.sheet_2d.frames_h); + getfloatfield(L, index, "frame_length", + anim.sheet_2d.frame_length); + } + + return anim; +} + /******************************************************************************/ ToolCapabilities read_tool_capabilities( lua_State *L, int table) diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 2a2228b6d..9641f5c9e 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -79,6 +79,7 @@ void push_hit_params (lua_State *L, ItemStack read_item (lua_State *L, int index, Server *srv); +struct TileAnimationParams read_animation_definition(lua_State *L, int index); ToolCapabilities read_tool_capabilities (lua_State *L, int table); void push_tool_capabilities (lua_State *L, diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 667ac7272..7f415844a 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_object.h" #include "lua_api/l_internal.h" #include "common/c_converter.h" +#include "common/c_content.h" #include "server.h" #include "particles.h" @@ -34,6 +35,8 @@ with this program; if not, write to the Free Software Foundation, Inc., // collision_removal = bool // vertical = bool // texture = e.g."default_wood.png" +// animation = TileAnimation definition +// glow = num int ModApiParticles::l_add_particle(lua_State *L) { MAP_LOCK_REQUIRED; @@ -47,10 +50,13 @@ int ModApiParticles::l_add_particle(lua_State *L) bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; + struct TileAnimationParams animation; std::string texture = ""; std::string playername = ""; + u8 glow = 0; + if (lua_gettop(L) > 1) // deprecated { log_deprecated(L, "Deprecated add_particle call with individual parameters instead of definition"); @@ -101,11 +107,18 @@ int ModApiParticles::l_add_particle(lua_State *L) collision_removal = getboolfield_default(L, 1, "collision_removal", collision_removal); vertical = getboolfield_default(L, 1, "vertical", vertical); + + lua_getfield(L, 1, "animation"); + animation = read_animation_definition(L, -1); + lua_pop(L, 1); + texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); + + glow = getintfield_default(L, 1, "glow", 0); } getServer(L)->spawnParticle(playername, pos, vel, acc, expirationtime, size, - collisiondetection, collision_removal, vertical, texture); + collisiondetection, collision_removal, vertical, texture, animation, glow); return 1; } @@ -127,6 +140,8 @@ int ModApiParticles::l_add_particle(lua_State *L) // collision_removal = bool // vertical = bool // texture = e.g."default_wood.png" +// animation = TileAnimation definition +// glow = num int ModApiParticles::l_add_particlespawner(lua_State *L) { MAP_LOCK_REQUIRED; @@ -139,9 +154,11 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) time= minexptime= maxexptime= minsize= maxsize= 1; bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; + struct TileAnimationParams animation; ServerActiveObject *attached = NULL; std::string texture = ""; std::string playername = ""; + u8 glow = 0; if (lua_gettop(L) > 1) //deprecated { @@ -201,6 +218,10 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) collision_removal = getboolfield_default(L, 1, "collision_removal", collision_removal); + lua_getfield(L, 1, "animation"); + animation = read_animation_definition(L, -1); + lua_pop(L, 1); + lua_getfield(L, 1, "attached"); if (!lua_isnil(L, -1)) { ObjectRef *ref = ObjectRef::checkobject(L, -1); @@ -211,6 +232,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) vertical = getboolfield_default(L, 1, "vertical", vertical); texture = getstringfield_default(L, 1, "texture", ""); playername = getstringfield_default(L, 1, "playername", ""); + glow = getintfield_default(L, 1, "glow", 0); } u32 id = getServer(L)->addParticleSpawner(amount, time, @@ -223,7 +245,8 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) collision_removal, attached, vertical, - texture, playername); + texture, playername, + animation, glow); lua_pushnumber(L, id); return 1; diff --git a/src/server.cpp b/src/server.cpp index 74d9541c9..d3d5fd3d1 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1662,12 +1662,28 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec, } // Spawns a particle on peer with peer_id -void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, +void Server::SendSpawnParticle(u16 peer_id, u16 protocol_version, + v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture) + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + if (peer_id == PEER_ID_INEXISTENT) { + // This sucks and should be replaced by a better solution in a refactor: + std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + SendSpawnParticle(*i, player->protocol_version, + pos, velocity, acceleration, + expirationtime, size, collisiondetection, + collision_removal, vertical, texture, animation, glow); + } + return; + } NetworkPacket pkt(TOCLIENT_SPAWN_PARTICLE, 0, peer_id); @@ -1676,22 +1692,39 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat pkt.putLongString(texture); pkt << vertical; pkt << collision_removal; + // This is horrible but required (why are there two ways to serialize pkts?) + std::ostringstream os(std::ios_base::binary); + animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + pkt << glow; - if (peer_id != PEER_ID_INEXISTENT) { - Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); - } + Send(&pkt); } // Adds a ParticleSpawner on peer with peer_id -void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, +void Server::SendAddParticleSpawner(u16 peer_id, u16 protocol_version, + u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, - u16 attached_id, bool vertical, const std::string &texture, u32 id) + u16 attached_id, bool vertical, const std::string &texture, u32 id, + const struct TileAnimationParams &animation, u8 glow) { DSTACK(FUNCTION_NAME); + if (peer_id == PEER_ID_INEXISTENT) { + // This sucks and should be replaced: + std::vector clients = m_clients.getClientIDs(); + for (std::vector::iterator i = clients.begin(); i != clients.end(); ++i) { + RemotePlayer *player = m_env->getPlayer(*i); + if (!player) + continue; + SendAddParticleSpawner(*i, player->protocol_version, + amount, spawntime, minpos, maxpos, + minvel, maxvel, minacc, maxacc, minexptime, maxexptime, + minsize, maxsize, collisiondetection, collision_removal, + attached_id, vertical, texture, id, animation, glow); + } + return; + } NetworkPacket pkt(TOCLIENT_ADD_PARTICLESPAWNER, 0, peer_id); @@ -1704,13 +1737,13 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 pkt << id << vertical; pkt << collision_removal; pkt << attached_id; + // This is horrible but required + std::ostringstream os(std::ios_base::binary); + animation.serialize(os, protocol_version); + pkt.putRawString(os.str()); + pkt << glow; - if (peer_id != PEER_ID_INEXISTENT) { - Send(&pkt); - } - else { - m_clients.sendToAll(0, &pkt, true); - } + Send(&pkt); } void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) @@ -3165,23 +3198,25 @@ void Server::spawnParticle(const std::string &playername, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture) + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow) { // m_env will be NULL if the server is initializing if (!m_env) return; - u16 peer_id = PEER_ID_INEXISTENT; + u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0; if (playername != "") { RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return; peer_id = player->peer_id; + proto_ver = player->protocol_version; } - SendSpawnParticle(peer_id, pos, velocity, acceleration, + SendSpawnParticle(peer_id, proto_ver, pos, velocity, acceleration, expirationtime, size, collisiondetection, - collision_removal, vertical, texture); + collision_removal, vertical, texture, animation, glow); } u32 Server::addParticleSpawner(u16 amount, float spawntime, @@ -3189,18 +3224,20 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, float minexptime, float maxexptime, float minsize, float maxsize, bool collisiondetection, bool collision_removal, ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername) + const std::string &playername, const struct TileAnimationParams &animation, + u8 glow) { // m_env will be NULL if the server is initializing if (!m_env) return -1; - u16 peer_id = PEER_ID_INEXISTENT; + u16 peer_id = PEER_ID_INEXISTENT, proto_ver = 0; if (playername != "") { RemotePlayer *player = m_env->getPlayer(playername.c_str()); if (!player) return -1; peer_id = player->peer_id; + proto_ver = player->protocol_version; } u16 attached_id = attached ? attached->getId() : 0; @@ -3211,11 +3248,11 @@ u32 Server::addParticleSpawner(u16 amount, float spawntime, else id = m_env->addParticleSpawner(spawntime, attached_id); - SendAddParticleSpawner(peer_id, amount, spawntime, + SendAddParticleSpawner(peer_id, proto_ver, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, collisiondetection, collision_removal, attached_id, vertical, - texture, id); + texture, id, animation, glow); return id; } diff --git a/src/server.h b/src/server.h index a86f75f1d..e5121bdc3 100644 --- a/src/server.h +++ b/src/server.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mods.h" #include "inventorymanager.h" #include "subgame.h" +#include "tileanimation.h" // struct TileAnimationParams #include "util/numeric.h" #include "util/thread.h" #include "util/basic_macros.h" @@ -252,7 +253,8 @@ public: v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture); + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow); u32 addParticleSpawner(u16 amount, float spawntime, v3f minpos, v3f maxpos, @@ -263,7 +265,8 @@ public: bool collisiondetection, bool collision_removal, ServerActiveObject *attached, bool vertical, const std::string &texture, - const std::string &playername); + const std::string &playername, const struct TileAnimationParams &animation, + u8 glow); void deleteParticleSpawner(const std::string &playername, u32 id); @@ -428,7 +431,8 @@ private: void sendDetachedInventories(u16 peer_id); // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) - void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, + void SendAddParticleSpawner(u16 peer_id, u16 protocol_version, + u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, @@ -436,16 +440,18 @@ private: float minsize, float maxsize, bool collisiondetection, bool collision_removal, u16 attached_id, - bool vertical, const std::string &texture, u32 id); + bool vertical, const std::string &texture, u32 id, + const struct TileAnimationParams &animation, u8 glow); void SendDeleteParticleSpawner(u16 peer_id, u32 id); // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) - void SendSpawnParticle(u16 peer_id, + void SendSpawnParticle(u16 peer_id, u16 protocol_version, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool collision_removal, - bool vertical, const std::string &texture); + bool vertical, const std::string &texture, + const struct TileAnimationParams &animation, u8 glow); u32 SendActiveObjectRemoveAdd(u16 peer_id, const std::string &datas); void SendActiveObjectMessages(u16 peer_id, const std::string &datas, bool reliable = true); diff --git a/src/tileanimation.cpp b/src/tileanimation.cpp index a23eecc2e..67d27d396 100644 --- a/src/tileanimation.cpp +++ b/src/tileanimation.cpp @@ -69,7 +69,8 @@ void TileAnimationParams::deSerialize(std::istream &is, u16 protocol_version) } } -void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const +void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, + int *frame_length_ms, v2u32 *frame_size) const { if (type == TAT_VERTICAL_FRAMES) { int frame_height = (float)texture_size.X / @@ -80,15 +81,17 @@ void TileAnimationParams::determineParams(v2u32 texture_size, int *frame_count, *frame_count = _frame_count; if (frame_length_ms) *frame_length_ms = 1000.0 * vertical_frames.length / _frame_count; + if (frame_size) + *frame_size = v2u32(texture_size.X, frame_height); } else if (type == TAT_SHEET_2D) { if (frame_count) *frame_count = sheet_2d.frames_w * sheet_2d.frames_h; if (frame_length_ms) *frame_length_ms = 1000 * sheet_2d.frame_length; - } else { // TAT_NONE - *frame_count = 1; - *frame_length_ms = 1000; + if (frame_size) + *frame_size = v2u32(texture_size.X / sheet_2d.frames_w, texture_size.Y / sheet_2d.frames_h); } + // caller should check for TAT_NONE } void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const @@ -97,7 +100,7 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size return; if (type == TAT_VERTICAL_FRAMES) { int frame_count; - determineParams(texture_size, &frame_count, NULL); + determineParams(texture_size, &frame_count, NULL, NULL); os << "^[verticalframe:" << frame_count << ":" << frame; } else if (type == TAT_SHEET_2D) { int q, r; @@ -107,3 +110,22 @@ void TileAnimationParams::getTextureModifer(std::ostream &os, v2u32 texture_size << ":" << r << "," << q; } } + +v2f TileAnimationParams::getTextureCoords(v2u32 texture_size, int frame) const +{ + v2u32 ret(0, 0); + if (type == TAT_VERTICAL_FRAMES) { + int frame_height = (float)texture_size.X / + (float)vertical_frames.aspect_w * + (float)vertical_frames.aspect_h; + ret = v2u32(0, frame_height * frame); + } else if (type == TAT_SHEET_2D) { + v2u32 frame_size; + determineParams(texture_size, NULL, NULL, &frame_size); + int q, r; + q = frame / sheet_2d.frames_w; + r = frame % sheet_2d.frames_w; + ret = v2u32(r * frame_size.X, q * frame_size.Y); + } + return v2f(ret.X / (float) texture_size.X, ret.Y / (float) texture_size.Y); +} diff --git a/src/tileanimation.h b/src/tileanimation.h index 289ce515b..eecd3eb96 100644 --- a/src/tileanimation.h +++ b/src/tileanimation.h @@ -48,8 +48,10 @@ struct TileAnimationParams { void serialize(std::ostream &os, u16 protocol_version) const; void deSerialize(std::istream &is, u16 protocol_version); - void determineParams(v2u32 texture_size, int *frame_count, int *frame_length_ms) const; + void determineParams(v2u32 texture_size, int *frame_count, + int *frame_length_ms, v2u32 *frame_size) const; void getTextureModifer(std::ostream &os, v2u32 texture_size, int frame) const; + v2f getTextureCoords(v2u32 texture_size, int frame) const; }; #endif -- cgit v1.2.3 From dd282e646c42e3524bc546372c6a648e99b3c9db Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 18 Jan 2017 21:03:15 +0100 Subject: Fix MSVC build Build broken by 98e36d7 --- src/content_sao.cpp | 1 + src/treegen.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index bf8282af4..840e04ed9 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "content_sao.h" +#include "util/mathconstants.h" #include "util/serialize.h" #include "collision.h" #include "environment.h" diff --git a/src/treegen.cpp b/src/treegen.cpp index 4df574f34..e6c514ab9 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/pointer.h" #include "util/numeric.h" +#include "util/mathconstants.h" #include "map.h" #include "serverenvironment.h" #include "nodedef.h" -- cgit v1.2.3 From efa54f9c460239c23a2014076764d6c6830589e6 Mon Sep 17 00:00:00 2001 From: Elijah Duffy Date: Fri, 20 Jan 2017 10:49:20 -0800 Subject: Add chatcommand unregister and override API (#5076) Introduces two functions to unregister and override chatcommands. minetest.unregister_chatcommand("") and minetest.override_chatcommand("", {}) --- builtin/game/chatcommands.lua | 18 ++++++++++++++++++ doc/lua_api.txt | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 199b9e964..08dc1bb1d 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -15,6 +15,24 @@ function core.register_chatcommand(cmd, def) core.registered_chatcommands[cmd] = def end +function core.unregister_chatcommand(name) + if core.registered_chatcommands[name] then + core.registered_chatcommands[name] = nil + else + core.log("warning", "Not unregistering chatcommand " ..name.. + " because it doesn't exist.") + end +end + +function core.override_chatcommand(name, redefinition) + local chatcommand = core.registered_chatcommands[name] + assert(chatcommand, "Attempt to override non-existent chatcommand "..name) + for k, v in pairs(redefinition) do + rawset(chatcommand, k, v) + end + core.registered_chatcommands[name] = chatcommand +end + core.register_on_chat_message(function(name, message) local cmd, param = string.match(message, "^/([^ ]+) *(.*)") if not param then diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9bdc01c07..aada851a1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2077,6 +2077,10 @@ Call these functions only at load time! ### Other registration functions * `minetest.register_chatcommand(cmd, chatcommand definition)` * Adds definition to minetest.registered_chatcommands +* `minetest.override_chatcommand(name, redefinition)` + * Overrides fields of a chatcommand registered with register_chatcommand. +* `minetest.unregister_chatcommand(name)` + * Unregisters a chatcommands registered with register_chatcommand. * `minetest.register_privilege(name, definition)` * `definition`: `"description text"` * `definition`: `{ description = "description text", give_to_singleplayer = boolean}` -- cgit v1.2.3 From 0dada51a550c5eb52faf700dcbde4cee22a6bc70 Mon Sep 17 00:00:00 2001 From: red-001 Date: Fri, 20 Jan 2017 22:19:41 +0000 Subject: Remove `mathconstants.h` and use the correct way to get `M_PI` in MSVC. (#5072) --- src/CMakeLists.txt | 2 ++ src/camera.cpp | 1 - src/clientiface.cpp | 1 - src/clientmap.cpp | 1 - src/content_cao.cpp | 1 - src/content_sao.cpp | 1 - src/map.cpp | 1 - src/mg_biome.cpp | 1 - src/server.cpp | 1 - src/treegen.cpp | 1 - src/util/mathconstants.h | 7 ------- src/util/numeric.cpp | 1 - 12 files changed, 2 insertions(+), 17 deletions(-) delete mode 100644 src/util/mathconstants.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f90542be9..cab5a1139 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -284,6 +284,8 @@ if(WIN32) set(PLATFORM_LIBS dbghelp.lib ${PLATFORM_LIBS}) # Surpress some useless warnings add_definitions ( /D "_CRT_SECURE_NO_DEPRECATE" /W1 ) + # Get M_PI to work + add_definitions(/D "_USE_MATH_DEFINES") else() # Probably MinGW = GCC set(PLATFORM_LIBS "") endif() diff --git a/src/camera.cpp b/src/camera.cpp index 4768e8778..2ad835817 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -31,7 +31,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "event.h" #include "profiler.h" #include "util/numeric.h" -#include "util/mathconstants.h" #include "constants.h" #include "fontengine.h" diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 1610c21fd..47730343c 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientiface.h" #include "util/numeric.h" -#include "util/mathconstants.h" #include "remoteplayer.h" #include "settings.h" #include "mapblock.h" diff --git a/src/clientmap.cpp b/src/clientmap.cpp index fa326f0b4..11719539f 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "settings.h" #include "camera.h" // CameraModes -#include "util/mathconstants.h" #include "util/basic_macros.h" #include diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 83756c963..93ac1f785 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -26,7 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_cao.h" #include "util/numeric.h" // For IntervalLimiter #include "util/serialize.h" -#include "util/mathconstants.h" #include "util/basic_macros.h" #include "client/tile.h" #include "environment.h" diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 840e04ed9..bf8282af4 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "content_sao.h" -#include "util/mathconstants.h" #include "util/serialize.h" #include "collision.h" #include "environment.h" diff --git a/src/map.cpp b/src/map.cpp index 0659f66aa..f2a4b7ffe 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -32,7 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "gamedef.h" #include "util/directiontables.h" -#include "util/mathconstants.h" #include "util/basic_macros.h" #include "rollback_interface.h" #include "environment.h" diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp index d564e9415..ef7e52685 100644 --- a/src/mg_biome.cpp +++ b/src/mg_biome.cpp @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "map.h" //for MMVManip #include "util/numeric.h" -#include "util/mathconstants.h" #include "porting.h" #include "settings.h" diff --git a/src/server.cpp b/src/server.cpp index d3d5fd3d1..1656b9f5a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -53,7 +53,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "event_manager.h" #include "serverlist.h" #include "util/string.h" -#include "util/mathconstants.h" #include "rollback.h" #include "util/serialize.h" #include "util/thread.h" diff --git a/src/treegen.cpp b/src/treegen.cpp index e6c514ab9..4df574f34 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/pointer.h" #include "util/numeric.h" -#include "util/mathconstants.h" #include "map.h" #include "serverenvironment.h" #include "nodedef.h" diff --git a/src/util/mathconstants.h b/src/util/mathconstants.h deleted file mode 100644 index 1b478aa95..000000000 --- a/src/util/mathconstants.h +++ /dev/null @@ -1,7 +0,0 @@ -#include - -// MSVC doesn't seem to define this -#ifndef M_PI - #define M_PI 3.1415926535 -#endif - diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index a9e7ae584..87f1040ea 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "numeric.h" -#include "mathconstants.h" #include "log.h" #include "../constants.h" // BS, MAP_BLOCKSIZE -- cgit v1.2.3 From 2ea60156437962d7d29d20606bf5d9189059f76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sat, 21 Jan 2017 10:41:00 +0100 Subject: Do not force deletion of players when mapblock is full (#5081) This fixes #4067 --- src/serverenvironment.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index d3b85a430..41cd63684 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -2146,6 +2146,12 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } + // If it's a forced delete, there are too many objects in mapblock + // Ignore players, they should not be removed on force delete + if (force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + continue; + } + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Sat, 21 Jan 2017 11:29:18 +0100 Subject: Warning fix for 2ea60156437962d7d29d20606bf5d9189059f76b (#5082) Neither flag as force delete nor show the warning when mapblock is full and object is a player --- src/serverenvironment.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 41cd63684..01dc3ff10 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -2086,7 +2086,9 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(block) { - if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + // Force delete object if mapblock is full, but ignore players + if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER && + block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { warningstream << "ServerEnv: Trying to store id = " << obj->getId() << " statically but block " << PP(blockpos) << " already contains " @@ -2146,12 +2148,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } - // If it's a forced delete, there are too many objects in mapblock - // Ignore players, they should not be removed on force delete - if (force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - continue; - } - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Sat, 21 Jan 2017 15:11:55 +0000 Subject: Detach the player from entities on death. (#5077) --- src/content_sao.cpp | 10 ++++++++++ src/content_sao.h | 1 + src/script/lua_api/l_object.cpp | 15 +-------------- src/server.cpp | 2 ++ src/serverobject.h | 2 ++ 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index bf8282af4..d6581144f 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -203,6 +203,16 @@ void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, *rotation = m_attachment_rotation; } +void UnitSAO::detachFromParent() +{ + ServerActiveObject *parent = NULL; + if (m_attachment_parent_id) + parent = m_env->getActiveObject(m_attachment_parent_id); + setAttachment(NULL, "", v3f(0, 0, 0), v3f(0, 0, 0)); + if (parent != NULL) + parent->removeAttachmentChild(m_id); +} + void UnitSAO::addAttachmentChild(int child_id) { m_attachment_child_ids.insert(child_id); diff --git a/src/content_sao.h b/src/content_sao.h index 4abe93250..884f0f406 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -49,6 +49,7 @@ public: void setBonePosition(const std::string &bone, v3f position, v3f rotation); void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); + void detachFromParent(); void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 59c04f301..f579c0b86 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -710,20 +710,7 @@ int ObjectRef::l_set_detach(lua_State *L) ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; - - int parent_id = 0; - std::string bone = ""; - v3f position; - v3f rotation; - co->getAttachment(&parent_id, &bone, &position, &rotation); - ServerActiveObject *parent = NULL; - if (parent_id) - parent = env->getActiveObject(parent_id); - - // Do it - co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0)); - if (parent != NULL) - parent->removeAttachmentChild(co->getId()); + co->detachFromParent(); return 0; } diff --git a/src/server.cpp b/src/server.cpp index 1656b9f5a..1e5704042 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2560,6 +2560,8 @@ void Server::DiePlayer(u16 peer_id) if (!playersao) return; + playersao->detachFromParent(); + infostream << "Server::DiePlayer(): Player " << playersao->getPlayer()->getName() << " dies" << std::endl; diff --git a/src/serverobject.h b/src/serverobject.h index 38204980e..dfe6312f0 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -166,6 +166,8 @@ public: {} virtual void removeAttachmentChild(int child_id) {} + virtual void detachFromParent() + {} virtual const UNORDERED_SET &getAttachmentChildIds() { static const UNORDERED_SET rv; return rv; } virtual ObjectProperties* accessObjectProperties() -- cgit v1.2.3 From c57b4ff9b592617539aa978374c13cdd5f1603a6 Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 14 Jan 2017 19:32:10 +0100 Subject: Add Entity get_texture_mod() to Lua API Send texture modifier to clients connecting later too --- doc/lua_api.txt | 1 + src/content_cao.cpp | 26 ++++++++++++++++++++------ src/content_cao.h | 4 +++- src/content_sao.cpp | 12 +++++++++++- src/content_sao.h | 2 ++ src/script/lua_api/l_object.cpp | 13 +++++++++++++ src/script/lua_api/l_object.h | 3 +++ 7 files changed, 53 insertions(+), 8 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index aada851a1..e5a3362ee 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2848,6 +2848,7 @@ This is basically a reference to a C++ `ServerActiveObject` * `set_yaw(radians)` * `get_yaw()`: returns number in radians * `set_texture_mod(mod)` +* `get_texture_mod()` returns current texture modifier * `set_sprite(p={x=0,y=0}, num_frames=1, framelength=0.2, select_horiz_by_yawpitch=false)` * Select sprite from spritesheet with optional animation and DM-style diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 93ac1f785..e829fc761 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -574,6 +574,8 @@ GenericCAO::GenericCAO(Client *client, ClientEnvironment *env): m_anim_framelength(0.2), m_anim_timer(0), m_reset_textures_timer(-1), + m_previous_texture_modifier(""), + m_current_texture_modifier(""), m_visuals_expired(false), m_step_distance_counter(0), m_last_light(255), @@ -952,7 +954,10 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, infostream<<"GenericCAO::addToScene(): \""<= 0) { m_reset_textures_timer -= dtime; - if(m_reset_textures_timer <= 0){ + if(m_reset_textures_timer <= 0) { m_reset_textures_timer = -1; - updateTextures(""); + updateTextures(m_previous_texture_modifier); } } if(getParent() == NULL && fabs(m_prop.automatic_rotate) > 0.001) @@ -1301,7 +1306,7 @@ void GenericCAO::updateTexturePos() } } -void GenericCAO::updateTextures(const std::string &mod) +void GenericCAO::updateTextures(const std::string mod) { ITextureSource *tsrc = m_client->tsrc(); @@ -1309,6 +1314,9 @@ void GenericCAO::updateTextures(const std::string &mod) bool use_bilinear_filter = g_settings->getBool("bilinear_filter"); bool use_anisotropic_filter = g_settings->getBool("anisotropic_filter"); + m_previous_texture_modifier = m_current_texture_modifier; + m_current_texture_modifier = mod; + if(m_spritenode) { if(m_prop.visual == "sprite") @@ -1611,6 +1619,12 @@ void GenericCAO::processMessage(const std::string &data) updateNodePos(); } else if (cmd == GENERIC_CMD_SET_TEXTURE_MOD) { std::string mod = deSerializeString(is); + + // immediatly reset a engine issued texture modifier if a mod sends a different one + if (m_reset_textures_timer > 0) { + m_reset_textures_timer = -1; + updateTextures(m_previous_texture_modifier); + } updateTextures(mod); } else if (cmd == GENERIC_CMD_SET_SPRITE) { v2s16 p = readV2S16(is); @@ -1734,7 +1748,7 @@ void GenericCAO::processMessage(const std::string &data) m_reset_textures_timer = 0.05; if(damage >= 2) m_reset_textures_timer += 0.05 * damage; - updateTextures("^[brighten"); + updateTextures(m_current_texture_modifier + "^[brighten"); } } } else if (cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) { @@ -1802,7 +1816,7 @@ bool GenericCAO::directReportPunch(v3f dir, const ItemStack *punchitem, m_reset_textures_timer = 0.05; if(result.damage >= 2) m_reset_textures_timer += 0.05 * result.damage; - updateTextures("^[brighten"); + updateTextures(m_current_texture_modifier + "^[brighten"); } return false; diff --git a/src/content_cao.h b/src/content_cao.h index 846e0690a..f30e90e21 100644 --- a/src/content_cao.h +++ b/src/content_cao.h @@ -102,6 +102,8 @@ private: float m_anim_timer; ItemGroupList m_armor_groups; float m_reset_textures_timer; + std::string m_previous_texture_modifier; // stores texture modifier before punch update + std::string m_current_texture_modifier; // last applied texture modifier bool m_visuals_expired; float m_step_distance_counter; u8 m_last_light; @@ -198,7 +200,7 @@ public: void updateTexturePos(); - void updateTextures(const std::string &mod); + void updateTextures(const std::string mod); void updateAnimation(); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index d6581144f..f0973082d 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -257,7 +257,8 @@ LuaEntitySAO::LuaEntitySAO(ServerEnvironment *env, v3f pos, m_last_sent_position(0,0,0), m_last_sent_velocity(0,0,0), m_last_sent_position_timer(0), - m_last_sent_move_precision(0) + m_last_sent_move_precision(0), + m_current_texture_modifier("") { // Only register type if no environment supplied if(env == NULL){ @@ -511,6 +512,9 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) } } + msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier)); + message_count++; + writeU8(os, message_count); os.write(msg_os.str().c_str(), msg_os.str().size()); } @@ -687,11 +691,17 @@ v3f LuaEntitySAO::getAcceleration() void LuaEntitySAO::setTextureMod(const std::string &mod) { std::string str = gob_cmd_set_texture_mod(mod); + m_current_texture_modifier = mod; // create message and add to list ActiveObjectMessage aom(getId(), true, str); m_messages_out.push(aom); } +std::string LuaEntitySAO::getTextureMod() const +{ + return m_current_texture_modifier; +} + void LuaEntitySAO::setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch) { diff --git a/src/content_sao.h b/src/content_sao.h index 884f0f406..56a26fb0d 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -122,6 +122,7 @@ public: v3f getAcceleration(); void setTextureMod(const std::string &mod); + std::string getTextureMod() const; void setSprite(v2s16 p, int num_frames, float framelength, bool select_horiz_by_yawpitch); std::string getName(); @@ -143,6 +144,7 @@ private: v3f m_last_sent_velocity; float m_last_sent_position_timer; float m_last_sent_move_precision; + std::string m_current_texture_modifier; }; /* diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f579c0b86..1fa3663ca 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -900,6 +900,19 @@ int ObjectRef::l_set_texture_mod(lua_State *L) return 0; } +// get_texture_mod(self) +int ObjectRef::l_get_texture_mod(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkobject(L, 1); + LuaEntitySAO *co = getluaobject(ref); + if (co == NULL) return 0; + // Do it + std::string mod = co->getTextureMod(); + lua_pushstring(L, mod.c_str()); + return 1; +} + // set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, // select_horiz_by_yawpitch=false) int ObjectRef::l_set_sprite(lua_State *L) diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 06b1bb79b..96d0abae8 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -164,6 +164,9 @@ private: // set_texture_mod(self, mod) static int l_set_texture_mod(lua_State *L); + // l_get_texture_mod(self) + static int l_get_texture_mod(lua_State *L); + // set_sprite(self, p={x=0,y=0}, num_frames=1, framelength=0.2, // select_horiz_by_yawpitch=false) static int l_set_sprite(lua_State *L); -- cgit v1.2.3 From b9c1a758a1e987dfb0284c08dd96d4d17114882f Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 21 Jan 2017 17:11:06 +0100 Subject: Fix unknown command message not providing number of cmd --- src/content_cao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content_cao.cpp b/src/content_cao.cpp index e829fc761..5dc3866cf 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -1779,7 +1779,7 @@ void GenericCAO::processMessage(const std::string &data) } else { warningstream << FUNCTION_NAME << ": unknown command or outdated client \"" - << cmd << std::endl; + << +cmd << "\"" << std::endl; } } -- cgit v1.2.3 From bc29e03b597be350fa856b1ae3d8623778a808b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sat, 21 Jan 2017 17:30:55 +0100 Subject: Revert "Detach the player from entities on death." (#5087) --- src/content_sao.cpp | 10 ---------- src/content_sao.h | 1 - src/script/lua_api/l_object.cpp | 15 ++++++++++++++- src/server.cpp | 2 -- src/serverobject.h | 2 -- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f0973082d..bb62aea7d 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -203,16 +203,6 @@ void UnitSAO::getAttachment(int *parent_id, std::string *bone, v3f *position, *rotation = m_attachment_rotation; } -void UnitSAO::detachFromParent() -{ - ServerActiveObject *parent = NULL; - if (m_attachment_parent_id) - parent = m_env->getActiveObject(m_attachment_parent_id); - setAttachment(NULL, "", v3f(0, 0, 0), v3f(0, 0, 0)); - if (parent != NULL) - parent->removeAttachmentChild(m_id); -} - void UnitSAO::addAttachmentChild(int child_id) { m_attachment_child_ids.insert(child_id); diff --git a/src/content_sao.h b/src/content_sao.h index 56a26fb0d..c3674fa2d 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -49,7 +49,6 @@ public: void setBonePosition(const std::string &bone, v3f position, v3f rotation); void getBonePosition(const std::string &bone, v3f *position, v3f *rotation); void setAttachment(int parent_id, const std::string &bone, v3f position, v3f rotation); - void detachFromParent(); void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation); void addAttachmentChild(int child_id); void removeAttachmentChild(int child_id); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 1fa3663ca..be4451704 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -710,7 +710,20 @@ int ObjectRef::l_set_detach(lua_State *L) ServerActiveObject *co = getobject(ref); if (co == NULL) return 0; - co->detachFromParent(); + + int parent_id = 0; + std::string bone = ""; + v3f position; + v3f rotation; + co->getAttachment(&parent_id, &bone, &position, &rotation); + ServerActiveObject *parent = NULL; + if (parent_id) + parent = env->getActiveObject(parent_id); + + // Do it + co->setAttachment(0, "", v3f(0,0,0), v3f(0,0,0)); + if (parent != NULL) + parent->removeAttachmentChild(co->getId()); return 0; } diff --git a/src/server.cpp b/src/server.cpp index 1e5704042..1656b9f5a 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2560,8 +2560,6 @@ void Server::DiePlayer(u16 peer_id) if (!playersao) return; - playersao->detachFromParent(); - infostream << "Server::DiePlayer(): Player " << playersao->getPlayer()->getName() << " dies" << std::endl; diff --git a/src/serverobject.h b/src/serverobject.h index dfe6312f0..38204980e 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -166,8 +166,6 @@ public: {} virtual void removeAttachmentChild(int child_id) {} - virtual void detachFromParent() - {} virtual const UNORDERED_SET &getAttachmentChildIds() { static const UNORDERED_SET rv; return rv; } virtual ObjectProperties* accessObjectProperties() -- cgit v1.2.3 From 6d5a40713347424084af8ba04e76278961506881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sat, 21 Jan 2017 19:30:42 +0100 Subject: Add show_statusline_on_connect setting (#5084) Add show_statusline_on_connect setting --- builtin/settingtypes.txt | 3 +++ minetest.conf.example | 6 +++++- src/defaultsettings.cpp | 1 + src/server.cpp | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 2dfe86e04..0f416bc9a 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -703,6 +703,9 @@ map-dir (Map directory) path # Setting it to -1 disables the feature. item_entity_ttl (Item entity TTL) int 900 +# If enabled, show the server status message on player connection. +show_statusline_on_connect (Status message on connection) bool true + # Enable players getting damage and dying. enable_damage (Damage) bool false diff --git a/minetest.conf.example b/minetest.conf.example index 3b686aa61..515b6cfd4 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -841,6 +841,10 @@ # type: int # item_entity_ttl = 900 +# If enabled, show the server status message on player connection. +# type: bool +# show_statusline_on_connect = true + # Enable players getting damage and dying. # type: bool # enable_damage = false @@ -1558,7 +1562,7 @@ # profiler.default_report_format = txt # The file path relative to your worldpath in which profiles will be saved to. -# +# # type: string # profiler.report_path = "" diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 59cfdd50d..e154a63c8 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -255,6 +255,7 @@ void set_default_settings(Settings *settings) settings->setDefault("motd", ""); settings->setDefault("max_users", "15"); settings->setDefault("creative_mode", "false"); + settings->setDefault("show_statusline_on_connect", "true"); settings->setDefault("enable_damage", "true"); settings->setDefault("give_initial_stuff", "false"); settings->setDefault("default_password", ""); diff --git a/src/server.cpp b/src/server.cpp index 1656b9f5a..7e1ab30ac 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1118,7 +1118,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id) SendDeathscreen(peer_id, false, v3f(0,0,0)); // Note things in chat if not in simple singleplayer mode - if(!m_simple_singleplayer_mode) { + if (!m_simple_singleplayer_mode && g_settings->getBool("show_statusline_on_connect")) { // Send information about server to player in chat SendChatMessage(peer_id, getStatusString()); } -- cgit v1.2.3 From 39123fcce5e983e83234971571b3cfaa458970b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sat, 21 Jan 2017 22:05:54 +0100 Subject: Remove os.exit from the Lua secure sandbox (#5090) os.exit will exit not using proper resource liberation paths. Mods should call the proper exit mod using our API --- src/script/cpp_api/s_security.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 1b1f148cd..be2b884cc 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -99,7 +99,6 @@ void ScriptApiSecurity::initializeSecurity() "clock", "date", "difftime", - "exit", "getenv", "setlocale", "time", -- cgit v1.2.3 From 43822de5c6b35646feced5ac65331313f82f78ce Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 22 Jan 2017 20:17:13 +0100 Subject: Fix potential crash in chat handling (since 2f56a00d9eef82052614e5854a07b39b087efd0b) --- src/server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.cpp b/src/server.cpp index 7e1ab30ac..c7d4fcba9 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2864,7 +2864,7 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna */ u16 peer_id_to_avoid_sending = (player ? player->peer_id : PEER_ID_INEXISTENT); - if (player->protocol_version >= 29) + if (player && player->protocol_version >= 29) peer_id_to_avoid_sending = PEER_ID_INEXISTENT; for (u16 i = 0; i < clients.size(); i++) { -- cgit v1.2.3 From d04d8aba7029a2501854a2838fd282b81358a54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Thu, 12 Jan 2017 15:46:30 +0100 Subject: Add hardware node coloring. Includes: - Increase ContentFeatures serialization version - Color property and palettes for nodes - paramtype2 = "color", "colored facedir" or "colored wallmounted" --- client/shaders/nodes_shader/opengl_vertex.glsl | 43 +- .../water_surface_shader/opengl_vertex.glsl | 45 +- client/shaders/wielded_shader/opengl_vertex.glsl | 1 - doc/lua_api.txt | 27 +- src/client/tile.cpp | 3 - src/client/tile.h | 34 +- src/clientenvironment.cpp | 4 +- src/content_mapblock.cpp | 326 ++++++++----- src/game.cpp | 30 +- src/mapblock_mesh.cpp | 263 ++++++----- src/mapblock_mesh.h | 57 ++- src/mapnode.cpp | 20 +- src/mapnode.h | 12 +- src/mesh.cpp | 47 +- src/mesh.h | 14 +- src/minimap.cpp | 31 +- src/minimap.h | 4 +- src/network/networkprotocol.h | 5 +- src/nodedef.cpp | 513 +++++++++++++++++---- src/nodedef.h | 108 +++-- src/particles.cpp | 65 ++- src/particles.h | 19 +- src/script/common/c_content.cpp | 18 + src/script/cpp_api/s_node.cpp | 3 + src/shader.cpp | 2 +- src/wieldmesh.cpp | 62 ++- src/wieldmesh.h | 5 + 27 files changed, 1207 insertions(+), 554 deletions(-) diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index 44c48cc4c..3ac79c26d 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -1,7 +1,8 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; +// Color of the light emitted by the sun. +uniform vec3 dayLight; uniform vec3 eyePosition; uniform float animationTimer; @@ -14,6 +15,8 @@ varying vec3 tsEyeVec; varying vec3 tsLightVec; varying float area_enable_parallax; +// Color of the light emitted by the light sources. +const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; const float BS = 10.0; @@ -119,31 +122,23 @@ float disp_z; v.z = dot(eyeVec, normal); tsEyeVec = normalize (v); + // Calculate color. + // Red, green and blue components are pre-multiplied with + // the brightness, so now we have to multiply these + // colors with the color of the incoming light. + // The pre-baked colors are halved to prevent overflow. vec4 color; - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 2.5; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 23.0; - + // The alpha gives the ratio of sunlight in the incoming light. + float nightRatio = 1 - gl_Color.a; + color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + + nightRatio * artificialLight.rgb) * 2; + color.a = 1; + // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13) / 0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85) / 0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/water_surface_shader/opengl_vertex.glsl b/client/shaders/water_surface_shader/opengl_vertex.glsl index a930e7b8f..112db9bb5 100644 --- a/client/shaders/water_surface_shader/opengl_vertex.glsl +++ b/client/shaders/water_surface_shader/opengl_vertex.glsl @@ -1,7 +1,8 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; +// Color of the light emitted by the sun. +uniform vec3 dayLight; uniform vec3 eyePosition; uniform float animationTimer; @@ -13,6 +14,8 @@ varying vec3 lightVec; varying vec3 tsEyeVec; varying vec3 tsLightVec; +// Color of the light emitted by the light sources. +const vec3 artificialLight = vec3(1.04, 1.04, 1.04); const float e = 2.718281828459; const float BS = 10.0; @@ -112,31 +115,23 @@ void main(void) eyeVec = (gl_ModelViewMatrix * gl_Vertex).xyz; tsEyeVec = eyeVec * tbnMatrix; + // Calculate color. + // Red, green and blue components are pre-multiplied with + // the brightness, so now we have to multiply these + // colors with the color of the incoming light. + // The pre-baked colors are halved to prevent overflow. vec4 color; - float day = gl_Color.r; - float night = gl_Color.g; - float light_source = gl_Color.b; - - float rg = mix(night, day, dayNightRatio); - rg += light_source * 2.5; // Make light sources brighter - float b = rg; - - // Moonlight is blue - b += (day - night) / 13.0; - rg -= (day - night) / 23.0; - + // The alpha gives the ratio of sunlight in the incoming light. + float nightRatio = 1 - gl_Color.a; + color.rgb = gl_Color.rgb * (gl_Color.a * dayLight.rgb + + nightRatio * artificialLight.rgb) * 2; + color.a = 1; + // Emphase blue a bit in darker places // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - b += max(0.0, (1.0 - abs(b - 0.13)/0.17) * 0.025); - - // Artificial light is yellow-ish - // See C++ implementation in mapblock_mesh.cpp finalColorBlend() - rg += max(0.0, (1.0 - abs(rg - 0.85)/0.15) * 0.065); - - color.r = rg; - color.g = rg; - color.b = b; - - color.a = gl_Color.a; - gl_FrontColor = gl_BackColor = clamp(color,0.0,1.0); + float brightness = (color.r + color.g + color.b) / 3; + color.b += max(0.0, 0.021 - abs(0.2 * brightness - 0.021) + + 0.07 * brightness); + + gl_FrontColor = gl_BackColor = clamp(color, 0.0, 1.0); } diff --git a/client/shaders/wielded_shader/opengl_vertex.glsl b/client/shaders/wielded_shader/opengl_vertex.glsl index 86c626896..9f05b833a 100644 --- a/client/shaders/wielded_shader/opengl_vertex.glsl +++ b/client/shaders/wielded_shader/opengl_vertex.glsl @@ -1,7 +1,6 @@ uniform mat4 mWorldViewProj; uniform mat4 mWorld; -uniform float dayNightRatio; uniform vec3 eyePosition; uniform float animationTimer; diff --git a/doc/lua_api.txt b/doc/lua_api.txt index e5a3362ee..2a0b72053 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -638,6 +638,19 @@ node definition: bit 4 (0x10) - Makes the plant mesh 1.4x larger bit 5 (0x20) - Moves each face randomly a small bit down (1/8 max) bits 6-7 are reserved for future use. + paramtype2 == "color" + ^ `param2` tells which color is picked from the palette. + The palette should have 256 pixels. + paramtype2 == "colorfacedir" + ^ Same as `facedir`, but with colors. + The first three bits of `param2` tells which color + is picked from the palette. + The palette should have 8 pixels. + paramtype2 == "colorwallmounted" + ^ Same as `wallmounted`, but with colors. + The first five bits of `param2` tells which color + is picked from the palette. + The palette should have 32 pixels. collision_box = { type = "fixed", fixed = { @@ -3707,6 +3720,9 @@ Definition tables when displacement mapping is used Directions are from the point of view of the tile texture, not the node it's on +* `{name="image.png", color=ColorSpec}` + * the texture's color will be multiplied with this color. + * the tile's color overrides the owning node's color in all cases. * deprecated, yet still supported field names: * `image` (name) @@ -3749,8 +3765,17 @@ Definition tables special_tiles = {tile definition 1, Tile definition 2}, --[[ ^ Special textures of node; used rarely (old field name: special_materials) ^ List can be shortened to needed length ]] - alpha = 255, + color = ColorSpec, --[[ + ^ The node's original color will be multiplied with this color. + ^ If the node has a palette, then this setting only has an effect + ^ in the inventory and on the wield item. ]] use_texture_alpha = false, -- Use texture's alpha channel + palette = "palette.png", --[[ + ^ The node's `param2` is used to select a pixel from the image + ^ (pixels are arranged from left to right and from top to bottom). + ^ The node's color will be multiplied with the selected pixel's + ^ color. Tiles can override this behavior. + ^ Only when `paramtype2` supports palettes. ]] post_effect_color = "green#0F", -- If player is inside node, see "ColorSpec" paramtype = "none", -- See "Nodes" --[[ ^ paramtype = "light" allows light to propagate from or through the node with light value diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 4d2166342..539c29445 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -378,9 +378,6 @@ public: video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms); - // Generates an image from a full string like - // "stone.png^mineral_coal.png^[crack:1:0". - // Shall be called from the main thread. video::IImage* generateImage(const std::string &name); video::ITexture* getNormalTexture(const std::string &name); diff --git a/src/client/tile.h b/src/client/tile.h index 452804801..d04ab918a 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -108,6 +108,12 @@ public: const std::string &name, u32 *id = NULL) = 0; virtual IrrlichtDevice* getDevice()=0; virtual bool isKnownSourceImage(const std::string &name)=0; + /*! Generates an image from a full string like + * "stone.png^mineral_coal.png^[crack:1:0". + * Shall be called from the main thread. + * The returned Image should be dropped. + */ + virtual video::IImage* generateImage(const std::string &name)=0; virtual video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms)=0; virtual video::ITexture* getNormalTexture(const std::string &name)=0; @@ -192,7 +198,6 @@ struct TileSpec texture(NULL), normal_texture(NULL), flags_texture(NULL), - alpha(255), material_type(TILE_MATERIAL_BASIC), material_flags( //0 // <- DEBUG, Use the one below @@ -201,22 +206,30 @@ struct TileSpec shader_id(0), animation_frame_count(1), animation_frame_length_ms(0), - rotation(0) + rotation(0), + has_color(false), + color(), + emissive_light(0) { } + /*! + * Two tiles are equal if they can be appended to + * the same mesh buffer. + */ bool operator==(const TileSpec &other) const { return ( texture_id == other.texture_id && - /* texture == other.texture && */ - alpha == other.alpha && material_type == other.material_type && material_flags == other.material_flags && rotation == other.rotation ); } + /*! + * Two tiles are not equal if they must be in different mesh buffers. + */ bool operator!=(const TileSpec &other) const { return !(*this == other); @@ -233,7 +246,7 @@ struct TileSpec material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_TRANSPARENT: - material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_OPAQUE: material.MaterialType = video::EMT_SOLID; @@ -274,8 +287,6 @@ struct TileSpec video::ITexture *normal_texture; video::ITexture *flags_texture; - // Vertex alpha (when MATERIAL_ALPHA_VERTEX is used) - u8 alpha; // Material parameters u8 material_type; u8 material_flags; @@ -286,5 +297,14 @@ struct TileSpec std::vector frames; u8 rotation; + //! If true, the tile has its own color. + bool has_color; + /*! + * The color of the tile, or if the tile does not own + * a color then the color of the node owning this tile. + */ + video::SColor color; + //! This much light does the tile emit. + u8 emissive_light; }; #endif diff --git a/src/clientenvironment.cpp b/src/clientenvironment.cpp index b32a02f2d..77f53d214 100644 --- a/src/clientenvironment.cpp +++ b/src/clientenvironment.cpp @@ -334,9 +334,7 @@ void ClientEnvironment::step(float dtime) node_at_lplayer = m_map->getNodeNoEx(p); u16 light = getInteriorLight(node_at_lplayer, 0, m_client->ndef()); - u8 day = light & 0xff; - u8 night = (light >> 8) & 0xff; - finalColorBlend(lplayer->light_color, day, night, day_night_ratio); + final_color_blend(&lplayer->light_color, light, day_night_ratio); } /* diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index a7134590b..742bfb1fd 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -32,28 +32,29 @@ with this program; if not, write to the Free Software Foundation, Inc., // Create a cuboid. -// collector - the MeshCollector for the resulting polygons -// box - the position and size of the box -// tiles - the tiles (materials) to use (for all 6 faces) -// tilecount - number of entries in tiles, 1<=tilecount<=6 -// c - vertex colour - used for all -// txc - texture coordinates - this is a list of texture coordinates -// for the opposite corners of each face - therefore, there -// should be (2+2)*6=24 values in the list. Alternatively, pass -// NULL to use the entire texture for each face. The order of -// the faces in the list is up-down-right-left-back-front -// (compatible with ContentFeatures). If you specified 0,0,1,1 -// for each face, that would be the same as passing NULL. +// collector - the MeshCollector for the resulting polygons +// box - the position and size of the box +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 +// c - colors of the cuboid's six sides +// txc - texture coordinates - this is a list of texture coordinates +// for the opposite corners of each face - therefore, there +// should be (2+2)*6=24 values in the list. Alternatively, +// pass NULL to use the entire texture for each face. The +// order of the faces in the list is up-down-right-left-back- +// front (compatible with ContentFeatures). If you specified +// 0,0,1,1 for each face, that would be the same as +// passing NULL. +// light source - if greater than zero, the box's faces will not be shaded void makeCuboid(MeshCollector *collector, const aabb3f &box, - TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc) + TileSpec *tiles, int tilecount, const video::SColor *c, + const f32* txc, const u8 light_source) { assert(tilecount >= 1 && tilecount <= 6); // pre-condition v3f min = box.MinEdge; v3f max = box.MaxEdge; - - if(txc == NULL) { static const f32 txc_default[24] = { 0,0,1,1, @@ -66,38 +67,53 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, txc = txc_default; } + video::SColor c1 = c[0]; + video::SColor c2 = c[1]; + video::SColor c3 = c[2]; + video::SColor c4 = c[3]; + video::SColor c5 = c[4]; + video::SColor c6 = c[5]; + if (!light_source) { + applyFacesShading(c1, v3f(0, 1, 0)); + applyFacesShading(c2, v3f(0, -1, 0)); + applyFacesShading(c3, v3f(1, 0, 0)); + applyFacesShading(c4, v3f(-1, 0, 0)); + applyFacesShading(c5, v3f(0, 0, 1)); + applyFacesShading(c6, v3f(0, 0, -1)); + } + video::S3DVertex vertices[24] = { // up - video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]), - video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]), - video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]), - video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]), + video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c1, txc[0],txc[1]), + video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c1, txc[2],txc[1]), + video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c1, txc[2],txc[3]), + video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c1, txc[0],txc[3]), // down - video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]), - video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]), - video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]), - video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]), + video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c2, txc[4],txc[5]), + video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c2, txc[6],txc[5]), + video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c2, txc[6],txc[7]), + video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c2, txc[4],txc[7]), // right - video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]), - video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]), - video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]), - video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]), + video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c3, txc[ 8],txc[9]), + video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c3, txc[10],txc[9]), + video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c3, txc[10],txc[11]), + video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c3, txc[ 8],txc[11]), // left - video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]), - video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]), - video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]), - video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]), + video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c4, txc[12],txc[13]), + video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c4, txc[14],txc[13]), + video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c4, txc[14],txc[15]), + video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c4, txc[12],txc[15]), // back - video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]), - video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]), - video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]), - video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]), + video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c5, txc[16],txc[17]), + video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c5, txc[18],txc[17]), + video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c5, txc[18],txc[19]), + video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c5, txc[16],txc[19]), // front - video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]), - video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]), - video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]), - video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]), + video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c6, txc[20],txc[21]), + video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c6, txc[22],txc[21]), + video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c6, txc[22],txc[23]), + video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c6, txc[20],txc[23]), }; for(int i = 0; i < 6; i++) @@ -164,6 +180,31 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, } } +// Create a cuboid. +// collector - the MeshCollector for the resulting polygons +// box - the position and size of the box +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 +// c - color of the cuboid +// txc - texture coordinates - this is a list of texture coordinates +// for the opposite corners of each face - therefore, there +// should be (2+2)*6=24 values in the list. Alternatively, +// pass NULL to use the entire texture for each face. The +// order of the faces in the list is up-down-right-left-back- +// front (compatible with ContentFeatures). If you specified +// 0,0,1,1 for each face, that would be the same as +// passing NULL. +// light source - if greater than zero, the box's faces will not be shaded +void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles, + int tilecount, const video::SColor &c, const f32* txc, + const u8 light_source) +{ + video::SColor color[6]; + for (u8 i = 0; i < 6; i++) + color[i] = c; + makeCuboid(collector, box, tiles, tilecount, color, txc, light_source); +} + static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, MeshMakeData *data, MapNode n, int v, int *neighbors) { @@ -181,6 +222,18 @@ static inline int NeighborToIndex(const v3s16 &pos) return 9 * pos.X + 3 * pos.Y + pos.Z + 13; } +/*! + * Returns the i-th special tile for a map node. + */ +static TileSpec getSpecialTile(const ContentFeatures &f, + const MapNode &n, u8 i) +{ + TileSpec copy = f.special_tiles[i]; + if (!copy.has_color) + n.getColor(f, ©.color); + return copy; +} + /* TODO: Fix alpha blending for special nodes Currently only the last element rendered is blended correct @@ -227,8 +280,13 @@ 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 = getSpecialTile(f, n, 0); TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data); + u16 l = getInteriorLight(n, 0, nodedef); + video::SColor c1 = encode_light_and_color(l, + tile_liquid.color, f.light_source); + video::SColor c2 = encode_light_and_color(l, + tile_liquid_bfculled.color, f.light_source); bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); @@ -237,9 +295,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) top_is_same_liquid = true; - u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source); - /* Generate sides */ @@ -285,15 +340,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // 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) + video::SColor *c = &c1; + if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) { current_tile = &tile_liquid_bfculled; + c = &c2; + } video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2,0,0,0, *c, 0,1), + video::S3DVertex(BS/2,0,BS/2,0,0,0, *c, 1,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0), }; /* @@ -359,10 +417,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0), + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), }; v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS); @@ -380,8 +438,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Add flowing liquid to mesh */ - TileSpec tile_liquid = f.special_tiles[0]; - TileSpec tile_liquid_bfculled = f.special_tiles[1]; + TileSpec tile_liquid = getSpecialTile(f, n, 0); + TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1); bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); @@ -404,7 +462,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Otherwise use the light of this node (the liquid) else l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source); + video::SColor c1 = encode_light_and_color(l, + tile_liquid.color, f.light_source); + video::SColor c2 = encode_light_and_color(l, + tile_liquid_bfculled.color, f.light_source); u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8); @@ -552,7 +613,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, is liquid, don't draw side face */ if (top_is_same_liquid && - neighbor_data.flags & neighborflag_top_is_same_liquid) + (neighbor_data.flags & neighborflag_top_is_same_liquid)) continue; content_t neighbor_content = neighbor_data.content; @@ -574,15 +635,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // 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) + video::SColor *c = &c1; + if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) { current_tile = &tile_liquid_bfculled; + c = &c2; + } video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0), }; /* @@ -656,10 +720,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0), - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0), + video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1), + video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1), + video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0), + video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), }; // To get backface culling right, the vertices need to go @@ -720,8 +784,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); - + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); for(u32 j=0; j<6; j++) { // Check this neighbor @@ -731,13 +795,17 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Don't make face if neighbor is of same type if(n2.getContent() == n.getContent()) continue; + video::SColor c2=c; + if(!f.light_source) + applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z)); + // The face at Z+ video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,1), - video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,1), - video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,0), - video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,0), + video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,1), + video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,1), + video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,0), + video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,0), }; // Rotations in the g_6dirs format @@ -784,12 +852,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16( 0, 0,-1) }; + u16 l = getInteriorLight(n, 1, nodedef); u8 i; TileSpec tiles[6]; for (i = 0; i < 6; i++) tiles[i] = getNodeTile(n, p, dirs[i], data); + video::SColor tile0color = encode_light_and_color(l, + tiles[0].color, f.light_source); + video::SColor tile0colors[6]; + for (i = 0; i < 6; i++) + tile0colors[i] = tile0color; + TileSpec glass_tiles[6]; + video::SColor glasscolor[6]; if (tiles[1].texture && tiles[2].texture && tiles[3].texture) { glass_tiles[0] = tiles[2]; glass_tiles[1] = tiles[3]; @@ -801,14 +877,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for (i = 0; i < 6; i++) glass_tiles[i] = tiles[1]; } + for (i = 0; i < 6; i++) + glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color, + f.light_source); u8 param2 = n.getParam2(); bool H_merge = ! bool(param2 & 128); bool V_merge = ! bool(param2 & 64); param2 = param2 & 63; - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); v3f pos = intToFloat(p, BS); static const float a = BS / 2; static const float g = a - 0.003; @@ -947,7 +1024,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 1-tx2, 1-ty2, 1-tx1, 1-ty1, tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, &tiles[0], 1, c, txc1); + makeCuboid(&collector, box, &tiles[0], 1, tile0colors, + txc1, f.light_source); } for(i = 0; i < 6; i++) @@ -971,16 +1049,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 1-tx2, 1-ty2, 1-tx1, 1-ty1, tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2); + makeCuboid(&collector, box, &glass_tiles[i], 1, glasscolor, + txc2, f.light_source); } if (param2 > 0 && f.special_tiles[0].texture) { // Interior volume level is in range 0 .. 63, // convert it to -0.5 .. 0.5 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0); + TileSpec tile=getSpecialTile(f, n, 0); + video::SColor special_color = encode_light_and_color(l, + tile.color, f.light_source); TileSpec interior_tiles[6]; for (i = 0; i < 6; i++) - interior_tiles[i] = f.special_tiles[0]; + interior_tiles[i] = tile; + float offset = 0.003; box = aabb3f(visible_faces[3] ? -b : -a + offset, visible_faces[1] ? -b : -a + offset, @@ -1004,22 +1087,24 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 1-tx2, 1-ty2, 1-tx1, 1-ty1, tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, interior_tiles, 6, c, txc3); + makeCuboid(&collector, box, interior_tiles, 6, special_color, + txc3, f.light_source); } break;} case NDT_ALLFACES: { TileSpec tile_leaves = getNodeTile(n, p, v3s16(0,0,0), data); - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, + tile_leaves.color, f.light_source); v3f pos = intToFloat(p, BS); aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2); box.MinEdge += pos; box.MaxEdge += pos; - makeCuboid(&collector, box, &tile_leaves, 1, c, NULL); + makeCuboid(&collector, box, &tile_leaves, 1, c, NULL, + f.light_source); break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else @@ -1046,7 +1131,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float s = BS/2*f.visual_scale; // Wall at X+ of node @@ -1087,7 +1173,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float d = (float)BS/16; float s = BS/2*f.visual_scale; @@ -1132,7 +1219,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float s = BS / 2 * f.visual_scale; // add sqrt(2) visual scale @@ -1302,7 +1390,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float s = BS / 2 * f.visual_scale; @@ -1437,7 +1526,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile_rot.rotation = 1; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); const f32 post_rad=(f32)BS/8; const f32 bar_rad=(f32)BS/16; @@ -1456,7 +1546,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 4/16.,0,8/16.,1, 8/16.,0,12/16.,1, 12/16.,0,16/16.,1}; - makeCuboid(&collector, post, &tile_rot, 1, c, postuv); + makeCuboid(&collector, post, &tile_rot, 1, c, postuv, + f.light_source); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; @@ -1477,11 +1568,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0/16.,8/16.,16/16.,10/16., 0/16.,14/16.,16/16.,16/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, xrailuv); + c, xrailuv, f.light_source); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, xrailuv); + c, xrailuv, f.light_source); } // Now a section of fence, +Z, if there's a post there @@ -1503,11 +1594,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16.}; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, zrailuv); + c, zrailuv, f.light_source); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; makeCuboid(&collector, bar, &tile_nocrack, 1, - c, zrailuv); + c, zrailuv, f.light_source); } break;} case NDT_RAILLIKE: @@ -1616,7 +1707,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + video::SColor c = encode_light_and_color(l, tile.color, + f.light_source); float d = (float)BS/64; float s = BS/2; @@ -1627,10 +1719,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { - video::S3DVertex(-s, -s+d,-s, 0,0,0, c,0,1), - video::S3DVertex( s, -s+d,-s, 0,0,0, c,1,1), - video::S3DVertex( s, g*s+d, s, 0,0,0, c,1,0), - video::S3DVertex(-s, g*s+d, s, 0,0,0, c,0,0), + video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1), + video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1), + video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0), + video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0), }; for(s32 i=0; i<4; i++) @@ -1653,10 +1745,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16(0, 0, 1), v3s16(0, 0, -1) }; - TileSpec tiles[6]; u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = MapBlock_LightColor(255, l, f.light_source); + TileSpec tiles[6]; + video::SColor colors[6]; + for(int j = 0; j < 6; j++) { + // Handles facedir rotation for textures + tiles[j] = getNodeTile(n, p, tile_dirs[j], data); + colors[j]= encode_light_and_color(l, tiles[j].color, + f.light_source); + } v3f pos = intToFloat(p, BS); @@ -1696,11 +1794,6 @@ 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; @@ -1747,18 +1840,19 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // front tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, tiles, 6, c, txc); + makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source); } break;} case NDT_MESH: { v3f pos = intToFloat(p, BS); - video::SColor c = MapBlock_LightColor(255, getInteriorLight(n, 1, nodedef), f.light_source); - + u16 l = getInteriorLight(n, 1, nodedef); u8 facedir = 0; - if (f.param_type_2 == CPT2_FACEDIR) { + if (f.param_type_2 == CPT2_FACEDIR || + f.param_type_2 == CPT2_COLORED_FACEDIR) { facedir = n.getFaceDir(nodedef); - } else if (f.param_type_2 == CPT2_WALLMOUNTED) { + } else if (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { //convert wallmounted to 6dfacedir. //when cache enabled, it is already converted facedir = n.getWallMounted(nodedef); @@ -1771,10 +1865,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if (f.mesh_ptr[facedir]) { // use cached meshes for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) { + const TileSpec &tile = getNodeTileN(n, p, j, data); scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j); - collector.append(getNodeTileN(n, p, j, data), - (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(), - buf->getIndices(), buf->getIndexCount(), pos, c); + collector.append(tile, (video::S3DVertex *) + buf->getVertices(), buf->getVertexCount(), + buf->getIndices(), buf->getIndexCount(), pos, + encode_light_and_color(l, tile.color, f.light_source), + f.light_source); } } else if (f.mesh_ptr[0]) { // no cache, clone and rotate mesh @@ -1783,10 +1880,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, recalculateBoundingBox(mesh); meshmanip->recalculateNormals(mesh, true, false); for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) { + const TileSpec &tile = getNodeTileN(n, p, j, data); scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - collector.append(getNodeTileN(n, p, j, data), - (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(), - buf->getIndices(), buf->getIndexCount(), pos, c); + collector.append(tile, (video::S3DVertex *) + buf->getVertices(), buf->getVertexCount(), + buf->getIndices(), buf->getIndexCount(), pos, + encode_light_and_color(l, tile.color, f.light_source), + f.light_source); } mesh->drop(); } diff --git a/src/game.cpp b/src/game.cpp index 1070cb1b2..07d429c6b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -642,7 +642,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_fog_distance; CachedVertexShaderSetting m_animation_timer_vertex; CachedPixelShaderSetting m_animation_timer_pixel; - CachedPixelShaderSetting m_day_night_ratio; + CachedPixelShaderSetting m_day_light; CachedPixelShaderSetting m_eye_position_pixel; CachedVertexShaderSetting m_eye_position_vertex; CachedPixelShaderSetting m_minimap_yaw; @@ -674,7 +674,7 @@ public: m_fog_distance("fogDistance"), m_animation_timer_vertex("animationTimer"), m_animation_timer_pixel("animationTimer"), - m_day_night_ratio("dayNightRatio"), + m_day_light("dayLight"), m_eye_position_pixel("eyePosition"), m_eye_position_vertex("eyePosition"), m_minimap_yaw("yawVec"), @@ -717,8 +717,14 @@ public: m_fog_distance.set(&fog_distance, services); - float daynight_ratio = (float)m_client->getEnv().getDayNightRatio() / 1000.f; - m_day_night_ratio.set(&daynight_ratio, services); + u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio(); + video::SColorf sunlight; + get_sunlight_color(&sunlight, daynight_ratio); + float dnc[3] = { + sunlight.r, + sunlight.g, + sunlight.b }; + m_day_light.set(dnc, services); u32 animation_timer = porting::getTimeMs() % 100000; float animation_timer_f = (float)animation_timer / 100000.f; @@ -840,7 +846,8 @@ bool nodePlacementPrediction(Client &client, // Predict param2 for facedir and wallmounted nodes u8 param2 = 0; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) { + if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || + nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) { v3s16 dir = nodepos - neighbourpos; if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { @@ -852,7 +859,8 @@ bool nodePlacementPrediction(Client &client, } } - if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) { + if (nodedef->get(id).param_type_2 == CPT2_FACEDIR || + nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) { v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS); if (abs(dir.X) > abs(dir.Z)) { @@ -3749,11 +3757,9 @@ PointedThing Game::updatePointedThing( light_level = node_light; } - video::SColor c = MapBlock_LightColor(255, light_level, 0); - u8 day = c.getRed(); - u8 night = c.getGreen(); u32 daynight_ratio = client->getEnv().getDayNightRatio(); - finalColorBlend(c, day, night, daynight_ratio); + video::SColor c; + final_color_blend(&c, light_level, daynight_ratio); // Modify final color a bit with time u32 timer = porting::getTimeMs() % 5000; @@ -3964,7 +3970,7 @@ void Game::handleDigging(GameRunData *runData, const ContentFeatures &features = client->getNodeDefManager()->get(n); client->getParticleManager()->addPunchingParticles(client, smgr, - player, nodepos, features.tiles); + player, nodepos, n, features); } } @@ -4011,7 +4017,7 @@ void Game::handleDigging(GameRunData *runData, const ContentFeatures &features = client->getNodeDefManager()->get(wasnode); client->getParticleManager()->addDiggingParticles(client, smgr, - player, nodepos, features.tiles); + player, nodepos, wasnode, features); } runData->dig_time = 0; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 143adb410..dfd6f55a5 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -32,12 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/directiontables.h" #include -static void applyFacesShading(video::SColor &color, const float factor) -{ - color.setRed(core::clamp(core::round32(color.getRed() * factor), 0, 255)); - color.setGreen(core::clamp(core::round32(color.getGreen() * factor), 0, 255)); -} - /* MeshMakeData */ @@ -321,19 +315,34 @@ u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data) return getSmoothLightCombined(p, data); } -/* - Converts from day + night color values (0..255) - and a given daynight_ratio to the final SColor shown on screen. -*/ -void finalColorBlend(video::SColor& result, - u8 day, u8 night, u32 daynight_ratio) +void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){ + f32 rg = daynight_ratio / 1000.0f - 0.04f; + f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f; + sunlight->r = rg; + sunlight->g = rg; + sunlight->b = b; +} + +void final_color_blend(video::SColor *result, + u16 light, u32 daynight_ratio) { - s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000; - s32 b = rg; + video::SColorf dayLight; + get_sunlight_color(&dayLight, daynight_ratio); + final_color_blend(result, + encode_light_and_color(light, video::SColor(0xFFFFFFFF), 0), dayLight); +} - // Moonlight is blue - b += (day - night) / 13; - rg -= (day - night) / 23; +void final_color_blend(video::SColor *result, + const video::SColor &data, const video::SColorf &dayLight) +{ + static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f); + + video::SColorf c(data); + f32 n = 1 - c.a; + + f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f; + f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f; + f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f; // Emphase blue a bit in darker places // Each entry of this array represents a range of 8 blue levels @@ -341,19 +350,13 @@ void finalColorBlend(video::SColor& result, 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8]; - b = irr::core::clamp(b, 0, 255); - // Artificial light is yellow-ish - static const u8 emphase_yellow_when_artificial[16] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15 - }; - rg += emphase_yellow_when_artificial[night/16]; - rg = irr::core::clamp(rg, 0, 255); + b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255), + 0, 255) / 8] / 255.0f; - result.setRed(rg); - result.setGreen(rg); - result.setBlue(b); + result->setRed(core::clamp((s32) (r * 255.0f), 0, 255)); + result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255)); + result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255)); } /* @@ -430,7 +433,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, std::vector &dest) + v3f p, v3s16 dir, v3f scale, std::vector &dest) { // Position is at the center of the cube. v3f pos = p * BS; @@ -580,24 +583,25 @@ static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, v3f normal(dir.X, dir.Y, dir.Z); - u8 alpha = tile.alpha; - dest.push_back(FastFace()); FastFace& face = *dest.rbegin(); - face.vertices[0] = video::S3DVertex(vertex_pos[0], normal, - MapBlock_LightColor(alpha, li0, light_source), - core::vector2d(x0+w*abs_scale, y0+h)); - face.vertices[1] = video::S3DVertex(vertex_pos[1], normal, - MapBlock_LightColor(alpha, li1, light_source), - core::vector2d(x0, y0+h)); - face.vertices[2] = video::S3DVertex(vertex_pos[2], normal, - MapBlock_LightColor(alpha, li2, light_source), - core::vector2d(x0, y0)); - face.vertices[3] = video::S3DVertex(vertex_pos[3], normal, - MapBlock_LightColor(alpha, li3, light_source), - core::vector2d(x0+w*abs_scale, y0)); + u16 li[4] = { li0, li1, li2, li3 }; + v2f32 f[4] = { + core::vector2d(x0 + w * abs_scale, y0 + h), + core::vector2d(x0, y0 + h), + core::vector2d(x0, y0), + core::vector2d(x0 + w * abs_scale, y0) }; + + for (u8 i = 0; i < 4; i++) { + video::SColor c = encode_light_and_color(li[i], tile.color, + tile.emissive_light); + if (!tile.emissive_light) + applyFacesShading(c, normal); + + face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]); + } face.tile = tile; } @@ -664,7 +668,10 @@ static u8 face_contents(content_t m1, content_t m2, bool *equivalent, TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data) { INodeDefManager *ndef = data->m_client->ndef(); - TileSpec spec = ndef->get(mn).tiles[tileindex]; + const ContentFeatures &f = ndef->get(mn); + TileSpec spec = f.tiles[tileindex]; + if (!spec.has_color) + mn.getColor(f, &spec.color); // Apply temporary crack if (p == data->m_crack_pos_relative) spec.material_flags |= MATERIAL_FLAG_CRACK; @@ -747,8 +754,7 @@ static void getTileInfo( v3s16 &p_corrected, v3s16 &face_dir_corrected, u16 *lights, - TileSpec &tile, - u8 &light_source + TileSpec &tile ) { VoxelManipulator &vmanip = data->m_vmanip; @@ -763,7 +769,8 @@ static void getTileInfo( return; } - const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir); + const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags( + blockpos_nodes + p + face_dir); if (n1.getContent() == CONTENT_IGNORE) { makes_face = false; @@ -783,26 +790,25 @@ static void getTileInfo( makes_face = true; - if(mf == 1) - { - tile = getNodeTile(n0, p, face_dir, data); + MapNode n = n0; + + if (mf == 1) { p_corrected = p; face_dir_corrected = face_dir; - light_source = ndef->get(n0).light_source; - } - else - { - tile = getNodeTile(n1, p + face_dir, -face_dir, data); + } else { + n = n1; p_corrected = p + face_dir; face_dir_corrected = -face_dir; - light_source = ndef->get(n1).light_source; } + tile = getNodeTile(n, p_corrected, face_dir_corrected, data); + const ContentFeatures &f = ndef->get(n); + tile.emissive_light = f.light_source; // eg. water and glass - if(equivalent) + if (equivalent) tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; - if(data->m_smooth_lighting == false) + if (data->m_smooth_lighting == false) { lights[0] = lights[1] = lights[2] = lights[3] = getFaceLight(n0, n1, face_dir, ndef); @@ -845,10 +851,9 @@ static void updateFastFaceRow( v3s16 face_dir_corrected; u16 lights[4] = {0,0,0,0}; TileSpec tile; - u8 light_source = 0; getTileInfo(data, p, face_dir, makes_face, p_corrected, face_dir_corrected, - lights, tile, light_source); + lights, tile); for(u16 j=0; javg("Meshgen: faces drawn by tiling", 0); for(int i = 1; i < continuous_tiles_count; i++){ @@ -958,7 +962,6 @@ static void updateFastFaceRow( lights[2] = next_lights[2]; lights[3] = next_lights[3]; tile = next_tile; - light_source = next_light_source; p = p_next; } } @@ -1083,12 +1086,14 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): const u16 *indices_p = indices; /* - Revert triangles for nicer looking gradient if vertices - 1 and 3 have same color or 0 and 2 have different color. - getRed() is the day color. + Revert triangles for nicer looking gradient if the + brightness of vertices 1 and 3 differ less than + the brightness of vertices 0 and 2. */ - if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed() - || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed()) + if (abs(f.vertices[0].Color.getAverage() + - f.vertices[2].Color.getAverage()) + > abs(f.vertices[1].Color.getAverage() + - f.vertices[3].Color.getAverage())) indices_p = indices_alternate; collector.append(f.tile, f.vertices, 4, indices_p, 6); @@ -1148,43 +1153,30 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): p.tile.texture = animation_frame.texture; } - u32 vertex_count = m_use_tangent_vertices ? - p.tangent_vertices.size() : p.vertices.size(); - for (u32 j = 0; j < vertex_count; j++) { - v3f *Normal; - video::SColor *vc; - if (m_use_tangent_vertices) { - vc = &p.tangent_vertices[j].Color; - Normal = &p.tangent_vertices[j].Normal; - } else { - vc = &p.vertices[j].Color; - Normal = &p.vertices[j].Normal; - } - // Note applyFacesShading second parameter is precalculated sqrt - // value for speed improvement - // Skip it for lightsources and top faces. - if (!vc->getBlue()) { - if (Normal->Y < -0.5) { - applyFacesShading(*vc, 0.447213); - } else if (Normal->X > 0.5) { - applyFacesShading(*vc, 0.670820); - } else if (Normal->X < -0.5) { - applyFacesShading(*vc, 0.670820); - } else if (Normal->Z > 0.5) { - applyFacesShading(*vc, 0.836660); - } else if (Normal->Z < -0.5) { - applyFacesShading(*vc, 0.836660); - } - } - if (!m_enable_shaders) { - // - Classic lighting (shaders handle this by themselves) - // Set initial real color and store for later updates - u8 day = vc->getRed(); - u8 night = vc->getGreen(); - finalColorBlend(*vc, day, night, 1000); - if (day != night) { - m_daynight_diffs[i][j] = std::make_pair(day, night); + if (!m_enable_shaders) { + // Extract colors for day-night animation + // Dummy sunlight to handle non-sunlit areas + video::SColorf sunlight; + get_sunlight_color(&sunlight, 0); + u32 vertex_count = + m_use_tangent_vertices ? + p.tangent_vertices.size() : p.vertices.size(); + for (u32 j = 0; j < vertex_count; j++) { + video::SColor *vc; + if (m_use_tangent_vertices) { + vc = &p.tangent_vertices[j].Color; + } else { + vc = &p.vertices[j].Color; } + video::SColor copy(*vc); + if (vc->getAlpha() == 0) // No sunlight - no need to animate + final_color_blend(vc, copy, sunlight); // Finalize color + else // Record color to animate + m_daynight_diffs[i][j] = copy; + + // The sunlight ratio has been stored, + // delete alpha (for the final rendering). + vc->setAlpha(255); } } @@ -1358,19 +1350,19 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_rat if (m_enable_vbo) { m_mesh->setDirty(); } - for(std::map > >::iterator + video::SColorf day_color; + get_sunlight_color(&day_color, daynight_ratio); + for(std::map >::iterator i = m_daynight_diffs.begin(); i != m_daynight_diffs.end(); ++i) { scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first); video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); - for(std::map >::iterator + for(std::map::iterator j = i->second.begin(); j != i->second.end(); ++j) { - u8 day = j->second.first; - u8 night = j->second.second; - finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio); + final_color_blend(&(vertices[j->first].Color), j->second, day_color); } } m_last_daynight_ratio = daynight_ratio; @@ -1452,7 +1444,7 @@ void MeshCollector::append(const TileSpec &tile, void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - v3f pos, video::SColor c) + v3f pos, video::SColor c, u8 light_source) { if (numIndices > 65535) { dstream<<"FIXME: MeshCollector::append() called with numIndices="<tangent_vertices.size(); for (u32 i = 0; i < numVertices; i++) { + if (!light_source) { + c = original_c; + applyFacesShading(c, vertices[i].Normal); + } video::S3DVertexTangents vert(vertices[i].Pos + pos, vertices[i].Normal, c, vertices[i].TCoords); p->tangent_vertices.push_back(vert); @@ -1489,8 +1486,12 @@ void MeshCollector::append(const TileSpec &tile, } else { vertex_count = p->vertices.size(); for (u32 i = 0; i < numVertices; i++) { - video::S3DVertex vert(vertices[i].Pos + pos, - vertices[i].Normal, c, vertices[i].TCoords); + if (!light_source) { + c = original_c; + applyFacesShading(c, vertices[i].Normal); + } + video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c, + vertices[i].TCoords); p->vertices.push_back(vert); } } @@ -1500,3 +1501,33 @@ void MeshCollector::append(const TileSpec &tile, p->indices.push_back(j); } } + +video::SColor encode_light_and_color(u16 light, const video::SColor &color, + u8 emissive_light) +{ + // Get components + f32 day = (light & 0xff) / 255.0f; + f32 night = (light >> 8) / 255.0f; + // Add emissive light + night += emissive_light * 0.01f; + if (night > 255) + night = 255; + // Since we don't know if the day light is sunlight or + // artificial light, assume it is artificial when the night + // light bank is also lit. + if (day < night) + day = 0; + else + day = day - night; + f32 sum = day + night; + // Ratio of sunlight: + float r; + if (sum > 0) + r = day / sum; + else + r = 0; + // Average light: + float b = (day + night) / 2; + return video::SColor(r * 255, b * color.getRed(), b * color.getGreen(), + b * color.getBlue()); +} diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 5adb7df3f..916703f3e 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -156,8 +156,8 @@ private: // Animation info: day/night transitions // Last daynight_ratio value passed to animate() u32 m_last_daynight_ratio; - // For each meshbuffer, maps vertex indices to (day,night) pairs - std::map > > m_daynight_diffs; + // For each meshbuffer, stores pre-baked colors of sunlit vertices + std::map > m_daynight_diffs; // Camera offset info -> do we have to translate the mesh? v3s16 m_camera_offset; @@ -192,28 +192,53 @@ struct MeshCollector void append(const TileSpec &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - v3f pos, video::SColor c); + v3f pos, video::SColor c, u8 light_source); }; -// This encodes -// alpha in the A channel of the returned SColor -// day light (0-255) in the R channel of the returned SColor -// night light (0-255) in the G channel of the returned SColor -// light source (0-255) in the B channel of the returned SColor -inline video::SColor MapBlock_LightColor(u8 alpha, u16 light, u8 light_source=0) -{ - return video::SColor(alpha, (light & 0xff), (light >> 8), light_source); -} +/*! + * Encodes light and color of a node. + * The result is not the final color, but a + * half-baked vertex color. + * + * \param light the first 8 bits are day light, + * the last 8 bits are night light + * \param color the node's color + * \param emissive_light amount of light the surface emits, + * from 0 to LIGHT_SUN. + */ +video::SColor encode_light_and_color(u16 light, const video::SColor &color, + u8 emissive_light); // Compute light at node u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef); u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef); u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data); -// Converts from day + night color values (0..255) -// and a given daynight_ratio to the final SColor shown on screen. -void finalColorBlend(video::SColor& result, - u8 day, u8 night, u32 daynight_ratio); +/*! + * Returns the sunlight's color from the current + * day-night ratio. + */ +void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio); + +/*! + * Gives the final SColor shown on screen. + * + * \param result output color + * \param light first 8 bits are day light, second 8 bits are + * night light + */ +void final_color_blend(video::SColor *result, + u16 light, u32 daynight_ratio); + +/*! + * Gives the final SColor shown on screen. + * + * \param result output color + * \param data the half-baked vertex color + * \param dayLight color of the sunlight + */ +void final_color_blend(video::SColor *result, + const video::SColor &data, const video::SColorf &dayLight); // Retrieves the TileSpec of a face of a node // Adds MATERIAL_FLAG_CRACK if the node is cracked diff --git a/src/mapnode.cpp b/src/mapnode.cpp index f1a7f3e61..d835daba2 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -55,6 +55,15 @@ MapNode::MapNode(INodeDefManager *ndef, const std::string &name, param2 = a_param2; } +void MapNode::getColor(const ContentFeatures &f, video::SColor *color) const +{ + if (f.palette) { + *color = (*f.palette)[param2]; + return; + } + *color = f.color; +} + void MapNode::setLight(enum LightBank bank, u8 a_light, const ContentFeatures &f) { // If node doesn't contain light data, ignore this @@ -146,7 +155,8 @@ bool MapNode::getLightBanks(u8 &lightday, u8 &lightnight, INodeDefManager *nodem u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const { const ContentFeatures &f = nodemgr->get(*this); - if(f.param_type_2 == CPT2_FACEDIR) + if (f.param_type_2 == CPT2_FACEDIR || + f.param_type_2 == CPT2_COLORED_FACEDIR) return (getParam2() & 0x1F) % 24; return 0; } @@ -154,7 +164,8 @@ u8 MapNode::getFaceDir(INodeDefManager *nodemgr) const u8 MapNode::getWallMounted(INodeDefManager *nodemgr) const { const ContentFeatures &f = nodemgr->get(*this); - if(f.param_type_2 == CPT2_WALLMOUNTED) + if (f.param_type_2 == CPT2_WALLMOUNTED || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED) return getParam2() & 0x07; return 0; } @@ -176,7 +187,7 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) { ContentParamType2 cpt2 = nodemgr->get(*this).param_type_2; - if (cpt2 == CPT2_FACEDIR) { + if (cpt2 == CPT2_FACEDIR || cpt2 == CPT2_COLORED_FACEDIR) { static const u8 rotate_facedir[24 * 4] = { // Table value = rotated facedir // Columns: 0, 90, 180, 270 degrees rotation around vertical axis @@ -216,7 +227,8 @@ void MapNode::rotateAlongYAxis(INodeDefManager *nodemgr, Rotation rot) u8 index = facedir * 4 + rot; param2 &= ~31; param2 |= rotate_facedir[index]; - } else if (cpt2 == CPT2_WALLMOUNTED) { + } else if (cpt2 == CPT2_WALLMOUNTED || + cpt2 == CPT2_COLORED_WALLMOUNTED) { u8 wmountface = (param2 & 7); if (wmountface <= 1) return; diff --git a/src/mapnode.h b/src/mapnode.h index ae0245cfe..9c56a7e17 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -20,9 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef MAPNODE_HEADER #define MAPNODE_HEADER -#include "irrlichttypes.h" -#include "irr_v3d.h" -#include "irr_aabb3d.h" +#include "irrlichttypes_bloated.h" #include "light.h" #include #include @@ -187,6 +185,14 @@ struct MapNode param2 = p; } + /*! + * Returns the color of the node. + * + * \param f content features of this node + * \param color output, contains the node's color. + */ + void getColor(const ContentFeatures &f, video::SColor *color) const; + void setLight(enum LightBank bank, u8 a_light, const ContentFeatures &f); void setLight(enum LightBank bank, u8 a_light, INodeDefManager *nodemgr); diff --git a/src/mesh.cpp b/src/mesh.cpp index 50465748c..84689b631 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -33,13 +33,25 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MY_ETLM_READ_ONLY video::ETLM_READ_ONLY #endif -static void applyFacesShading(video::SColor& color, float factor) +inline static void applyShadeFactor(video::SColor& color, float factor) { color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255)); color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255)); color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255)); } +void applyFacesShading(video::SColor &color, const v3f &normal) +{ + // Many special drawtypes have normals set to 0,0,0 and this + // must result in maximum brightness (no face shadng). + if (normal.Y < -0.5f) + applyShadeFactor (color, 0.447213f); + else if (normal.X > 0.5f || normal.X < -0.5f) + applyShadeFactor (color, 0.670820f); + else if (normal.Z > 0.5f || normal.Z < -0.5f) + applyShadeFactor (color, 0.836660f); +} + scene::IAnimatedMesh* createCubeMesh(v3f scale) { video::SColor c(255,255,255,255); @@ -172,29 +184,18 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) } } -void shadeMeshFaces(scene::IMesh *mesh) +void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor) { - if (mesh == NULL) - return; - - u32 mc = mesh->getMeshBufferCount(); - for (u32 j = 0; j < mc; j++) { - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) { - video::S3DVertex *vertex = (video::S3DVertex *)(vertices + i * stride); - video::SColor &vc = vertex->Color; - // Many special drawtypes have normals set to 0,0,0 and this - // must result in maximum brightness (no face shadng). - if (vertex->Normal.Y < -0.5f) - applyFacesShading (vc, 0.447213f); - else if (vertex->Normal.X > 0.5f || vertex->Normal.X < -0.5f) - applyFacesShading (vc, 0.670820f); - else if (vertex->Normal.Z > 0.5f || vertex->Normal.Z < -0.5f) - applyFacesShading (vc, 0.836660f); - } + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + u32 vertex_count = buf->getVertexCount(); + u8 *vertices = (u8 *) buf->getVertices(); + for (u32 i = 0; i < vertex_count; i++) { + video::S3DVertex *vertex = (video::S3DVertex *) (vertices + i * stride); + video::SColor *vc = &(vertex->Color); + // Reset color + *vc = *buffercolor; + // Apply shading + applyFacesShading(*vc, vertex->Normal); } } diff --git a/src/mesh.h b/src/mesh.h index 10df97015..bcf0d771c 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -23,6 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "nodedef.h" +/*! + * Applies shading to a color based on the surface's + * normal vector. + */ +void applyFacesShading(video::SColor &color, const v3f &normal); + /* Create a new cube mesh. Vertices are at (+-scale.X/2, +-scale.Y/2, +-scale.Z/2). @@ -48,11 +54,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec); */ void setMeshColor(scene::IMesh *mesh, const video::SColor &color); -/* - Shade mesh faces according to their normals -*/ - -void shadeMeshFaces(scene::IMesh *mesh); +void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolor); /* Set the color of all vertices in the mesh. @@ -87,7 +89,7 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees); scene::IMesh* cloneMesh(scene::IMesh *src_mesh); /* - Convert nodeboxes to mesh. + Convert nodeboxes to mesh. Each tile goes into a different buffer. boxes - set of nodeboxes to be converted into cuboids uv_coords[24] - table of texture uv coords for each cuboid face expand - factor by which cuboids will be resized diff --git a/src/minimap.cpp b/src/minimap.cpp index f49adb517..a2e501751 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -144,7 +144,7 @@ MinimapPixel *MinimapUpdateThread::getMinimapPixel(v3s16 pos, if (it != m_blocks_cache.end()) { MinimapMapblock *mmblock = it->second; MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X]; - if (pixel->id != CONTENT_AIR) { + if (pixel->n.param0 != CONTENT_AIR) { *pixel_height = height + pixel->height; return pixel; } @@ -187,7 +187,7 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar) for (s16 x = 0; x < size; x++) for (s16 z = 0; z < size; z++) { - u16 id = CONTENT_AIR; + MapNode n(CONTENT_AIR); MinimapPixel *mmpixel = &data->minimap_scan[x + z * size]; if (!is_radar) { @@ -195,14 +195,14 @@ void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar) MinimapPixel *cached_pixel = getMinimapPixel(v3s16(p.X + x, p.Y, p.Z + z), height, &pixel_height); if (cached_pixel) { - id = cached_pixel->id; + n = cached_pixel->n; mmpixel->height = pixel_height; } } else { mmpixel->air_count = getAirCount(v3s16(p.X + x, p.Y, p.Z + z), height); } - mmpixel->id = id; + mmpixel->n = n; } } @@ -372,10 +372,21 @@ void Mapper::blitMinimapPixelsToImageSurface( for (s16 z = 0; z < data->map_size; z++) { MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size]; - video::SColor c = m_ndef->get(mmpixel->id).minimap_color; - c.setAlpha(240); - - map_image->setPixel(x, data->map_size - z - 1, c); + const ContentFeatures &f = m_ndef->get(mmpixel->n); + const TileDef *tile = &f.tiledef[0]; + // Color of the 0th tile (mostly this is the topmost) + video::SColor tilecolor; + if(tile->has_color) + tilecolor = tile->color; + else + mmpixel->n.getColor(f, &tilecolor); + tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255); + tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() + / 255); + tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255); + tilecolor.setAlpha(240); + + map_image->setPixel(x, data->map_size - z - 1, tilecolor); u32 h = mmpixel->height; heightmap_image->setPixel(x,data->map_size - z - 1, @@ -617,7 +628,7 @@ void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos) MapNode n = vmanip->getNodeNoEx(pos + p); if (!surface_found && n.getContent() != CONTENT_AIR) { mmpixel->height = y; - mmpixel->id = n.getContent(); + mmpixel->n = n; surface_found = true; } else if (n.getContent() == CONTENT_AIR) { air_count++; @@ -625,7 +636,7 @@ void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, v3s16 pos) } if (!surface_found) - mmpixel->id = CONTENT_AIR; + mmpixel->n = MapNode(CONTENT_AIR); mmpixel->air_count = air_count; } diff --git a/src/minimap.h b/src/minimap.h index 743b2bff2..81ed0e49f 100644 --- a/src/minimap.h +++ b/src/minimap.h @@ -52,10 +52,10 @@ struct MinimapModeDef { }; struct MinimapPixel { - u16 id; + //! The topmost node that the minimap displays. + MapNode n; u16 height; u16 air_count; - u16 light; }; struct MinimapMapblock { diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 23c8a665b..a511d169b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -143,9 +143,12 @@ with this program; if not, write to the Free Software Foundation, Inc., serialization of TileAnimation params changed TAT_SHEET_2D Removed client-sided chat perdiction + PROTOCOL VERSION 30: + New ContentFeatures serialization version + Add node and tile color and palette */ -#define LATEST_PROTOCOL_VERSION 29 +#define LATEST_PROTOCOL_VERSION 30 // Server's supported network protocol range #define SERVER_PROTOCOL_VERSION_MIN 13 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index a4af26e87..98f795c7a 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -189,7 +189,9 @@ void NodeBox::deSerialize(std::istream &is) void TileDef::serialize(std::ostream &os, u16 protocol_version) const { - if (protocol_version >= 29) + if (protocol_version >= 30) + writeU8(os, 4); + else if (protocol_version >= 29) writeU8(os, 3); else if (protocol_version >= 26) writeU8(os, 2); @@ -205,6 +207,14 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, tileable_horizontal); writeU8(os, tileable_vertical); } + if (protocol_version >= 30) { + writeU8(os, has_color); + if (has_color) { + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + } + } } void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype) @@ -218,6 +228,14 @@ void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, con tileable_horizontal = readU8(is); tileable_vertical = readU8(is); } + if (version >= 4) { + has_color = readU8(is); + if (has_color) { + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + } + } if ((contenfeatures_version < 8) && ((drawtype == NDT_MESH) || @@ -351,172 +369,222 @@ void ContentFeatures::reset() connects_to.clear(); connects_to_ids.clear(); connect_sides = 0; + color = video::SColor(0xFFFFFFFF); + palette_name = ""; + palette = NULL; } void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version < 24){ + if (protocol_version < 30) { serializeOld(os, protocol_version); return; } - writeU8(os, protocol_version < 27 ? 7 : 8); + // version + writeU8(os, 9); - os<first); + for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end(); + ++i) { + os << serializeString(i->first); writeS16(os, i->second); } + writeU8(os, param_type); + writeU8(os, param_type_2); + + // visual writeU8(os, drawtype); + os << serializeString(mesh); writeF1000(os, visual_scale); writeU8(os, 6); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++){ + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); + writeU8(os, color.getRed()); + writeU8(os, color.getGreen()); + writeU8(os, color.getBlue()); + os << serializeString(palette_name); + writeU8(os, waving); + writeU8(os, connect_sides); + writeU16(os, connects_to_ids.size()); + for (std::set::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); writeU8(os, post_effect_color.getAlpha()); writeU8(os, post_effect_color.getRed()); writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); - writeU8(os, param_type); - if ((protocol_version < 28) && (param_type_2 == CPT2_MESHOPTIONS)) - writeU8(os, CPT2_NONE); - else - writeU8(os, param_type_2); - writeU8(os, is_ground_content); + writeU8(os, leveled); + + // lighting writeU8(os, light_propagates); writeU8(os, sunlight_propagates); + writeU8(os, light_source); + + // map generation + writeU8(os, is_ground_content); + + // interaction writeU8(os, walkable); writeU8(os, pointable); writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); - os<::const_iterator i = connects_to_ids.begin(); - i != connects_to_ids.end(); ++i) - writeU16(os, *i); - writeU8(os, connect_sides); + + // legacy + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); +} + +void ContentFeatures::correctAlpha() +{ + if (alpha == 0 || alpha == 255) + return; + + for (u32 i = 0; i < 6; i++) { + std::stringstream s; + s << tiledef[i].name << "^[noalpha^[opacity:" << ((int)alpha); + tiledef[i].name = s.str(); + } + + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { + std::stringstream s; + s << tiledef_special[i].name << "^[noalpha^[opacity:" << ((int)alpha); + tiledef_special[i].name = s.str(); + } } void ContentFeatures::deSerialize(std::istream &is) { + // version detection int version = readU8(is); - if (version < 7) { + if (version < 9) { deSerializeOld(is, version); return; - } else if (version > 8) { + } else if (version > 9) { throw SerializationError("unsupported ContentFeatures version"); } + // general name = deSerializeString(is); groups.clear(); u32 groups_size = readU16(is); - for(u32 i = 0; i < groups_size; i++){ + for (u32 i = 0; i < groups_size; i++) { std::string name = deSerializeString(is); int value = readS16(is); groups[name] = value; } - drawtype = (enum NodeDrawType)readU8(is); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + // visual + drawtype = (enum NodeDrawType) readU8(is); + mesh = deSerializeString(is); visual_scale = readF1000(is); - if(readU8(is) != 6) + if (readU8(is) != 6) throw SerializationError("unsupported tile count"); - for(u32 i = 0; i < 6; i++) + for (u32 i = 0; i < 6; i++) tiledef[i].deSerialize(is, version, drawtype); - if(readU8(is) != CF_SPECIAL_COUNT) + if (readU8(is) != CF_SPECIAL_COUNT) throw SerializationError("unsupported CF_SPECIAL_COUNT"); - for(u32 i = 0; i < CF_SPECIAL_COUNT; i++) + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) tiledef_special[i].deSerialize(is, version, drawtype); alpha = readU8(is); + color.setRed(readU8(is)); + color.setGreen(readU8(is)); + color.setBlue(readU8(is)); + palette_name = deSerializeString(is); + waving = readU8(is); + connect_sides = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); post_effect_color.setAlpha(readU8(is)); post_effect_color.setRed(readU8(is)); post_effect_color.setGreen(readU8(is)); post_effect_color.setBlue(readU8(is)); - param_type = (enum ContentParamType)readU8(is); - param_type_2 = (enum ContentParamType2)readU8(is); - is_ground_content = readU8(is); + leveled = readU8(is); + + // lighting-related light_propagates = readU8(is); sunlight_propagates = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + + // map generation + is_ground_content = readU8(is); + + // interaction walkable = readU8(is); pointable = readU8(is); diggable = readU8(is); climbable = readU8(is); buildable_to = readU8(is); - deSerializeString(is); // legacy: used to be metadata_name - liquid_type = (enum LiquidType)readU8(is); + rightclickable = readU8(is); + damage_per_second = readU32(is); + + // liquid + liquid_type = (enum LiquidType) readU8(is); liquid_alternative_flowing = deSerializeString(is); liquid_alternative_source = deSerializeString(is); liquid_viscosity = readU8(is); liquid_renewable = readU8(is); - light_source = readU8(is); - light_source = MYMIN(light_source, LIGHT_MAX); - damage_per_second = readU32(is); + liquid_range = readU8(is); + drowning = readU8(is); + floodable = readU8(is); + + // node boxes node_box.deSerialize(is); selection_box.deSerialize(is); - legacy_facedir_simple = readU8(is); - legacy_wallmounted = readU8(is); + collision_box.deSerialize(is); + + // sounds deSerializeSimpleSoundSpec(sound_footstep, is); deSerializeSimpleSoundSpec(sound_dig, is); deSerializeSimpleSoundSpec(sound_dug, is); - rightclickable = readU8(is); - drowning = readU8(is); - leveled = readU8(is); - liquid_range = readU8(is); - waving = 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 - mesh = deSerializeString(is); - collision_box.deSerialize(is); - floodable = readU8(is); - u16 connects_to_size = readU16(is); - connects_to_ids.clear(); - for (u16 i = 0; i < connects_to_size; i++) - connects_to_ids.insert(readU16(is)); - connect_sides = readU8(is); - }catch(SerializationError &e) {}; + + // read legacy properties + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); } #ifndef SERVER void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, - bool backface_culling, u8 alpha, u8 material_type) + bool backface_culling, u8 material_type) { tile->shader_id = shader_id; tile->texture = tsrc->getTextureForMesh(tiledef->name, &tile->texture_id); - tile->alpha = alpha; tile->material_type = material_type; // Normal texture and shader flags texture @@ -536,6 +604,13 @@ void ContentFeatures::fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, if (tiledef->tileable_vertical) tile->material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL; + // Color + tile->has_color = tiledef->has_color; + if (tiledef->has_color) + tile->color = tiledef->color; + else + tile->color = color; + // Animation parameters int frame_count = 1; if (tile->material_flags & MATERIAL_FLAG_ANIMATION) { @@ -681,6 +756,9 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc is_water_surface = true; } + // Vertex alpha is no longer supported, correct if necessary. + correctAlpha(); + u32 tile_shader[6]; for (u16 j = 0; j < 6; j++) { tile_shader[j] = shdsrc->getShader("nodes_shader", @@ -696,14 +774,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc for (u16 j = 0; j < 6; j++) { fillTileAttribs(tsrc, &tiles[j], &tdef[j], tile_shader[j], tsettings.use_normal_texture, - tiledef[j].backface_culling, alpha, material_type); + tiledef[j].backface_culling, material_type); } // Special tiles (fill in f->special_tiles[]) for (u16 j = 0; j < CF_SPECIAL_COUNT; j++) { fillTileAttribs(tsrc, &special_tiles[j], &tiledef_special[j], tile_shader[j], tsettings.use_normal_texture, - tiledef_special[j].backface_culling, alpha, material_type); + tiledef_special[j].backface_culling, material_type); } if ((drawtype == NDT_MESH) && (mesh != "")) { @@ -731,15 +809,19 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc } //Cache 6dfacedir and wallmounted rotated clones of meshes - if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_FACEDIR)) { + if (tsettings.enable_mesh_cache && mesh_ptr[0] && + (param_type_2 == CPT2_FACEDIR + || param_type_2 == CPT2_COLORED_FACEDIR)) { for (u16 j = 1; j < 24; j++) { mesh_ptr[j] = cloneMesh(mesh_ptr[0]); rotateMeshBy6dFacedir(mesh_ptr[j], j); recalculateBoundingBox(mesh_ptr[j]); meshmanip->recalculateNormals(mesh_ptr[j], true, false); } - } else if (tsettings.enable_mesh_cache && mesh_ptr[0] && (param_type_2 == CPT2_WALLMOUNTED)) { - static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; + } else if (tsettings.enable_mesh_cache && mesh_ptr[0] + && (param_type_2 == CPT2_WALLMOUNTED || + param_type_2 == CPT2_COLORED_WALLMOUNTED)) { + static const u8 wm_to_6d[6] = { 20, 0, 16 + 1, 12 + 3, 8, 4 + 2 }; for (u16 j = 1; j < 6; j++) { mesh_ptr[j] = cloneMesh(mesh_ptr[0]); rotateMeshBy6dFacedir(mesh_ptr[j], wm_to_6d[j]); @@ -775,6 +857,9 @@ public: virtual void removeNode(const std::string &name); virtual void updateAliases(IItemDefManager *idef); virtual void applyTextureOverrides(const std::string &override_filepath); + //! Returns a palette or NULL if not found. Only on client. + std::vector *getPalette(const ContentFeatures &f, + const IGameDef *gamedef); virtual void updateTextures(IGameDef *gamedef, void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress), void *progress_cbk_args); @@ -823,6 +908,9 @@ private: // Next possibly free id content_t m_next_id; + // Maps image file names to loaded palettes. + UNORDERED_MAP > m_palettes; + // NodeResolvers to callback once node registration has ended std::vector m_pending_resolve_callbacks; @@ -1062,7 +1150,8 @@ void getNodeBoxUnion(const NodeBox &nodebox, const ContentFeatures &features, if (nodebox.type == NODEBOX_LEVELED) { half_processed.MaxEdge.Y = +BS / 2; } - if (features.param_type_2 == CPT2_FACEDIR) { + if (features.param_type_2 == CPT2_FACEDIR || + features.param_type_2 == CPT2_COLORED_FACEDIR) { // Get maximal coordinate f32 coords[] = { fabsf(half_processed.MinEdge.X), @@ -1309,6 +1398,78 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath } } +std::vector *CNodeDefManager::getPalette( + const ContentFeatures &f, const IGameDef *gamedef) +{ +#ifndef SERVER + // This works because colors always use the most significant bits + // of param2. If you add a new colored type which uses param2 + // in a more advanced way, you should change this code, too. + u32 palette_pixels = 0; + switch (f.param_type_2) { + case CPT2_COLOR: + palette_pixels = 256; + break; + case CPT2_COLORED_FACEDIR: + palette_pixels = 8; + break; + case CPT2_COLORED_WALLMOUNTED: + palette_pixels = 32; + break; + default: + return NULL; + } + // This many param2 values will have the same color + u32 step = 256 / palette_pixels; + const std::string &name = f.palette_name; + if (name == "") + return NULL; + Client *client = (Client *) gamedef; + ITextureSource *tsrc = client->tsrc(); + + UNORDERED_MAP >::iterator it = + m_palettes.find(name); + if (it == m_palettes.end()) { + // Create palette + if (!tsrc->isKnownSourceImage(name)) { + warningstream << "CNodeDefManager::getPalette(): palette \"" << name + << "\" could not be loaded." << std::endl; + return NULL; + } + video::IImage *img = tsrc->generateImage(name); + std::vector new_palette; + u32 w = img->getDimension().Width; + u32 h = img->getDimension().Height; + // Real area of the image + u32 area = h * w; + if (area != palette_pixels) + warningstream << "CNodeDefManager::getPalette(): the " + << "specified palette image \"" << name << "\" does not " + << "contain exactly " << palette_pixels + << " pixels." << std::endl; + if (area > palette_pixels) + area = palette_pixels; + // For each pixel in the image + for (u32 i = 0; i < area; i++) { + video::SColor c = img->getPixel(i % w, i / w); + // Fill in palette with 'step' colors + for (u32 j = 0; j < step; j++) + new_palette.push_back(c); + } + img->drop(); + // Fill in remaining elements + while (new_palette.size() < 256) + new_palette.push_back(video::SColor(0xFFFFFFFF)); + m_palettes[name] = new_palette; + it = m_palettes.find(name); + } + if (it != m_palettes.end()) + return &((*it).second); + +#endif + return NULL; +} + void CNodeDefManager::updateTextures(IGameDef *gamedef, void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress), void *progress_callback_args) @@ -1325,10 +1486,13 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, TextureSettings tsettings; tsettings.readSettings(); + m_palettes.clear(); u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { - m_content_features[i].updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); + ContentFeatures *f = &(m_content_features[i]); + f->palette = getPalette(*f, gamedef); + f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } #endif @@ -1429,6 +1593,19 @@ IWritableNodeDefManager *createNodeDefManager() //// Serialization of old ContentFeatures formats void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const { + u8 compatible_param_type_2 = param_type_2; + if ((protocol_version < 28) + && (compatible_param_type_2 == CPT2_MESHOPTIONS)) + compatible_param_type_2 = CPT2_NONE; + else if (protocol_version < 30) { + if (compatible_param_type_2 == CPT2_COLOR) + compatible_param_type_2 = CPT2_NONE; + else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR) + compatible_param_type_2 = CPT2_FACEDIR; + else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED) + compatible_param_type_2 = CPT2_WALLMOUNTED; + } + if (protocol_version == 13) { writeU8(os, 5); // version @@ -1454,7 +1631,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + writeU8(os, compatible_param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); @@ -1483,9 +1660,9 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const os<first); - writeS16(os, i->second); + i = groups.begin(); i != groups.end(); ++i) { + os<first); + writeS16(os, i->second); } writeU8(os, drawtype); writeF1000(os, visual_scale); @@ -1502,7 +1679,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); - writeU8(os, param_type_2); + writeU8(os, compatible_param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); @@ -1530,6 +1707,68 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeU8(os, drowning); writeU8(os, leveled); writeU8(os, liquid_range); + } + else if(protocol_version >= 24 && protocol_version < 30) { + writeU8(os, protocol_version < 27 ? 7 : 8); + + os << serializeString(name); + writeU16(os, groups.size()); + for (ItemGroupList::const_iterator i = groups.begin(); + i != groups.end(); ++i) { + os << serializeString(i->first); + writeS16(os, i->second); + } + writeU8(os, drawtype); + writeF1000(os, visual_scale); + writeU8(os, 6); + for (u32 i = 0; i < 6; i++) + 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, protocol_version); + writeU8(os, alpha); + writeU8(os, post_effect_color.getAlpha()); + writeU8(os, post_effect_color.getRed()); + writeU8(os, post_effect_color.getGreen()); + writeU8(os, post_effect_color.getBlue()); + writeU8(os, param_type); + writeU8(os, compatible_param_type_2); + writeU8(os, is_ground_content); + writeU8(os, light_propagates); + writeU8(os, sunlight_propagates); + writeU8(os, walkable); + writeU8(os, pointable); + writeU8(os, diggable); + writeU8(os, climbable); + writeU8(os, buildable_to); + os << serializeString(""); // legacy: used to be metadata_name + writeU8(os, liquid_type); + os << serializeString(liquid_alternative_flowing); + os << serializeString(liquid_alternative_source); + writeU8(os, liquid_viscosity); + writeU8(os, liquid_renewable); + writeU8(os, light_source); + writeU32(os, damage_per_second); + node_box.serialize(os, protocol_version); + selection_box.serialize(os, protocol_version); + writeU8(os, legacy_facedir_simple); + writeU8(os, legacy_wallmounted); + serializeSimpleSoundSpec(sound_footstep, os); + serializeSimpleSoundSpec(sound_dig, os); + serializeSimpleSoundSpec(sound_dug, os); + writeU8(os, rightclickable); + writeU8(os, drowning); + writeU8(os, leveled); + writeU8(os, liquid_range); + writeU8(os, waving); + os << serializeString(mesh); + collision_box.serialize(os, protocol_version); + writeU8(os, floodable); + writeU16(os, connects_to_ids.size()); + for (std::set::const_iterator i = connects_to_ids.begin(); + i != connects_to_ids.end(); ++i) + writeU16(os, *i); + writeU8(os, connect_sides); } else throw SerializationError("ContentFeatures::serialize(): " "Unsupported version requested"); @@ -1642,7 +1881,73 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version) drowning = readU8(is); leveled = readU8(is); liquid_range = readU8(is); - } else { + } else if (version == 7 || version == 8){ + name = deSerializeString(is); + groups.clear(); + u32 groups_size = readU16(is); + for (u32 i = 0; i < groups_size; i++) { + std::string name = deSerializeString(is); + int value = readS16(is); + groups[name] = value; + } + drawtype = (enum NodeDrawType) readU8(is); + + visual_scale = readF1000(is); + if (readU8(is) != 6) + throw SerializationError("unsupported tile count"); + for (u32 i = 0; i < 6; i++) + tiledef[i].deSerialize(is, version, drawtype); + if (readU8(is) != CF_SPECIAL_COUNT) + throw SerializationError("unsupported CF_SPECIAL_COUNT"); + for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) + tiledef_special[i].deSerialize(is, version, drawtype); + alpha = readU8(is); + post_effect_color.setAlpha(readU8(is)); + post_effect_color.setRed(readU8(is)); + post_effect_color.setGreen(readU8(is)); + post_effect_color.setBlue(readU8(is)); + param_type = (enum ContentParamType) readU8(is); + param_type_2 = (enum ContentParamType2) readU8(is); + is_ground_content = readU8(is); + light_propagates = readU8(is); + sunlight_propagates = readU8(is); + walkable = readU8(is); + pointable = readU8(is); + diggable = readU8(is); + climbable = readU8(is); + buildable_to = readU8(is); + deSerializeString(is); // legacy: used to be metadata_name + liquid_type = (enum LiquidType) readU8(is); + liquid_alternative_flowing = deSerializeString(is); + liquid_alternative_source = deSerializeString(is); + liquid_viscosity = readU8(is); + liquid_renewable = readU8(is); + light_source = readU8(is); + light_source = MYMIN(light_source, LIGHT_MAX); + damage_per_second = readU32(is); + node_box.deSerialize(is); + selection_box.deSerialize(is); + legacy_facedir_simple = readU8(is); + legacy_wallmounted = readU8(is); + deSerializeSimpleSoundSpec(sound_footstep, is); + deSerializeSimpleSoundSpec(sound_dig, is); + deSerializeSimpleSoundSpec(sound_dug, is); + rightclickable = readU8(is); + drowning = readU8(is); + leveled = readU8(is); + liquid_range = readU8(is); + waving = readU8(is); + try { + mesh = deSerializeString(is); + collision_box.deSerialize(is); + floodable = readU8(is); + u16 connects_to_size = readU16(is); + connects_to_ids.clear(); + for (u16 i = 0; i < connects_to_size; i++) + connects_to_ids.insert(readU16(is)); + connect_sides = readU8(is); + } catch (SerializationError &e) {}; + }else{ throw SerializationError("unsupported ContentFeatures version"); } } @@ -1736,19 +2041,23 @@ bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) // does to node declare usable faces? if (f2.connect_sides > 0) { - if ((f2.param_type_2 == CPT2_FACEDIR) && (connect_face >= 4)) { - static const u8 rot[33 * 4] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back - 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 32, 16, 8, 4 // 32 - left - }; - return (f2.connect_sides & rot[(connect_face * 4) + to.param2]); + if ((f2.param_type_2 == CPT2_FACEDIR || + f2.param_type_2 == CPT2_COLORED_FACEDIR) + && (connect_face >= 4)) { + static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 4 - back + 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 8 - right + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // 16 - front + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left + }; + return (f2.connect_sides + & rot[(connect_face * 4) + (to.param2 & 0x1F)]); } return (f2.connect_sides & connect_face); } diff --git a/src/nodedef.h b/src/nodedef.h index 183b95d87..6275b41ce 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -68,7 +68,13 @@ enum ContentParamType2 // 2D rotation for things like plants CPT2_DEGROTATE, // Mesh options for plants - CPT2_MESHOPTIONS + CPT2_MESHOPTIONS, + // Index for palette + CPT2_COLOR, + // 3 bits of palette index, then facedir + CPT2_COLORED_FACEDIR, + // 5 bits of palette index, then wallmounted + CPT2_COLORED_WALLMOUNTED }; enum LiquidType @@ -170,6 +176,11 @@ struct TileDef bool backface_culling; // Takes effect only in special cases bool tileable_horizontal; bool tileable_vertical; + //! If true, the tile has its own color. + bool has_color; + //! The color of the tile. + video::SColor color; + struct TileAnimationParams animation; TileDef() @@ -178,6 +189,8 @@ struct TileDef backface_culling = true; tileable_horizontal = true; tileable_vertical = true; + has_color = false; + color = video::SColor(0xFFFFFFFF); animation.type = TAT_NONE; } @@ -191,7 +204,7 @@ struct ContentFeatures { /* Cached stuff - */ + */ #ifndef SERVER // 0 1 2 3 4 5 // up down right left back front @@ -211,12 +224,19 @@ struct ContentFeatures /* Actual data - */ + */ + + // --- GENERAL PROPERTIES --- std::string name; // "" = undefined node ItemGroupList groups; // Same as in itemdef + // Type of MapNode::param1 + ContentParamType param_type; + // Type of MapNode::param2 + ContentParamType2 param_type_2; + + // --- VISUAL PROPERTIES --- - // Visual definition enum NodeDrawType drawtype; std::string mesh; #ifndef SERVER @@ -226,19 +246,38 @@ struct ContentFeatures float visual_scale; // Misc. scale parameter TileDef tiledef[6]; TileDef tiledef_special[CF_SPECIAL_COUNT]; // eg. flowing liquid + // If 255, the node is opaque. + // Otherwise it uses texture alpha. u8 alpha; - + // The color of the node. + video::SColor color; + std::string palette_name; + std::vector *palette; + // Used for waving leaves/plants + u8 waving; + // for NDT_CONNECTED pairing + u8 connect_sides; + std::vector connects_to; + std::set connects_to_ids; // Post effect color, drawn when the camera is inside the node. video::SColor post_effect_color; + // Flowing liquid or snow, value = default level + u8 leveled; + + // --- LIGHTING-RELATED --- - // Type of MapNode::param1 - ContentParamType param_type; - // Type of MapNode::param2 - ContentParamType2 param_type_2; - // True for all ground-like things like stone and mud, false for eg. trees - bool is_ground_content; bool light_propagates; bool sunlight_propagates; + // Amount of light the node emits + u8 light_source; + + // --- MAP GENERATION --- + + // True for all ground-like things like stone and mud, false for eg. trees + bool is_ground_content; + + // --- INTERACTION PROPERTIES --- + // This is used for collision detection. // Also for general solidness queries. bool walkable; @@ -250,12 +289,12 @@ struct ContentFeatures bool climbable; // Player can build on these bool buildable_to; - // Liquids flow into and replace node - bool floodable; // Player cannot build to these (placement prediction disabled) bool rightclickable; - // Flowing liquid or snow, value = default level - u8 leveled; + u32 damage_per_second; + + // --- LIQUID PROPERTIES --- + // Whether the node is non-liquid, source liquid or flowing liquid enum LiquidType liquid_type; // If the content is liquid, this is the flowing version of the liquid. @@ -271,29 +310,28 @@ struct ContentFeatures // Number of flowing liquids surrounding source u8 liquid_range; u8 drowning; - // Amount of light the node emits - u8 light_source; - u32 damage_per_second; + // Liquids flow into and replace node + bool floodable; + + // --- NODEBOXES --- + NodeBox node_box; NodeBox selection_box; NodeBox collision_box; - // Used for waving leaves/plants - u8 waving; - // Compatibility with old maps - // Set to true if paramtype used to be 'facedir_simple' - bool legacy_facedir_simple; - // Set to true if wall_mounted used to be set to true - bool legacy_wallmounted; - // for NDT_CONNECTED pairing - u8 connect_sides; - // Sound properties + // --- SOUND PROPERTIES --- + SimpleSoundSpec sound_footstep; SimpleSoundSpec sound_dig; SimpleSoundSpec sound_dug; - std::vector connects_to; - std::set connects_to_ids; + // --- LEGACY --- + + // Compatibility with old maps + // Set to true if paramtype used to be 'facedir_simple' + bool legacy_facedir_simple; + // Set to true if wall_mounted used to be set to true + bool legacy_wallmounted; /* Methods @@ -306,6 +344,14 @@ struct ContentFeatures void deSerialize(std::istream &is); void serializeOld(std::ostream &os, u16 protocol_version) const; void deSerializeOld(std::istream &is, int version); + /*! + * Since vertex alpha is no lnger supported, this method + * adds instructions to the texture names to blend alpha there. + * + * tiledef, tiledef_special and alpha must be initialized + * before calling this. + */ + void correctAlpha(); /* Some handy methods @@ -321,7 +367,7 @@ struct ContentFeatures #ifndef SERVER void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, bool backface_culling, - u8 alpha, u8 material_type); + u8 material_type); void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc, scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings); #endif diff --git a/src/particles.cpp b/src/particles.cpp index 5f17763e0..e1f292fb6 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -56,7 +56,8 @@ Particle::Particle( v2f texpos, v2f texsize, const struct TileAnimationParams &anim, - u8 glow + u8 glow, + video::SColor color ): scene::ISceneNode(smgr->getRootSceneNode(), smgr) { @@ -77,6 +78,10 @@ Particle::Particle( m_animation_frame = 0; m_animation_time = 0.0; + // Color + m_base_color = color; + m_color = color; + // Particle related m_pos = pos; m_velocity = velocity; @@ -183,12 +188,15 @@ void Particle::updateLight() else light = blend_light(m_env->getDayNightRatio(), LIGHT_SUN, 0); - m_light = decode_light(light + m_glow); + u8 m_light = decode_light(light + m_glow); + m_color.set(255, + m_light * m_base_color.getRed() / 255, + m_light * m_base_color.getGreen() / 255, + m_light * m_base_color.getBlue() / 255); } void Particle::updateVertices() { - video::SColor c(255, m_light, m_light, m_light); f32 tx0, tx1, ty0, ty1; if (m_animation.type != TAT_NONE) { @@ -210,14 +218,14 @@ void Particle::updateVertices() ty1 = m_texpos.Y + m_texsize.Y; } - m_vertices[0] = video::S3DVertex(-m_size/2,-m_size/2,0, 0,0,0, - c, tx0, ty1); - m_vertices[1] = video::S3DVertex(m_size/2,-m_size/2,0, 0,0,0, - c, tx1, ty1); - m_vertices[2] = video::S3DVertex(m_size/2,m_size/2,0, 0,0,0, - c, tx1, ty0); - m_vertices[3] = video::S3DVertex(-m_size/2,m_size/2,0, 0,0,0, - c, tx0, ty0); + m_vertices[0] = video::S3DVertex(-m_size / 2, -m_size / 2, + 0, 0, 0, 0, m_color, tx0, ty1); + m_vertices[1] = video::S3DVertex(m_size / 2, -m_size / 2, + 0, 0, 0, 0, m_color, tx1, ty1); + m_vertices[2] = video::S3DVertex(m_size / 2, m_size / 2, + 0, 0, 0, 0, m_color, tx1, ty0); + m_vertices[3] = video::S3DVertex(-m_size / 2, m_size / 2, + 0, 0, 0, 0, m_color, tx0, ty0); v3s16 camera_offset = m_env->getCameraOffset(); for(u16 i=0; i<4; i++) @@ -589,35 +597,39 @@ void ParticleManager::handleParticleEvent(ClientEvent *event, Client *client, } } -void ParticleManager::addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void ParticleManager::addDiggingParticles(IGameDef* gamedef, + scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, + const MapNode &n, const ContentFeatures &f) { for (u16 j = 0; j < 32; j++) // set the amount of particles here { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, pos, n, f); } } -void ParticleManager::addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void ParticleManager::addPunchingParticles(IGameDef* gamedef, + scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, + const MapNode &n, const ContentFeatures &f) { - addNodeParticle(gamedef, smgr, player, pos, tiles); + addNodeParticle(gamedef, smgr, player, pos, n, f); } -void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]) +void ParticleManager::addNodeParticle(IGameDef* gamedef, + scene::ISceneManager* smgr, LocalPlayer *player, v3s16 pos, + const MapNode &n, const ContentFeatures &f) { // Texture u8 texid = myrand_range(0, 5); + const TileSpec &tile = f.tiles[texid]; video::ITexture *texture; struct TileAnimationParams anim; anim.type = TAT_NONE; // Only use first frame of animated texture - if (tiles[texid].material_flags & MATERIAL_FLAG_ANIMATION) - texture = tiles[texid].frames[0].texture; + if (tile.material_flags & MATERIAL_FLAG_ANIMATION) + texture = tile.frames[0].texture; else - texture = tiles[texid].texture; + texture = tile.texture; float size = rand() % 64 / 512.; float visual_size = BS * size; @@ -638,6 +650,12 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s (f32) pos.Z + rand() %100 /200. - 0.25 ); + video::SColor color; + if (tile.has_color) + color = tile.color; + else + n.getColor(f, &color); + Particle* toadd = new Particle( gamedef, smgr, @@ -655,7 +673,8 @@ void ParticleManager::addNodeParticle(IGameDef* gamedef, scene::ISceneManager* s texpos, texsize, anim, - 0); + 0, + color); addParticle(toadd); } diff --git a/src/particles.h b/src/particles.h index 5464e6672..3177f2cfd 100644 --- a/src/particles.h +++ b/src/particles.h @@ -32,6 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc., struct ClientEvent; class ParticleManager; class ClientEnvironment; +class MapNode; +class ContentFeatures; class Particle : public scene::ISceneNode { @@ -53,7 +55,8 @@ class Particle : public scene::ISceneNode v2f texpos, v2f texsize, const struct TileAnimationParams &anim, - u8 glow + u8 glow, + video::SColor color = video::SColor(0xFFFFFFFF) ); ~Particle(); @@ -100,7 +103,10 @@ private: v3f m_acceleration; LocalPlayer *m_player; float m_size; - u8 m_light; + //! Color without lighting + video::SColor m_base_color; + //! Final rendered color + video::SColor m_color; bool m_collisiondetection; bool m_collision_removal; bool m_vertical; @@ -184,13 +190,16 @@ public: scene::ISceneManager* smgr, LocalPlayer *player); void addDiggingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + LocalPlayer *player, v3s16 pos, const MapNode &n, + const ContentFeatures &f); void addPunchingParticles(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + LocalPlayer *player, v3s16 pos, const MapNode &n, + const ContentFeatures &f); void addNodeParticle(IGameDef* gamedef, scene::ISceneManager* smgr, - LocalPlayer *player, v3s16 pos, const TileSpec tiles[]); + LocalPlayer *player, v3s16 pos, const MapNode &n, + const ContentFeatures &f); protected: void addParticle(Particle* toadd); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 84af4583b..ebc951295 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -332,6 +332,10 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype) L, index, "tileable_horizontal", default_tiling); tiledef.tileable_vertical = getboolfield_default( L, index, "tileable_vertical", default_tiling); + // color = ... + lua_getfield(L, index, "color"); + tiledef.has_color = read_color(L, -1, &tiledef.color); + lua_pop(L, 1); // animation = {} lua_getfield(L, index, "animation"); tiledef.animation = read_animation_definition(L, -1); @@ -450,6 +454,13 @@ ContentFeatures read_content_features(lua_State *L, int index) if (usealpha) f.alpha = 0; + // Read node color. + lua_getfield(L, index, "color"); + read_color(L, -1, &f.color); + lua_pop(L, 1); + + getstringfield(L, index, "palette", f.palette_name); + /* Other stuff */ lua_getfield(L, index, "post_effect_color"); @@ -461,6 +472,13 @@ ContentFeatures read_content_features(lua_State *L, int index) f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", ScriptApiNode::es_ContentParamType2, CPT2_NONE); + if (f.palette_name != "" && + !(f.param_type_2 == CPT2_COLOR || + f.param_type_2 == CPT2_COLORED_FACEDIR || + f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) + warningstream << "Node " << f.name.c_str() + << " has a palette, but not a suitable paramtype2." << std::endl; + // Warn about some deprecated fields warn_if_field_exists(L, index, "wall_mounted", "Deprecated; use paramtype2 = 'wallmounted'"); diff --git a/src/script/cpp_api/s_node.cpp b/src/script/cpp_api/s_node.cpp index 379ed773f..23c8f43b9 100644 --- a/src/script/cpp_api/s_node.cpp +++ b/src/script/cpp_api/s_node.cpp @@ -59,6 +59,9 @@ struct EnumString ScriptApiNode::es_ContentParamType2[] = {CPT2_LEVELED, "leveled"}, {CPT2_DEGROTATE, "degrotate"}, {CPT2_MESHOPTIONS, "meshoptions"}, + {CPT2_COLOR, "color"}, + {CPT2_COLORED_FACEDIR, "colorfacedir"}, + {CPT2_COLORED_WALLMOUNTED, "colorwallmounted"}, {0, NULL}, }; diff --git a/src/shader.cpp b/src/shader.cpp index c0ecf738d..79485025b 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -543,7 +543,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype, shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_TRANSPARENT: - shaderinfo.base_material = video::EMT_TRANSPARENT_VERTEX_ALPHA; + shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL; break; case TILE_MATERIAL_LIQUID_OPAQUE: shaderinfo.base_material = video::EMT_SOLID; diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index c305238fe..089a67f33 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -318,6 +318,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } + m_colors.clear(); // If wield_image is defined, it overrides everything else if (def.wield_image != "") { @@ -358,28 +359,30 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) material_count = 6; } for (u32 i = 0; i < material_count; ++i) { + const TileSpec *tile = &(f.tiles[i]); video::SMaterial &material = m_meshnode->getMaterial(i); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_BILINEAR_FILTER, m_bilinear_filter); material.setFlag(video::EMF_TRILINEAR_FILTER, m_trilinear_filter); - bool animated = (f.tiles[i].animation_frame_count > 1); + bool animated = (tile->animation_frame_count > 1); if (animated) { - FrameSpec animation_frame = f.tiles[i].frames[0]; + FrameSpec animation_frame = tile->frames[0]; material.setTexture(0, animation_frame.texture); } else { - material.setTexture(0, f.tiles[i].texture); + material.setTexture(0, tile->texture); } + m_colors.push_back(tile->color); material.MaterialType = m_material_type; if (m_enable_shaders) { - if (f.tiles[i].normal_texture) { + if (tile->normal_texture) { if (animated) { - FrameSpec animation_frame = f.tiles[i].frames[0]; + FrameSpec animation_frame = tile->frames[0]; material.setTexture(1, animation_frame.normal_texture); } else { - material.setTexture(1, f.tiles[i].normal_texture); + material.setTexture(1, tile->normal_texture); } } - material.setTexture(2, f.tiles[i].flags_texture); + material.setTexture(2, tile->flags_texture); } } return; @@ -393,11 +396,28 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) changeToMesh(NULL); } -void WieldMeshSceneNode::setColor(video::SColor color) +void WieldMeshSceneNode::setColor(video::SColor c) { assert(!m_lighting); - setMeshColor(m_meshnode->getMesh(), color); - shadeMeshFaces(m_meshnode->getMesh()); + scene::IMesh *mesh=m_meshnode->getMesh(); + if (mesh == NULL) + return; + + u8 red = c.getRed(); + u8 green = c.getGreen(); + u8 blue = c.getBlue(); + u32 mc = mesh->getMeshBufferCount(); + for (u32 j = 0; j < mc; j++) { + video::SColor bc(0xFFFFFFFF); + if (m_colors.size() > j) + bc = m_colors[j]; + video::SColor buffercolor(255, + bc.getRed() * red / 255, + bc.getGreen() * green / 255, + bc.getBlue() * blue / 255); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + colorizeMeshBuffer(buf, &buffercolor); + } } void WieldMeshSceneNode::render() @@ -464,7 +484,6 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) } else if (f.drawtype == NDT_PLANTLIKE) { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.tiles[0].texture_id)); - return mesh; } else if (f.drawtype == NDT_NORMAL || f.drawtype == NDT_ALLFACES || f.drawtype == NDT_LIQUID || f.drawtype == NDT_FLOWINGLIQUID) { mesh = cloneMesh(g_extrusion_mesh_cache->createCube()); @@ -477,8 +496,6 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) mesh = cloneMesh(mapblock_mesh.getMesh()); translateMesh(mesh, v3f(-BS, -BS, -BS)); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); - rotateMeshXZby(mesh, -45); - rotateMeshYZby(mesh, -30); u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { @@ -492,28 +509,29 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) material1.setTexture(3, material2.getTexture(3)); material1.MaterialType = material2.MaterialType; } - return mesh; } - shadeMeshFaces(mesh); - rotateMeshXZby(mesh, -45); - rotateMeshYZby(mesh, -30); - u32 mc = mesh->getMeshBufferCount(); for (u32 i = 0; i < mc; ++i) { - video::SMaterial &material = mesh->getMeshBuffer(i)->getMaterial(); + const TileSpec *tile = &(f.tiles[i]); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + colorizeMeshBuffer(buf, &tile->color); + video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.setFlag(video::EMF_BILINEAR_FILTER, false); material.setFlag(video::EMF_TRILINEAR_FILTER, false); material.setFlag(video::EMF_BACK_FACE_CULLING, true); material.setFlag(video::EMF_LIGHTING, false); - if (f.tiles[i].animation_frame_count > 1) { - FrameSpec animation_frame = f.tiles[i].frames[0]; + if (tile->animation_frame_count > 1) { + FrameSpec animation_frame = tile->frames[0]; material.setTexture(0, animation_frame.texture); } else { - material.setTexture(0, f.tiles[i].texture); + material.setTexture(0, tile->texture); } } + + rotateMeshXZby(mesh, -45); + rotateMeshYZby(mesh, -30); return mesh; } return NULL; diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 0162c5e5a..2e78232ae 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -70,6 +70,11 @@ private: bool m_anisotropic_filter; bool m_bilinear_filter; bool m_trilinear_filter; + /*! + * Stores the colors of the mesh's mesh buffers. + * This does not include lighting. + */ + std::vector m_colors; // Bounding box culling is disabled for this type of scene node, // so this variable is just required so we can implement -- cgit v1.2.3 From 2d7a6f2cc0717eb92de4a91326a871d525ce513d Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 12 Jan 2017 11:27:39 -0800 Subject: Vector: Add vector.sort(a, b): return box edges This function returns the box corners of the smallest box that includes the two given coordinates. --- builtin/common/vector.lua | 5 +++++ doc/lua_api.txt | 1 + 2 files changed, 6 insertions(+) diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index 90ba3cc8b..0549f9a56 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -138,3 +138,8 @@ function vector.divide(a, b) z = a.z / b} end end + +function vector.sort(a, b) + return {x = math.min(a.x, b.x), y = math.min(a.y, b.y), z = math.min(a.z, b.z)}, + {x = math.max(a.x, b.x), y = math.max(a.y, b.y), z = math.max(a.z, b.z)} +end diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 2a0b72053..31a1daefb 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1850,6 +1850,7 @@ Spatial Vectors * `vector.round(v)`: returns a vector, each dimension rounded to nearest int * `vector.apply(v, func)`: returns a vector * `vector.equals(v1, v2)`: returns a boolean +* `vector.sort(v1, v2)`: returns minp, maxp vectors of the cuboid defined by v1 and v2 For the following functions `x` can be either a vector or a number: -- cgit v1.2.3 From 7fc67199683d3c60fe0b3ddcb2a9594b4804cc38 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Thu, 12 Jan 2017 11:56:41 -0800 Subject: core: Add dir_to_yaw and yaw_to_dir helpers These are needed to go from things like entity yaw to a vector and vice versa. --- builtin/game/item.lua | 8 ++++++++ doc/lua_api.txt | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index bf456a4e0..e51da6d6b 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -147,6 +147,14 @@ function core.wallmounted_to_dir(wallmounted) return wallmounted_to_dir[wallmounted] end +function core.dir_to_yaw(dir) + return -math.atan2(dir.x, dir.z) +end + +function core.yaw_to_dir(yaw) + return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)} +end + function core.get_node_drops(nodename, toolname) local drop = ItemStack({name=nodename}):get_definition().drop if drop == nil then diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 31a1daefb..62a7b81f7 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2403,6 +2403,10 @@ and `minetest.auth_reload` call the authetification handler. * Convert a vector to a wallmounted value, used for `paramtype2="wallmounted"` * `minetest.wallmounted_to_dir(wallmounted)` * Convert a wallmounted value back into a vector aimed directly out the "back" of a node +* `minetest.dir_to_yaw(dir)` + * Convert a vector into a yaw (angle) +* `minetest.yaw_to_dir(yaw)` + * Convert yaw (angle) to a vector * `minetest.get_node_drops(nodename, toolname)` * Returns list of item names. * **Note**: This will be removed or modified in a future version. -- cgit v1.2.3 From d413dfe87c326385be4259afc2830e3e1638bf3c Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 21 Jan 2017 06:30:33 +0000 Subject: Dungeons: Support nodebox stairs wider than 1 node Previously, code did not support stair nodeboxes in corridors wider than 1 node. Make stair nodeboxes full width even in corridors with different widths in X and Z directions. --- src/dungeongen.cpp | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index 78573da04..5dc87f8b0 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -437,14 +437,22 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, // rotate face 180 deg if // making stairs backwards int facedir = dir_to_facedir(dir * make_stairs); - - u32 vi = vm->m_area.index(p.X - dir.X, p.Y - 1, p.Z - dir.Z); - if (vm->m_data[vi].getContent() == dp.c_wall) - vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); - - vi = vm->m_area.index(p.X, p.Y, p.Z); - if (vm->m_data[vi].getContent() == dp.c_wall) - vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); + v3s16 ps = p; + u16 stair_width = (dir.Z != 0) ? dp.holesize.X : dp.holesize.Z; + // Stair width direction vector + v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1); + + for (u16 st = 0; st < stair_width; st++) { + u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z); + if (vm->m_data[vi].getContent() == dp.c_wall) + vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); + + vi = vm->m_area.index(ps.X, ps.Y, ps.Z); + if (vm->m_data[vi].getContent() == dp.c_wall) + vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); + + ps += swv; + } } } else { makeFill(p + v3s16(-1, -1, -1), -- cgit v1.2.3 From 59fdf57134f62be8b8b4801c5f0fc4a2fca25f43 Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 22 Jan 2017 04:21:29 +0000 Subject: Zoom FOV: Reduce minimum zoom FOV to 7 degrees The default of 15 is unchanged. 7 degrees is x10 magnification which is common for binoculars. Alter hardcoded limits in camera.cpp: Minimum 7 degrees. Maximum 160 degrees to match upper limits in advanced settings. --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- src/camera.cpp | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 0f416bc9a..581eef315 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -440,7 +440,7 @@ fov (Field of view) int 72 30 160 # Field of view while zooming in degrees. # This requires the "zoom" privilege on the server. -zoom_fov (Field of view for zoom) int 15 15 160 +zoom_fov (Field of view for zoom) int 15 7 160 # Adjust the gamma encoding for the light tables. Higher numbers are brighter. # This setting is for the client only and is ignored by the server. diff --git a/minetest.conf.example b/minetest.conf.example index 515b6cfd4..642b028c7 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -502,7 +502,7 @@ # Field of view while zooming in degrees. # This requires the "zoom" privilege on the server. -# type: int min: 15 max: 160 +# type: int min: 7 max: 160 # zoom_fov = 15 # Adjust the gamma encoding for the light tables. Higher numbers are brighter. diff --git a/src/camera.cpp b/src/camera.cpp index 2ad835817..a7679e43a 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -393,8 +393,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 busytime, } else { fov_degrees = m_cache_fov; } - fov_degrees = MYMAX(fov_degrees, 10.0); - fov_degrees = MYMIN(fov_degrees, 170.0); + fov_degrees = rangelim(fov_degrees, 7.0, 160.0); // FOV and aspect ratio m_aspect = (f32) porting::getWindowSize().X / (f32) porting::getWindowSize().Y; -- cgit v1.2.3 From 0cde270bf59c151e9a2f668631cd11b0d03f35ae Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 24 Jan 2017 15:19:29 +0100 Subject: Initialize TileAnimationParams to prevent crashes/bugs for legacy invocations of add_particle{,spawner} (fixes #5108) --- src/script/lua_api/l_particles.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 7f415844a..2f3e9a58d 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -51,6 +51,7 @@ int ModApiParticles::l_add_particle(lua_State *L) bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; struct TileAnimationParams animation; + animation.type = TAT_NONE; std::string texture = ""; std::string playername = ""; @@ -155,6 +156,7 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) bool collisiondetection, vertical, collision_removal; collisiondetection = vertical = collision_removal = false; struct TileAnimationParams animation; + animation.type = TAT_NONE; ServerActiveObject *attached = NULL; std::string texture = ""; std::string playername = ""; -- cgit v1.2.3 From 87e9466cafd72ab9b78218c67352c80d20e008a8 Mon Sep 17 00:00:00 2001 From: raymoo Date: Tue, 24 Jan 2017 08:25:11 -0800 Subject: Wrap to positive degree values (#5106) --- src/network/serverpackethandler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 408fe7706..ac428e8ed 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -811,7 +811,7 @@ void Server::process_PlayerPos(RemotePlayer *player, PlayerSAO *playersao, v3f speed((f32)ss.X / 100.0, (f32)ss.Y / 100.0, (f32)ss.Z / 100.0); pitch = modulo360f(pitch); - yaw = modulo360f(yaw); + yaw = wrapDegrees_0_360(yaw); playersao->setBasePosition(position); player->setSpeed(speed); -- cgit v1.2.3 From 33e0eedbfb116111fa79cbc506d9e94ffbb1543b Mon Sep 17 00:00:00 2001 From: number Zero Date: Wed, 25 Jan 2017 00:33:01 +0300 Subject: Add smooth lighting for all nodes Note: Smooth lighting disables the mesh cache. --- src/content_mapblock.cpp | 518 +++++++++++++++++++++++++++++++++++------------ src/mapblock_mesh.cpp | 2 +- src/nodedef.cpp | 5 + 3 files changed, 396 insertions(+), 129 deletions(-) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 742bfb1fd..45822666f 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -30,6 +30,27 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "noise.h" +// Distance of light extrapolation (for oversized nodes) +// After this distance, it gives up and considers light level constant +#define SMOOTH_LIGHTING_OVERSIZE 1.0 + +struct LightFrame +{ + f32 lightsA[8]; + f32 lightsB[8]; + u8 light_source; +}; + +static const v3s16 light_dirs[8] = { + v3s16(-1, -1, -1), + v3s16(-1, -1, 1), + v3s16(-1, 1, -1), + v3s16(-1, 1, 1), + v3s16( 1, -1, -1), + v3s16( 1, -1, 1), + v3s16( 1, 1, -1), + v3s16( 1, 1, 1), +}; // Create a cuboid. // collector - the MeshCollector for the resulting polygons @@ -180,6 +201,150 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, } } +// Create a cuboid. +// collector - the MeshCollector for the resulting polygons +// box - the position and size of the box +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 +// lights - vertex light levels. The order is the same as in light_dirs +// txc - texture coordinates - this is a list of texture coordinates +// for the opposite corners of each face - therefore, there +// should be (2+2)*6=24 values in the list. Alternatively, pass +// NULL to use the entire texture for each face. The order of +// the faces in the list is up-down-right-left-back-front +// (compatible with ContentFeatures). If you specified 0,0,1,1 +// for each face, that would be the same as passing NULL. +// light_source - node light emission +static void makeSmoothLightedCuboid(MeshCollector *collector, const aabb3f &box, + TileSpec *tiles, int tilecount, const u16 *lights , const f32 *txc, + const u8 light_source) +{ + assert(tilecount >= 1 && tilecount <= 6); // pre-condition + + v3f min = box.MinEdge; + v3f max = box.MaxEdge; + + if (txc == NULL) { + static const f32 txc_default[24] = { + 0,0,1,1, + 0,0,1,1, + 0,0,1,1, + 0,0,1,1, + 0,0,1,1, + 0,0,1,1 + }; + txc = txc_default; + } + static const u8 light_indices[24] = { + 3, 7, 6, 2, + 0, 4, 5, 1, + 6, 7, 5, 4, + 3, 2, 0, 1, + 7, 3, 1, 5, + 2, 6, 4, 0 + }; + video::S3DVertex vertices[24] = { + // up + video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, video::SColor(), txc[0], txc[1]), + video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, video::SColor(), txc[2], txc[1]), + video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, video::SColor(), txc[2], txc[3]), + video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, video::SColor(), txc[0], txc[3]), + // down + video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, video::SColor(), txc[4], txc[5]), + video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, video::SColor(), txc[6], txc[5]), + video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, video::SColor(), txc[6], txc[7]), + video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, video::SColor(), txc[4], txc[7]), + // right + video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, video::SColor(), txc[ 8], txc[9]), + video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, video::SColor(), txc[10], txc[9]), + video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, video::SColor(), txc[10], txc[11]), + video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, video::SColor(), txc[ 8], txc[11]), + // left + video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, video::SColor(), txc[12], txc[13]), + video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, video::SColor(), txc[14], txc[13]), + video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, video::SColor(), txc[14], txc[15]), + video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, video::SColor(), txc[12], txc[15]), + // back + video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, video::SColor(), txc[16], txc[17]), + video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, video::SColor(), txc[18], txc[17]), + video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, video::SColor(), txc[18], txc[19]), + video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, video::SColor(), txc[16], txc[19]), + // front + video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, video::SColor(), txc[20], txc[21]), + video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, video::SColor(), txc[22], txc[21]), + video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, video::SColor(), txc[22], txc[23]), + video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, video::SColor(), txc[20], txc[23]), + }; + + for(int i = 0; i < 6; i++) { + switch (tiles[MYMIN(i, tilecount-1)].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.X = 1.0 - vertices[i*4+x].TCoords.X; + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + } + break; + case 5: //FXR270 + for (int x = 0; x < 4; x++) { + vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + } + break; + case 6: //FYR90 + for (int x = 0; x < 4; x++) { + vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; + vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + } + break; + case 7: //FYR270 + for (int x = 0; x < 4; x++) { + vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; + vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); + } + break; + case 8: //FX + for (int x = 0; x < 4; x++) { + vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; + } + break; + case 9: //FY + for (int x = 0; x < 4; x++) { + vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; + } + break; + default: + break; + } + } + u16 indices[] = {0,1,2,2,3,0}; + for (s32 j = 0; j < 24; ++j) { + int tileindex = MYMIN(j / 4, tilecount - 1); + vertices[j].Color = encode_light_and_color(lights[light_indices[j]], + tiles[tileindex].color, light_source); + if (!light_source) + applyFacesShading(vertices[j].Color, vertices[j].Normal); + } + // Add to mesh collector + for (s32 k = 0; k < 6; ++k) { + int tileindex = MYMIN(k, tilecount - 1); + collector->append(tiles[tileindex], vertices + 4 * k, 4, indices, 6); + } +} + // Create a cuboid. // collector - the MeshCollector for the resulting polygons // box - the position and size of the box @@ -205,6 +370,64 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles, makeCuboid(collector, box, tiles, tilecount, color, txc, light_source); } +// Gets the base lighting values for a node +// frame - resulting (opaque) data +// p - node position (absolute) +// data - ... +// light_source - node light emission level +static void getSmoothLightFrame(LightFrame *frame, const v3s16 &p, MeshMakeData *data, u8 light_source) +{ + for (int k = 0; k < 8; ++k) { + u16 light = getSmoothLight(p, light_dirs[k], data); + frame->lightsA[k] = light & 0xff; + frame->lightsB[k] = light >> 8; + } + frame->light_source = light_source; +} + +// Calculates vertex light level +// frame - light values from getSmoothLightFrame() +// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) +static u16 blendLight(const LightFrame &frame, const core::vector3df& vertex_pos) +{ + f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE); + f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE); + f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE); + f32 lightA = 0.0; + f32 lightB = 0.0; + for (int k = 0; k < 8; ++k) { + f32 dx = (k & 4) ? x : 1 - x; + f32 dy = (k & 2) ? y : 1 - y; + f32 dz = (k & 1) ? z : 1 - z; + lightA += dx * dy * dz * frame.lightsA[k]; + lightB += dx * dy * dz * frame.lightsB[k]; + } + return + core::clamp(core::round32(lightA), 0, 255) | + core::clamp(core::round32(lightB), 0, 255) << 8; +} + +// Calculates vertex color to be used in mapblock mesh +// frame - light values from getSmoothLightFrame() +// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) +// tile_color - node's tile color +static video::SColor blendLight(const LightFrame &frame, + const core::vector3df& vertex_pos, video::SColor tile_color) +{ + u16 light = blendLight(frame, vertex_pos); + return encode_light_and_color(light, tile_color, frame.light_source); +} + +static video::SColor blendLight(const LightFrame &frame, + const core::vector3df& vertex_pos, const core::vector3df& vertex_normal, + video::SColor tile_color) +{ + video::SColor color = blendLight(frame, vertex_pos, tile_color); + if (!frame.light_source) + applyFacesShading(color, vertex_normal); + return color; +} + static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, MeshMakeData *data, MapNode n, int v, int *neighbors) { @@ -213,6 +436,74 @@ static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, *neighbors |= v; } +static void makeAutoLightedCuboid(MeshCollector *collector, MeshMakeData *data, + const v3f &pos, aabb3f box, TileSpec &tile, + /* pre-computed, for non-smooth lighting only */ const video::SColor color, + /* for smooth lighting only */ const LightFrame &frame) +{ + f32 dx1 = box.MinEdge.X; + f32 dy1 = box.MinEdge.Y; + f32 dz1 = box.MinEdge.Z; + f32 dx2 = box.MaxEdge.X; + f32 dy2 = box.MaxEdge.Y; + f32 dz2 = box.MaxEdge.Z; + box.MinEdge += pos; + box.MaxEdge += pos; + 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] = { + tx1, 1-tz2, tx2, 1-tz1, // up + tx1, tz1, tx2, tz2, // down + tz1, 1-ty2, tz2, 1-ty1, // right + 1-tz2, 1-ty2, 1-tz1, 1-ty1, // left + 1-tx2, 1-ty2, 1-tx1, 1-ty1, // back + tx1, 1-ty2, tx2, 1-ty1, // front + }; + if (data->m_smooth_lighting) { + u16 lights[8]; + for (int j = 0; j < 8; ++j) { + f32 x = (j & 4) ? dx2 : dx1; + f32 y = (j & 2) ? dy2 : dy1; + f32 z = (j & 1) ? dz2 : dz1; + lights[j] = blendLight(frame, core::vector3df(x, y, z)); + } + makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source); + } else { + makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source); + } +} + +static void makeAutoLightedCuboidEx(MeshCollector *collector, MeshMakeData *data, + const v3f &pos, aabb3f box, TileSpec &tile, f32 *txc, + /* pre-computed, for non-smooth lighting only */ const video::SColor color, + /* for smooth lighting only */ const LightFrame &frame) +{ + f32 dx1 = box.MinEdge.X; + f32 dy1 = box.MinEdge.Y; + f32 dz1 = box.MinEdge.Z; + f32 dx2 = box.MaxEdge.X; + f32 dy2 = box.MaxEdge.Y; + f32 dz2 = box.MaxEdge.Z; + box.MinEdge += pos; + box.MaxEdge += pos; + if (data->m_smooth_lighting) { + u16 lights[8]; + for (int j = 0; j < 8; ++j) { + f32 x = (j & 4) ? dx2 : dx1; + f32 y = (j & 2) ? dy2 : dy1; + f32 z = (j & 1) ? dz2 : dz1; + lights[j] = blendLight(frame, core::vector3df(x, y, z)); + } + makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source); + } else { + makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source); + } +} + // For use in mapblock_mesh_generate_special // X,Y,Z of position must be -1,0,1 // This expression is a simplification of @@ -251,7 +542,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, /* Some settings */ - bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); + bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache") && + !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; @@ -268,13 +560,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(f.solidness != 0) continue; - switch(f.drawtype){ + if (f.drawtype == NDT_AIRLIKE) + continue; + + LightFrame frame; + if (data->m_smooth_lighting) + getSmoothLightFrame(&frame, blockpos_nodes + p, data, f.light_source); + else + frame.light_source = f.light_source; + + switch(f.drawtype) { default: infostream << "Got " << f.drawtype << std::endl; FATAL_ERROR("Unknown drawtype"); break; - case NDT_AIRLIKE: - break; case NDT_LIQUID: { /* @@ -383,8 +682,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[1].Pos.Y = -0.5 * BS; } - for(s32 j=0; j<4; j++) - { + for (s32 j = 0; j < 4; j++) { if(dir == v3s16(0,0,1)) vertices[j].Pos.rotateXZBy(0); if(dir == v3s16(0,0,-1)) @@ -401,6 +699,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[j].Pos.Z *= 0.98; }*/ + if (data->m_smooth_lighting) + vertices[j].Color = blendLight(frame, vertices[j].Pos, current_tile->color); vertices[j].Pos += intToFloat(p, BS); } @@ -423,10 +723,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), }; - v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS); - for(s32 i=0; i<4; i++) - { - vertices[i].Pos += offset; + for (s32 i = 0; i < 4; i++) { + vertices[i].Pos.Y += 0.5 * BS; + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile_liquid.color); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; @@ -704,6 +1005,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[j].Pos.Z *= 0.98; }*/ + if (data->m_smooth_lighting) + vertices[j].Color = blendLight(frame, vertices[j].Pos, current_tile->color); vertices[j].Pos += intToFloat(p, BS); } @@ -737,6 +1040,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)]; s32 j = corner_resolve[i]; vertices[i].Pos.Y += corner_levels[j]; + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile_liquid.color); vertices[i].Pos += intToFloat(p, BS); } @@ -828,7 +1133,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for(u16 i=0; i<4; i++) vertices[i].Pos.rotateXZBy(90); - for(u16 i=0; i<4; i++){ + for (u16 i = 0; i < 4; i++) { + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, vertices[i].Normal, tile.color); vertices[i].Pos += intToFloat(p, BS); } @@ -860,9 +1167,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::SColor tile0color = encode_light_and_color(l, tiles[0].color, f.light_source); - video::SColor tile0colors[6]; - for (i = 0; i < 6; i++) - tile0colors[i] = tile0color; TileSpec glass_tiles[6]; video::SColor glasscolor[6]; @@ -995,7 +1299,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0,1, 8, 0,4,16, 3,4,17, 3,1, 9 }; - f32 tx1, ty1, tz1, tx2, ty2, tz2; aabb3f box; for(i = 0; i < 12; i++) @@ -1008,24 +1311,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if (edge_invisible) continue; box = frame_edges[i]; - box.MinEdge += pos; - box.MaxEdge += pos; - tx1 = (box.MinEdge.X / BS) + 0.5; - ty1 = (box.MinEdge.Y / BS) + 0.5; - tz1 = (box.MinEdge.Z / BS) + 0.5; - tx2 = (box.MaxEdge.X / BS) + 0.5; - ty2 = (box.MaxEdge.Y / BS) + 0.5; - tz2 = (box.MaxEdge.Z / BS) + 0.5; - f32 txc1[24] = { - tx1, 1-tz2, tx2, 1-tz1, - tx1, tz1, tx2, tz2, - tz1, 1-ty2, tz2, 1-ty1, - 1-tz2, 1-ty2, 1-tz1, 1-ty1, - 1-tx2, 1-ty2, 1-tx1, 1-ty1, - tx1, 1-ty2, tx2, 1-ty1, - }; - makeCuboid(&collector, box, &tiles[0], 1, tile0colors, - txc1, f.light_source); + makeAutoLightedCuboid(&collector, data, pos, box, tiles[0], tile0color, frame); } for(i = 0; i < 6; i++) @@ -1033,37 +1319,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if (!visible_faces[i]) continue; box = glass_faces[i]; - box.MinEdge += pos; - box.MaxEdge += pos; - tx1 = (box.MinEdge.X / BS) + 0.5; - ty1 = (box.MinEdge.Y / BS) + 0.5; - tz1 = (box.MinEdge.Z / BS) + 0.5; - tx2 = (box.MaxEdge.X / BS) + 0.5; - ty2 = (box.MaxEdge.Y / BS) + 0.5; - tz2 = (box.MaxEdge.Z / BS) + 0.5; - f32 txc2[24] = { - tx1, 1-tz2, tx2, 1-tz1, - tx1, tz1, tx2, tz2, - tz1, 1-ty2, tz2, 1-ty1, - 1-tz2, 1-ty2, 1-tz1, 1-ty1, - 1-tx2, 1-ty2, 1-tx1, 1-ty1, - tx1, 1-ty2, tx2, 1-ty1, - }; - makeCuboid(&collector, box, &glass_tiles[i], 1, glasscolor, - txc2, f.light_source); + makeAutoLightedCuboid(&collector, data, pos, box, glass_tiles[i], glasscolor[i], frame); } if (param2 > 0 && f.special_tiles[0].texture) { // Interior volume level is in range 0 .. 63, // convert it to -0.5 .. 0.5 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0); - TileSpec tile=getSpecialTile(f, n, 0); + TileSpec tile = getSpecialTile(f, n, 0); video::SColor special_color = encode_light_and_color(l, tile.color, f.light_source); - TileSpec interior_tiles[6]; - for (i = 0; i < 6; i++) - interior_tiles[i] = tile; - float offset = 0.003; box = aabb3f(visible_faces[3] ? -b : -a + offset, visible_faces[1] ? -b : -a + offset, @@ -1071,24 +1336,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, visible_faces[2] ? b : a - offset, visible_faces[0] ? b * vlev : a * vlev - offset, visible_faces[4] ? b : a - offset); - box.MinEdge += pos; - box.MaxEdge += pos; - tx1 = (box.MinEdge.X / BS) + 0.5; - ty1 = (box.MinEdge.Y / BS) + 0.5; - tz1 = (box.MinEdge.Z / BS) + 0.5; - tx2 = (box.MaxEdge.X / BS) + 0.5; - ty2 = (box.MaxEdge.Y / BS) + 0.5; - tz2 = (box.MaxEdge.Z / BS) + 0.5; - f32 txc3[24] = { - tx1, 1-tz2, tx2, 1-tz1, - tx1, tz1, tx2, tz2, - tz1, 1-ty2, tz2, 1-ty1, - 1-tz2, 1-ty2, 1-tz1, 1-ty1, - 1-tx2, 1-ty2, 1-tx1, 1-ty1, - tx1, 1-ty2, tx2, 1-ty1, - }; - makeCuboid(&collector, box, interior_tiles, 6, special_color, - txc3, f.light_source); + makeAutoLightedCuboid(&collector, data, pos, box, tile, special_color, frame); } break;} case NDT_ALLFACES: @@ -1101,10 +1349,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3f pos = intToFloat(p, BS); aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2); - box.MinEdge += pos; - box.MaxEdge += pos; - makeCuboid(&collector, box, &tile_leaves, 1, c, NULL, - f.light_source); + makeAutoLightedCuboid(&collector, data, pos, box, tile_leaves, c, frame); break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else @@ -1144,7 +1389,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex(-s, s,0, 0,0,0, c, 0,0), }; - for(s32 i=0; i<4; i++) + for (s32 i = 0; i < 4; i++) { if(dir == v3s16(1,0,0)) vertices[i].Pos.rotateXZBy(0); @@ -1159,6 +1404,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXZBy(-45); + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); } @@ -1189,7 +1436,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16 dir = n.getWallMountedDir(nodedef); - for(s32 i=0; i<4; i++) + for (s32 i = 0; i < 4; i++) { if(dir == v3s16(1,0,0)) vertices[i].Pos.rotateXZBy(0); @@ -1204,6 +1451,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXYBy(90); + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); } @@ -1355,6 +1604,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for (int i = 0; i < 4; i++) { vertices[i].Pos *= f.visual_scale; vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1); + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); // move to a random spot to avoid moire if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { @@ -1507,6 +1758,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for (int i = 0; i < 4; i++) { vertices[i].Pos *= f.visual_scale; + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); } @@ -1537,8 +1790,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // The post - always present aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad); - post.MinEdge += pos; - post.MaxEdge += pos; f32 postuv[24]={ 6/16.,6/16.,10/16.,10/16., 6/16.,6/16.,10/16.,10/16., @@ -1546,8 +1797,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 4/16.,0,8/16.,1, 8/16.,0,12/16.,1, 12/16.,0,16/16.,1}; - makeCuboid(&collector, post, &tile_rot, 1, c, postuv, - f.light_source); + makeAutoLightedCuboidEx(&collector, data, pos, post, tile_rot, postuv, c, frame); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; @@ -1558,8 +1808,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad, bar_len+BS/2,bar_rad+BS/4,bar_rad); - bar.MinEdge += pos; - bar.MaxEdge += pos; f32 xrailuv[24]={ 0/16.,2/16.,16/16.,4/16., 0/16.,4/16.,16/16.,6/16., @@ -1567,12 +1815,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 10/16.,10/16.,12/16.,12/16., 0/16.,8/16.,16/16.,10/16., 0/16.,14/16.,16/16.,16/16.}; - makeCuboid(&collector, bar, &tile_nocrack, 1, - c, xrailuv, f.light_source); + makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; - makeCuboid(&collector, bar, &tile_nocrack, 1, - c, xrailuv, f.light_source); + makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame); } // Now a section of fence, +Z, if there's a post there @@ -1584,8 +1830,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2, bar_rad,bar_rad+BS/4,bar_len+BS/2); - bar.MinEdge += pos; - bar.MaxEdge += pos; f32 zrailuv[24]={ 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch 4/16.,1/16.,6/16.,5/16., // for wood texture instead @@ -1593,12 +1837,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0/16.,6/16.,16/16.,8/16., 6/16.,6/16.,8/16.,8/16., 10/16.,10/16.,12/16.,12/16.}; - makeCuboid(&collector, bar, &tile_nocrack, 1, - c, zrailuv, f.light_source); + makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; - makeCuboid(&collector, bar, &tile_nocrack, 1, - c, zrailuv, f.light_source); + makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame); } break;} case NDT_RAILLIKE: @@ -1729,6 +1971,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { if(angle != 0) vertices[i].Pos.rotateXZBy(angle); + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); } @@ -1746,14 +1990,16 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16(0, 0, -1) }; - u16 l = getInteriorLight(n, 1, nodedef); TileSpec tiles[6]; video::SColor colors[6]; - for(int j = 0; j < 6; j++) { + for (int j = 0; j < 6; j++) { // Handles facedir rotation for textures tiles[j] = getNodeTile(n, p, tile_dirs[j], data); - colors[j]= encode_light_and_color(l, tiles[j].color, - f.light_source); + } + if (!data->m_smooth_lighting) { + u16 l = getInteriorLight(n, 1, nodedef); + for (int j = 0; j < 6; j++) + colors[j] = encode_light_and_color(l, tiles[j].color, f.light_source); } v3f pos = intToFloat(p, BS); @@ -1790,33 +2036,27 @@ void mapblock_mesh_generate_special(MeshMakeData *data, std::vector boxes; n.getNodeBoxes(nodedef, &boxes, neighbors); - for(std::vector::iterator + for (std::vector::iterator i = boxes.begin(); - i != boxes.end(); ++i) - { + i != boxes.end(); ++i) { aabb3f box = *i; + + f32 dx1 = box.MinEdge.X; + f32 dy1 = box.MinEdge.Y; + f32 dz1 = box.MinEdge.Z; + f32 dx2 = box.MaxEdge.X; + f32 dy2 = box.MaxEdge.Y; + f32 dz2 = box.MaxEdge.Z; + 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; - } + std::swap(box.MinEdge.X, box.MaxEdge.X); if (box.MinEdge.Y > box.MaxEdge.Y) - { - temp=box.MinEdge.Y; - box.MinEdge.Y=box.MaxEdge.Y; - box.MaxEdge.Y=temp; - } + std::swap(box.MinEdge.Y, box.MaxEdge.Y); if (box.MinEdge.Z > box.MaxEdge.Z) - { - temp=box.MinEdge.Z; - box.MinEdge.Z=box.MaxEdge.Z; - box.MaxEdge.Z=temp; - } + std::swap(box.MinEdge.Z, box.MaxEdge.Z); // // Compute texture coords @@ -1840,7 +2080,18 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // front tx1, 1-ty2, tx2, 1-ty1, }; - makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source); + if (data->m_smooth_lighting) { + u16 lights[8]; + for (int j = 0; j < 8; ++j) { + f32 x = (j & 4) ? dx2 : dx1; + f32 y = (j & 2) ? dy2 : dy1; + f32 z = (j & 1) ? dz2 : dz1; + lights[j] = blendLight(frame, core::vector3df(x, y, z)); + } + makeSmoothLightedCuboid(&collector, box, tiles, 6, lights, txc, f.light_source); + } else { + makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source); + } } break;} case NDT_MESH: @@ -1862,9 +2113,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } } - if (f.mesh_ptr[facedir]) { + if (!data->m_smooth_lighting && f.mesh_ptr[facedir]) { // use cached meshes - for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) { + for (u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) { const TileSpec &tile = getNodeTileN(n, p, j, data); scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j); collector.append(tile, (video::S3DVertex *) @@ -1879,14 +2130,25 @@ void mapblock_mesh_generate_special(MeshMakeData *data, rotateMeshBy6dFacedir(mesh, facedir); recalculateBoundingBox(mesh); meshmanip->recalculateNormals(mesh, true, false); - for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) { + for (u16 j = 0; j < mesh->getMeshBufferCount(); j++) { const TileSpec &tile = getNodeTileN(n, p, j, data); scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - collector.append(tile, (video::S3DVertex *) - buf->getVertices(), buf->getVertexCount(), - buf->getIndices(), buf->getIndexCount(), pos, - encode_light_and_color(l, tile.color, f.light_source), - f.light_source); + video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); + u32 vertex_count = buf->getVertexCount(); + if (data->m_smooth_lighting) { + for (u16 m = 0; m < vertex_count; ++m) { + video::S3DVertex &vertex = vertices[m]; + vertex.Color = blendLight(frame, vertex.Pos, vertex.Normal, tile.color); + vertex.Pos += pos; + } + collector.append(tile, vertices, vertex_count, + buf->getIndices(), buf->getIndexCount()); + } else { + collector.append(tile, vertices, vertex_count, + buf->getIndices(), buf->getIndexCount(), pos, + encode_light_and_color(l, tile.color, f.light_source), + f.light_source); + } } mesh->drop(); } diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index dfd6f55a5..0744cd527 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -236,7 +236,7 @@ static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data) for (u32 i = 0; i < 8; i++) { - const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]); + MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p - dirs8[i]); // if it's CONTENT_IGNORE we can't do any light calculations if (n.getContent() == CONTENT_IGNORE) { diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 98f795c7a..0bb150267 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -269,10 +269,15 @@ void TextureSettings::readSettings() bool enable_shaders = g_settings->getBool("enable_shaders"); bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); + bool smooth_lighting = g_settings->getBool("smooth_lighting"); enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); enable_minimap = g_settings->getBool("enable_minimap"); std::string leaves_style_str = g_settings->get("leaves_style"); + // Mesh cache is not supported in combination with smooth lighting + if (smooth_lighting) + enable_mesh_cache = false; + use_normal_texture = enable_shaders && (enable_bumpmapping || enable_parallax_occlusion); if (leaves_style_str == "fancy") { -- cgit v1.2.3 From 08911160aa5294883a7ebfb681fbf0c1a858dc78 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 25 Jan 2017 09:57:33 +0000 Subject: Block spam messages before calling on_chatmessage callbacks (#4805) Fixes #4799 --- src/server.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index c7d4fcba9..e5714ac03 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2790,40 +2790,41 @@ std::wstring Server::handleChat(const std::string &name, const std::wstring &wna RollbackScopeActor rollback_scope(m_rollback, std::string("player:") + name); - // Line to send - std::wstring line; - // Whether to send line to the player that sent the message, or to all players - bool broadcast_line = true; - - // Run script hook - bool ate = m_script->on_chat_message(name, - wide_to_utf8(wmessage)); - // If script ate the message, don't proceed - if (ate) - return L""; - if (player) { switch (player->canSendChatMessage()) { case RPLAYER_CHATRESULT_FLOODING: { std::wstringstream ws; ws << L"You cannot send more messages. You are limited to " - << g_settings->getFloat("chat_message_limit_per_10sec") - << L" messages per 10 seconds."; + << g_settings->getFloat("chat_message_limit_per_10sec") + << L" messages per 10 seconds."; return ws.str(); } case RPLAYER_CHATRESULT_KICK: - DenyAccess_Legacy(player->peer_id, L"You have been kicked due to message flooding."); + DenyAccess_Legacy(player->peer_id, + L"You have been kicked due to message flooding."); return L""; - case RPLAYER_CHATRESULT_OK: break; - default: FATAL_ERROR("Unhandled chat filtering result found."); + case RPLAYER_CHATRESULT_OK: + break; + default: + FATAL_ERROR("Unhandled chat filtering result found."); } } - if (m_max_chatmessage_length > 0 && wmessage.length() > m_max_chatmessage_length) { + if (m_max_chatmessage_length > 0 + && wmessage.length() > m_max_chatmessage_length) { return L"Your message exceed the maximum chat message limit set on the server. " - L"It was refused. Send a shorter message"; + L"It was refused. Send a shorter message"; } + // Run script hook, exit if script ate the chat message + if (m_script->on_chat_message(name, wide_to_utf8(wmessage))) + return L""; + + // Line to send + std::wstring line; + // Whether to send line to the player that sent the message, or to all players + bool broadcast_line = true; + // Commands are implemented in Lua, so only catch invalid // commands that were not "eaten" and send an error back if (wmessage[0] == L'/') { -- cgit v1.2.3 From 9f108b56d35cb607b538562ac369c457cd129eaa Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 25 Jan 2017 15:23:29 +0000 Subject: Dungeongen: Fix out-of-voxelmanip access segfault My recent dungeon commit allowed stairs to be placed across the full width of corridors, but some of the new node positions accessed were missing checks for being within the voxelmanip, causing occasional segfaults near dungeons with corridors wider than 1 node. Add 'vm->m_area.contains(pos)' checks just before stair position voxelmanip access. This allows an earlier check to be removed as it is now redundant. --- src/dungeongen.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index 5dc87f8b0..b8ee2b924 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -415,8 +415,8 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, if (partcount != 0) p.Y += make_stairs; - if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0)) && - vm->m_area.contains(v3s16(p.X - dir.X, p.Y - 1, p.Z - dir.Z))) { + // Check segment of minimum size corridor is in voxelmanip + if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) { if (make_stairs) { makeFill(p + v3s16(-1, -1, -1), dp.holesize + v3s16(2, 3, 2), @@ -444,11 +444,13 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, for (u16 st = 0; st < stair_width; st++) { u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z); - if (vm->m_data[vi].getContent() == dp.c_wall) + if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) && + vm->m_data[vi].getContent() == dp.c_wall) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); vi = vm->m_area.index(ps.X, ps.Y, ps.Z); - if (vm->m_data[vi].getContent() == dp.c_wall) + if (vm->m_area.contains(ps) && + vm->m_data[vi].getContent() == dp.c_wall) vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir); ps += swv; -- cgit v1.2.3 From c268db7b46645b8000bd77ce80e919080a75f152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Thu, 26 Jan 2017 16:10:56 +0100 Subject: Fix after hardware node coloring (#5114) --- src/mapblock_mesh.cpp | 8 ++++---- src/particles.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 0744cd527..79d02d6ff 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -1090,10 +1090,10 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): brightness of vertices 1 and 3 differ less than the brightness of vertices 0 and 2. */ - if (abs(f.vertices[0].Color.getAverage() - - f.vertices[2].Color.getAverage()) - > abs(f.vertices[1].Color.getAverage() - - f.vertices[3].Color.getAverage())) + if (abs(f.vertices[0].Color.getLuminance() + - f.vertices[2].Color.getLuminance()) + > abs(f.vertices[1].Color.getLuminance() + - f.vertices[3].Color.getLuminance())) indices_p = indices_alternate; collector.append(f.tile, f.vertices, 4, indices_p, 6); diff --git a/src/particles.h b/src/particles.h index 3177f2cfd..7ffb1c728 100644 --- a/src/particles.h +++ b/src/particles.h @@ -32,8 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc., struct ClientEvent; class ParticleManager; class ClientEnvironment; -class MapNode; -class ContentFeatures; +struct MapNode; +struct ContentFeatures; class Particle : public scene::ISceneNode { -- cgit v1.2.3 From ae929ce2fdcfcfc95cffece35f3d38ea4d96dd9f Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 23 Jan 2017 08:07:34 +0000 Subject: Dungeons: Add nodebox stairs to desert and sandstone dungeons Desert and sandstone dungeons have 2 node wide corridors. Previously, nodebox stairs were disabled because dungeon generation code did not support nodebox stairs wider than 1 node, now it does. Add 'stair desert stone' content id to MappgenBasic. Requires 'mapgen stair desert stone' to be added to Minetest Game. --- src/mapgen.cpp | 5 +++-- src/mapgen.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index fd4f5858f..e42fc7467 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -604,6 +604,7 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emer c_cobble = ndef->getId("mapgen_cobble"); c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); + c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); @@ -867,7 +868,7 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) case MGSTONE_DESERT_STONE: dp.c_wall = c_desert_stone; dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_desert_stone; + dp.c_stair = c_stair_desert_stone; dp.diagonal_dirs = true; dp.holesize = v3s16(2, 3, 2); @@ -877,7 +878,7 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) case MGSTONE_SANDSTONE: dp.c_wall = c_sandstonebrick; dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_sandstonebrick; + dp.c_stair = c_stair_sandstonebrick; dp.diagonal_dirs = false; dp.holesize = v3s16(2, 2, 2); diff --git a/src/mapgen.h b/src/mapgen.h index b18bfb930..a95e1942a 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -266,6 +266,7 @@ protected: content_t c_cobble; content_t c_stair_cobble; content_t c_mossycobble; + content_t c_stair_desert_stone; content_t c_sandstonebrick; content_t c_stair_sandstonebrick; -- cgit v1.2.3 From 2a8953107181b4df6ff55d0ae214490575609f49 Mon Sep 17 00:00:00 2001 From: paramat Date: Thu, 26 Jan 2017 15:38:18 +0000 Subject: Dungeongen: Fix selection of diagonal corridors The do .. while loop is waiting for both dir.X and dir.Z to be non-zero, so should continue to loop if either dir.X or dir.Z are zero. The brackets present suggest this was intended to be OR not AND. --- src/dungeongen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index b8ee2b924..7825e9e2c 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -622,7 +622,7 @@ v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs) dir.Z = random.next() % 3 - 1; dir.Y = 0; dir.X = random.next() % 3 - 1; - } while ((dir.X == 0 && dir.Z == 0) && trycount < 10); + } while ((dir.X == 0 || dir.Z == 0) && trycount < 10); return dir; } else { -- cgit v1.2.3 From b7a98e98500402c3bbdb6d56d0fe42b4f5b3cedb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Fri, 27 Jan 2017 08:59:30 +0100 Subject: Implement player attribute backend (#4155) * This backend permit mods to store extra players attributes to a common interface. * Add the obj:set_attribute(attr, value) Lua call * Add the obj:get_attribute(attr) Lua call Examples: * player:set_attribute("home:home", "10,25,-78") * player:get_attribute("default:mana") Attributes are saved as a json in the player file in extended_attributes key They are saved only if a modification on the attributes occurs and loaded when emergePlayer is called (they are attached to PlayerSAO). --- doc/lua_api.txt | 2 ++ src/content_sao.cpp | 1 + src/content_sao.h | 37 +++++++++++++++++++++++++++++++++++++ src/remoteplayer.cpp | 38 +++++++++++++++++++++++++++++++++++--- src/remoteplayer.h | 5 ++++- src/script/lua_api/l_object.cpp | 41 +++++++++++++++++++++++++++++++++++++++++ src/script/lua_api/l_object.h | 6 ++++++ src/server.h | 1 - src/serverenvironment.cpp | 3 ++- 9 files changed, 128 insertions(+), 6 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 62a7b81f7..ee7d57c2f 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2899,6 +2899,8 @@ This is basically a reference to a C++ `ServerActiveObject` * `0`: player is drowning, * `1`-`10`: remaining number of bubbles * `11`: bubbles bar is not shown +* `set_attribute(attribute, value)`: sets an extra attribute with value on player +* `get_attribute(attribute)`: returns value for extra attribute. Returns nil if no attribute found. * `set_inventory_formspec(formspec)` * Redefine player's inventory form * Should usually be called in on_joinplayer diff --git a/src/content_sao.cpp b/src/content_sao.cpp index bb62aea7d..35133490e 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -791,6 +791,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer m_pitch(0), m_fov(0), m_wanted_range(0), + m_extended_attributes_modified(false), // public m_physics_override_speed(1), m_physics_override_jump(1), diff --git a/src/content_sao.h b/src/content_sao.h index c3674fa2d..bbf244742 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -180,6 +180,7 @@ public: } }; +typedef UNORDERED_MAP PlayerAttributes; class RemotePlayer; class PlayerSAO : public UnitSAO @@ -249,6 +250,39 @@ public: int getWieldIndex() const; void setWieldIndex(int i); + /* + Modding interface + */ + inline void setExtendedAttribute(const std::string &attr, const std::string &value) + { + m_extra_attributes[attr] = value; + m_extended_attributes_modified = true; + } + + inline bool getExtendedAttribute(const std::string &attr, std::string *value) + { + if (m_extra_attributes.find(attr) == m_extra_attributes.end()) + return false; + + *value = m_extra_attributes[attr]; + return true; + } + + inline const PlayerAttributes &getExtendedAttributes() + { + return m_extra_attributes; + } + + inline bool extendedAttributesModified() const + { + return m_extended_attributes_modified; + } + + inline void setExtendedAttributeModified(bool v) + { + m_extended_attributes_modified = v; + } + /* PlayerSAO-specific */ @@ -343,6 +377,9 @@ private: f32 m_pitch; f32 m_fov; s16 m_wanted_range; + + PlayerAttributes m_extra_attributes; + bool m_extended_attributes_modified; public: float m_physics_override_speed; float m_physics_override_jump; diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 18bfa1030..6853ad6d9 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -19,13 +19,14 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "remoteplayer.h" +#include #include "content_sao.h" #include "filesys.h" #include "gamedef.h" #include "porting.h" // strlcpy +#include "server.h" #include "settings.h" - /* RemotePlayer */ @@ -112,9 +113,23 @@ void RemotePlayer::save(std::string savedir, IGameDef *gamedef) } infostream << "Didn't find free file for player " << m_name << std::endl; - return; } +void RemotePlayer::serializeExtraAttributes(std::string &output) +{ + assert(m_sao); + Json::Value json_root; + const PlayerAttributes &attrs = m_sao->getExtendedAttributes(); + for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) { + json_root[(*it).first] = (*it).second; + } + + Json::FastWriter writer; + output = writer.write(json_root); + m_sao->setExtendedAttributeModified(false); +} + + void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, PlayerSAO *sao) { @@ -150,6 +165,20 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, try { sao->setBreath(args.getS32("breath"), false); } catch (SettingNotFoundException &e) {} + + try { + std::string extended_attributes = args.get("extended_attributes"); + Json::Reader reader; + Json::Value attr_root; + reader.parse(extended_attributes, attr_root); + + const Json::Value::Members attr_list = attr_root.getMemberNames(); + for (Json::Value::Members::const_iterator it = attr_list.begin(); + it != attr_list.end(); ++it) { + Json::Value attr_value = attr_root[*it]; + sao->setExtendedAttribute(*it, attr_value.asString()); + } + } catch (SettingNotFoundException &e) {} } inventory.deSerialize(is); @@ -175,7 +204,6 @@ void RemotePlayer::serialize(std::ostream &os) Settings args; args.setS32("version", 1); args.set("name", m_name); - //args.set("password", m_password); // This should not happen assert(m_sao); @@ -185,6 +213,10 @@ void RemotePlayer::serialize(std::ostream &os) args.setFloat("yaw", m_sao->getYaw()); args.setS32("breath", m_sao->getBreath()); + std::string extended_attrs = ""; + serializeExtraAttributes(extended_attrs); + args.set("extended_attributes", extended_attrs); + args.writeLines(os); os<<"PlayerArgsEnd\n"; diff --git a/src/remoteplayer.h b/src/remoteplayer.h index 61b5a23de..f44fb9332 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -25,11 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc., class PlayerSAO; -enum RemotePlayerChatResult { +enum RemotePlayerChatResult +{ RPLAYER_CHATRESULT_OK, RPLAYER_CHATRESULT_FLOODING, RPLAYER_CHATRESULT_KICK, }; + /* Player on the server */ @@ -135,6 +137,7 @@ private: deSerialize stops reading exactly at the right point. */ void serialize(std::ostream &os); + void serializeExtraAttributes(std::string &output); PlayerSAO *m_sao; bool m_dirty; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index be4451704..9352812ab 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -1181,6 +1181,45 @@ int ObjectRef::l_get_breath(lua_State *L) return 1; } +// set_attribute(self, attribute, value) +int ObjectRef::l_set_attribute(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if (co == NULL) { + return 0; + } + + std::string attr = luaL_checkstring(L, 2); + std::string value = luaL_checkstring(L, 3); + + if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + co->setExtendedAttribute(attr, value); + } + return 1; +} + +// get_attribute(self, attribute) +int ObjectRef::l_get_attribute(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO* co = getplayersao(ref); + if (co == NULL) { + return 0; + } + + std::string attr = luaL_checkstring(L, 2); + + std::string value = ""; + if (co->getExtendedAttribute(attr, &value)) { + lua_pushstring(L, value.c_str()); + return 1; + } + + return 0; +} + + // set_inventory_formspec(self, formspec) int ObjectRef::l_set_inventory_formspec(lua_State *L) { @@ -1839,6 +1878,8 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, set_look_pitch), luamethod(ObjectRef, get_breath), luamethod(ObjectRef, set_breath), + luamethod(ObjectRef, get_attribute), + luamethod(ObjectRef, set_attribute), luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_player_control), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 96d0abae8..2c9aa559a 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -226,6 +226,12 @@ private: // get_breath(self, breath) static int l_get_breath(lua_State *L); + // set_attribute(self, attribute, value) + static int l_set_attribute(lua_State *L); + + // get_attribute(self, attribute) + static int l_get_attribute(lua_State *L); + // set_inventory_formspec(self, formspec) static int l_set_inventory_formspec(lua_State *L); diff --git a/src/server.h b/src/server.h index e5121bdc3..8f553ce38 100644 --- a/src/server.h +++ b/src/server.h @@ -576,7 +576,6 @@ private: float m_time_of_day_send_timer; // Uptime of server in seconds MutexedVariable m_uptime; - /* Client interface */ diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 01dc3ff10..7a5cfafd6 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -500,7 +500,8 @@ void ServerEnvironment::saveLoadedPlayers() for (std::vector::iterator it = m_players.begin(); it != m_players.end(); ++it) { - if ((*it)->checkModified()) { + if ((*it)->checkModified() || + ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) { (*it)->save(players_path, m_server); } } -- cgit v1.2.3 From 953cbb3b15997a0e7c7c32af2365cb5046a9e476 Mon Sep 17 00:00:00 2001 From: Paramat Date: Sat, 28 Jan 2017 10:07:35 +0000 Subject: Plantlike: Fix visual_scale being applied squared (#5115) Visual_scale was applied twice to plantlike by accident sometime between 2011 and 2013, squaring the requested scale value. Visual_scale is correctly applied once in it's other uses in signlike and torchlike. Two lines of code are removed, they also had no effect for the vast majority of nodes with the default visual_scale of 1.0. The texture continues to have it's base at ground level. --- src/content_mapblock.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 45822666f..9923647bc 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -1602,8 +1602,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } for (int i = 0; i < 4; i++) { - vertices[i].Pos *= f.visual_scale; - vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1); if (data->m_smooth_lighting) vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); -- cgit v1.2.3 From 814ee971f70a8ef1fa4a470bcf385300686e9e70 Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 21 Jan 2017 15:58:07 +0100 Subject: Make entity on_punch have same signature and behaviour as player on_punch --- doc/lua_api.txt | 5 ++++- src/content_sao.cpp | 32 ++++++++++++++++++-------------- src/script/cpp_api/s_entity.cpp | 15 +++++++++------ src/script/cpp_api/s_entity.h | 4 ++-- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ee7d57c2f..ff745c1c2 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1392,7 +1392,7 @@ a non-tool item, so that it can do something else than take damage. On the Lua side, every punch calls: - entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction) + entity:on_punch(puncher, time_from_last_punch, tool_capabilities, direction, damage) This should never be called directly, because damage is usually not handled by the entity itself. @@ -1403,6 +1403,9 @@ the entity itself. * `tool_capabilities` can be `nil`. * `direction` is a unit vector, pointing from the source of the punch to the punched object. +* `damage` damage that will be done to entity +Return value of this function will determin if damage is done by this function +(retval true) or shall be done by engine (retval false) To punch an entity/object in Lua, call: diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 35133490e..7a1171eb7 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -578,28 +578,32 @@ int LuaEntitySAO::punch(v3f dir, punchitem, time_from_last_punch); - if (result.did_punch) { - setHP(getHP() - result.damage); + bool damage_handled = m_env->getScriptIface()->luaentity_Punch(m_id, puncher, + time_from_last_punch, toolcap, dir, result.did_punch ? result.damage : 0); - if (result.damage > 0) { - std::string punchername = puncher ? puncher->getDescription() : "nil"; + if (!damage_handled) { + if (result.did_punch) { + setHP(getHP() - result.damage); - actionstream << getDescription() << " punched by " - << punchername << ", damage " << result.damage - << " hp, health now " << getHP() << " hp" << std::endl; - } + if (result.damage > 0) { + std::string punchername = puncher ? puncher->getDescription() : "nil"; - std::string str = gob_cmd_punched(result.damage, getHP()); - // create message and add to list - ActiveObjectMessage aom(getId(), true, str); - m_messages_out.push(aom); + actionstream << getDescription() << " punched by " + << punchername << ", damage " << result.damage + << " hp, health now " << getHP() << " hp" << std::endl; + } + + std::string str = gob_cmd_punched(result.damage, getHP()); + // create message and add to list + ActiveObjectMessage aom(getId(), true, str); + m_messages_out.push(aom); + } } if (getHP() == 0) m_removed = true; - m_env->getScriptIface()->luaentity_Punch(m_id, puncher, - time_from_last_punch, toolcap, dir); + return result.wear; } diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 378a6bf09..fcd8dd4a0 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -224,10 +224,10 @@ void ScriptApiEntity::luaentity_Step(u16 id, float dtime) } // Calls entity:on_punch(ObjectRef puncher, time_from_last_punch, -// tool_capabilities, direction) -void ScriptApiEntity::luaentity_Punch(u16 id, +// tool_capabilities, direction, damage) +bool ScriptApiEntity::luaentity_Punch(u16 id, ServerActiveObject *puncher, float time_from_last_punch, - const ToolCapabilities *toolcap, v3f dir) + const ToolCapabilities *toolcap, v3f dir, s16 damage) { SCRIPTAPI_PRECHECKHEADER @@ -242,8 +242,8 @@ void ScriptApiEntity::luaentity_Punch(u16 id, // Get function lua_getfield(L, -1, "on_punch"); if (lua_isnil(L, -1)) { - lua_pop(L, 2); // Pop on_punch and entitu - return; + lua_pop(L, 2); // Pop on_punch and entity + return false; } luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self @@ -251,11 +251,14 @@ void ScriptApiEntity::luaentity_Punch(u16 id, lua_pushnumber(L, time_from_last_punch); push_tool_capabilities(L, *toolcap); push_v3f(L, dir); + lua_pushnumber(L, damage); setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 5, 0, error_handler)); + PCALL_RES(lua_pcall(L, 6, 0, error_handler)); + bool retval = lua_toboolean(L, -1); lua_pop(L, 2); // Pop object and error handler + return retval; } // Calls entity:on_rightclick(ObjectRef clicker) diff --git a/src/script/cpp_api/s_entity.h b/src/script/cpp_api/s_entity.h index 8df9d7f00..4e2a056bb 100644 --- a/src/script/cpp_api/s_entity.h +++ b/src/script/cpp_api/s_entity.h @@ -38,9 +38,9 @@ public: void luaentity_GetProperties(u16 id, ObjectProperties *prop); void luaentity_Step(u16 id, float dtime); - void luaentity_Punch(u16 id, + bool luaentity_Punch(u16 id, ServerActiveObject *puncher, float time_from_last_punch, - const ToolCapabilities *toolcap, v3f dir); + const ToolCapabilities *toolcap, v3f dir, s16 damage); void luaentity_Rightclick(u16 id, ServerActiveObject *clicker); }; -- cgit v1.2.3 From 79d752ba4f6f0197627cc20f99b2540f63b6dc88 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 2 Jan 2017 15:17:28 +0100 Subject: from_table: Fix crash for missing inventory or field --- doc/lua_api.txt | 3 ++- src/script/lua_api/l_nodemeta.cpp | 46 +++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ff745c1c2..9a1cb6bac 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2781,8 +2781,9 @@ Can be gotten via `minetest.get_meta(pos)`. * `get_inventory()`: returns `InvRef` * `to_table()`: returns `nil` or `{fields = {...}, inventory = {list1 = {}, ...}}` * `from_table(nil or {})` - * to clear metadata, use from_table(nil) + * Any non-table value will clear the metadata * See "Node Metadata" + * returns `true` on success ### `NodeTimerRef` Node Timers: a high resolution persistent per-node timer. diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 3d03c0c41..631aa68ce 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -250,7 +250,7 @@ int NodeMetaRef::l_from_table(lua_State *L) // clear old metadata first ref->m_env->getMap().removeNodeMetadata(ref->m_p); - if(lua_isnil(L, base)){ + if (!lua_istable(L, base)) { // No metadata lua_pushboolean(L, true); return 1; @@ -258,34 +258,42 @@ int NodeMetaRef::l_from_table(lua_State *L) // Create new metadata NodeMetadata *meta = getmeta(ref, true); - if(meta == NULL){ + if (meta == NULL) { lua_pushboolean(L, false); return 1; } + // 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 + if (lua_istable(L, -1)) { + 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); + meta->setString(name, std::string(cs, cl)); + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); } + // 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); - read_inventory_list(L, -1, inv, name.c_str(), getServer(L)); - lua_pop(L, 1); // removes value, keeps key for next iteration + if (lua_istable(L, -1)) { + 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); + read_inventory_list(L, -1, inv, name.c_str(), getServer(L)); + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); } + reportMetadataChange(ref); lua_pushboolean(L, true); return 1; -- cgit v1.2.3 From c93f7f5ceaf255bc9314be022b7bebb55b1d0605 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sat, 28 Jan 2017 17:02:43 -0500 Subject: Fix synchronization issue at thread start If a newly spawned thread called getThreadId or getThreadHandle before the spawning thread finished saving the thread handle, then the handle/id would be used uninitialized. This would cause the threading tests to fail since isCurrentThread would return false, and if Minetest is built with C++11 support the std::thread object pointer would be dereferenced while ininitialized, causing a segmentation fault. This fixes the issue by using a mutex to force the spawned thread to wait for the spawning thread to finish initializing the thread object. An alternative way to handle this would be to also set the thread handle/id in the started thread but this wouldn't work for C++11 builds because there's no way to get the partially constructed object. --- src/threading/mutex.cpp | 9 +++++++++ src/threading/mutex.h | 2 ++ src/threading/thread.cpp | 15 +++++++++++++++ src/threading/thread.h | 1 + src/unittest/test_threading.cpp | 2 -- 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/threading/mutex.cpp b/src/threading/mutex.cpp index 0908b5d37..d864f12c5 100644 --- a/src/threading/mutex.cpp +++ b/src/threading/mutex.cpp @@ -88,6 +88,15 @@ void Mutex::lock() #endif } +bool Mutex::try_lock() +{ +#if USE_WIN_MUTEX + return TryEnterCriticalSection(&mutex) != 0; +#else + return pthread_mutex_trylock(&mutex) == 0; +#endif +} + void Mutex::unlock() { #if USE_WIN_MUTEX diff --git a/src/threading/mutex.h b/src/threading/mutex.h index fb5c029fc..6feb23c25 100644 --- a/src/threading/mutex.h +++ b/src/threading/mutex.h @@ -56,6 +56,8 @@ public: void lock(); void unlock(); + bool try_lock(); + protected: Mutex(bool recursive); void init_mutex(bool recursive); diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp index fbe4ba1f3..4f4c9474a 100644 --- a/src/threading/thread.cpp +++ b/src/threading/thread.cpp @@ -101,6 +101,11 @@ Thread::Thread(const std::string &name) : Thread::~Thread() { kill(); + + // Make sure start finished mutex is unlocked before it's destroyed + m_start_finished_mutex.try_lock(); + m_start_finished_mutex.unlock(); + } @@ -113,6 +118,9 @@ bool Thread::start() m_request_stop = false; + // The mutex may already be locked if the thread is being restarted + m_start_finished_mutex.try_lock(); + #if USE_CPP11_THREADS try { @@ -135,6 +143,9 @@ bool Thread::start() #endif + // Allow spawned thread to continue + m_start_finished_mutex.unlock(); + while (!m_running) sleep_ms(1); @@ -249,6 +260,10 @@ DWORD WINAPI Thread::threadProc(LPVOID param) g_logger.registerThread(thr->m_name); thr->m_running = true; + // Wait for the thread that started this one to finish initializing the + // thread handle so that getThreadId/getThreadHandle will work. + thr->m_start_finished_mutex.lock(); + thr->m_retval = thr->run(); thr->m_running = false; diff --git a/src/threading/thread.h b/src/threading/thread.h index 14a0e13ab..4785d3e03 100644 --- a/src/threading/thread.h +++ b/src/threading/thread.h @@ -153,6 +153,7 @@ private: Atomic m_request_stop; Atomic m_running; Mutex m_mutex; + Mutex m_start_finished_mutex; #if USE_CPP11_THREADS std::thread *m_thread_obj; diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index cdbf9674e..e1e1d3660 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -39,9 +39,7 @@ static TestThreading g_test_instance; void TestThreading::runTests(IGameDef *gamedef) { -#if !(defined(__MACH__) && defined(__APPLE__)) TEST(testStartStopWait); -#endif TEST(testThreadKill); TEST(testAtomicSemaphoreThread); } -- cgit v1.2.3 From 3eecc6ff4492ca21772544451fd8aa78871bac3b Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Sat, 28 Jan 2017 17:23:30 -0500 Subject: Fix AIX threading build --- src/threading/thread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/threading/thread.cpp b/src/threading/thread.cpp index 4f4c9474a..1909da61d 100644 --- a/src/threading/thread.cpp +++ b/src/threading/thread.cpp @@ -252,7 +252,7 @@ DWORD WINAPI Thread::threadProc(LPVOID param) Thread *thr = (Thread *)param; #ifdef _AIX - m_kernel_thread_id = thread_self(); + thr->m_kernel_thread_id = thread_self(); #endif thr->setName(thr->m_name); -- cgit v1.2.3 From 707e27b5c26b6c082abbd2f385a976c5bc8964fe Mon Sep 17 00:00:00 2001 From: Zeno- Date: Sun, 29 Jan 2017 19:26:00 +1000 Subject: Rename height to scale for openConsole() (#5139) For Game::openConsole() and GUIChatConsole::openConsole() the parameter name 'height' is misleading because it's actually a percentage of the screen/window height. --- src/game.cpp | 8 +++++--- src/guiChatConsole.cpp | 8 +++++--- src/guiChatConsole.h | 2 +- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/game.cpp b/src/game.cpp index 07d429c6b..4b4597a7a 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1372,7 +1372,7 @@ protected: void dropSelectedItem(); void openInventory(); - void openConsole(float height, const wchar_t *line=NULL); + void openConsole(float scale, const wchar_t *line=NULL); void toggleFreeMove(float *statustext_time); void toggleFreeMoveAlt(float *statustext_time, float *jump_timer); void toggleFast(float *statustext_time); @@ -2779,15 +2779,17 @@ void Game::openInventory() } -void Game::openConsole(float height, const wchar_t *line) +void Game::openConsole(float scale, const wchar_t *line) { + assert(scale > 0.0f && scale <= 1.0f); + #ifdef __ANDROID__ porting::showInputDialog(gettext("ok"), "", "", 2); m_android_chat_open = true; #else if (gui_chat_console->isOpenInhibited()) return; - gui_chat_console->openConsole(height); + gui_chat_console->openConsole(scale); if (line) { gui_chat_console->setCloseOnEnter(true); gui_chat_console->replaceAndAddToHistory(line); diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp index 867576175..bea5571f4 100644 --- a/src/guiChatConsole.cpp +++ b/src/guiChatConsole.cpp @@ -116,11 +116,13 @@ GUIChatConsole::~GUIChatConsole() m_font->drop(); } -void GUIChatConsole::openConsole(f32 height) +void GUIChatConsole::openConsole(f32 scale) { + assert(scale > 0.0f && scale <= 1.0f); + m_open = true; - m_desired_height_fraction = height; - m_desired_height = height * m_screensize.Y; + m_desired_height_fraction = scale; + m_desired_height = scale * m_screensize.Y; reformatConsole(); m_animate_time_old = getTimeMs(); IGUIElement::setVisible(true); diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h index 3013a1d31..4e3cae13f 100644 --- a/src/guiChatConsole.h +++ b/src/guiChatConsole.h @@ -41,7 +41,7 @@ public: // Open the console (height = desired fraction of screen size) // This doesn't open immediately but initiates an animation. // You should call isOpenInhibited() before this. - void openConsole(f32 height); + void openConsole(f32 scale); bool isOpen() const; -- cgit v1.2.3 From 0c9189d10989f85cd5148107b643dc17a49c06ff Mon Sep 17 00:00:00 2001 From: Ezhh Date: Sun, 29 Jan 2017 16:10:17 +0000 Subject: Add console height setting (#5136) --- builtin/settingtypes.txt | 3 +++ minetest.conf.example | 4 ++++ src/defaultsettings.cpp | 1 + src/game.cpp | 3 ++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 581eef315..c81dde7de 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -477,6 +477,9 @@ fall_bobbing_amount (Fall bobbing) float 0.0 # - pageflip: quadbuffer based 3d. 3d_mode (3D mode) enum none none,anaglyph,interlaced,topbottom,sidebyside,pageflip +# In-game chat console height, between 0.1 (10%) and 1.0 (100%). +console_height (Console height) float 1.0 0.1 1.0 + # In-game chat console background color (R,G,B). console_color (Console color) string (0,0,0) diff --git a/minetest.conf.example b/minetest.conf.example index 642b028c7..5c7533e93 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -548,6 +548,10 @@ # type: enum values: none, anaglyph, interlaced, topbottom, sidebyside, pageflip # 3d_mode = none +# In-game chat console height, between 0.1 (10%) and 1.0 (100%). +# type: float +# console_height = 1.0 + # In-game chat console background color (R,G,B). # type: string # console_color = (0,0,0) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index e154a63c8..84046f61c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -138,6 +138,7 @@ void set_default_settings(Settings *settings) settings->setDefault("cloud_radius", "12"); settings->setDefault("menu_clouds", "true"); settings->setDefault("opaque_water", "false"); + settings->setDefault("console_height", "1.0"); settings->setDefault("console_color", "(0,0,0)"); settings->setDefault("console_alpha", "200"); settings->setDefault("selectionbox_color", "(0,0,0)"); diff --git a/src/game.cpp b/src/game.cpp index 4b4597a7a..1cb054cab 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2627,7 +2627,8 @@ void Game::processKeyInput(VolatileRunFlags *flags, } else if (wasKeyDown(KeyType::CMD)) { openConsole(0.2, L"/"); } else if (wasKeyDown(KeyType::CONSOLE)) { - openConsole(1); + openConsole(core::clamp( + g_settings->getFloat("console_height"), 0.1f, 1.0f)); } else if (wasKeyDown(KeyType::FREEMOVE)) { toggleFreeMove(statustext_time); } else if (wasKeyDown(KeyType::JUMP)) { -- cgit v1.2.3 From f0e3abc64d1c79c024f588e6d7ccd27e021c4a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Mon, 30 Jan 2017 02:47:36 +0100 Subject: Re-add halo highlight (#5130) Due to a rebase mistake halo highlighting was disabled. This commit re-adds that feature. --- src/game.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/game.cpp b/src/game.cpp index 1cb054cab..840403c42 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3697,6 +3697,7 @@ PointedThing Game::updatePointedThing( { std::vector *selectionboxes = hud->getSelectionBoxes(); selectionboxes->clear(); + hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0)); static const bool show_entity_selectionbox = g_settings->getBool( "show_entity_selectionbox"); @@ -3741,6 +3742,10 @@ PointedThing Game::updatePointedThing( } hud->setSelectionPos(intToFloat(result.node_undersurface, BS), camera_offset); + hud->setSelectedFaceNormal(v3f( + result.intersection_normal.X, + result.intersection_normal.Y, + result.intersection_normal.Z)); } // Update selection mesh light level and vertex colors -- cgit v1.2.3 From 6642c8583a8635a5d61f11ca53254b4ae03d9c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Mon, 30 Jan 2017 08:18:18 +0100 Subject: Use fabs() instead of abs() (#5141) --- src/mapblock_mesh.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 79d02d6ff..2520b5459 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -1090,9 +1090,9 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): brightness of vertices 1 and 3 differ less than the brightness of vertices 0 and 2. */ - if (abs(f.vertices[0].Color.getLuminance() + if (fabs(f.vertices[0].Color.getLuminance() - f.vertices[2].Color.getLuminance()) - > abs(f.vertices[1].Color.getLuminance() + > fabs(f.vertices[1].Color.getLuminance() - f.vertices[3].Color.getLuminance())) indices_p = indices_alternate; -- cgit v1.2.3 From e761b9f48626db2af8b62a0cf85691208951cf0d Mon Sep 17 00:00:00 2001 From: sapier Date: Sat, 14 Jan 2017 23:16:58 +0100 Subject: Add multiply texture modifier Allows colorizing of textures using a color multiplication method. --- doc/lua_api.txt | 7 +++++++ src/client/tile.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9a1cb6bac..219882f46 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -419,6 +419,13 @@ the word "`alpha`", then each texture pixel will contain the RGB of `` and the alpha of `` multiplied by the alpha of the texture pixel. +#### `[multiply:` +Multiplies texture colors with the given color. +`` is specified as a `ColorString`. +Result is more like what you'd expect if you put a color on top of another +color. Meaning white surfaces get a lot of your new color while black parts don't +change very much. + Sounds ------ Only Ogg Vorbis files are supported. diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 539c29445..fbc0f1709 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -555,7 +555,11 @@ static void blit_with_alpha_overlay(video::IImage *src, video::IImage *dst, // color alpha with the destination alpha. // Otherwise, any pixels that are not fully transparent get the color alpha. static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size, - video::SColor color, int ratio, bool keep_alpha); + const video::SColor &color, int ratio, bool keep_alpha); + +// paint a texture using the given color +static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size, + const video::SColor &color); // Apply a mask to an image static void apply_mask(video::IImage *mask, video::IImage *dst, @@ -1656,6 +1660,30 @@ bool TextureSource::generateImagePart(std::string part_of_name, << filename << "\"."; } } + /* + [multiply:color + multiplys a given color to any pixel of an image + color = color as ColorString + */ + else if (str_starts_with(part_of_name, "[multiply:")) { + Strfnd sf(part_of_name); + sf.next(":"); + std::string color_str = sf.next(":"); + + if (baseimg == NULL) { + errorstream << "generateImagePart(): baseimg != NULL " + << "for part_of_name=\"" << part_of_name + << "\", cancelling." << std::endl; + return false; + } + + video::SColor color; + + if (!parseColorString(color_str, color, false)) + return false; + + apply_multiplication(baseimg, v2u32(0, 0), baseimg->getDimension(), color); + } /* [colorize:color Overlays image with given color @@ -1960,7 +1988,7 @@ static void blit_with_interpolate_overlay(video::IImage *src, video::IImage *dst Apply color to destination */ static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size, - video::SColor color, int ratio, bool keep_alpha) + const video::SColor &color, int ratio, bool keep_alpha) { u32 alpha = color.getAlpha(); video::SColor dst_c; @@ -1994,6 +2022,27 @@ static void apply_colorize(video::IImage *dst, v2u32 dst_pos, v2u32 size, } } +/* + Apply color to destination +*/ +static void apply_multiplication(video::IImage *dst, v2u32 dst_pos, v2u32 size, + const video::SColor &color) +{ + video::SColor dst_c; + + for (u32 y = dst_pos.Y; y < dst_pos.Y + size.Y; y++) + for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) { + dst_c = dst->getPixel(x, y); + dst_c.set( + dst_c.getAlpha(), + (dst_c.getRed() * color.getRed()) / 255, + (dst_c.getGreen() * color.getGreen()) / 255, + (dst_c.getBlue() * color.getBlue()) / 255 + ); + dst->setPixel(x, y, dst_c); + } +} + /* Apply mask to destination */ -- cgit v1.2.3 From cdc538e0a242167cd7031d40670d2d4464b87f2c Mon Sep 17 00:00:00 2001 From: paramat Date: Sun, 29 Jan 2017 06:29:40 +0000 Subject: Plantlike visual scale: Send sqrt(visual_scale) to old clients Keep compatibility with protocol < 30 clients now that visual_scale is no longer applied twice to plantlike drawtype and mods are being updated to a new value. --- src/network/networkprotocol.h | 2 ++ src/nodedef.cpp | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index a511d169b..5301cc91c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -146,6 +146,8 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 30: New ContentFeatures serialization version Add node and tile color and palette + Fix plantlike visual_scale being applied squared and add compatibility + with pre-30 clients by sending sqrt(visual_scale) */ #define LATEST_PROTOCOL_VERSION 30 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 0bb150267..c717b62b9 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1611,6 +1611,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const compatible_param_type_2 = CPT2_WALLMOUNTED; } + float compatible_visual_scale = visual_scale; + if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) + compatible_visual_scale = sqrt(visual_scale); + if (protocol_version == 13) { writeU8(os, 5); // version @@ -1622,7 +1626,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); @@ -1670,7 +1674,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); @@ -1724,7 +1728,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); -- cgit v1.2.3 From 74afe6fe6bf5bd6eeba3b6f4fbbfbe8758e7f750 Mon Sep 17 00:00:00 2001 From: numberZero Date: Tue, 31 Jan 2017 11:42:39 +0400 Subject: Fix fog weirdness (#5146) --- client/shaders/nodes_shader/opengl_fragment.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 914f9f873..7c5b9b613 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -37,7 +37,7 @@ const float fogShadingParameter = 1 / ( 1 - fogStart); vec3 uncharted2Tonemap(vec3 x) { - return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03334; + return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333; } vec4 applyToneMapping(vec4 color) -- cgit v1.2.3 From 3e355ab7d5ccdcd77f104eee57237828410b85d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathana=C3=ABl=20Courant?= Date: Wed, 1 Feb 2017 00:02:30 +0100 Subject: Make facedir_to_dir and wallmounted_to_dir work with coloured nodes as well. (#5153) --- builtin/game/item.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index e51da6d6b..a8dc51d61 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -109,7 +109,7 @@ local facedir_to_dir_map = { 1, 4, 3, 2, } function core.facedir_to_dir(facedir) - return facedir_to_dir[facedir_to_dir_map[facedir]] + return facedir_to_dir[facedir_to_dir_map[facedir % 32]] end function core.dir_to_wallmounted(dir) @@ -144,7 +144,7 @@ local wallmounted_to_dir = { {x = 0, y = 0, z = -1}, } function core.wallmounted_to_dir(wallmounted) - return wallmounted_to_dir[wallmounted] + return wallmounted_to_dir[wallmounted % 8] end function core.dir_to_yaw(dir) -- cgit v1.2.3 From d873545ac70331a224967493f9296a854d292dd8 Mon Sep 17 00:00:00 2001 From: Ekdohibs Date: Mon, 30 Jan 2017 07:58:43 +0100 Subject: Fix anticheat resetting client position after the client is teleported Previously, m_move_pool could accomodate the client moving from the new position to the old one, and the server accepted the client to go back to its old position. However, it couldn't then accomodate the client moving from its old to its new position, and therefore would reset position to the old position. Thus, by emptying m_move_pool after a teleport, the server no longer accepts the client to go back to its old position. A drawback is however that a laggy client *will* trigger a few "moved_too_fast" anticheats before being told about its new position. Don't report player cheated if caused by lag. Fixes #5118 --- src/content_sao.cpp | 19 +++++++++++++++---- src/content_sao.h | 9 +++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 7a1171eb7..f87977f80 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -785,6 +785,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer m_inventory(NULL), m_damage(0), m_last_good_position(0,0,0), + m_time_from_last_teleport(0), m_time_from_last_punch(0), m_nocheat_dig_pos(32767, 32767, 32767), m_nocheat_dig_time(0), @@ -1000,6 +1001,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) // Increment cheat prevention timers m_dig_pool.add(dtime); m_move_pool.add(dtime); + m_time_from_last_teleport += dtime; m_time_from_last_punch += dtime; m_nocheat_dig_time += dtime; @@ -1106,6 +1108,8 @@ void PlayerSAO::setPos(const v3f &pos) setBasePosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; m_env->getGameDef()->SendMovePlayer(m_peer_id); } @@ -1117,6 +1121,8 @@ void PlayerSAO::moveTo(v3f pos, bool continuous) setBasePosition(pos); // Movement caused by this command is always valid m_last_good_position = pos; + m_move_pool.empty(); + m_time_from_last_teleport = 0.0; m_env->getGameDef()->SendMovePlayer(m_peer_id); } @@ -1405,11 +1411,16 @@ bool PlayerSAO::checkMovementCheat() if (m_move_pool.grab(required_time)) { m_last_good_position = m_base_position; } else { - actionstream << "Player " << m_player->getName() - << " moved too fast; resetting position" - << std::endl; + const float LAG_POOL_MIN = 5.0; + float lag_pool_max = m_env->getMaxLagEstimate() * 2.0; + lag_pool_max = MYMAX(lag_pool_max, LAG_POOL_MIN); + if (m_time_from_last_teleport > lag_pool_max) { + actionstream << "Player " << m_player->getName() + << " moved too fast; resetting position" + << std::endl; + cheated = true; + } setBasePosition(m_last_good_position); - cheated = true; } return cheated; } diff --git a/src/content_sao.h b/src/content_sao.h index bbf244742..1ccea0d78 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -157,18 +157,26 @@ class LagPool public: LagPool(): m_pool(15), m_max(15) {} + void setMax(float new_max) { m_max = new_max; if(m_pool > new_max) m_pool = new_max; } + void add(float dtime) { m_pool -= dtime; if(m_pool < 0) m_pool = 0; } + + void empty() + { + m_pool = m_max; + } + bool grab(float dtime) { if(dtime <= 0) @@ -358,6 +366,7 @@ private: LagPool m_dig_pool; LagPool m_move_pool; v3f m_last_good_position; + float m_time_from_last_teleport; float m_time_from_last_punch; v3s16 m_nocheat_dig_pos; float m_nocheat_dig_time; -- cgit v1.2.3 From 326cc5df741120dcfdfef21a7acaef5322601764 Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 30 Jan 2017 18:06:17 +0000 Subject: Mgvalleys: Fix missing decorations and incorrect function order Fix missing decorations at horizontal chunk borders by adding 'updateHeightmap()' after terrain generation. Swap order of 'calculateNoise' and 'calcBiomeNoise' because 'calculateNoise' modifies the heat and humidity maps created in 'calcBiomeNoise'. Remove confusing comment, code block is not just for mods and seems essential for correct mapgen behaviour. --- src/mapgen_valleys.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index ce7a95329..ccf797eff 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -238,17 +238,21 @@ void MapgenValleys::makeChunk(BlockMakeData *data) blockseed = getBlockSeed2(full_node_min, seed); - // Generate noise maps and base terrain height. - calculateNoise(); - // Generate biome noises. Note this must be executed strictly before // generateTerrain, because generateTerrain depends on intermediate // biome-related noises. m_bgen->calcBiomeNoise(node_min); + // Generate noise maps and base terrain height. + // Modify heat and humidity maps. + calculateNoise(); + // Generate base terrain with initial heightmaps s16 stone_surface_max_y = generateTerrain(); + // Recalculate heightmap + updateHeightmap(node_min, node_max); + // Place biome-specific nodes and build biomemap MgStoneType stone_type = generateBiomes(); @@ -549,10 +553,6 @@ int MapgenValleys::generateTerrain() index_3d += ystride; } - // This happens if we're generating a chunk that doesn't - // contain the terrain surface, in which case, we need - // to set heightmap to a value outside of the chunk, - // to avoid confusing lua mods that use heightmap. if (heightmap[index_2d] == -MAX_MAP_GENERATION_LIMIT) { s16 surface_y_int = myround(surface_y); if (surface_y_int > node_max.Y + 1 || surface_y_int < node_min.Y - 1) { -- cgit v1.2.3 From 3b9ae409c7d7e9445099b86c85392dba4771e08a Mon Sep 17 00:00:00 2001 From: Duane Robertson Date: Mon, 30 Jan 2017 22:37:04 -0600 Subject: Tell on_punch to expect a return value The return value should be interpreted as a boolean saying whether the lua on_punch function handled damage or the system needs to. --- src/script/cpp_api/s_entity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index fcd8dd4a0..9e2193970 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -254,7 +254,7 @@ bool ScriptApiEntity::luaentity_Punch(u16 id, lua_pushnumber(L, damage); setOriginFromTable(object); - PCALL_RES(lua_pcall(L, 6, 0, error_handler)); + PCALL_RES(lua_pcall(L, 6, 1, error_handler)); bool retval = lua_toboolean(L, -1); lua_pop(L, 2); // Pop object and error handler -- cgit v1.2.3 From 03b34cb3dd0647b3e378f00cdc7203e580c9dcc8 Mon Sep 17 00:00:00 2001 From: kilbith Date: Fri, 3 Feb 2017 14:53:43 +0100 Subject: Serverlist: Add ping indicators (#5164) --- builtin/mainmenu/common.lua | 21 ++++++++++++++++++++- builtin/mainmenu/tab_multiplayer.lua | 1 + builtin/mainmenu/tab_simple_main.lua | 1 + src/script/lua_api/l_mainmenu.cpp | 7 +++++++ textures/base/pack/server_ping_1.png | Bin 0 -> 251 bytes textures/base/pack/server_ping_2.png | Bin 0 -> 244 bytes textures/base/pack/server_ping_3.png | Bin 0 -> 245 bytes textures/base/pack/server_ping_4.png | Bin 0 -> 213 bytes 8 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 textures/base/pack/server_ping_1.png create mode 100644 textures/base/pack/server_ping_2.png create mode 100644 textures/base/pack/server_ping_3.png create mode 100644 textures/base/pack/server_ping_4.png diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index 5e3df0864..17d910e8b 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -54,7 +54,11 @@ end function image_column(tooltip, flagname) return "image,tooltip=" .. core.formspec_escape(tooltip) .. "," .. "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. - "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_" .. flagname .. ".png") + "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_" .. flagname .. ".png") .. "," .. + "2=" .. core.formspec_escape(defaulttexturedir .. "server_ping_4.png") .. "," .. + "3=" .. core.formspec_escape(defaulttexturedir .. "server_ping_3.png") .. "," .. + "4=" .. core.formspec_escape(defaulttexturedir .. "server_ping_2.png") .. "," .. + "5=" .. core.formspec_escape(defaulttexturedir .. "server_ping_1.png") end -------------------------------------------------------------------------------- @@ -97,6 +101,21 @@ function render_serverlist_row(spec, is_favorite) details = "0," end + if spec.ping then + local ping = spec.ping * 1000 + if ping <= 50 then + details = details .. "2," + elseif ping <= 100 then + details = details .. "3," + elseif ping <= 250 then + details = details .. "4," + else + details = details .. "5," + end + else + details = details .. "0," + end + if spec.clients and spec.clients_max then local clients_color = '' local clients_percent = 100 * spec.clients / spec.clients_max diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua index f8edeaddd..033ba38d8 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_multiplayer.lua @@ -69,6 +69,7 @@ local function get_formspec(tabview, name, tabdata) --favourites retval = retval .. "tablecolumns[" .. image_column(fgettext("Favorite"), "favorite") .. ";" .. + image_column(fgettext("Ping"), "") .. ",padding=0.25;" .. "color,span=3;" .. "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" diff --git a/builtin/mainmenu/tab_simple_main.lua b/builtin/mainmenu/tab_simple_main.lua index a773a4912..23820aab7 100644 --- a/builtin/mainmenu/tab_simple_main.lua +++ b/builtin/mainmenu/tab_simple_main.lua @@ -43,6 +43,7 @@ local function get_formspec(tabview, name, tabdata) retval = retval .. "tablecolumns[" .. image_column(fgettext("Favorite"), "favorite") .. ";" .. + image_column(fgettext("Ping"), "") .. ",padding=0.25;" .. "color,span=3;" .. "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 4a2484613..de8890b11 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -577,6 +577,13 @@ int ModApiMainMenu::l_get_favorites(lua_State *L) lua_settable(L, top_lvl2); } + if (servers[i].isMember("ping")) { + float ping = servers[i]["ping"].asFloat(); + lua_pushstring(L, "ping"); + lua_pushnumber(L, ping); + lua_settable(L, top_lvl2); + } + lua_settable(L, top); index++; } diff --git a/textures/base/pack/server_ping_1.png b/textures/base/pack/server_ping_1.png new file mode 100644 index 000000000..ba5bba1e3 Binary files /dev/null and b/textures/base/pack/server_ping_1.png differ diff --git a/textures/base/pack/server_ping_2.png b/textures/base/pack/server_ping_2.png new file mode 100644 index 000000000..8dca0be0d Binary files /dev/null and b/textures/base/pack/server_ping_2.png differ diff --git a/textures/base/pack/server_ping_3.png b/textures/base/pack/server_ping_3.png new file mode 100644 index 000000000..c2cab017e Binary files /dev/null and b/textures/base/pack/server_ping_3.png differ diff --git a/textures/base/pack/server_ping_4.png b/textures/base/pack/server_ping_4.png new file mode 100644 index 000000000..03b4b5b83 Binary files /dev/null and b/textures/base/pack/server_ping_4.png differ -- cgit v1.2.3 From 2d03cfd24c89c66e5ace4f335dae42f7c2c84f6b Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 4 Feb 2017 04:04:03 +0000 Subject: MapgenBasic node resolver: Various fixes Add a fallback node for stair_desert_stone to avoid ignore placed in Minimal subgame desert dungeons. Don't allow river_water_source to fallback to water_source as river water needs to be non-renewable and have a short flow range. Make stair_sandstonebrick fall back to sandstonebrick instead of sandstone. Re-order some lines. Add a comment. --- src/mapgen.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index e42fc7467..00ae917a1 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -587,23 +587,23 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emer //// Look up some commonly used content c_stone = ndef->getId("mapgen_stone"); - c_water_source = ndef->getId("mapgen_water_source"); c_desert_stone = ndef->getId("mapgen_desert_stone"); c_sandstone = ndef->getId("mapgen_sandstone"); + c_water_source = ndef->getId("mapgen_water_source"); c_river_water_source = ndef->getId("mapgen_river_water_source"); // Fall back to more basic content if not defined + // river_water_source cannot fallback to water_source because river water + // needs to be non-renewable and have a short flow range. if (c_desert_stone == CONTENT_IGNORE) c_desert_stone = c_stone; if (c_sandstone == CONTENT_IGNORE) c_sandstone = c_stone; - if (c_river_water_source == CONTENT_IGNORE) - c_river_water_source = c_water_source; //// Content used for dungeon generation c_cobble = ndef->getId("mapgen_cobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_mossycobble = ndef->getId("mapgen_mossycobble"); + c_stair_cobble = ndef->getId("mapgen_stair_cobble"); c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone"); c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); @@ -613,10 +613,12 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emer c_mossycobble = c_cobble; if (c_stair_cobble == CONTENT_IGNORE) c_stair_cobble = c_cobble; + if (c_stair_desert_stone == CONTENT_IGNORE) + c_stair_desert_stone = c_desert_stone; if (c_sandstonebrick == CONTENT_IGNORE) c_sandstonebrick = c_sandstone; if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstone; + c_stair_sandstonebrick = c_sandstonebrick; } -- cgit v1.2.3 From 8e4c11406e20a29ad659b99c840b5281941ae585 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 4 Feb 2017 04:28:02 +0000 Subject: Mgv6: Add stairs to desert stone dungeons As with the other mapgens, now that wide stairs in dungeons are possible we can now finally add stairs to desert stone dungeons. Re-order some lines. --- src/mapgen_v6.cpp | 26 +++++++++++++++----------- src/mapgen_v6.h | 1 + 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 79617a830..d4e3fa8d7 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -99,18 +99,10 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) c_snowblock = ndef->getId("mapgen_snowblock"); c_ice = ndef->getId("mapgen_ice"); - c_cobble = ndef->getId("mapgen_cobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - - if (c_desert_sand == CONTENT_IGNORE) - c_desert_sand = c_sand; if (c_desert_stone == CONTENT_IGNORE) c_desert_stone = c_stone; - if (c_mossycobble == CONTENT_IGNORE) - c_mossycobble = c_cobble; - if (c_stair_cobble == CONTENT_IGNORE) - c_stair_cobble = c_cobble; + if (c_desert_sand == CONTENT_IGNORE) + c_desert_sand = c_sand; if (c_dirt_with_snow == CONTENT_IGNORE) c_dirt_with_snow = c_dirt_with_grass; if (c_snow == CONTENT_IGNORE) @@ -119,6 +111,18 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) c_snowblock = c_dirt_with_grass; if (c_ice == CONTENT_IGNORE) c_ice = c_water_source; + + c_cobble = ndef->getId("mapgen_cobble"); + c_mossycobble = ndef->getId("mapgen_mossycobble"); + c_stair_cobble = ndef->getId("mapgen_stair_cobble"); + c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone"); + + if (c_mossycobble == CONTENT_IGNORE) + c_mossycobble = c_cobble; + if (c_stair_cobble == CONTENT_IGNORE) + c_stair_cobble = c_cobble; + if (c_stair_desert_stone == CONTENT_IGNORE) + c_stair_desert_stone = c_desert_stone; } @@ -571,7 +575,7 @@ void MapgenV6::makeChunk(BlockMakeData *data) if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) { dp.c_wall = c_desert_stone; dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_desert_stone; + dp.c_stair = c_stair_desert_stone; dp.diagonal_dirs = true; dp.holesize = v3s16(2, 3, 2); diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index f018ffaca..44591e3dc 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -123,6 +123,7 @@ public: content_t c_cobble; content_t c_mossycobble; content_t c_stair_cobble; + content_t c_stair_desert_stone; MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge); ~MapgenV6(); -- cgit v1.2.3 From 047168ca839677bd967d6186e22f5476e2264709 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 4 Feb 2017 06:03:04 +0000 Subject: Mgv6: Add fallback node for gravel Gravel now falls back to stone. Gravel is not a particularly fundamental node, allowing it to fall back to stone frees up subgames from having to include a gravel node. Non-blob-ore gravel is only present in mgv6 as extremely rare 'gravel biomes'. --- src/mapgen_v6.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index d4e3fa8d7..807bbe751 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -99,6 +99,8 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) c_snowblock = ndef->getId("mapgen_snowblock"); c_ice = ndef->getId("mapgen_ice"); + if (c_gravel == CONTENT_IGNORE) + c_gravel = c_stone; if (c_desert_stone == CONTENT_IGNORE) c_desert_stone = c_stone; if (c_desert_sand == CONTENT_IGNORE) -- cgit v1.2.3 From de664b1c6d4b2bca47f918a6a865a920434bf664 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 4 Feb 2017 13:31:21 +0100 Subject: Fix PlayerSAO deletion warning (0eede97af2927dcda3545192403b0a44f30bcd1f) --- src/serverenvironment.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 7a5cfafd6..8d86a4e0a 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -2087,9 +2087,7 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) if(block) { - // Force delete object if mapblock is full, but ignore players - if (obj->getType() != ACTIVEOBJECT_TYPE_PLAYER && - block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { + if (block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")) { warningstream << "ServerEnv: Trying to store id = " << obj->getId() << " statically but block " << PP(blockpos) << " already contains " @@ -2149,6 +2147,13 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } + if (!force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + warningstream << "ServerEnvironment::deactivateFarObjects(): " + << "Trying to delete player object, THIS SHOULD NEVER HAPPEN!" + << std::endl; + continue; + } + verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Tue, 31 Jan 2017 14:45:28 +0000 Subject: Derive NodeMetadata from Metadata --- build/android/jni/Android.mk | 2 +- src/CMakeLists.txt | 2 +- src/metadata.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++ src/metadata.h | 59 +++++++++++++++++++++++++++++++++++++ src/nodemetadata.cpp | 40 ++----------------------- src/nodemetadata.h | 19 ++---------- 6 files changed, 135 insertions(+), 56 deletions(-) create mode 100644 src/metadata.cpp create mode 100644 src/metadata.h diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 3be856770..8f333c3d4 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -184,6 +184,7 @@ LOCAL_SRC_FILES := \ jni/src/mapnode.cpp \ jni/src/mapsector.cpp \ jni/src/mesh.cpp \ + jni/src/metadata.cpp \ jni/src/mg_biome.cpp \ jni/src/mg_decoration.cpp \ jni/src/mg_ore.cpp \ @@ -384,4 +385,3 @@ ifdef GPROF $(call import-module,android-ndk-profiler) endif $(call import-module,android/native_app_glue) - diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cab5a1139..afb591a04 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -430,6 +430,7 @@ set(common_SRCS mapgen_valleys.cpp mapnode.cpp mapsector.cpp + metadata.cpp mg_biome.cpp mg_decoration.cpp mg_ore.cpp @@ -893,4 +894,3 @@ endif() if (BUILD_CLIENT AND USE_FREETYPE) add_subdirectory(cguittfont) endif() - diff --git a/src/metadata.cpp b/src/metadata.cpp new file mode 100644 index 000000000..8e04aa2d3 --- /dev/null +++ b/src/metadata.cpp @@ -0,0 +1,69 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "metadata.h" +#include "exceptions.h" +#include "gamedef.h" +#include "log.h" +#include + +/* + Metadata +*/ + +void Metadata::clear() +{ + m_stringvars.clear(); +} + +bool Metadata::empty() const +{ + return m_stringvars.size() == 0; +} + +std::string Metadata::getString(const std::string &name, + u16 recursion) const +{ + StringMap::const_iterator it = m_stringvars.find(name); + if (it == m_stringvars.end()) + return ""; + + return resolveString(it->second, recursion); +} + +void Metadata::setString(const std::string &name, const std::string &var) +{ + if (var.empty()) { + m_stringvars.erase(name); + } else { + m_stringvars[name] = var; + } +} + +std::string Metadata::resolveString(const std::string &str, + u16 recursion) const +{ + if (recursion > 1) { + return str; + } + if (str.substr(0, 2) == "${" && str[str.length() - 1] == '}') { + return getString(str.substr(2, str.length() - 3), recursion + 1); + } + return str; +} diff --git a/src/metadata.h b/src/metadata.h new file mode 100644 index 000000000..a96bfc408 --- /dev/null +++ b/src/metadata.h @@ -0,0 +1,59 @@ +/* +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef METADATA_HEADER +#define METADATA_HEADER + +#include "irr_v3d.h" +#include +#include +#include "util/string.h" + +/* + NodeMetadata stores arbitary amounts of data for special blocks. + Used for furnaces, chests and signs. + + There are two interaction methods: inventory menu and text input. + Only one can be used for a single metadata, thus only inventory OR + text input should exist in a metadata. +*/ + +class Inventory; +class IItemDefManager; + +class Metadata +{ +public: + void clear(); + bool empty() const; + + // Generic key/value store + std::string getString(const std::string &name, u16 recursion = 0) const; + void setString(const std::string &name, const std::string &var); + // Support variable names in values + std::string resolveString(const std::string &str, u16 recursion = 0) const; + const StringMap &getStrings() const + { + return m_stringvars; + } +private: + StringMap m_stringvars; +}; + +#endif diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp index 0801a028b..9b60cf33e 100644 --- a/src/nodemetadata.cpp +++ b/src/nodemetadata.cpp @@ -31,10 +31,8 @@ with this program; if not, write to the Free Software Foundation, Inc., */ NodeMetadata::NodeMetadata(IItemDefManager *item_def_mgr): - m_stringvars(), m_inventory(new Inventory(item_def_mgr)) -{ -} +{} NodeMetadata::~NodeMetadata() { @@ -70,13 +68,13 @@ void NodeMetadata::deSerialize(std::istream &is) void NodeMetadata::clear() { - m_stringvars.clear(); + Metadata::clear(); m_inventory->clear(); } bool NodeMetadata::empty() const { - return m_stringvars.size() == 0 && m_inventory->getLists().size() == 0; + return Metadata::empty() && m_inventory->getLists().size() == 0; } /* @@ -216,35 +214,3 @@ int NodeMetadataList::countNonEmpty() const } return n; } - -std::string NodeMetadata::getString(const std::string &name, - unsigned short recursion) const -{ - StringMap::const_iterator it = m_stringvars.find(name); - if (it == m_stringvars.end()) - return ""; - - return resolveString(it->second, recursion); -} - -void NodeMetadata::setString(const std::string &name, const std::string &var) -{ - if (var.empty()) { - m_stringvars.erase(name); - } else { - m_stringvars[name] = var; - } -} - -std::string NodeMetadata::resolveString(const std::string &str, - unsigned short recursion) const -{ - if (recursion > 1) { - return str; - } - if (str.substr(0, 2) == "${" && str[str.length() - 1] == '}') { - return getString(str.substr(2, str.length() - 3), recursion + 1); - } - return str; -} - diff --git a/src/nodemetadata.h b/src/nodemetadata.h index da1bf595d..f46c0fe91 100644 --- a/src/nodemetadata.h +++ b/src/nodemetadata.h @@ -20,10 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef NODEMETADATA_HEADER #define NODEMETADATA_HEADER -#include "irr_v3d.h" -#include -#include -#include "util/string.h" +#include "metadata.h" /* NodeMetadata stores arbitary amounts of data for special blocks. @@ -37,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Inventory; class IItemDefManager; -class NodeMetadata +class NodeMetadata : public Metadata { public: NodeMetadata(IItemDefManager *item_def_mgr); @@ -49,16 +46,6 @@ public: void clear(); bool empty() const; - // Generic key/value store - std::string getString(const std::string &name, unsigned short recursion = 0) const; - void setString(const std::string &name, const std::string &var); - // Support variable names in values - std::string resolveString(const std::string &str, unsigned short recursion = 0) const; - StringMap getStrings() const - { - return m_stringvars; - } - // The inventory Inventory *getInventory() { @@ -66,7 +53,6 @@ public: } private: - StringMap m_stringvars; Inventory *m_inventory; }; @@ -101,4 +87,3 @@ private: }; #endif - -- cgit v1.2.3 From 13f94ecad5d4fcda08663ee91e45b4a205a1dadb Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 31 Jan 2017 16:18:45 +0000 Subject: Make NodeMetaRef::getmeta a non-static member --- src/script/lua_api/l_nodemeta.cpp | 28 ++++++++++++++-------------- src/script/lua_api/l_nodemeta.h | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 631aa68ce..8391de03c 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -36,12 +36,12 @@ NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg) return *(NodeMetaRef**)ud; // unbox pointer } -NodeMetadata* NodeMetaRef::getmeta(NodeMetaRef *ref, bool auto_create) +NodeMetadata* NodeMetaRef::getmeta(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()->idef()); - if(!ref->m_env->getMap().setNodeMetadata(ref->m_p, meta)) { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(m_p); + if (meta == NULL && auto_create) { + meta = new NodeMetadata(m_env->getGameDef()->idef()); + if (!m_env->getMap().setNodeMetadata(m_p, meta)) { delete meta; return NULL; } @@ -83,7 +83,7 @@ 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); + NodeMetadata *meta = ref->getmeta(false); if(meta == NULL){ lua_pushlstring(L, "", 0); return 1; @@ -104,7 +104,7 @@ int NodeMetaRef::l_set_string(lua_State *L) const char *s = lua_tolstring(L, 3, &len); std::string str(s, len); - NodeMetadata *meta = getmeta(ref, !str.empty()); + NodeMetadata *meta = ref->getmeta(!str.empty()); if(meta == NULL || str == meta->getString(name)) return 0; meta->setString(name, str); @@ -120,7 +120,7 @@ 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); + NodeMetadata *meta = ref->getmeta(false); if(meta == NULL){ lua_pushnumber(L, 0); return 1; @@ -140,7 +140,7 @@ int NodeMetaRef::l_set_int(lua_State *L) int a = lua_tointeger(L, 3); std::string str = itos(a); - NodeMetadata *meta = getmeta(ref, true); + NodeMetadata *meta = ref->getmeta(true); if(meta == NULL || str == meta->getString(name)) return 0; meta->setString(name, str); @@ -156,7 +156,7 @@ 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); + NodeMetadata *meta = ref->getmeta(false); if(meta == NULL){ lua_pushnumber(L, 0); return 1; @@ -176,7 +176,7 @@ int NodeMetaRef::l_set_float(lua_State *L) float a = lua_tonumber(L, 3); std::string str = ftos(a); - NodeMetadata *meta = getmeta(ref, true); + NodeMetadata *meta = ref->getmeta(true); if(meta == NULL || str == meta->getString(name)) return 0; meta->setString(name, str); @@ -190,7 +190,7 @@ int NodeMetaRef::l_get_inventory(lua_State *L) MAP_LOCK_REQUIRED; NodeMetaRef *ref = checkobject(L, 1); - getmeta(ref, true); // try to ensure the metadata exists + ref->getmeta(true); // try to ensure the metadata exists InvRef::createNodeMeta(L, ref->m_p); return 1; } @@ -202,7 +202,7 @@ int NodeMetaRef::l_to_table(lua_State *L) NodeMetaRef *ref = checkobject(L, 1); - NodeMetadata *meta = getmeta(ref, true); + NodeMetadata *meta = ref->getmeta(true); if (meta == NULL) { lua_pushnil(L); return 1; @@ -257,7 +257,7 @@ int NodeMetaRef::l_from_table(lua_State *L) } // Create new metadata - NodeMetadata *meta = getmeta(ref, true); + NodeMetadata *meta = ref->getmeta(true); if (meta == NULL) { lua_pushboolean(L, false); return 1; diff --git a/src/script/lua_api/l_nodemeta.h b/src/script/lua_api/l_nodemeta.h index e39ac3931..4ce338b18 100644 --- a/src/script/lua_api/l_nodemeta.h +++ b/src/script/lua_api/l_nodemeta.h @@ -52,7 +52,7 @@ private: * @param auto_create when true, try to create metadata information for the node if it has none. * @return pointer to a @c NodeMetadata object or @c NULL in case of error. */ - static NodeMetadata* getmeta(NodeMetaRef *ref, bool auto_create); + virtual NodeMetadata* getmeta(bool auto_create); static void reportMetadataChange(NodeMetaRef *ref); -- cgit v1.2.3 From c2e7b1f57941cb34cb7e3d71dc040fad53a64e3e Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 31 Jan 2017 16:43:45 +0000 Subject: Derive NodeMetaRef from MetaDataRef --- .gitignore | 2 +- build/android/jni/Android.mk | 1 + src/metadata.cpp | 48 ++++++-- src/metadata.h | 4 +- src/script/lua_api/CMakeLists.txt | 2 +- src/script/lua_api/l_metadata.cpp | 252 ++++++++++++++++++++++++++++++++++++++ src/script/lua_api/l_metadata.h | 71 +++++++++++ src/script/lua_api/l_nodemeta.cpp | 224 +++++---------------------------- src/script/lua_api/l_nodemeta.h | 35 ++---- 9 files changed, 407 insertions(+), 232 deletions(-) create mode 100644 src/script/lua_api/l_metadata.cpp create mode 100644 src/script/lua_api/l_metadata.h diff --git a/.gitignore b/.gitignore index d7e0daab4..c2600afeb 100644 --- a/.gitignore +++ b/.gitignore @@ -74,6 +74,7 @@ locale/ *.a *.ninja .ninja* +*.gch ## Android build files build/android/src/main/assets @@ -86,4 +87,3 @@ build/android/obj build/android/local.properties build/android/.gradle timestamp - diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 8f333c3d4..70a3d29c0 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -307,6 +307,7 @@ LOCAL_SRC_FILES += \ jni/src/script/lua_api/l_item.cpp \ jni/src/script/lua_api/l_mainmenu.cpp \ jni/src/script/lua_api/l_mapgen.cpp \ + jni/src/script/lua_api/l_metadata.cpp \ jni/src/script/lua_api/l_nodemeta.cpp \ jni/src/script/lua_api/l_nodetimer.cpp \ jni/src/script/lua_api/l_noise.cpp \ diff --git a/src/metadata.cpp b/src/metadata.cpp index 8e04aa2d3..96453d710 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -37,12 +37,39 @@ bool Metadata::empty() const return m_stringvars.size() == 0; } -std::string Metadata::getString(const std::string &name, - u16 recursion) const +size_t Metadata::size() const +{ + return m_stringvars.size(); +} + +bool Metadata::contains(const std::string &name) const +{ + return m_stringvars.find(name) != m_stringvars.end(); +} + +bool Metadata::operator==(const Metadata &other) const +{ + if (size() != other.size()) + return false; + + for (StringMap::const_iterator it = m_stringvars.begin(); + it != m_stringvars.end(); ++it) { + if (!other.contains(it->first) || + other.getString(it->first) != it->second) + return false; + } + + return true; +} + +const std::string &Metadata::getString(const std::string &name, + u16 recursion) const { StringMap::const_iterator it = m_stringvars.find(name); - if (it == m_stringvars.end()) - return ""; + if (it == m_stringvars.end()) { + static const std::string empty_string = std::string(""); + return empty_string; + } return resolveString(it->second, recursion); } @@ -56,14 +83,13 @@ void Metadata::setString(const std::string &name, const std::string &var) } } -std::string Metadata::resolveString(const std::string &str, - u16 recursion) const +const std::string &Metadata::resolveString(const std::string &str, + u16 recursion) const { - if (recursion > 1) { - return str; - } - if (str.substr(0, 2) == "${" && str[str.length() - 1] == '}') { + if (recursion <= 1 && + str.substr(0, 2) == "${" && str[str.length() - 1] == '}') { return getString(str.substr(2, str.length() - 3), recursion + 1); + } else { + return str; } - return str; } diff --git a/src/metadata.h b/src/metadata.h index a96bfc408..a629c0615 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -44,10 +44,10 @@ public: bool empty() const; // Generic key/value store - std::string getString(const std::string &name, u16 recursion = 0) const; + const std::string &getString(const std::string &name, u16 recursion = 0) const; void setString(const std::string &name, const std::string &var); // Support variable names in values - std::string resolveString(const std::string &str, u16 recursion = 0) const; + const std::string &resolveString(const std::string &str, u16 recursion = 0) const; const StringMap &getStrings() const { return m_stringvars; diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index d507dcf70..efccce515 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -6,6 +6,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_nodetimer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_noise.cpp @@ -22,4 +23,3 @@ set(common_SCRIPT_LUA_API_SRCS set(client_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp PARENT_SCOPE) - diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp new file mode 100644 index 000000000..b54005bac --- /dev/null +++ b/src/script/lua_api/l_metadata.cpp @@ -0,0 +1,252 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_metadata.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" +#include "serverenvironment.h" +#include "map.h" +#include "server.h" + +// LUALIB_API +void *luaL_checkudata_is_metadataref(lua_State *L, int ud) { + void *p = lua_touserdata(L, ud); + if (p != NULL && // value is a userdata? + lua_getmetatable(L, ud)) { // does it have a metatable? + lua_getfield(L, -1, "metadata_class"); + if (lua_type(L, -1) == LUA_TSTRING) { // does it have a metadata_class field? + return p; + } + } + luaL_typerror(L, ud, "MetaDataRef"); + return NULL; +} + +MetaDataRef* MetaDataRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata_is_metadataref(L, narg); + if (!ud) + luaL_typerror(L, narg, "MetaDataRef"); + + return *(MetaDataRef**)ud; // unbox pointer +} + +// Exported functions + +// get_string(self, name) +int MetaDataRef::l_get_string(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = luaL_checkstring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) { + lua_pushlstring(L, "", 0); + return 1; + } + + const std::string &str = meta->getString(name); + lua_pushlstring(L, str.c_str(), str.size()); + return 1; +} + +// set_string(self, name, var) +int MetaDataRef::l_set_string(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *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); + + Metadata *meta = ref->getmeta(!str.empty()); + if (meta == NULL || str == meta->getString(name)) + return 0; + + meta->setString(name, str); + ref->reportMetadataChange(); + return 0; +} + +// get_int(self, name) +int MetaDataRef::l_get_int(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) { + lua_pushnumber(L, 0); + return 1; + } + + const std::string &str = meta->getString(name); + lua_pushnumber(L, stoi(str)); + return 1; +} + +// set_int(self, name, var) +int MetaDataRef::l_set_int(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + int a = lua_tointeger(L, 3); + std::string str = itos(a); + + Metadata *meta = ref->getmeta(true); + if (meta == NULL || str == meta->getString(name)) + return 0; + + meta->setString(name, str); + ref->reportMetadataChange(); + return 0; +} + +// get_float(self, name) +int MetaDataRef::l_get_float(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + + Metadata *meta = ref->getmeta(false); + if (meta == NULL) { + lua_pushnumber(L, 0); + return 1; + } + + const std::string &str = meta->getString(name); + lua_pushnumber(L, stof(str)); + return 1; +} + +// set_float(self, name, var) +int MetaDataRef::l_set_float(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + std::string name = lua_tostring(L, 2); + float a = lua_tonumber(L, 3); + std::string str = ftos(a); + + Metadata *meta = ref->getmeta(true); + if (meta == NULL || str == meta->getString(name)) + return 0; + + meta->setString(name, str); + ref->reportMetadataChange(); + return 0; +} + +// to_table(self) +int MetaDataRef::l_to_table(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + + Metadata *meta = ref->getmeta(true); + if (meta == NULL) { + lua_pushnil(L); + return 1; + } + lua_newtable(L); + + ref->handleToTable(L, meta); + + return 1; +} + +// from_table(self, table) +int MetaDataRef::l_from_table(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + MetaDataRef *ref = checkobject(L, 1); + int base = 2; + + ref->clearMeta(); + + if (!lua_istable(L, base)) { + // No metadata + lua_pushboolean(L, true); + return 1; + } + + // Create new metadata + Metadata *meta = ref->getmeta(true); + if (meta == NULL) { + lua_pushboolean(L, false); + return 1; + } + + bool was_successful = ref->handleFromTable(L, base, meta); + ref->reportMetadataChange(); + lua_pushboolean(L, was_successful); + return 1; +} + +void MetaDataRef::handleToTable(lua_State *L, Metadata *meta) +{ + lua_newtable(L); + { + const StringMap &fields = meta->getStrings(); + for (StringMap::const_iterator + it = fields.begin(); it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + } + lua_setfield(L, -2, "fields"); +} + +bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta) +{ + // Set fields + lua_getfield(L, table, "fields"); + if (lua_istable(L, -1)) { + 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); + meta->setString(name, std::string(cs, cl)); + lua_pop(L, 1); // Remove value, keep key for next iteration + } + lua_pop(L, 1); + } + + return true; +} diff --git a/src/script/lua_api/l_metadata.h b/src/script/lua_api/l_metadata.h new file mode 100644 index 000000000..561be6adf --- /dev/null +++ b/src/script/lua_api/l_metadata.h @@ -0,0 +1,71 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef L_METADATA_H_ +#define L_METADATA_H_ + +#include "lua_api/l_base.h" +#include "irrlichttypes_bloated.h" + +class Metadata; + +/* + NodeMetaRef +*/ + +class MetaDataRef : public ModApiBase { +public: + virtual ~MetaDataRef() {} +protected: + static MetaDataRef *checkobject(lua_State *L, int narg); + + virtual void reportMetadataChange() {} + virtual Metadata* getmeta(bool auto_create) = 0; + virtual void clearMeta() = 0; + + virtual void handleToTable(lua_State *L, Metadata *meta); + virtual bool handleFromTable(lua_State *L, int table, Metadata *meta); + + // Exported functions + + // 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); + + // to_table(self) + static int l_to_table(lua_State *L); + + // from_table(self, table) + static int l_from_table(lua_State *L); +}; + +#endif /* L_NODEMETA_H_ */ diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 8391de03c..4b2b392da 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -36,7 +36,7 @@ NodeMetaRef* NodeMetaRef::checkobject(lua_State *L, int narg) return *(NodeMetaRef**)ud; // unbox pointer } -NodeMetadata* NodeMetaRef::getmeta(bool auto_create) +Metadata* NodeMetaRef::getmeta(bool auto_create) { NodeMetadata *meta = m_env->getMap().getNodeMetadata(m_p); if (meta == NULL && auto_create) { @@ -49,17 +49,22 @@ NodeMetadata* NodeMetaRef::getmeta(bool auto_create) return meta; } -void NodeMetaRef::reportMetadataChange(NodeMetaRef *ref) +void NodeMetaRef::clearMeta() +{ + m_env->getMap().removeNodeMetadata(m_p); +} + +void NodeMetaRef::reportMetadataChange() { // NOTE: This same code is in rollback_interface.cpp // Inform other things that the metadata has changed - v3s16 blockpos = getNodeBlockPos(ref->m_p); + v3s16 blockpos = getNodeBlockPos(m_p); MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; event.p = blockpos; - ref->m_env->getMap().dispatchEvent(&event); + m_env->getMap().dispatchEvent(&event); // Set the block to be saved - MapBlock *block = ref->m_env->getMap().getBlockNoCreateNoEx(blockpos); + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); if (block) { block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REPORT_META_CHANGE); @@ -75,115 +80,6 @@ int NodeMetaRef::gc_object(lua_State *L) { return 0; } -// get_string(self, name) -int NodeMetaRef::l_get_string(lua_State *L) -{ - MAP_LOCK_REQUIRED; - - NodeMetaRef *ref = checkobject(L, 1); - std::string name = luaL_checkstring(L, 2); - - NodeMetadata *meta = ref->getmeta(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) -{ - MAP_LOCK_REQUIRED; - - 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 = ref->getmeta(!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) -{ - MAP_LOCK_REQUIRED; - - NodeMetaRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - - NodeMetadata *meta = ref->getmeta(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) -{ - MAP_LOCK_REQUIRED; - - 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 = ref->getmeta(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) -{ - MAP_LOCK_REQUIRED; - - NodeMetaRef *ref = checkobject(L, 1); - std::string name = lua_tostring(L, 2); - - NodeMetadata *meta = ref->getmeta(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) -{ - MAP_LOCK_REQUIRED; - - 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 = ref->getmeta(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) { @@ -195,34 +91,12 @@ int NodeMetaRef::l_get_inventory(lua_State *L) return 1; } -// to_table(self) -int NodeMetaRef::l_to_table(lua_State *L) +void NodeMetaRef::handleToTable(lua_State *L, Metadata *_meta) { - MAP_LOCK_REQUIRED; - - NodeMetaRef *ref = checkobject(L, 1); - - NodeMetadata *meta = ref->getmeta(true); - if (meta == NULL) { - lua_pushnil(L); - return 1; - } - lua_newtable(L); - // fields - lua_newtable(L); - { - StringMap fields = meta->getStrings(); - for (StringMap::const_iterator - it = fields.begin(); it != fields.end(); ++it) { - const std::string &name = it->first; - const std::string &value = it->second; - lua_pushlstring(L, name.c_str(), name.size()); - lua_pushlstring(L, value.c_str(), value.size()); - lua_settable(L, -3); - } - } - lua_setfield(L, -2, "fields"); + MetaDataRef::handleToTable(L, _meta); + + NodeMetadata *meta = (NodeMetadata*) _meta; // inventory lua_newtable(L); @@ -236,52 +110,20 @@ int NodeMetaRef::l_to_table(lua_State *L) } } lua_setfield(L, -2, "inventory"); - return 1; } // from_table(self, table) -int NodeMetaRef::l_from_table(lua_State *L) +bool NodeMetaRef::handleFromTable(lua_State *L, int table, Metadata *_meta) { - MAP_LOCK_REQUIRED; - - NodeMetaRef *ref = checkobject(L, 1); - int base = 2; - - // clear old metadata first - ref->m_env->getMap().removeNodeMetadata(ref->m_p); - - if (!lua_istable(L, base)) { - // No metadata - lua_pushboolean(L, true); - return 1; - } + // fields + if (!MetaDataRef::handleFromTable(L, table, _meta)) + return false; - // Create new metadata - NodeMetadata *meta = ref->getmeta(true); - if (meta == NULL) { - lua_pushboolean(L, false); - return 1; - } + NodeMetadata *meta = (NodeMetadata*) _meta; - // Set fields - lua_getfield(L, base, "fields"); - if (lua_istable(L, -1)) { - 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); - meta->setString(name, std::string(cs, cl)); - lua_pop(L, 1); // Remove value, keep key for next iteration - } - lua_pop(L, 1); - } - - // Set inventory + // inventory Inventory *inv = meta->getInventory(); - lua_getfield(L, base, "inventory"); + lua_getfield(L, table, "inventory"); if (lua_istable(L, -1)) { int inventorytable = lua_gettop(L); lua_pushnil(L); @@ -294,9 +136,7 @@ int NodeMetaRef::l_from_table(lua_State *L) lua_pop(L, 1); } - reportMetadataChange(ref); - lua_pushboolean(L, true); - return 1; + return true; } @@ -332,6 +172,10 @@ void NodeMetaRef::Register(lua_State *L) lua_pushvalue(L, methodtable); lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_pushliteral(L, "metadata_class"); + lua_pushlstring(L, className, strlen(className)); + lua_settable(L, metatable); + lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); lua_settable(L, metatable); @@ -351,14 +195,14 @@ void NodeMetaRef::Register(lua_State *L) 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(MetaDataRef, get_string), + luamethod(MetaDataRef, set_string), + luamethod(MetaDataRef, get_int), + luamethod(MetaDataRef, set_int), + luamethod(MetaDataRef, get_float), + luamethod(MetaDataRef, set_float), + luamethod(MetaDataRef, to_table), + luamethod(MetaDataRef, from_table), luamethod(NodeMetaRef, get_inventory), - luamethod(NodeMetaRef, to_table), - luamethod(NodeMetaRef, from_table), {0,0} }; diff --git a/src/script/lua_api/l_nodemeta.h b/src/script/lua_api/l_nodemeta.h index 4ce338b18..d03f086c9 100644 --- a/src/script/lua_api/l_nodemeta.h +++ b/src/script/lua_api/l_nodemeta.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define L_NODEMETA_H_ #include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" #include "irrlichttypes_bloated.h" class ServerEnvironment; @@ -29,7 +30,7 @@ class NodeMetadata; NodeMetaRef */ -class NodeMetaRef : public ModApiBase { +class NodeMetaRef : public MetaDataRef { private: v3s16 m_p; ServerEnvironment *m_env; @@ -52,42 +53,22 @@ private: * @param auto_create when true, try to create metadata information for the node if it has none. * @return pointer to a @c NodeMetadata object or @c NULL in case of error. */ - virtual NodeMetadata* getmeta(bool auto_create); + virtual Metadata* getmeta(bool auto_create); + virtual void clearMeta(); - static void reportMetadataChange(NodeMetaRef *ref); + virtual void reportMetadataChange(); + + virtual void handleToTable(lua_State *L, Metadata *_meta); + virtual bool handleFromTable(lua_State *L, int table, Metadata *_meta); // 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); -- cgit v1.2.3 From f2aa2c6a986dec47856c49ae5f54fbf3c688e027 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 31 Jan 2017 19:49:01 +0000 Subject: Add ItemStack key-value meta storage --- build/android/jni/Android.mk | 2 + doc/lua_api.txt | 45 ++++++++++--- src/CMakeLists.txt | 1 + src/content_cao.cpp | 2 +- src/craftdef.cpp | 3 +- src/inventory.cpp | 88 ++++--------------------- src/inventory.h | 14 ++-- src/itemstackmetadata.cpp | 43 ++++++++++++ src/itemstackmetadata.h | 35 ++++++++++ src/metadata.cpp | 2 + src/metadata.h | 38 +++++------ src/script/common/c_content.cpp | 31 +++++++-- src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_item.cpp | 38 +++++++++-- src/script/lua_api/l_item.h | 5 ++ src/script/lua_api/l_itemstackmeta.cpp | 115 +++++++++++++++++++++++++++++++++ src/script/lua_api/l_itemstackmeta.h | 59 +++++++++++++++++ src/script/scripting_game.cpp | 2 + src/util/serialize.cpp | 49 ++++++++++++++ src/util/serialize.h | 7 ++ 21 files changed, 459 insertions(+), 123 deletions(-) create mode 100644 src/itemstackmetadata.cpp create mode 100644 src/itemstackmetadata.h create mode 100644 src/script/lua_api/l_itemstackmeta.cpp create mode 100644 src/script/lua_api/l_itemstackmeta.h diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 70a3d29c0..47f37cfa5 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \ jni/src/inventory.cpp \ jni/src/inventorymanager.cpp \ jni/src/itemdef.cpp \ + jni/src/itemstackmetadata.cpp \ jni/src/keycode.cpp \ jni/src/light.cpp \ jni/src/localplayer.cpp \ @@ -305,6 +306,7 @@ LOCAL_SRC_FILES += \ jni/src/script/lua_api/l_env.cpp \ jni/src/script/lua_api/l_inventory.cpp \ jni/src/script/lua_api/l_item.cpp \ + jni/src/script/lua_api/l_itemstackmeta.cpp\ jni/src/script/lua_api/l_mainmenu.cpp \ jni/src/script/lua_api/l_mapgen.cpp \ jni/src/script/lua_api/l_metadata.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 219882f46..2f5e3706c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1411,7 +1411,7 @@ the entity itself. * `direction` is a unit vector, pointing from the source of the punch to the punched object. * `damage` damage that will be done to entity -Return value of this function will determin if damage is done by this function +Return value of this function will determin if damage is done by this function (retval true) or shall be done by engine (retval false) To punch an entity/object in Lua, call: @@ -1427,9 +1427,9 @@ Node Metadata ------------- The instance of a node in the world normally only contains the three values mentioned in "Nodes". However, it is possible to insert extra data into a -node. It is called "node metadata"; See "`NodeMetaRef`". +node. It is called "node metadata"; See `NodeMetaRef`. -Metadata contains two things: +Node metadata contains two things: * A key-value store * An inventory @@ -1467,6 +1467,18 @@ Example stuff: } }) +Item Metadata +------------- +Item stacks can store metadata too. See `ItemStackMetaRef`. + +Item metadata only contains a key-value store. + +Example stuff: + + local meta = stack:get_meta() + meta:set_string("key", "value") + print(dump(meta:to_table())) + Formspec -------- Formspec defines a menu. Currently not much else than inventories are @@ -2774,9 +2786,8 @@ These functions return the leftover itemstack. Class reference --------------- -### `NodeMetaRef` -Node metadata: reference extra data and functionality stored in a node. -Can be gotten via `minetest.get_meta(pos)`. +### `MetaDataRef` +See `NodeMetaRef` and `ItemStackMetaRef`. #### Methods * `set_string(name, value)` @@ -2785,13 +2796,29 @@ Can be gotten via `minetest.get_meta(pos)`. * `get_int(name)` * `set_float(name, value)` * `get_float(name)` -* `get_inventory()`: returns `InvRef` -* `to_table()`: returns `nil` or `{fields = {...}, inventory = {list1 = {}, ...}}` +* `to_table()`: returns `nil` or a table with keys: + * `fields`: key-value storage + * `inventory`: `{list1 = {}, ...}}` (NodeMetaRef only) * `from_table(nil or {})` * Any non-table value will clear the metadata - * See "Node Metadata" + * See "Node Metadata" for an example * returns `true` on success +### `NodeMetaRef` +Node metadata: reference extra data and functionality stored in a node. +Can be gotten via `minetest.get_meta(pos)`. + +#### Methods +* All methods in MetaDataRef +* `get_inventory()`: returns `InvRef` + +### `ItemStackMetaRef` +ItemStack metadata: reference extra data and functionality stored in a stack. +Can be gotten via `item:get_meta()`. + +#### Methods +* All methods in MetaDataRef + ### `NodeTimerRef` Node Timers: a high resolution persistent per-node timer. Can be gotten via `minetest.get_node_timer(pos)`. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index afb591a04..30e6c85e4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -415,6 +415,7 @@ set(common_SRCS inventory.cpp inventorymanager.cpp itemdef.cpp + itemstackmetadata.cpp light.cpp log.cpp map.cpp diff --git a/src/content_cao.cpp b/src/content_cao.cpp index 5dc3866cf..e0b1c4cd2 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -938,7 +938,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, if(m_prop.textures.size() >= 1){ infostream<<"textures[0]: "<idef(); - ItemStack item(m_prop.textures[0], 1, 0, "", idef); + ItemStack item(m_prop.textures[0], 1, 0, idef); m_wield_meshnode = new WieldMeshSceneNode( smgr->getRootSceneNode(), smgr, -1); diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 45d3018a7..286d1eada 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -139,7 +139,7 @@ static std::vector craftGetItems( for (std::vector::size_type i = 0; i < items.size(); i++) { result.push_back(ItemStack(std::string(items[i]), (u16)1, - (u16)0, "", gamedef->getItemDefManager())); + (u16)0, gamedef->getItemDefManager())); } return result; } @@ -1126,4 +1126,3 @@ IWritableCraftDefManager* createCraftDefManager() { return new CCraftDefManager(); } - diff --git a/src/inventory.cpp b/src/inventory.cpp index cb8faecbc..6d5b49916 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -45,82 +45,16 @@ static content_t content_translate_from_19_to_internal(content_t c_from) return c_from; } -// If the string contains spaces, quotes or control characters, encodes as JSON. -// Else returns the string unmodified. -static std::string serializeJsonStringIfNeeded(const std::string &s) -{ - for(size_t i = 0; i < s.size(); ++i) - { - if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"') - return serializeJsonString(s); - } - return s; -} - -// Parses a string serialized by serializeJsonStringIfNeeded. -static std::string deSerializeJsonStringIfNeeded(std::istream &is) -{ - std::ostringstream tmp_os; - bool expect_initial_quote = true; - bool is_json = false; - bool was_backslash = false; - for(;;) - { - char c = is.get(); - if(is.eof()) - break; - if(expect_initial_quote && c == '"') - { - tmp_os << c; - is_json = true; - } - else if(is_json) - { - tmp_os << c; - if(was_backslash) - was_backslash = false; - else if(c == '\\') - was_backslash = true; - else if(c == '"') - break; // Found end of string - } - else - { - if(c == ' ') - { - // Found end of word - is.unget(); - break; - } - else - { - tmp_os << c; - } - } - expect_initial_quote = false; - } - if(is_json) - { - std::istringstream tmp_is(tmp_os.str(), std::ios::binary); - return deSerializeJsonString(tmp_is); - } - else - return tmp_os.str(); -} - - -ItemStack::ItemStack(std::string name_, u16 count_, - u16 wear_, std::string metadata_, - IItemDefManager *itemdef) +ItemStack::ItemStack(const std::string &name_, u16 count_, + u16 wear_, IItemDefManager *itemdef) { name = itemdef->getAlias(name_); count = count_; wear = wear_; - metadata = metadata_; - if(name.empty() || count == 0) + if (name.empty() || count == 0) clear(); - else if(itemdef->get(name).type == ITEM_TOOL) + else if (itemdef->get(name).type == ITEM_TOOL) count = 1; } @@ -137,7 +71,7 @@ void ItemStack::serialize(std::ostream &os) const parts = 2; if(wear != 0) parts = 3; - if(metadata != "") + if (!metadata.empty()) parts = 4; os<= 3) os<<" "<= 4) - os<<" "<= 4) { + os << " "; + metadata.serialize(os); + } } void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) @@ -289,7 +225,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) wear = stoi(wear_str); // Read metadata - metadata = deSerializeJsonStringIfNeeded(is); + metadata.deSerialize(is); // In case fields are added after metadata, skip space here: //std::getline(is, tmp, ' '); @@ -335,7 +271,7 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_, *this = newitem; newitem.clear(); } - // If item name or metadata differs, bail out + // If item name or metadata differs, bail out else if (name != newitem.name || metadata != newitem.metadata) { @@ -375,7 +311,7 @@ bool ItemStack::itemFits(const ItemStack &newitem_, { newitem.clear(); } - // If item name or metadata differs, bail out + // If item name or metadata differs, bail out else if (name != newitem.name || metadata != newitem.metadata) { diff --git a/src/inventory.h b/src/inventory.h index 7d7e58d61..fe1639728 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "itemdef.h" #include "irrlichttypes.h" +#include "itemstackmetadata.h" #include #include #include @@ -32,10 +33,10 @@ struct ToolCapabilities; struct ItemStack { - ItemStack(): name(""), count(0), wear(0), metadata("") {} - ItemStack(std::string name_, u16 count_, - u16 wear, std::string metadata_, - IItemDefManager *itemdef); + ItemStack(): name(""), count(0), wear(0) {} + ItemStack(const std::string &name_, u16 count_, + u16 wear, IItemDefManager *itemdef); + ~ItemStack() {} // Serialization @@ -61,7 +62,7 @@ struct ItemStack name = ""; count = 0; wear = 0; - metadata = ""; + metadata.clear(); } void add(u16 n) @@ -166,7 +167,7 @@ struct ItemStack std::string name; u16 count; u16 wear; - std::string metadata; + ItemStackMetadata metadata; }; class InventoryList @@ -313,4 +314,3 @@ private: }; #endif - diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp new file mode 100644 index 000000000..c3d602245 --- /dev/null +++ b/src/itemstackmetadata.cpp @@ -0,0 +1,43 @@ +#include "itemstackmetadata.h" +#include "util/serialize.h" +#include "util/strfnd.h" + +#define DESERIALIZE_START '\x01' +#define DESERIALIZE_KV_DELIM '\x02' +#define DESERIALIZE_PAIR_DELIM '\x03' +#define DESERIALIZE_START_STR "\x01" +#define DESERIALIZE_KV_DELIM_STR "\x02" +#define DESERIALIZE_PAIR_DELIM_STR "\x03" + +void ItemStackMetadata::serialize(std::ostream &os) const +{ + std::ostringstream os2; + os2 << DESERIALIZE_START; + for (StringMap::const_iterator + it = m_stringvars.begin(); + it != m_stringvars.end(); ++it) { + os2 << it->first << DESERIALIZE_KV_DELIM + << it->second << DESERIALIZE_PAIR_DELIM; + } + os << serializeJsonStringIfNeeded(os2.str()); +} + +void ItemStackMetadata::deSerialize(std::istream &is) +{ + std::string in = deSerializeJsonStringIfNeeded(is); + + m_stringvars.clear(); + + if (!in.empty() && in[0] == DESERIALIZE_START) { + Strfnd fnd(in); + fnd.to(1); + while (!fnd.at_end()) { + std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR); + std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR); + m_stringvars[name] = var; + } + } else { + // BACKWARDS COMPATIBILITY + m_stringvars[""] = in; + } +} diff --git a/src/itemstackmetadata.h b/src/itemstackmetadata.h new file mode 100644 index 000000000..c56c58fd2 --- /dev/null +++ b/src/itemstackmetadata.h @@ -0,0 +1,35 @@ +/* +Minetest +Copyright (C) 2010-2013 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef ITEMSTACKMETADATA_HEADER +#define ITEMSTACKMETADATA_HEADER + +#include "metadata.h" + +class Inventory; +class IItemDefManager; + +class ItemStackMetadata : public Metadata +{ +public: + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); +}; + +#endif diff --git a/src/metadata.cpp b/src/metadata.cpp index 96453d710..3cc45f919 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gamedef.h" #include "log.h" #include +#include "constants.h" // MAP_BLOCKSIZE +#include /* Metadata diff --git a/src/metadata.h b/src/metadata.h index a629c0615..4bb3c2ee7 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -25,35 +25,37 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "util/string.h" -/* - NodeMetadata stores arbitary amounts of data for special blocks. - Used for furnaces, chests and signs. - - There are two interaction methods: inventory menu and text input. - Only one can be used for a single metadata, thus only inventory OR - text input should exist in a metadata. -*/ - -class Inventory; -class IItemDefManager; - class Metadata { public: - void clear(); - bool empty() const; + virtual ~Metadata() {} + + virtual void clear(); + virtual bool empty() const; - // Generic key/value store + bool operator==(const Metadata &other) const; + inline bool operator!=(const Metadata &other) const + { + return !(*this == other); + } + + // + // Key-value related + // + + size_t size() const; + bool contains(const std::string &name) const; const std::string &getString(const std::string &name, u16 recursion = 0) const; void setString(const std::string &name, const std::string &var); - // Support variable names in values - const std::string &resolveString(const std::string &str, u16 recursion = 0) const; const StringMap &getStrings() const { return m_stringvars; } -private: + // Add support for variable names in values + const std::string &resolveString(const std::string &str, u16 recursion = 0) const; +protected: StringMap m_stringvars; + }; #endif diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index ebc951295..8925b51f4 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -824,11 +824,32 @@ ItemStack read_item(lua_State* L, int index,Server* srv) 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 - { + + ItemStack istack(name, count, wear, idef); + + lua_getfield(L, index, "metadata"); + + // Support old metadata format by checking type + int fieldstable = lua_gettop(L); + if (lua_istable(L, fieldstable)) { + lua_pushnil(L); + while (lua_next(L, fieldstable) != 0) { + // key at index -2 and value at index -1 + std::string key = lua_tostring(L, -2); + size_t value_len; + const char *value_cs = lua_tolstring(L, -1, &value_len); + std::string value(value_cs, value_len); + istack.metadata.setString(name, value); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + } else { + // BACKWARDS COMPATIBLITY + std::string value = getstringfield_default(L, index, "metadata", ""); + istack.metadata.setString("", value); + } + + return istack; + } else { throw LuaError("Expecting itemstack, itemstring, table or nil"); } } diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index efccce515..070234eba 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -5,6 +5,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_metadata.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 3d9db7917..2722e35a4 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -284,7 +284,7 @@ int ModApiEnvMod::l_place_node(lua_State *L) return 1; } // Create item to place - ItemStack item(ndef->get(n).name, 1, 0, "", idef); + ItemStack item(ndef->get(n).name, 1, 0, idef); // Make pointed position PointedThing pointed; pointed.type = POINTEDTHING_NODE; diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index ff0baea14..f0293bed8 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "common/c_content.h" @@ -137,16 +138,28 @@ int LuaItemStack::l_set_wear(lua_State *L) return 1; } +// get_meta(self) -> string +int LuaItemStack::l_get_meta(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaItemStack *o = checkobject(L, 1); + ItemStackMetaRef::create(L, &o->m_stack); + return 1; +} + +// DEPRECATED // get_metadata(self) -> string int LuaItemStack::l_get_metadata(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaItemStack *o = checkobject(L, 1); ItemStack &item = o->m_stack; - lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); + const std::string &value = item.metadata.getString(""); + lua_pushlstring(L, value.c_str(), value.size()); return 1; } +// DEPRECATED // set_metadata(self, string) int LuaItemStack::l_set_metadata(lua_State *L) { @@ -156,7 +169,7 @@ int LuaItemStack::l_set_metadata(lua_State *L) size_t len = 0; const char *ptr = luaL_checklstring(L, 2, &len); - item.metadata.assign(ptr, len); + item.metadata.setString("", std::string(ptr, len)); lua_pushboolean(L, true); return 1; @@ -211,8 +224,24 @@ int LuaItemStack::l_to_table(lua_State *L) 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"); + + if (item.metadata.size() == 1 && item.metadata.contains("")) { + const std::string &value = item.metadata.getString(""); + lua_pushlstring(L, value.c_str(), value.size()); + lua_setfield(L, -2, "metadata"); + } else { + lua_newtable(L); + const StringMap &fields = item.metadata.getStrings(); + for (StringMap::const_iterator it = fields.begin(); + it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; + lua_pushlstring(L, name.c_str(), name.size()); + lua_pushlstring(L, value.c_str(), value.size()); + lua_settable(L, -3); + } + lua_setfield(L, -2, "metadata"); + } } return 1; } @@ -443,6 +472,7 @@ const luaL_reg LuaItemStack::methods[] = { luamethod(LuaItemStack, set_count), luamethod(LuaItemStack, get_wear), luamethod(LuaItemStack, set_wear), + luamethod(LuaItemStack, get_meta), luamethod(LuaItemStack, get_metadata), luamethod(LuaItemStack, set_metadata), luamethod(LuaItemStack, clear), diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h index be919b701..1ba5d79e0 100644 --- a/src/script/lua_api/l_item.h +++ b/src/script/lua_api/l_item.h @@ -56,9 +56,14 @@ private: // set_wear(self, number) static int l_set_wear(lua_State *L); + // get_meta(self) -> string + static int l_get_meta(lua_State *L); + + // DEPRECATED // get_metadata(self) -> string static int l_get_metadata(lua_State *L); + // DEPRECATED // set_metadata(self, string) static int l_set_metadata(lua_State *L); diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp new file mode 100644 index 000000000..304a7cdf3 --- /dev/null +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -0,0 +1,115 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" + +/* + NodeMetaRef +*/ +ItemStackMetaRef* ItemStackMetaRef::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 *(ItemStackMetaRef**)ud; // unbox pointer +} + +Metadata* ItemStackMetaRef::getmeta(bool auto_create) +{ + return &istack->metadata; +} + +void ItemStackMetaRef::clearMeta() +{ + istack->metadata.clear(); +} + +void ItemStackMetaRef::reportMetadataChange() +{ + // TODO +} + +// Exported functions + +// garbage collector +int ItemStackMetaRef::gc_object(lua_State *L) { + ItemStackMetaRef *o = *(ItemStackMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// Creates an NodeMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void ItemStackMetaRef::create(lua_State *L, ItemStack *istack) +{ + ItemStackMetaRef *o = new ItemStackMetaRef(istack); + //infostream<<"NodeMetaRef::create: o="< + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef L_ITEMSTACKMETA_H_ +#define L_ITEMSTACKMETA_H_ + +#include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" +#include "irrlichttypes_bloated.h" +#include "inventory.h" + +class ItemStackMetaRef : public MetaDataRef +{ +private: + ItemStack *istack; + + static const char className[]; + static const luaL_reg methods[]; + + static ItemStackMetaRef *checkobject(lua_State *L, int narg); + + virtual Metadata* getmeta(bool auto_create); + + virtual void clearMeta(); + + virtual void reportMetadataChange(); + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); +public: + ItemStackMetaRef(ItemStack *istack): istack(istack) {} + ~ItemStackMetaRef() {} + + // Creates an ItemStackMetaRef 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, ItemStack *istack); + + static void Register(lua_State *L); +}; + +#endif diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index e313d55f8..7becef6dc 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_env.h" #include "lua_api/l_inventory.h" #include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" #include "lua_api/l_mapgen.h" #include "lua_api/l_nodemeta.h" #include "lua_api/l_nodetimer.h" @@ -94,6 +95,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) // Register reference classes (userdata) InvRef::Register(L); + ItemStackMetaRef::Register(L); LuaAreaStore::Register(L); LuaItemStack::Register(L); LuaPerlinNoise::Register(L); diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp index 99cb990f1..61d369bc4 100644 --- a/src/util/serialize.cpp +++ b/src/util/serialize.cpp @@ -354,6 +354,55 @@ std::string deSerializeJsonString(std::istream &is) return os.str(); } +std::string serializeJsonStringIfNeeded(const std::string &s) +{ + for (size_t i = 0; i < s.size(); ++i) { + if (s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"') + return serializeJsonString(s); + } + return s; +} + +std::string deSerializeJsonStringIfNeeded(std::istream &is) +{ + std::ostringstream tmp_os; + bool expect_initial_quote = true; + bool is_json = false; + bool was_backslash = false; + for (;;) { + char c = is.get(); + if (is.eof()) + break; + + if (expect_initial_quote && c == '"') { + tmp_os << c; + is_json = true; + } else if(is_json) { + tmp_os << c; + if (was_backslash) + was_backslash = false; + else if (c == '\\') + was_backslash = true; + else if (c == '"') + break; // Found end of string + } else { + if (c == ' ') { + // Found end of word + is.unget(); + break; + } else { + tmp_os << c; + } + } + expect_initial_quote = false; + } + if (is_json) { + std::istringstream tmp_is(tmp_os.str(), std::ios::binary); + return deSerializeJsonString(tmp_is); + } else + return tmp_os.str(); +} + //// //// String/Struct conversions //// diff --git a/src/util/serialize.h b/src/util/serialize.h index 36324a675..e22434191 100644 --- a/src/util/serialize.h +++ b/src/util/serialize.h @@ -405,6 +405,13 @@ std::string serializeJsonString(const std::string &plain); // Reads a string encoded in JSON format std::string deSerializeJsonString(std::istream &is); +// If the string contains spaces, quotes or control characters, encodes as JSON. +// Else returns the string unmodified. +std::string serializeJsonStringIfNeeded(const std::string &s); + +// Parses a string serialized by serializeJsonStringIfNeeded. +std::string deSerializeJsonStringIfNeeded(std::istream &is); + // Creates a string consisting of the hexadecimal representation of `data` std::string serializeHexString(const std::string &data, bool insert_spaces=false); -- cgit v1.2.3 From f2f9a923515386d787a245fac52f78e815b3a839 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 3 Feb 2017 22:28:09 +0000 Subject: Add per-stack descriptions using ItemStack Metadata --- doc/lua_api.txt | 4 ++++ src/guiFormSpecMenu.cpp | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 2f5e3706c..dd20ae904 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1473,6 +1473,10 @@ Item stacks can store metadata too. See `ItemStackMetaRef`. Item metadata only contains a key-value store. +Some of the values in the key-value store are handled specially: + +* `description`: Set the itemstack's description. Defaults to idef.description + Example stuff: local meta = stack:get_meta() diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 45b0e9c11..67b3a9ad0 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -2303,7 +2303,12 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase, // Draw tooltip std::wstring tooltip_text = L""; if (hovering && !m_selected_item) { - tooltip_text = utf8_to_wide(item.getDefinition(m_client->idef()).description); + const std::string &desc = item.metadata.getString("description"); + if (desc.empty()) + tooltip_text = + utf8_to_wide(item.getDefinition(m_client->idef()).description); + else + tooltip_text = utf8_to_wide(desc); } if (tooltip_text != L"") { std::vector tt_rows = str_split(tooltip_text, L'\n'); -- cgit v1.2.3 From 80c75272a6ffff34069ed470e959f7ff007e6c06 Mon Sep 17 00:00:00 2001 From: numberZero Date: Sun, 5 Feb 2017 13:27:58 +0400 Subject: Improve mesh shading (#5172) --- src/mesh.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/mesh.cpp b/src/mesh.cpp index 84689b631..6a055abb2 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -44,12 +44,13 @@ void applyFacesShading(video::SColor &color, const v3f &normal) { // Many special drawtypes have normals set to 0,0,0 and this // must result in maximum brightness (no face shadng). - if (normal.Y < -0.5f) - applyShadeFactor (color, 0.447213f); - else if (normal.X > 0.5f || normal.X < -0.5f) - applyShadeFactor (color, 0.670820f); - else if (normal.Z > 0.5f || normal.Z < -0.5f) - applyShadeFactor (color, 0.836660f); + float x2 = normal.X * normal.X; + float y2 = normal.Y * normal.Y; + float z2 = normal.Z * normal.Z; + if (normal.Y < 0) + applyShadeFactor(color, 0.670820f * x2 + 0.447213f * y2 + 0.836660f * z2); + else if ((x2 > 1e-3) || (z2 > 1e-3)) + applyShadeFactor(color, 0.670820f * x2 + 1.000000f * y2 + 0.836660f * z2); } scene::IAnimatedMesh* createCubeMesh(v3f scale) -- cgit v1.2.3 From 3e30731c1ac313b504ff15eb7f40ce6a387d3da2 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Sun, 5 Feb 2017 23:59:18 -0800 Subject: Prevent SIGFPE on entity tile loading issue. (#5178) While experimenting with entities I ran into this unresolvable error where the server is sending some texture that the client crashes on. The crash prevents the client from ever reconnecting, resulting in a server that has to use clearobjects. We shouldn't crash but just ignore the object and move on. ``` 0x00000000004dc0de in TextureSource::generateImagePart (this=this@entry=0x7118eb0, part_of_name="[applyfiltersformesh", baseimg=@0x7fffffffbe98: 0x9f1b010) at /home/sofar/git/minetest/src/client/tile.cpp:1744 1744 u32 xscale = scaleto / dim.Width; (gdb) bt #0 0x00000000004dc0de in TextureSource::generateImagePart (this=this@entry=0x7118eb0, part_of_name="[applyfiltersformesh", baseimg=@0x7fffffffbe98: 0x9f1b010) at /home/sofar/git/minetest/src/client/tile.cpp:1744 ``` After reconnecting, the client now can connect without issues and displays an error message: ``` ERROR[Main]: generateImagePart(): Illegal 0 dimension for part_of_name="[applyfiltersformesh", cancelling. ERROR[Main]: generateImage(): Failed to generate "[applyfiltersformesh" ERROR[Main]: Irrlicht: Invalid size of image for OpenGL Texture. ``` --- src/client/tile.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/client/tile.cpp b/src/client/tile.cpp index fbc0f1709..000c766d0 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -1741,6 +1741,12 @@ bool TextureSource::generateImagePart(std::string part_of_name, * equal to the target minimum. If e.g. this is a vertical frames * animation, the short dimension will be the real size. */ + if ((dim.Width == 0) || (dim.Height == 0)) { + errorstream << "generateImagePart(): Illegal 0 dimension " + << "for part_of_name=\""<< part_of_name + << "\", cancelling." << std::endl; + return false; + } u32 xscale = scaleto / dim.Width; u32 yscale = scaleto / dim.Height; u32 scale = (xscale > yscale) ? xscale : yscale; -- cgit v1.2.3 From 5da3ed19a38ba5a342ba16c61be7bfb7c17b2308 Mon Sep 17 00:00:00 2001 From: Travis Burtrum Date: Mon, 6 Feb 2017 13:10:03 -0500 Subject: Add support for unix socket connection to redis (#5179) --- src/database-redis.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/database-redis.cpp b/src/database-redis.cpp index 3bcedad9b..3df186944 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -44,7 +44,8 @@ Database_Redis::Database_Redis(Settings &conf) } const char *addr = tmp.c_str(); int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379; - ctx = redisConnect(addr, port); + // if redis_address contains '/' assume unix socket, else hostname/ip + ctx = tmp.find('/') != std::string::npos ? redisConnectUnix(addr) : redisConnect(addr, port); if (!ctx) { throw DatabaseException("Cannot allocate redis context"); } else if (ctx->err) { -- cgit v1.2.3 From 8bc6a303b461662b7434a5ee8557292d43682cc9 Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 6 Feb 2017 21:48:54 +0000 Subject: Face shading: Add shade factor comments --- src/mesh.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mesh.cpp b/src/mesh.cpp index 6a055abb2..a79264ef0 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -42,8 +42,15 @@ inline static void applyShadeFactor(video::SColor& color, float factor) void applyFacesShading(video::SColor &color, const v3f &normal) { - // Many special drawtypes have normals set to 0,0,0 and this - // must result in maximum brightness (no face shadng). + /* + Some drawtypes have normals set to (0, 0, 0), this must result in + maximum brightness: shade factor 1.0. + Shade factors for aligned cube faces are: + +Y 1.000000 sqrt(1.0) + -Y 0.447213 sqrt(0.2) + +-X 0.670820 sqrt(0.45) + +-Z 0.836660 sqrt(0.7) + */ float x2 = normal.X * normal.X; float y2 = normal.Y * normal.Y; float z2 = normal.Z * normal.Z; -- cgit v1.2.3 From 0680c47d6c7d3e98e2b96b823f8cc9ca76d5e7f8 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 5 Feb 2017 18:15:46 +0000 Subject: Fix incompatibility of ItemStack.to_table() introduced by stack meta --- src/script/common/c_content.cpp | 15 +++++++++++++++ src/script/lua_api/l_item.cpp | 28 +++++++++++++--------------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 8925b51f4..399aa88d0 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -848,6 +848,21 @@ ItemStack read_item(lua_State* L, int index,Server* srv) istack.metadata.setString("", value); } + lua_getfield(L, index, "meta"); + fieldstable = lua_gettop(L); + if (lua_istable(L, fieldstable)) { + lua_pushnil(L); + while (lua_next(L, fieldstable) != 0) { + // key at index -2 and value at index -1 + std::string key = lua_tostring(L, -2); + size_t value_len; + const char *value_cs = lua_tolstring(L, -1, &value_len); + std::string value(value_cs, value_len); + istack.metadata.setString(name, value); + lua_pop(L, 1); // removes value, keeps key for next iteration + } + } + return istack; } else { throw LuaError("Expecting itemstack, itemstring, table or nil"); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index f0293bed8..9638740e8 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -225,23 +225,21 @@ int LuaItemStack::l_to_table(lua_State *L) lua_pushinteger(L, item.wear); lua_setfield(L, -2, "wear"); - if (item.metadata.size() == 1 && item.metadata.contains("")) { - const std::string &value = item.metadata.getString(""); + const std::string &metadata_str = item.metadata.getString(""); + lua_pushlstring(L, metadata_str.c_str(), metadata_str.size()); + lua_setfield(L, -2, "metadata"); + + lua_newtable(L); + const StringMap &fields = item.metadata.getStrings(); + for (StringMap::const_iterator it = fields.begin(); + it != fields.end(); ++it) { + const std::string &name = it->first; + const std::string &value = it->second; + lua_pushlstring(L, name.c_str(), name.size()); lua_pushlstring(L, value.c_str(), value.size()); - lua_setfield(L, -2, "metadata"); - } else { - lua_newtable(L); - const StringMap &fields = item.metadata.getStrings(); - for (StringMap::const_iterator it = fields.begin(); - it != fields.end(); ++it) { - const std::string &name = it->first; - const std::string &value = it->second; - lua_pushlstring(L, name.c_str(), name.size()); - lua_pushlstring(L, value.c_str(), value.size()); - lua_settable(L, -3); - } - lua_setfield(L, -2, "metadata"); + lua_settable(L, -3); } + lua_setfield(L, -2, "meta"); } return 1; } -- cgit v1.2.3 From ef6feca501fcf0d5a1fd2021f1d4df96a4533f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Wed, 8 Feb 2017 00:15:55 +0100 Subject: Add ModMetadata API (#5131) * mod can create a ModMetadata object where store its values and retrieve it. * Modmetadata object can only be fetched at mod loading * Save when modified using same time as map interval or at server stop * add helper function to get mod storage path * ModMetadata has exactly same calls than all every other Metadata --- doc/lua_api.txt | 10 ++- src/metadata.cpp | 20 +++++- src/metadata.h | 2 +- src/mods.cpp | 80 ++++++++++++++++++++- src/mods.h | 21 ++++++ src/remoteplayer.cpp | 2 +- src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_storage.cpp | 143 ++++++++++++++++++++++++++++++++++++++ src/script/lua_api/l_storage.h | 62 +++++++++++++++++ src/script/scripting_game.cpp | 3 + src/server.cpp | 43 +++++++++++- src/server.h | 9 ++- 12 files changed, 384 insertions(+), 12 deletions(-) create mode 100644 src/script/lua_api/l_storage.cpp create mode 100644 src/script/lua_api/l_storage.h diff --git a/doc/lua_api.txt b/doc/lua_api.txt index dd20ae904..4774e8a5a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2629,6 +2629,11 @@ These functions return the leftover itemstack. * `HTTPApiTable.fetch_async_get(handle)`: returns HTTPRequestResult * Return response data for given asynchronous HTTP request +### Storage API: +* `minetest.get_mod_storage()`: + * returns reference to mod private `StorageRef` + * must be called during mod load time + ### Misc. * `minetest.get_connected_players()`: returns list of `ObjectRefs` * `minetest.player_exists(name)`: boolean, whether player exists (regardless of online status) @@ -2791,7 +2796,7 @@ Class reference --------------- ### `MetaDataRef` -See `NodeMetaRef` and `ItemStackMetaRef`. +See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`. #### Methods * `set_string(name, value)` @@ -2845,6 +2850,9 @@ Can be gotten via `minetest.get_node_timer(pos)`. * `is_started()`: returns boolean state of timer * returns `true` if timer is started, otherwise `false` +### `StorageRef` +This is basically a reference to a C++ `ModMetadata` + ### `ObjectRef` Moving things in the game are generally these. diff --git a/src/metadata.cpp b/src/metadata.cpp index 3cc45f919..2ce9af5af 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -76,13 +76,27 @@ const std::string &Metadata::getString(const std::string &name, return resolveString(it->second, recursion); } -void Metadata::setString(const std::string &name, const std::string &var) +/** + * Sets var to name key in the metadata storage + * + * @param name + * @param var + * @return true if key-value pair is created or changed + */ +bool Metadata::setString(const std::string &name, const std::string &var) { if (var.empty()) { m_stringvars.erase(name); - } else { - m_stringvars[name] = var; + return true; + } + + StringMap::iterator it = m_stringvars.find(name); + if (it != m_stringvars.end() && it->second == var) { + return false; } + + m_stringvars[name] = var; + return true; } const std::string &Metadata::resolveString(const std::string &str, diff --git a/src/metadata.h b/src/metadata.h index 4bb3c2ee7..a8270b4c4 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -46,7 +46,7 @@ public: size_t size() const; bool contains(const std::string &name) const; const std::string &getString(const std::string &name, u16 recursion = 0) const; - void setString(const std::string &name, const std::string &var); + virtual bool setString(const std::string &name, const std::string &var); const StringMap &getStrings() const { return m_stringvars; diff --git a/src/mods.cpp b/src/mods.cpp index 1b1bdb07b..bae9a42d3 100644 --- a/src/mods.cpp +++ b/src/mods.cpp @@ -21,13 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "mods.h" #include "filesys.h" -#include "util/strfnd.h" #include "log.h" #include "subgame.h" #include "settings.h" -#include "util/strfnd.h" #include "convert_json.h" -#include "exceptions.h" static bool parseDependsLine(std::istream &is, std::string &dep, std::set &symbols) @@ -356,3 +353,80 @@ Json::Value getModstoreUrl(std::string url) } #endif + +ModMetadata::ModMetadata(const std::string &mod_name): + m_mod_name(mod_name), + m_modified(false) +{ + m_stringvars.clear(); +} + +void ModMetadata::clear() +{ + Metadata::clear(); + m_modified = true; +} + +bool ModMetadata::save(const std::string &root_path) +{ + Json::Value json; + for (StringMap::const_iterator it = m_stringvars.begin(); + it != m_stringvars.end(); ++it) { + json[it->first] = it->second; + } + + if (!fs::PathExists(root_path)) { + if (!fs::CreateAllDirs(root_path)) { + errorstream << "ModMetadata[" << m_mod_name << "]: Unable to save. '" + << root_path << "' tree cannot be created." << std::endl; + return false; + } + } else if (!fs::IsDir(root_path)) { + errorstream << "ModMetadata[" << m_mod_name << "]: Unable to save. '" + << root_path << "' is not a directory." << std::endl; + return false; + } + + bool w_ok = fs::safeWriteToFile(root_path + DIR_DELIM + m_mod_name, + Json::FastWriter().write(json)); + + if (w_ok) { + m_modified = false; + } else { + errorstream << "ModMetadata[" << m_mod_name << "]: failed write file." << std::endl; + } + return w_ok; +} + +bool ModMetadata::load(const std::string &root_path) +{ + m_stringvars.clear(); + + std::ifstream is((root_path + DIR_DELIM + m_mod_name).c_str(), std::ios_base::binary); + if (!is.good()) { + return false; + } + + Json::Reader reader; + Json::Value root; + if (!reader.parse(is, root)) { + errorstream << "ModMetadata[" << m_mod_name << "]: failed read data " + "(Json decoding failure)." << std::endl; + return false; + } + + const Json::Value::Members attr_list = root.getMemberNames(); + for (Json::Value::Members::const_iterator it = attr_list.begin(); + it != attr_list.end(); ++it) { + Json::Value attr_value = root[*it]; + m_stringvars[*it] = attr_value.asString(); + } + + return true; +} + +bool ModMetadata::setString(const std::string &name, const std::string &var) +{ + m_modified = Metadata::setString(name, var); + return m_modified; +} diff --git a/src/mods.h b/src/mods.h index af7777d18..61af5e5d1 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 #include #include "config.h" +#include "metadata.h" #define MODNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_" @@ -205,4 +206,24 @@ struct ModStoreModDetails { bool valid; }; +class ModMetadata: public Metadata +{ +public: + ModMetadata(const std::string &mod_name); + ~ModMetadata() {} + + virtual void clear(); + + bool save(const std::string &root_path); + bool load(const std::string &root_path); + + bool isModified() const { return m_modified; } + const std::string &getModName() const { return m_mod_name; } + + virtual bool setString(const std::string &name, const std::string &var); +private: + std::string m_mod_name; + bool m_modified; +}; + #endif diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index 6853ad6d9..0a4591410 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -174,7 +174,7 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, const Json::Value::Members attr_list = attr_root.getMemberNames(); for (Json::Value::Members::const_iterator it = attr_list.begin(); - it != attr_list.end(); ++it) { + it != attr_list.end(); ++it) { Json::Value attr_value = attr_root[*it]; sao->setExtendedAttribute(*it, attr_value.asString()); } diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index 070234eba..e82560696 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -15,6 +15,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_particles.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_rollback.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_server.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_storage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_util.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_vmanip.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp diff --git a/src/script/lua_api/l_storage.cpp b/src/script/lua_api/l_storage.cpp new file mode 100644 index 000000000..42928255f --- /dev/null +++ b/src/script/lua_api/l_storage.cpp @@ -0,0 +1,143 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_storage.h" +#include "l_internal.h" +#include "mods.h" +#include "server.h" + +int ModApiStorage::l_get_mod_storage(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + if (!lua_isstring(L, -1)) { + return 0; + } + + std::string mod_name = lua_tostring(L, -1); + + ModMetadata *store = new ModMetadata(mod_name); + // For server side + if (Server *server = getServer(L)) { + store->load(server->getModStoragePath()); + server->registerModStorage(store); + } else { + assert(false); // this should not happen + } + + StorageRef::create(L, store); + int object = lua_gettop(L); + + lua_pushvalue(L, object); + return 1; +} + +void ModApiStorage::Initialize(lua_State *L, int top) +{ + API_FCT(get_mod_storage); +} + +StorageRef::StorageRef(ModMetadata *object): + m_object(object) +{ +} + +void StorageRef::create(lua_State *L, ModMetadata *object) +{ + StorageRef *o = new StorageRef(object); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +int StorageRef::gc_object(lua_State *L) +{ + StorageRef *o = *(StorageRef **)(lua_touserdata(L, 1)); + // Server side + if (Server *server = getServer(L)) + server->unregisterModStorage(getobject(o)->getModName()); + delete o; + return 0; +} + +void StorageRef::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, "metadata_class"); + lua_pushlstring(L, className, strlen(className)); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable +} + +StorageRef* StorageRef::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 *(StorageRef**)ud; // unbox pointer +} + +ModMetadata* StorageRef::getobject(StorageRef *ref) +{ + ModMetadata *co = ref->m_object; + return co; +} + +Metadata* StorageRef::getmeta(bool auto_create) +{ + return m_object; +} + +void StorageRef::clearMeta() +{ + m_object->clear(); +} + +const char StorageRef::className[] = "StorageRef"; +const luaL_reg StorageRef::methods[] = { + luamethod(MetaDataRef, get_string), + luamethod(MetaDataRef, set_string), + luamethod(MetaDataRef, get_int), + luamethod(MetaDataRef, set_int), + luamethod(MetaDataRef, get_float), + luamethod(MetaDataRef, set_float), + luamethod(MetaDataRef, to_table), + luamethod(MetaDataRef, from_table), + {0,0} +}; diff --git a/src/script/lua_api/l_storage.h b/src/script/lua_api/l_storage.h new file mode 100644 index 000000000..fde2828ad --- /dev/null +++ b/src/script/lua_api/l_storage.h @@ -0,0 +1,62 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef __L_STORAGE_H__ +#define __L_STORAGE_H__ + +#include "lua_api/l_base.h" +#include "l_metadata.h" + +class ModMetadata; + +class ModApiStorage: public ModApiBase +{ +protected: + static int l_get_mod_storage(lua_State *L); +public: + static void Initialize(lua_State *L, int top); + +}; + +class StorageRef: public MetaDataRef +{ +private: + ModMetadata *m_object; + + static const char className[]; + static const luaL_reg methods[]; + + virtual Metadata* getmeta(bool auto_create); + virtual void clearMeta(); + + // garbage collector + static int gc_object(lua_State *L); +public: + StorageRef(ModMetadata *object); + ~StorageRef() {} + + static void Register(lua_State *L); + static void create(lua_State *L, ModMetadata *object); + + static StorageRef *checkobject(lua_State *L, int narg); + static ModMetadata* getobject(StorageRef *ref); +}; + +#endif /* __L_STORAGE_H__ */ diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index 7becef6dc..4da752263 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -41,6 +41,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "lua_api/l_settings.h" #include "lua_api/l_http.h" +#include "lua_api/l_storage.h" extern "C" { #include "lualib.h" @@ -92,6 +93,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) ModApiServer::Initialize(L, top); ModApiUtil::Initialize(L, top); ModApiHttp::Initialize(L, top); + ModApiStorage::Initialize(L, top); // Register reference classes (userdata) InvRef::Register(L); @@ -108,6 +110,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) NodeTimerRef::Register(L); ObjectRef::Register(L); LuaSettings::Register(L); + StorageRef::Register(L); } void log_deprecated(const std::string &message) diff --git a/src/server.cpp b/src/server.cpp index e5714ac03..8b9f46f85 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -178,8 +178,8 @@ Server::Server( m_admin_chat(iface), m_ignore_map_edit_events(false), m_ignore_map_edit_events_peer_id(0), - m_next_sound_id(0) - + m_next_sound_id(0), + m_mod_storage_save_timer(10.0f) { m_liquid_transform_timer = 0.0; m_liquid_transform_every = 1.0; @@ -788,6 +788,18 @@ void Server::AsyncRunStep(bool initial_step) << "packet size is " << pktSize << std::endl; } m_clients.unlock(); + + m_mod_storage_save_timer -= dtime; + if (m_mod_storage_save_timer <= 0.0f) { + infostream << "Saving registered mod storages." << std::endl; + m_mod_storage_save_timer = g_settings->getFloat("server_map_save_interval"); + for (UNORDERED_MAP::const_iterator + it = m_mod_storages.begin(); it != m_mod_storages.end(); ++it) { + if (it->second->isModified()) { + it->second->save(getModStoragePath()); + } + } + } } /* @@ -3404,6 +3416,11 @@ std::string Server::getBuiltinLuaPath() return porting::path_share + DIR_DELIM + "builtin"; } +std::string Server::getModStoragePath() const +{ + return m_path_world + DIR_DELIM + "mod_storage"; +} + v3f Server::findSpawnPos() { ServerMap &map = m_env->getServerMap(); @@ -3525,6 +3542,28 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version return playersao; } +bool Server::registerModStorage(ModMetadata *storage) +{ + if (m_mod_storages.find(storage->getModName()) != m_mod_storages.end()) { + errorstream << "Unable to register same mod storage twice. Storage name: " + << storage->getModName() << std::endl; + return false; + } + + m_mod_storages[storage->getModName()] = storage; + return true; +} + +void Server::unregisterModStorage(const std::string &name) +{ + UNORDERED_MAP::const_iterator it = m_mod_storages.find(name); + if (it != m_mod_storages.end()) { + // Save unconditionaly on unregistration + it->second->save(getModStoragePath()); + m_mod_storages.erase(name); + } +} + void dedicated_server_loop(Server &server, bool &kill) { DSTACK(FUNCTION_NAME); diff --git a/src/server.h b/src/server.h index 8f553ce38..3eee67b78 100644 --- a/src/server.h +++ b/src/server.h @@ -299,7 +299,8 @@ public: const ModSpec* getModSpec(const std::string &modname) const; void getModNames(std::vector &modlist); std::string getBuiltinLuaPath(); - inline std::string getWorldPath() const { return m_path_world; } + inline const std::string &getWorldPath() const { return m_path_world; } + std::string getModStoragePath() const; inline bool isSingleplayer() { return m_simple_singleplayer_mode; } @@ -360,6 +361,9 @@ public: void SendInventory(PlayerSAO* playerSAO); void SendMovePlayer(u16 peer_id); + bool registerModStorage(ModMetadata *storage); + void unregisterModStorage(const std::string &name); + // Bind address Address m_bind_addr; @@ -650,6 +654,9 @@ private: // value = "" (visible to all players) or player name std::map m_detached_inventories_player; + UNORDERED_MAP m_mod_storages; + float m_mod_storage_save_timer; + DISABLE_CLASS_COPY(Server); }; -- cgit v1.2.3 From 607dab2b0dff3ca5bf59263e18f8eaab3c21b946 Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Wed, 8 Feb 2017 07:47:56 +0100 Subject: Fix android build This fixes #5190 --- build/android/jni/Android.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 47f37cfa5..accbcca1f 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -319,6 +319,7 @@ LOCAL_SRC_FILES += \ jni/src/script/lua_api/l_server.cpp \ jni/src/script/lua_api/l_settings.cpp \ jni/src/script/lua_api/l_http.cpp \ + jni/src/script/lua_api/l_storage.cpp \ jni/src/script/lua_api/l_util.cpp \ jni/src/script/lua_api/l_vmanip.cpp \ jni/src/script/scripting_game.cpp \ -- cgit v1.2.3 From e2ad76f91087215309012300eb9f1ebdd7fa40ee Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 21 Jan 2017 10:44:24 +0000 Subject: No longer auto-generate a 'guest####' player name when name is empty You can't join most servers with a 'guest####' player name anyway so it's only logical to remove them. --- src/client/clientlauncher.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 6145e3dde..1ac67a1de 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -403,11 +403,12 @@ bool ClientLauncher::launch_game(std::string &error_message, return false; } - if (menudata.name == "") - menudata.name = std::string("Guest") + itos(myrand_range(1000, 9999)); - else - playername = menudata.name; + if (menudata.name == "" && !simple_singleplayer_mode) { + error_message = gettext("Please choose a name!"); + return false; + } + playername = menudata.name; password = menudata.password; g_settings->set("name", playername); -- cgit v1.2.3 From a5ee7139a591eeffce5171e0df7a76f3c7a1d57e Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 24 Jan 2017 10:16:56 +0000 Subject: OpenAL sound: Use a simpler distance model In createPlayingSoundAt(), AL_ROLLOFF_FACTOR is not set, so it has the default value of 1.0, this makes the equation of the currently used AL_EXPONENT_DISTANCE distance model identical to the equation of the simpler AL_INVERSE_DISTANCE distance model. Using AL_INVERSE_DISTANCE means an exponent is not processed, exponents are quite intensive to process. There is no change in sound attenuation behaviour. The commented-out AL_ROLLOFF_FACTOR value is removed as it would now have a different effect if used. --- src/sound_openal.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sound_openal.cpp b/src/sound_openal.cpp index 317667f52..b9af9e3a9 100644 --- a/src/sound_openal.cpp +++ b/src/sound_openal.cpp @@ -317,7 +317,7 @@ public: return; } - alDistanceModel(AL_EXPONENT_DISTANCE); + alDistanceModel(AL_INVERSE_DISTANCE); infostream<<"Audio: Initialized: OpenAL "<source_id, AL_SOURCE_RELATIVE, false); alSource3f(sound->source_id, AL_POSITION, pos.X, pos.Y, pos.Z); alSource3f(sound->source_id, AL_VELOCITY, 0, 0, 0); - //alSourcef(sound->source_id, AL_ROLLOFF_FACTOR, 0.7); alSourcef(sound->source_id, AL_REFERENCE_DISTANCE, 30.0); alSourcei(sound->source_id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE); volume = MYMAX(0.0, volume); -- cgit v1.2.3 From 3ad68c052bb4d58892b4d1e8cd6be59ea860df8f Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Sat, 4 Feb 2017 16:35:54 -0800 Subject: Perform mesh animation only once per frame. --- src/clientmap.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clientmap.cpp b/src/clientmap.cpp index 11719539f..fb70a97e9 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -470,7 +470,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) continue; // Mesh animation - { + if (pass == scene::ESNRP_SOLID) { //MutexAutoLock lock(block->mesh_mutex); MapBlockMesh *mapBlockMesh = block->mesh; assert(mapBlockMesh); -- cgit v1.2.3 From 5707b739f38cc5cf651de5b69d91d4f46511dac0 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Wed, 8 Feb 2017 23:00:37 -0800 Subject: Change default nodetimer_interval to 0.2s. (#5193) We want to reduce the chance that we get lots and lots of node timers all happening once a second, because we're better off doing small bits of work as they are available. Reducing this to 0.2 seconds will greatly reduce the total amount of nodetimers that elapse at the same instance, while not effecting total work load. This results in a far better chance of the server keeping up with work loads. --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index c81dde7de..0e8783f83 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -840,7 +840,7 @@ active_block_mgmt_interval (Active Block Management interval) float 2.0 abm_interval (Active Block Modifier interval) float 1.0 # Length of time between NodeTimer execution cycles -nodetimer_interval (NodeTimer interval) float 1.0 +nodetimer_interval (NodeTimer interval) float 0.2 # If enabled, invalid world data won't cause the server to shut down. # Only enable this if you know what you are doing. diff --git a/minetest.conf.example b/minetest.conf.example index 5c7533e93..a90cc7d8e 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1027,7 +1027,7 @@ # Length of time between NodeTimer execution cycles # type: float -# nodetimer_interval = 1.0 +# nodetimer_interval = 0.2 # If enabled, invalid world data won't cause the server to shut down. # Only enable this if you know what you are doing. diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 84046f61c..bdf1f92ca 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -295,7 +295,7 @@ void set_default_settings(Settings *settings) settings->setDefault("dedicated_server_step", "0.1"); settings->setDefault("active_block_mgmt_interval", "2.0"); settings->setDefault("abm_interval", "1.0"); - settings->setDefault("nodetimer_interval", "1.0"); + settings->setDefault("nodetimer_interval", "0.2"); settings->setDefault("ignore_world_load_errors", "false"); settings->setDefault("remote_media", ""); settings->setDefault("debug_log_level", "action"); -- cgit v1.2.3 From 74b670a7930736fb4f43dcb5c9e0a366bf23cad4 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 10 Feb 2017 06:59:38 +0000 Subject: Correct lua_api.txt docs related to meta (#5198) --- doc/lua_api.txt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 4774e8a5a..0245ee7bd 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2815,7 +2815,7 @@ See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`. ### `NodeMetaRef` Node metadata: reference extra data and functionality stored in a node. -Can be gotten via `minetest.get_meta(pos)`. +Can be obtained via `minetest.get_meta(pos)`. #### Methods * All methods in MetaDataRef @@ -2823,7 +2823,14 @@ Can be gotten via `minetest.get_meta(pos)`. ### `ItemStackMetaRef` ItemStack metadata: reference extra data and functionality stored in a stack. -Can be gotten via `item:get_meta()`. +Can be obtained via `item:get_meta()`. + +#### Methods +* All methods in MetaDataRef + +### `StorageRef` +Mod metadata: per mod metadata, saved automatically. +Can be obtained via `minetest.get_mod_storage()` during load time. #### Methods * All methods in MetaDataRef @@ -2850,9 +2857,6 @@ Can be gotten via `minetest.get_node_timer(pos)`. * `is_started()`: returns boolean state of timer * returns `true` if timer is started, otherwise `false` -### `StorageRef` -This is basically a reference to a C++ `ModMetadata` - ### `ObjectRef` Moving things in the game are generally these. @@ -3100,8 +3104,9 @@ an itemstring, a table or `nil`. * `set_count(count)`: Returns boolean whether item was cleared * `get_wear()`: Returns tool wear (`0`-`65535`), `0` for non-tools. * `set_wear(wear)`: Returns boolean whether item was cleared -* `get_metadata()`: Returns metadata (a string attached to an item stack). -* `set_metadata(metadata)`: Returns true. +* `get_meta()`: Returns ItemStackMetaRef. See section for more details +* `get_metadata()`: (DEPRECATED) Returns metadata (a string attached to an item stack). +* `set_metadata(metadata)`: (DEPRECATED) Returns true. * `clear()`: removes all items from the stack, making it empty. * `replace(item)`: replace the contents of this stack. * `item` can also be an itemstring or table. -- cgit v1.2.3 From f5d4494a51a4f38553c10efd51a5c423cd357c87 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 10 Feb 2017 08:19:31 +0100 Subject: Add textures for air and ignore items (#5196) --- builtin/game/register.lua | 8 ++++---- textures/base/pack/air.png | Bin 0 -> 225 bytes textures/base/pack/ignore.png | Bin 0 -> 234 bytes 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 textures/base/pack/air.png create mode 100644 textures/base/pack/ignore.png diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 90f095e9f..ec6f28097 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -331,8 +331,8 @@ core.register_item(":unknown", { core.register_node(":air", { description = "Air (you hacker you!)", - inventory_image = "unknown_node.png", - wield_image = "unknown_node.png", + inventory_image = "air.png", + wield_image = "air.png", drawtype = "airlike", paramtype = "light", sunlight_propagates = true, @@ -348,8 +348,8 @@ core.register_node(":air", { core.register_node(":ignore", { description = "Ignore (you hacker you!)", - inventory_image = "unknown_node.png", - wield_image = "unknown_node.png", + inventory_image = "ignore.png", + wield_image = "ignore.png", drawtype = "airlike", paramtype = "none", sunlight_propagates = false, diff --git a/textures/base/pack/air.png b/textures/base/pack/air.png new file mode 100644 index 000000000..e2c487214 Binary files /dev/null and b/textures/base/pack/air.png differ diff --git a/textures/base/pack/ignore.png b/textures/base/pack/ignore.png new file mode 100644 index 000000000..a73d2222d Binary files /dev/null and b/textures/base/pack/ignore.png differ -- cgit v1.2.3 From 7bd3e2bceb45a371f3bc7029dbc7729cb3ce42fb Mon Sep 17 00:00:00 2001 From: Craig Robbins Date: Fri, 10 Feb 2017 22:21:23 +1000 Subject: Revert "Plantlike visual scale: Send sqrt(visual_scale) to old clients" This reverts commit cdc538e0a242167cd7031d40670d2d4464b87f2c. --- src/network/networkprotocol.h | 2 -- src/nodedef.cpp | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 5301cc91c..a511d169b 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -146,8 +146,6 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 30: New ContentFeatures serialization version Add node and tile color and palette - Fix plantlike visual_scale being applied squared and add compatibility - with pre-30 clients by sending sqrt(visual_scale) */ #define LATEST_PROTOCOL_VERSION 30 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index c717b62b9..0bb150267 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1611,10 +1611,6 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const compatible_param_type_2 = CPT2_WALLMOUNTED; } - float compatible_visual_scale = visual_scale; - if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) - compatible_visual_scale = sqrt(visual_scale); - if (protocol_version == 13) { writeU8(os, 5); // version @@ -1626,7 +1622,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, compatible_visual_scale); + writeF1000(os, visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); @@ -1674,7 +1670,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, compatible_visual_scale); + writeF1000(os, visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); @@ -1728,7 +1724,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, compatible_visual_scale); + writeF1000(os, visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); -- cgit v1.2.3 From 7760948b7ca436ce0c7d26140dcd3867e81c99c9 Mon Sep 17 00:00:00 2001 From: Craig Robbins Date: Fri, 10 Feb 2017 22:21:49 +1000 Subject: Revert "Plantlike: Fix visual_scale being applied squared (#5115)" This reverts commit 953cbb3b15997a0e7c7c32af2365cb5046a9e476. --- src/content_mapblock.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 9923647bc..45822666f 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -1602,6 +1602,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } for (int i = 0; i < 4; i++) { + vertices[i].Pos *= f.visual_scale; + vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1); if (data->m_smooth_lighting) vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); -- cgit v1.2.3 From bb4db84bdbc7038a6ac495dd5f732f89ac40bfcc Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 27 Dec 2016 23:26:36 +0000 Subject: Use tree to list mods rather than textlist --- builtin/mainmenu/dlg_config_world.lua | 110 ++++++++++------------------------ builtin/mainmenu/modmgr.lua | 54 ++++++++--------- builtin/mainmenu/tab_mods.lua | 7 ++- 3 files changed, 59 insertions(+), 112 deletions(-) diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index 7b3ab9852..619c927c5 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -17,14 +17,13 @@ -------------------------------------------------------------------------------- -local enabled_all = false +local enabled_all = false local function modname_valid(name) return not name:find("[^a-z0-9_]") end local function get_formspec(data) - local mod = data.list:get_list()[data.selected_mod] local retval = @@ -32,24 +31,12 @@ local function get_formspec(data) "label[0.5,0;" .. fgettext("World:") .. "]" .. "label[1.75,0;" .. data.worldspec.name .. "]" - if data.hide_gamemods then - retval = retval .. "checkbox[1,6;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";true]" - else - retval = retval .. "checkbox[1,6;cb_hide_gamemods;" .. fgettext("Hide Game") .. ";false]" - end - - if data.hide_modpackcontents then - retval = retval .. "checkbox[6,6;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";true]" - else - retval = retval .. "checkbox[6,6;cb_hide_mpcontent;" .. fgettext("Hide mp content") .. ";false]" - end - if mod == nil then mod = {name=""} end local hard_deps, soft_deps = modmgr.get_dependencies(mod.path) - + retval = retval .. "label[0,0.7;" .. fgettext("Mod:") .. "]" .. "label[0.75,0.7;" .. mod.name .. "]" .. @@ -62,41 +49,45 @@ local function get_formspec(data) "button[3.25,7;2.5,0.5;btn_config_world_save;" .. fgettext("Save") .. "]" .. "button[5.75,7;2.5,0.5;btn_config_world_cancel;" .. fgettext("Cancel") .. "]" - if mod ~= nil and mod.name ~= "" and mod.typ ~= "game_mod" then + if mod and mod.name ~= "" and mod.typ ~= "game_mod" then if mod.is_modpack then local rawlist = data.list:get_raw_list() local all_enabled = true - for j=1,#rawlist,1 do - if rawlist[j].modpack == mod.name and - rawlist[j].enabled ~= true then - all_enabled = false - break + for j = 1, #rawlist, 1 do + if rawlist[j].modpack == mod.name and not rawlist[j].enabled then + all_enabled = false + break end end - if all_enabled == false then - retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_enable;" .. fgettext("Enable MP") .. "]" + if all_enabled then + retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_disable;" .. + fgettext("Disable MP") .. "]" else - retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_disable;" .. fgettext("Disable MP") .. "]" + retval = retval .. "button[5.5,0.125;2.5,0.5;btn_mp_enable;" .. + fgettext("Enable MP") .. "]" end else if mod.enabled then - retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. fgettext("enabled") .. ";true]" + retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. + fgettext("enabled") .. ";true]" else - retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. fgettext("enabled") .. ";false]" + retval = retval .. "checkbox[5.5,-0.125;cb_mod_enable;" .. + fgettext("enabled") .. ";false]" end end end - if enabled_all then + if enabled_all then retval = retval .. - "button[8.75,0.125;2.5,0.5;btn_disable_all_mods;" .. fgettext("Disable all") .. "]" .. - "textlist[5.5,0.75;5.75,5.4;world_config_modlist;" + "button[8.75,0.125;2.5,0.5;btn_disable_all_mods;" .. fgettext("Disable all") .. "]" else retval = retval .. - "button[8.75,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]" .. - "textlist[5.5,0.75;5.75,5.4;world_config_modlist;" + "button[8.75,0.125;2.5,0.5;btn_enable_all_mods;" .. fgettext("Enable all") .. "]" end + retval = retval .. + "tablecolumns[color;tree;text]" .. + "table[5.5,0.75;5.75,6;world_config_modlist;" retval = retval .. modmgr.render_modlist(data.list) retval = retval .. ";" .. data.selected_mod .."]" @@ -129,16 +120,15 @@ end local function handle_buttons(this, fields) - if fields["world_config_modlist"] ~= nil then - local event = core.explode_textlist_event(fields["world_config_modlist"]) - this.data.selected_mod = event.index - core.setting_set("world_config_selected_mod", event.index) + local event = core.explode_table_event(fields["world_config_modlist"]) + this.data.selected_mod = event.row + core.setting_set("world_config_selected_mod", event.row) if event.type == "DCL" then enable_mod(this) end - + return true end @@ -160,44 +150,7 @@ local function handle_buttons(this, fields) return true end - if fields["cb_hide_gamemods"] ~= nil or - fields["cb_hide_mpcontent"] ~= nil then - local current = this.data.list:get_filtercriteria() - - if current == nil then - current = {} - end - - if fields["cb_hide_gamemods"] ~= nil then - if core.is_yes(fields["cb_hide_gamemods"]) then - current.hide_game = true - this.data.hide_gamemods = true - core.setting_set("world_config_hide_gamemods", "true") - else - current.hide_game = false - this.data.hide_gamemods = false - core.setting_set("world_config_hide_gamemods", "false") - end - end - - if fields["cb_hide_mpcontent"] ~= nil then - if core.is_yes(fields["cb_hide_mpcontent"]) then - current.hide_modpackcontents = true - this.data.hide_modpackcontents = true - core.setting_set("world_config_hide_modpackcontents", "true") - else - current.hide_modpackcontents = false - this.data.hide_modpackcontents = false - core.setting_set("world_config_hide_modpackcontents", "false") - end - end - - this.data.list:set_filtercriteria(current) - return true - end - if fields["btn_config_world_save"] then - local filename = this.data.worldspec.path .. DIR_DELIM .. "world.mt" @@ -231,7 +184,7 @@ local function handle_buttons(this, fields) if not worldfile:write() then core.log("error", "Failed to write world config file") end - + this:delete() return true end @@ -252,7 +205,7 @@ local function handle_buttons(this, fields) enabled_all = true return true end - + if fields.btn_disable_all_mods then local list = this.data.list:get_raw_list() @@ -269,14 +222,11 @@ local function handle_buttons(this, fields) end function create_configure_world_dlg(worldidx) - local dlg = dialog_create("sp_config_world", get_formspec, handle_buttons, nil) - dlg.data.hide_gamemods = core.setting_getbool("world_config_hide_gamemods") - dlg.data.hide_modpackcontents = core.setting_getbool("world_config_hide_modpackcontents") dlg.data.selected_mod = tonumber(core.setting_get("world_config_selected_mod")) if dlg.data.selected_mod == nil then dlg.data.selected_mod = 0 @@ -286,14 +236,14 @@ function create_configure_world_dlg(worldidx) if dlg.data.worldspec == nil then dlg:delete() return nil end dlg.data.worldconfig = modmgr.get_worldconfig(dlg.data.worldspec.path) - + if dlg.data.worldconfig == nil or dlg.data.worldconfig.id == nil or dlg.data.worldconfig.id == "" then dlg:delete() return nil end - + dlg.data.list = filterlist.create( modmgr.preparemodlist, --refresh modmgr.comparemod, --compare diff --git a/builtin/mainmenu/modmgr.lua b/builtin/mainmenu/modmgr.lua index 2b7b371bf..0fbfa3e6b 100644 --- a/builtin/mainmenu/modmgr.lua +++ b/builtin/mainmenu/modmgr.lua @@ -18,7 +18,7 @@ -------------------------------------------------------------------------------- function get_mods(path,retval,modpack) local mods = core.get_dir_list(path, true) - + for _, name in ipairs(mods) do if name:sub(1, 1) ~= "." then local prefix = path .. DIR_DELIM .. name .. DIR_DELIM @@ -237,49 +237,45 @@ function modmgr.render_modlist(render_list) local list = render_list:get_list() local last_modpack = nil - - for i,v in ipairs(list) do - if retval ~= "" then - retval = retval .."," + local retval = {} + local in_game_mods = false + for i, v in ipairs(list) do + if v.typ == "game_mod" and not in_game_mods then + in_game_mods = true + retval[#retval + 1] = mt_color_blue + retval[#retval + 1] = "0" + retval[#retval + 1] = fgettext("Subgame Mods") end local color = "" - if v.is_modpack then local rawlist = render_list:get_raw_list() + color = mt_color_dark_green - local all_enabled = true - for j=1,#rawlist,1 do + for j = 1, #rawlist, 1 do if rawlist[j].modpack == list[i].name and - rawlist[j].enabled ~= true then - all_enabled = false - break + rawlist[j].enabled ~= true then + -- Modpack not entirely enabled so showing as grey + color = mt_color_grey + break end end - - if all_enabled == false then - color = mt_color_grey - else - color = mt_color_dark_green - end - end - - if v.typ == "game_mod" then + elseif v.typ == "game_mod" then color = mt_color_blue - else - if v.enabled then - color = mt_color_green - end + elseif v.enabled then + color = mt_color_green end - retval = retval .. color - if v.modpack ~= nil then - retval = retval .. " " + retval[#retval + 1] = color + if v.modpack ~= nil or v.typ == "game_mod" then + retval[#retval + 1] = "1" + else + retval[#retval + 1] = "0" end - retval = retval .. v.name + retval[#retval + 1] = core.formspec_escape(v.name) end - return retval + return table.concat(retval, ",") end -------------------------------------------------------------------------------- diff --git a/builtin/mainmenu/tab_mods.lua b/builtin/mainmenu/tab_mods.lua index 4a5b6c041..29afd8a4e 100644 --- a/builtin/mainmenu/tab_mods.lua +++ b/builtin/mainmenu/tab_mods.lua @@ -28,7 +28,8 @@ local function get_formspec(tabview, name, tabdata) local retval = "label[0.05,-0.25;".. fgettext("Installed Mods:") .. "]" .. - "textlist[0,0.25;5.1,5;modlist;" .. + "tablecolumns[color;tree;text]" .. + "table[0,0.25;5.1,5;modlist;" .. modmgr.render_modlist(modmgr.global_mods) .. ";" .. tabdata.selected_mod .. "]" @@ -127,8 +128,8 @@ end -------------------------------------------------------------------------------- local function handle_buttons(tabview, fields, tabname, tabdata) if fields["modlist"] ~= nil then - local event = core.explode_textlist_event(fields["modlist"]) - tabdata.selected_mod = event.index + local event = core.explode_table_event(fields["modlist"]) + tabdata.selected_mod = event.row return true end -- cgit v1.2.3 From 1de08e196182498a931b496f79b1c1eaf3de7ca4 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 10 Feb 2017 17:15:22 +0000 Subject: Plantlike: Fix visual_scale being applied squared This re-applies 2 commits that were reverted. Visual_scale was applied twice to plantlike by accident sometime between 2011 and 2013, squaring the requested scale value. Visual_scale is correctly applied once in it's other uses in signlike and torchlike. Two lines of code are removed, they also had no effect for the vast majority of nodes with the default visual_scale of 1.0. The texture continues to have it's base at ground level. Send sqrt(visual_scale) to old clients. Keep compatibility with protocol < 30 clients now that visual_scale is no longer applied twice to plantlike drawtype and mods are being updated to a new value. --- src/content_mapblock.cpp | 2 -- src/network/networkprotocol.h | 2 ++ src/nodedef.cpp | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 45822666f..9923647bc 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -1602,8 +1602,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } for (int i = 0; i < 4; i++) { - vertices[i].Pos *= f.visual_scale; - vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1); if (data->m_smooth_lighting) vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); vertices[i].Pos += intToFloat(p, BS); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index a511d169b..5301cc91c 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -146,6 +146,8 @@ with this program; if not, write to the Free Software Foundation, Inc., PROTOCOL VERSION 30: New ContentFeatures serialization version Add node and tile color and palette + Fix plantlike visual_scale being applied squared and add compatibility + with pre-30 clients by sending sqrt(visual_scale) */ #define LATEST_PROTOCOL_VERSION 30 diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 0bb150267..c717b62b9 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1611,6 +1611,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const compatible_param_type_2 = CPT2_WALLMOUNTED; } + float compatible_visual_scale = visual_scale; + if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) + compatible_visual_scale = sqrt(visual_scale); + if (protocol_version == 13) { writeU8(os, 5); // version @@ -1622,7 +1626,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); @@ -1670,7 +1674,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); @@ -1724,7 +1728,7 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const writeS16(os, i->second); } writeU8(os, drawtype); - writeF1000(os, visual_scale); + writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); -- cgit v1.2.3 From 984e063374c032ed17764931fd00c19afb92ebb9 Mon Sep 17 00:00:00 2001 From: paramat Date: Thu, 9 Feb 2017 21:12:53 +0000 Subject: Footsteps: Fix offset footstep and shallow water sound bugs Fix footstep sounds coming from nodes to either side when walking on a 1 node wide row of nodebox slabs such as default:snow. Fix sand footsteps when swimming in 1 node deep water. Use a new function 'getFootstepNodePos()' instead of 'getStandingNodePos()' to avoid using a horizontally-offset 'sneak node' for sounds. Sound is selected from the node BS * 0.05 below the player's feet, so that 1/16th slabs will play the slab sound but 1/32nd slabs will not. If the player is not 'touching ground' the node detection position is changed to BS * 0.5 below to preserve footstep sounds when landing after a jump or fall. --- src/game.cpp | 2 +- src/localplayer.cpp | 12 ++++++++++++ src/localplayer.h | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/game.cpp b/src/game.cpp index 840403c42..1735737de 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -3530,7 +3530,7 @@ void Game::updateSound(f32 dtime) LocalPlayer *player = client->getEnv().getLocalPlayer(); ClientMap &map = client->getEnv().getClientMap(); - MapNode n = map.getNodeNoEx(player->getStandingNodePos()); + MapNode n = map.getNodeNoEx(player->getFootstepNodePos()); soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep; } diff --git a/src/localplayer.cpp b/src/localplayer.cpp index 857d95d8b..33932df0b 100644 --- a/src/localplayer.cpp +++ b/src/localplayer.cpp @@ -655,6 +655,18 @@ v3s16 LocalPlayer::getStandingNodePos() return floatToInt(getPosition() - v3f(0, BS, 0), BS); } +v3s16 LocalPlayer::getFootstepNodePos() +{ + if (touching_ground) + // BS * 0.05 below the player's feet ensures a 1/16th height + // nodebox is detected instead of the node below it. + return floatToInt(getPosition() - v3f(0, BS * 0.05f, 0), BS); + // A larger distance below is necessary for a footstep sound + // when landing after a jump or fall. BS * 0.5 ensures water + // sounds when swimming in 1 node deep water. + return floatToInt(getPosition() - v3f(0, BS * 0.5f, 0), BS); +} + v3s16 LocalPlayer::getLightPosition() const { return floatToInt(m_position + v3f(0,BS+BS/2,0), BS); diff --git a/src/localplayer.h b/src/localplayer.h index 685a78cb3..b48dacdb7 100644 --- a/src/localplayer.h +++ b/src/localplayer.h @@ -68,6 +68,7 @@ public: void applyControl(float dtime); v3s16 getStandingNodePos(); + v3s16 getFootstepNodePos(); // Used to check if anything changed and prevent sending packets if not v3f last_position; -- cgit v1.2.3 From 2bd10022cb06850a404f180c455954ed2f817ce3 Mon Sep 17 00:00:00 2001 From: Hybrid Dog Date: Sat, 11 Feb 2017 11:39:41 +0100 Subject: Mainmenu: Brighter text colours for readability --- builtin/mainmenu/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 1b527a053..79e6d5c02 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -16,9 +16,9 @@ --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. mt_color_grey = "#AAAAAA" -mt_color_blue = "#0000DD" -mt_color_green = "#00DD00" -mt_color_dark_green = "#003300" +mt_color_blue = "#6389FF" +mt_color_green = "#72FF63" +mt_color_dark_green = "#25C191" --for all other colors ask sfan5 to complete his work! -- cgit v1.2.3 From f17c9c45dc30a388675d46418d278a4a029206e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Thu, 27 Oct 2016 23:25:44 +0200 Subject: Lighting: Update lighting at block loading This commit updates mapblocks' light if necessary when they are loaded. This removes ghost lighting. --- src/clientiface.cpp | 6 -- src/map.cpp | 131 +++++++++++++++++++---------------------- src/mapblock.cpp | 21 +++---- src/mapblock.h | 62 ++++++++++++-------- src/serialization.h | 5 +- src/voxelalgorithms.cpp | 152 +++++++++++++++++++++++++++++++++++++++++++++--- src/voxelalgorithms.h | 17 +++++- 7 files changed, 273 insertions(+), 121 deletions(-) diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 47730343c..0eb68c9c1 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -283,12 +283,6 @@ void RemoteClient::GetNextBlocks ( surely_not_found_on_disk = true; } - // Block is valid if lighting is up-to-date and data exists - if(block->isValid() == false) - { - block_is_invalid = true; - } - if(block->isGenerated() == false) block_is_invalid = true; diff --git a/src/map.cpp b/src/map.cpp index f2a4b7ffe..ffba77262 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -824,7 +824,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, // Update lighting std::vector > oldnodes; oldnodes.push_back(std::pair(p, oldnode)); - voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks); + voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks); for(std::map::iterator i = modified_blocks.begin(); @@ -1523,7 +1523,7 @@ void Map::transformLiquids(std::map &modified_blocks) for (std::deque::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter) m_transforming_liquid.push_back(*iter); - voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks); + voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks); /* ---------------------------------------------------------------------- @@ -1955,27 +1955,10 @@ void ServerMap::finishBlockMake(BlockMakeData *data, v3s16 bpmax = data->blockpos_max; v3s16 extra_borders(1, 1, 1); - v3s16 full_bpmin = bpmin - extra_borders; - v3s16 full_bpmax = bpmax + extra_borders; bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax)); - /* - Set lighting to non-expired state in all of them. - This is cheating, but it is not fast enough if all of them - would actually be updated. - */ - for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++) - for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) - for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) { - MapBlock *block = emergeBlock(v3s16(x, y, z), false); - if (!block) - continue; - - block->setLightingExpired(false); - } - /* Blit generated stuff to map NOTE: blitBackAll adds nearly everything to changed_blocks @@ -2991,7 +2974,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool // We just loaded it from, so it's up-to-date. block->resetModified(); - } catch(SerializationError &e) { @@ -3015,71 +2997,80 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos) { DSTACK(FUNCTION_NAME); + bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL); + v2s16 p2d(blockpos.X, blockpos.Z); std::string ret; dbase->loadBlock(blockpos, &ret); if (ret != "") { loadBlock(&ret, blockpos, createSector(p2d), false); - return getBlockNoCreateNoEx(blockpos); - } - // Not found in database, try the files - - // The directory layout we're going to load from. - // 1 - original sectors/xxxxzzzz/ - // 2 - new sectors2/xxx/zzz/ - // If we load from anything but the latest structure, we will - // immediately save to the new one, and remove the old. - int loadlayout = 1; - std::string sectordir1 = getSectorDir(p2d, 1); - std::string sectordir; - if(fs::PathExists(sectordir1)) - { - sectordir = sectordir1; - } - else - { - loadlayout = 2; - sectordir = getSectorDir(p2d, 2); - } + } else { + // Not found in database, try the files + + // The directory layout we're going to load from. + // 1 - original sectors/xxxxzzzz/ + // 2 - new sectors2/xxx/zzz/ + // If we load from anything but the latest structure, we will + // immediately save to the new one, and remove the old. + int loadlayout = 1; + std::string sectordir1 = getSectorDir(p2d, 1); + std::string sectordir; + if (fs::PathExists(sectordir1)) { + sectordir = sectordir1; + } else { + loadlayout = 2; + sectordir = getSectorDir(p2d, 2); + } - /* + /* Make sure sector is loaded - */ + */ - MapSector *sector = getSectorNoGenerateNoEx(p2d); - if(sector == NULL) - { - try{ - sector = loadSectorMeta(sectordir, loadlayout != 2); - } - catch(InvalidFilenameException &e) - { - return NULL; - } - catch(FileNotGoodException &e) - { - return NULL; - } - catch(std::exception &e) - { - return NULL; + MapSector *sector = getSectorNoGenerateNoEx(p2d); + if (sector == NULL) { + try { + sector = loadSectorMeta(sectordir, loadlayout != 2); + } catch(InvalidFilenameException &e) { + return NULL; + } catch(FileNotGoodException &e) { + return NULL; + } catch(std::exception &e) { + return NULL; + } } - } - /* + + /* Make sure file exists - */ + */ - std::string blockfilename = getBlockFilename(blockpos); - if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false) - return NULL; + std::string blockfilename = getBlockFilename(blockpos); + if (fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false) + return NULL; - /* + /* Load block and save it to the database - */ - loadBlock(sectordir, blockfilename, sector, true); - return getBlockNoCreateNoEx(blockpos); + */ + loadBlock(sectordir, blockfilename, sector, true); + } + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if (created_new && (block != NULL)) { + std::map modified_blocks; + // Fix lighting if necessary + voxalgo::update_block_border_lighting(this, block, modified_blocks); + if (!modified_blocks.empty()) { + //Modified lighting, send event + MapEditEvent event; + event.type = MEET_OTHER; + std::map::iterator it; + for (it = modified_blocks.begin(); + it != modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + dispatchEvent(&event); + } + } + return block; } bool ServerMap::deleteBlock(v3s16 blockpos) diff --git a/src/mapblock.cpp b/src/mapblock.cpp index f8c3197bc..840cb9b39 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -73,7 +73,7 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy): m_modified(MOD_STATE_WRITE_NEEDED), m_modified_reason(MOD_REASON_INITIAL), is_underground(false), - m_lighting_expired(true), + m_lighting_complete(0xFFFF), m_day_night_differs(false), m_day_night_differs_expired(true), m_generated(false), @@ -571,11 +571,12 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk) flags |= 0x01; if(getDayNightDiff()) flags |= 0x02; - if(m_lighting_expired) - flags |= 0x04; if(m_generated == false) flags |= 0x08; writeU8(os, flags); + if (version >= 27) { + writeU16(os, m_lighting_complete); + } /* Bulk node data @@ -672,7 +673,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk) u8 flags = readU8(is); is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; - m_lighting_expired = (flags & 0x04) ? true : false; + if (version < 27) { + m_lighting_complete = 0xFFFF; + } else { + m_lighting_complete = readU16(is); + } m_generated = (flags & 0x08) ? false : true; /* @@ -783,7 +788,7 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) // Initialize default flags is_underground = false; m_day_night_differs = false; - m_lighting_expired = false; + m_lighting_complete = 0xFFFF; m_generated = true; // Make a temporary buffer @@ -849,7 +854,6 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) is.read((char*)&flags, 1); is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; - m_lighting_expired = (flags & 0x04) ? true : false; if(version >= 18) m_generated = (flags & 0x08) ? false : true; @@ -1027,10 +1031,7 @@ std::string analyze_block(MapBlock *block) else desc<<"is_ug [ ], "; - if(block->getLightingExpired()) - desc<<"lighting_exp [X], "; - else - desc<<"lighting_exp [ ], "; + desc<<"lighting_complete: "<getLightingComplete()<<", "; if(block->isDummy()) { diff --git a/src/mapblock.h b/src/mapblock.h index f80800109..5a0ec937a 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -105,7 +105,7 @@ public: #define MOD_REASON_INITIAL (1 << 0) #define MOD_REASON_REALLOCATE (1 << 1) #define MOD_REASON_SET_IS_UNDERGROUND (1 << 2) -#define MOD_REASON_SET_LIGHTING_EXPIRED (1 << 3) +#define MOD_REASON_SET_LIGHTING_COMPLETE (1 << 3) #define MOD_REASON_SET_GENERATED (1 << 4) #define MOD_REASON_SET_NODE (1 << 5) #define MOD_REASON_SET_NODE_NO_CHECK (1 << 6) @@ -213,17 +213,42 @@ public: raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND); } - inline void setLightingExpired(bool expired) + inline void setLightingComplete(u16 newflags) { - if (expired != m_lighting_expired){ - m_lighting_expired = expired; - raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_EXPIRED); + if (newflags != m_lighting_complete) { + m_lighting_complete = newflags; + raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE); } } - inline bool getLightingExpired() + inline u16 getLightingComplete() { - return m_lighting_expired; + return m_lighting_complete; + } + + inline void setLightingComplete(LightBank bank, u8 direction, + bool is_complete) + { + assert(direction >= 0 && direction <= 5); + if (bank == LIGHTBANK_NIGHT) { + direction += 6; + } + u16 newflags = m_lighting_complete; + if (is_complete) { + newflags |= 1 << direction; + } else { + newflags &= ~(1 << direction); + } + setLightingComplete(newflags); + } + + inline bool isLightingComplete(LightBank bank, u8 direction) + { + assert(direction >= 0 && direction <= 5); + if (bank == LIGHTBANK_NIGHT) { + direction += 6; + } + return (m_lighting_complete & (1 << direction)) != 0; } inline bool isGenerated() @@ -239,15 +264,6 @@ public: } } - inline bool isValid() - { - if (m_lighting_expired) - return false; - if (data == NULL) - return false; - return true; - } - //// //// Position stuff //// @@ -613,14 +629,14 @@ private: */ bool is_underground; - /* - Set to true if changes has been made that make the old lighting - values wrong but the lighting hasn't been actually updated. - - If this is false, lighting is exactly right. - If this is true, lighting might be wrong or right. + /*! + * Each bit indicates if light spreading was finished + * in a direction. (Because the neighbor could also be unloaded.) + * Bits: day X+, day Y+, day Z+, day Z-, day Y-, day X-, + * night X+, night Y+, night Z+, night Z-, night Y-, night X-, + * nothing, nothing, nothing, nothing. */ - bool m_lighting_expired; + u16 m_lighting_complete; // Whether day and night lighting differs bool m_day_night_differs; diff --git a/src/serialization.h b/src/serialization.h index 01d37d363..52c63098e 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -62,13 +62,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 24: 16-bit node ids and node timers (never released as stable) 25: Improved node timer format 26: Never written; read the same as 25 + 27: Added light spreading flags to blocks */ // This represents an uninitialized or invalid format #define SER_FMT_VER_INVALID 255 // Highest supported serialization version -#define SER_FMT_VER_HIGHEST_READ 26 +#define SER_FMT_VER_HIGHEST_READ 27 // Saved on disk version -#define SER_FMT_VER_HIGHEST_WRITE 25 +#define SER_FMT_VER_HIGHEST_WRITE 27 // Lowest supported serialization version #define SER_FMT_VER_LOWEST_READ 0 // Lowest serialization version for writing diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index c20917164..3c32bc125 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -423,6 +423,7 @@ void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) { neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos); if (neighbor_block == NULL) { + current.block->setLightingComplete(bank, i, false); continue; } } else { @@ -486,7 +487,8 @@ void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, * \param modified_blocks output, all modified map blocks are added to this */ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, - LightQueue &light_sources, std::map &modified_blocks) + LightQueue &light_sources, + std::map &modified_blocks) { // The light the current node can provide to its neighbors. u8 spreading_light; @@ -511,6 +513,7 @@ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) { neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos); if (neighbor_block == NULL) { + current.block->setLightingComplete(bank, i, false); continue; } } else { @@ -584,10 +587,11 @@ bool is_sunlight_above(Map *map, v3s16 pos, INodeDefManager *ndef) static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT }; -void update_lighting_nodes(Map *map, INodeDefManager *ndef, +void update_lighting_nodes(Map *map, std::vector > &oldnodes, std::map &modified_blocks) { + INodeDefManager *ndef = map->getNodeDefManager(); // For node getter functions bool is_valid_position; @@ -596,6 +600,22 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef, LightBank bank = banks[i]; UnlightQueue disappearing_lights(256); ReLightQueue light_sources(256); + // Nodes that are brighter than the brightest modified node was + // won't change, since they didn't get their light from a + // modified node. + u8 min_safe_light = 0; + for (std::vector >::iterator it = + oldnodes.begin(); it < oldnodes.end(); ++it) { + u8 old_light = it->second.getLight(bank, ndef); + if (old_light > min_safe_light) { + min_safe_light = old_light; + } + } + // If only one node changed, even nodes with the same brightness + // didn't get their light from the changed node. + if (oldnodes.size() > 1) { + min_safe_light++; + } // For each changed node process sunlight and initialize for (std::vector >::iterator it = oldnodes.begin(); it < oldnodes.end(); ++it) { @@ -634,11 +654,9 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef, MapNode n2 = map->getNodeNoEx(p2, &is_valid); if (is_valid) { u8 spread = n2.getLight(bank, ndef); - // If the neighbor is at least as bright as - // this node then its light is not from - // this node. - // Its light can spread to this node. - if (spread > new_light && spread >= old_light) { + // If it is sure that the neighbor won't be + // unlighted, its light can spread to this node. + if (spread > new_light && spread >= min_safe_light) { new_light = spread - 1; } } @@ -747,6 +765,126 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef, } } +/*! + * Borders of a map block in relative node coordinates. + * Compatible with type 'direction'. + */ +const VoxelArea block_borders[] = { + VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+ + VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+ + VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+ + VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z- + VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y- + VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X- +}; + +/*! + * Returns true if: + * -the node has unloaded neighbors + * -the node doesn't have light + * -the node's light is the same as the maximum of + * its light source and its brightest neighbor minus one. + * . + */ +bool is_light_locally_correct(Map *map, INodeDefManager *ndef, LightBank bank, + v3s16 pos) +{ + bool is_valid_position; + MapNode n = map->getNodeNoEx(pos, &is_valid_position); + const ContentFeatures &f = ndef->get(n); + if (f.param_type != CPT_LIGHT) { + return true; + } + u8 light = n.getLightNoChecks(bank, &f); + assert(f.light_source <= LIGHT_MAX); + u8 brightest_neighbor = f.light_source + 1; + for (direction d = 0; d < 6; ++d) { + MapNode n2 = map->getNodeNoEx(pos + neighbor_dirs[d], + &is_valid_position); + u8 light2 = n2.getLight(bank, ndef); + if (brightest_neighbor < light2) { + brightest_neighbor = light2; + } + } + assert(light <= LIGHT_SUN); + return brightest_neighbor == light + 1; +} + +void update_block_border_lighting(Map *map, MapBlock *block, + std::map &modified_blocks) +{ + INodeDefManager *ndef = map->getNodeDefManager(); + bool is_valid_position; + for (s32 i = 0; i < 2; i++) { + LightBank bank = banks[i]; + UnlightQueue disappearing_lights(256); + ReLightQueue light_sources(256); + // Get incorrect lights + for (direction d = 0; d < 6; d++) { + // For each direction + // Get neighbor block + v3s16 otherpos = block->getPos() + neighbor_dirs[d]; + MapBlock *other = map->getBlockNoCreateNoEx(otherpos); + if (other == NULL) { + continue; + } + // Only update if lighting was not completed. + if (block->isLightingComplete(bank, d) && + other->isLightingComplete(bank, 5 - d)) + continue; + // Reset flags + block->setLightingComplete(bank, d, true); + other->setLightingComplete(bank, 5 - d, true); + // The two blocks and their connecting surfaces + MapBlock *blocks[] = {block, other}; + VoxelArea areas[] = {block_borders[d], block_borders[5 - d]}; + // For both blocks + for (u8 blocknum = 0; blocknum < 2; blocknum++) { + MapBlock *b = blocks[blocknum]; + VoxelArea a = areas[blocknum]; + // For all nodes + for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++) + for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) + for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { + MapNode n = b->getNodeNoCheck(x, y, z, + &is_valid_position); + u8 light = n.getLight(bank, ndef); + // Sunlight is fixed + if (light < LIGHT_SUN) { + // Unlight if not correct + if (!is_light_locally_correct(map, ndef, bank, + v3s16(x, y, z) + b->getPosRelative())) { + // Initialize for unlighting + n.setLight(bank, 0, ndef); + b->setNodeNoCheck(x, y, z, n); + modified_blocks[b->getPos()]=b; + disappearing_lights.push(light, + relative_v3(x, y, z), b->getPos(), b, + 6); + } + } + } + } + } + // Remove lights + unspread_light(map, ndef, bank, disappearing_lights, light_sources, + modified_blocks); + // Initialize light values for light spreading. + for (u8 i = 0; i <= LIGHT_SUN; i++) { + const std::vector &lights = light_sources.lights[i]; + for (std::vector::const_iterator it = lights.begin(); + it < lights.end(); it++) { + MapNode n = it->block->getNodeNoCheck(it->rel_position, + &is_valid_position); + n.setLight(bank, i, ndef); + it->block->setNodeNoCheck(it->rel_position, n); + } + } + // Spread lights. + spread_light(map, ndef, bank, light_sources, modified_blocks); + } +} + VoxelLineIterator::VoxelLineIterator( const v3f &start_position, const v3f &line_vector) : diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index 5eff8f7ac..bf1638fa3 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -22,8 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "mapnode.h" -#include -#include +#include "util/container.h" +#include "util/cpp11_container.h" class Map; class MapBlock; @@ -69,10 +69,21 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a, */ void update_lighting_nodes( Map *map, - INodeDefManager *ndef, std::vector > &oldnodes, std::map &modified_blocks); +/*! + * Updates borders of the given mapblock. + * Only updates if the block was marked with incomplete + * lighting and the neighbor is also loaded. + * + * \param block the block to update + * \param modified_blocks output, contains all map blocks that + * the function modified + */ +void update_block_border_lighting(Map *map, MapBlock *block, + std::map &modified_blocks); + /*! * This class iterates trough voxels that intersect with * a line. The collision detection does not see nodeboxes, -- cgit v1.2.3 From eb49009d023e6e3b5d59a97b8fb5fed5eee83296 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 14 Feb 2017 01:08:17 -0800 Subject: FreeType: address font license issues (#5230) It appears we were shipping font files without license text, and I had my doubts about the bitmap fonts being usable directly. This replaces existing TTF fonts with Apache-2.0 licensed fonts from chome core (Cousine, Arimo, Tinos). Include the full license file for all three fonts. The Lucida Sans font bitmap is removed entirely for non-freetype builds. There is therefore only mono fonts for non-freetype builds. --- fonts/Arimo-LICENSE.txt | 202 ++++++++++++++++++++++++++++++++++++++++ fonts/Arimo-Regular.ttf | Bin 0 -> 436876 bytes fonts/Cousine-LICENSE.txt | 202 ++++++++++++++++++++++++++++++++++++++++ fonts/Cousine-Regular.ttf | Bin 0 -> 309040 bytes fonts/DroidSansFallbackFull.ttf | Bin 4529044 -> 0 bytes fonts/Tinos-LICENSE.txt | 202 ++++++++++++++++++++++++++++++++++++++++ fonts/Tinos-Regular.ttf | Bin 0 -> 475996 bytes fonts/liberationmono.ttf | Bin 333636 -> 0 bytes fonts/liberationsans.ttf | Bin 133828 -> 0 bytes fonts/lucida_sans_10.xml | Bin 156248 -> 0 bytes fonts/lucida_sans_100.png | Bin 12012 -> 0 bytes fonts/lucida_sans_11.xml | Bin 157272 -> 0 bytes fonts/lucida_sans_110.png | Bin 14739 -> 0 bytes fonts/lucida_sans_12.xml | Bin 157058 -> 0 bytes fonts/lucida_sans_120.png | Bin 16295 -> 0 bytes fonts/lucida_sans_14.xml | Bin 159272 -> 0 bytes fonts/lucida_sans_140.png | Bin 42429 -> 0 bytes fonts/lucida_sans_16.xml | Bin 160744 -> 0 bytes fonts/lucida_sans_160.png | Bin 48106 -> 0 bytes fonts/lucida_sans_18.xml | Bin 162284 -> 0 bytes fonts/lucida_sans_180.png | Bin 56766 -> 0 bytes fonts/lucida_sans_20.xml | Bin 162438 -> 0 bytes fonts/lucida_sans_200.png | Bin 69059 -> 0 bytes fonts/lucida_sans_22.xml | Bin 162936 -> 0 bytes fonts/lucida_sans_220.png | Bin 74427 -> 0 bytes fonts/lucida_sans_24.xml | Bin 166358 -> 0 bytes fonts/lucida_sans_240.png | Bin 83532 -> 0 bytes fonts/lucida_sans_26.xml | Bin 167054 -> 0 bytes fonts/lucida_sans_260.png | Bin 93958 -> 0 bytes fonts/lucida_sans_28.xml | Bin 167156 -> 0 bytes fonts/lucida_sans_280.png | Bin 101952 -> 0 bytes fonts/lucida_sans_36.xml | Bin 169606 -> 0 bytes fonts/lucida_sans_360.png | Bin 143453 -> 0 bytes fonts/lucida_sans_4.xml | Bin 136910 -> 0 bytes fonts/lucida_sans_40.png | Bin 7642 -> 0 bytes fonts/lucida_sans_48.xml | Bin 171972 -> 0 bytes fonts/lucida_sans_480.png | Bin 205578 -> 0 bytes fonts/lucida_sans_56.xml | Bin 174174 -> 0 bytes fonts/lucida_sans_560.png | Bin 246505 -> 0 bytes fonts/lucida_sans_6.xml | Bin 140552 -> 0 bytes fonts/lucida_sans_60.png | Bin 13992 -> 0 bytes fonts/lucida_sans_8.xml | Bin 154564 -> 0 bytes fonts/lucida_sans_80.png | Bin 9740 -> 0 bytes fonts/lucida_sans_9.xml | Bin 154830 -> 0 bytes fonts/lucida_sans_90.png | Bin 10704 -> 0 bytes src/defaultsettings.cpp | 8 +- 46 files changed, 610 insertions(+), 4 deletions(-) create mode 100644 fonts/Arimo-LICENSE.txt create mode 100644 fonts/Arimo-Regular.ttf create mode 100644 fonts/Cousine-LICENSE.txt create mode 100644 fonts/Cousine-Regular.ttf delete mode 100644 fonts/DroidSansFallbackFull.ttf create mode 100644 fonts/Tinos-LICENSE.txt create mode 100644 fonts/Tinos-Regular.ttf delete mode 100644 fonts/liberationmono.ttf delete mode 100644 fonts/liberationsans.ttf delete mode 100755 fonts/lucida_sans_10.xml delete mode 100755 fonts/lucida_sans_100.png delete mode 100755 fonts/lucida_sans_11.xml delete mode 100755 fonts/lucida_sans_110.png delete mode 100755 fonts/lucida_sans_12.xml delete mode 100755 fonts/lucida_sans_120.png delete mode 100755 fonts/lucida_sans_14.xml delete mode 100755 fonts/lucida_sans_140.png delete mode 100755 fonts/lucida_sans_16.xml delete mode 100755 fonts/lucida_sans_160.png delete mode 100755 fonts/lucida_sans_18.xml delete mode 100755 fonts/lucida_sans_180.png delete mode 100755 fonts/lucida_sans_20.xml delete mode 100755 fonts/lucida_sans_200.png delete mode 100755 fonts/lucida_sans_22.xml delete mode 100755 fonts/lucida_sans_220.png delete mode 100755 fonts/lucida_sans_24.xml delete mode 100755 fonts/lucida_sans_240.png delete mode 100755 fonts/lucida_sans_26.xml delete mode 100755 fonts/lucida_sans_260.png delete mode 100755 fonts/lucida_sans_28.xml delete mode 100755 fonts/lucida_sans_280.png delete mode 100755 fonts/lucida_sans_36.xml delete mode 100755 fonts/lucida_sans_360.png delete mode 100755 fonts/lucida_sans_4.xml delete mode 100755 fonts/lucida_sans_40.png delete mode 100755 fonts/lucida_sans_48.xml delete mode 100755 fonts/lucida_sans_480.png delete mode 100755 fonts/lucida_sans_56.xml delete mode 100755 fonts/lucida_sans_560.png delete mode 100755 fonts/lucida_sans_6.xml delete mode 100755 fonts/lucida_sans_60.png delete mode 100755 fonts/lucida_sans_8.xml delete mode 100755 fonts/lucida_sans_80.png delete mode 100755 fonts/lucida_sans_9.xml delete mode 100755 fonts/lucida_sans_90.png diff --git a/fonts/Arimo-LICENSE.txt b/fonts/Arimo-LICENSE.txt new file mode 100644 index 000000000..75b52484e --- /dev/null +++ b/fonts/Arimo-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/fonts/Arimo-Regular.ttf b/fonts/Arimo-Regular.ttf new file mode 100644 index 000000000..9be443c7d Binary files /dev/null and b/fonts/Arimo-Regular.ttf differ diff --git a/fonts/Cousine-LICENSE.txt b/fonts/Cousine-LICENSE.txt new file mode 100644 index 000000000..75b52484e --- /dev/null +++ b/fonts/Cousine-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/fonts/Cousine-Regular.ttf b/fonts/Cousine-Regular.ttf new file mode 100644 index 000000000..4d6990a25 Binary files /dev/null and b/fonts/Cousine-Regular.ttf differ diff --git a/fonts/DroidSansFallbackFull.ttf b/fonts/DroidSansFallbackFull.ttf deleted file mode 100644 index a9df00585..000000000 Binary files a/fonts/DroidSansFallbackFull.ttf and /dev/null differ diff --git a/fonts/Tinos-LICENSE.txt b/fonts/Tinos-LICENSE.txt new file mode 100644 index 000000000..75b52484e --- /dev/null +++ b/fonts/Tinos-LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/fonts/Tinos-Regular.ttf b/fonts/Tinos-Regular.ttf new file mode 100644 index 000000000..ae5030d14 Binary files /dev/null and b/fonts/Tinos-Regular.ttf differ diff --git a/fonts/liberationmono.ttf b/fonts/liberationmono.ttf deleted file mode 100644 index 7260bd65e..000000000 Binary files a/fonts/liberationmono.ttf and /dev/null differ diff --git a/fonts/liberationsans.ttf b/fonts/liberationsans.ttf deleted file mode 100644 index 59d2e251b..000000000 Binary files a/fonts/liberationsans.ttf and /dev/null differ diff --git a/fonts/lucida_sans_10.xml b/fonts/lucida_sans_10.xml deleted file mode 100755 index d54e9f6a6..000000000 Binary files a/fonts/lucida_sans_10.xml and /dev/null differ diff --git a/fonts/lucida_sans_100.png b/fonts/lucida_sans_100.png deleted file mode 100755 index 9f3dc25c8..000000000 Binary files a/fonts/lucida_sans_100.png and /dev/null differ diff --git a/fonts/lucida_sans_11.xml b/fonts/lucida_sans_11.xml deleted file mode 100755 index 33d06c376..000000000 Binary files a/fonts/lucida_sans_11.xml and /dev/null differ diff --git a/fonts/lucida_sans_110.png b/fonts/lucida_sans_110.png deleted file mode 100755 index 1c98efcca..000000000 Binary files a/fonts/lucida_sans_110.png and /dev/null differ diff --git a/fonts/lucida_sans_12.xml b/fonts/lucida_sans_12.xml deleted file mode 100755 index 382981dcf..000000000 Binary files a/fonts/lucida_sans_12.xml and /dev/null differ diff --git a/fonts/lucida_sans_120.png b/fonts/lucida_sans_120.png deleted file mode 100755 index c106feb27..000000000 Binary files a/fonts/lucida_sans_120.png and /dev/null differ diff --git a/fonts/lucida_sans_14.xml b/fonts/lucida_sans_14.xml deleted file mode 100755 index 99398d792..000000000 Binary files a/fonts/lucida_sans_14.xml and /dev/null differ diff --git a/fonts/lucida_sans_140.png b/fonts/lucida_sans_140.png deleted file mode 100755 index d314dc5b3..000000000 Binary files a/fonts/lucida_sans_140.png and /dev/null differ diff --git a/fonts/lucida_sans_16.xml b/fonts/lucida_sans_16.xml deleted file mode 100755 index b07c1119f..000000000 Binary files a/fonts/lucida_sans_16.xml and /dev/null differ diff --git a/fonts/lucida_sans_160.png b/fonts/lucida_sans_160.png deleted file mode 100755 index 233ee9d42..000000000 Binary files a/fonts/lucida_sans_160.png and /dev/null differ diff --git a/fonts/lucida_sans_18.xml b/fonts/lucida_sans_18.xml deleted file mode 100755 index 881aff18d..000000000 Binary files a/fonts/lucida_sans_18.xml and /dev/null differ diff --git a/fonts/lucida_sans_180.png b/fonts/lucida_sans_180.png deleted file mode 100755 index 68cbe3bc8..000000000 Binary files a/fonts/lucida_sans_180.png and /dev/null differ diff --git a/fonts/lucida_sans_20.xml b/fonts/lucida_sans_20.xml deleted file mode 100755 index 329c226c0..000000000 Binary files a/fonts/lucida_sans_20.xml and /dev/null differ diff --git a/fonts/lucida_sans_200.png b/fonts/lucida_sans_200.png deleted file mode 100755 index 7b57632b8..000000000 Binary files a/fonts/lucida_sans_200.png and /dev/null differ diff --git a/fonts/lucida_sans_22.xml b/fonts/lucida_sans_22.xml deleted file mode 100755 index 14d0cc2b9..000000000 Binary files a/fonts/lucida_sans_22.xml and /dev/null differ diff --git a/fonts/lucida_sans_220.png b/fonts/lucida_sans_220.png deleted file mode 100755 index 4f385f4d4..000000000 Binary files a/fonts/lucida_sans_220.png and /dev/null differ diff --git a/fonts/lucida_sans_24.xml b/fonts/lucida_sans_24.xml deleted file mode 100755 index 5956f46e6..000000000 Binary files a/fonts/lucida_sans_24.xml and /dev/null differ diff --git a/fonts/lucida_sans_240.png b/fonts/lucida_sans_240.png deleted file mode 100755 index 8fc4e9d6b..000000000 Binary files a/fonts/lucida_sans_240.png and /dev/null differ diff --git a/fonts/lucida_sans_26.xml b/fonts/lucida_sans_26.xml deleted file mode 100755 index ae10ff8d7..000000000 Binary files a/fonts/lucida_sans_26.xml and /dev/null differ diff --git a/fonts/lucida_sans_260.png b/fonts/lucida_sans_260.png deleted file mode 100755 index 9bae8de5b..000000000 Binary files a/fonts/lucida_sans_260.png and /dev/null differ diff --git a/fonts/lucida_sans_28.xml b/fonts/lucida_sans_28.xml deleted file mode 100755 index c1b3361d3..000000000 Binary files a/fonts/lucida_sans_28.xml and /dev/null differ diff --git a/fonts/lucida_sans_280.png b/fonts/lucida_sans_280.png deleted file mode 100755 index 527fa3734..000000000 Binary files a/fonts/lucida_sans_280.png and /dev/null differ diff --git a/fonts/lucida_sans_36.xml b/fonts/lucida_sans_36.xml deleted file mode 100755 index ca300d6de..000000000 Binary files a/fonts/lucida_sans_36.xml and /dev/null differ diff --git a/fonts/lucida_sans_360.png b/fonts/lucida_sans_360.png deleted file mode 100755 index eecf35e33..000000000 Binary files a/fonts/lucida_sans_360.png and /dev/null differ diff --git a/fonts/lucida_sans_4.xml b/fonts/lucida_sans_4.xml deleted file mode 100755 index edc5af951..000000000 Binary files a/fonts/lucida_sans_4.xml and /dev/null differ diff --git a/fonts/lucida_sans_40.png b/fonts/lucida_sans_40.png deleted file mode 100755 index 5c10c1ed3..000000000 Binary files a/fonts/lucida_sans_40.png and /dev/null differ diff --git a/fonts/lucida_sans_48.xml b/fonts/lucida_sans_48.xml deleted file mode 100755 index c8c75974d..000000000 Binary files a/fonts/lucida_sans_48.xml and /dev/null differ diff --git a/fonts/lucida_sans_480.png b/fonts/lucida_sans_480.png deleted file mode 100755 index 928a0d723..000000000 Binary files a/fonts/lucida_sans_480.png and /dev/null differ diff --git a/fonts/lucida_sans_56.xml b/fonts/lucida_sans_56.xml deleted file mode 100755 index 9f2cc831e..000000000 Binary files a/fonts/lucida_sans_56.xml and /dev/null differ diff --git a/fonts/lucida_sans_560.png b/fonts/lucida_sans_560.png deleted file mode 100755 index 7afb09912..000000000 Binary files a/fonts/lucida_sans_560.png and /dev/null differ diff --git a/fonts/lucida_sans_6.xml b/fonts/lucida_sans_6.xml deleted file mode 100755 index 069c9aa80..000000000 Binary files a/fonts/lucida_sans_6.xml and /dev/null differ diff --git a/fonts/lucida_sans_60.png b/fonts/lucida_sans_60.png deleted file mode 100755 index 0efcd9e32..000000000 Binary files a/fonts/lucida_sans_60.png and /dev/null differ diff --git a/fonts/lucida_sans_8.xml b/fonts/lucida_sans_8.xml deleted file mode 100755 index 44f0e6a74..000000000 Binary files a/fonts/lucida_sans_8.xml and /dev/null differ diff --git a/fonts/lucida_sans_80.png b/fonts/lucida_sans_80.png deleted file mode 100755 index 287c65663..000000000 Binary files a/fonts/lucida_sans_80.png and /dev/null differ diff --git a/fonts/lucida_sans_9.xml b/fonts/lucida_sans_9.xml deleted file mode 100755 index 99aa6447c..000000000 Binary files a/fonts/lucida_sans_9.xml and /dev/null differ diff --git a/fonts/lucida_sans_90.png b/fonts/lucida_sans_90.png deleted file mode 100755 index 822a465f2..000000000 Binary files a/fonts/lucida_sans_90.png and /dev/null differ diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index bdf1f92ca..4e4c2f92c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -214,11 +214,11 @@ void set_default_settings(Settings *settings) #if USE_FREETYPE settings->setDefault("freetype", "true"); - settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "liberationsans.ttf")); + settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "Arimo-Regular.ttf")); settings->setDefault("font_shadow", "1"); settings->setDefault("font_shadow_alpha", "127"); - settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "liberationmono.ttf")); - settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "DroidSansFallbackFull.ttf")); + settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "Cousine-Regular.ttf")); + settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "Tinos-Regular.ttf")); settings->setDefault("fallback_font_shadow", "1"); settings->setDefault("fallback_font_shadow_alpha", "128"); @@ -228,7 +228,7 @@ void set_default_settings(Settings *settings) settings->setDefault("fallback_font_size", font_size_str); #else settings->setDefault("freetype", "false"); - settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "lucida_sans")); + settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "mono_dejavu_sans")); settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "mono_dejavu_sans")); std::string font_size_str = std::to_string(DEFAULT_FONT_SIZE); -- cgit v1.2.3 From a5e4273575c7a82459c4f9025ee19ee57eed4698 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 15 Feb 2017 17:36:47 +0100 Subject: Fix >5 year old PlayerSAO deletion bug force_delete=true is usually set at shutdown in order to also remove PlayerSAOs, however when too many objects per block are detected force_delete is also set to true. This was intended only for the current loop iteration but obviously persisted to the next iterations thereby deleting all other remaining SAOs. --- src/serverenvironment.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 8d86a4e0a..61faaace7 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1941,11 +1941,14 @@ void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s) If block wasn't generated (not in memory or on disk), */ -void ServerEnvironment::deactivateFarObjects(bool force_delete) +void ServerEnvironment::deactivateFarObjects(bool _force_delete) { std::vector objects_to_remove; for(ActiveObjectMap::iterator i = m_active_objects.begin(); i != m_active_objects.end(); ++i) { + // force_delete might be overriden per object + bool force_delete = _force_delete; + ServerActiveObject* obj = i->second; assert(obj); @@ -2147,13 +2150,6 @@ void ServerEnvironment::deactivateFarObjects(bool force_delete) continue; } - if (!force_delete && obj->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - warningstream << "ServerEnvironment::deactivateFarObjects(): " - << "Trying to delete player object, THIS SHOULD NEVER HAPPEN!" - << std::endl; - continue; - } - verbosestream<<"ServerEnvironment::deactivateFarObjects(): " <<"object id="< Date: Wed, 15 Feb 2017 19:14:31 +0000 Subject: Fix a small regression caused by e2ad76f. --- src/client/clientlauncher.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 1ac67a1de..2adac53c2 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -411,8 +411,6 @@ bool ClientLauncher::launch_game(std::string &error_message, playername = menudata.name; password = menudata.password; - g_settings->set("name", playername); - current_playername = playername; current_password = password; current_address = address; @@ -425,13 +423,16 @@ bool ClientLauncher::launch_game(std::string &error_message, current_password = ""; current_address = ""; current_port = myrand_range(49152, 65535); - } else if (address != "") { - ServerListSpec server; - server["name"] = menudata.servername; - server["address"] = menudata.address; - server["port"] = menudata.port; - server["description"] = menudata.serverdescription; - ServerList::insert(server); + } else { + g_settings->set("name", playername); + if (address != "") { + ServerListSpec server; + server["name"] = menudata.servername; + server["address"] = menudata.address; + server["port"] = menudata.port; + server["description"] = menudata.serverdescription; + ServerList::insert(server); + } } infostream << "Selected world: " << worldspec.name -- cgit v1.2.3 From 3955f512538ca7c6f2d2187f22d5a696da8e84d3 Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 13 Feb 2017 04:37:25 +0000 Subject: Objectpos over limit: Avoid crash caused by sector over limit Reduce the object limit by mapblock size, to avoid objects being added just inside the map generation limit but in a block and sector that extend beyond the map generation limit. Change notification of 'objectpos over limit' from red in-chat ERROR to in-terminal only WARNING, since this will happen often using mob mods near the world's edge. --- src/mapblock.h | 20 ++++++++++++-------- src/serverenvironment.cpp | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/mapblock.h b/src/mapblock.h index 5a0ec937a..bec3b6f56 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -669,14 +669,18 @@ typedef std::vector MapBlockVect; inline bool objectpos_over_limit(v3f p) { - const float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")) * BS; - return (p.X < -map_gen_limit_bs - || p.X > map_gen_limit_bs - || p.Y < -map_gen_limit_bs - || p.Y > map_gen_limit_bs - || p.Z < -map_gen_limit_bs - || p.Z > map_gen_limit_bs); + // MAP_BLOCKSIZE must be subtracted to avoid an object being spawned just + // within the map generation limit but in a block and sector that extend + // beyond the map generation limit. + // This avoids crashes caused by sector over limit in createSector(). + const float object_limit = (MYMIN(MAX_MAP_GENERATION_LIMIT, + g_settings->getU16("map_generation_limit")) - MAP_BLOCKSIZE) * BS; + return (p.X < -object_limit + || p.X > object_limit + || p.Y < -object_limit + || p.Y > object_limit + || p.Z < -object_limit + || p.Z > object_limit); } /* diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 61faaace7..f3f489092 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1667,7 +1667,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, if (objectpos_over_limit(object->getBasePosition())) { v3f p = object->getBasePosition(); - errorstream << "ServerEnvironment::addActiveObjectRaw(): " + warningstream << "ServerEnvironment::addActiveObjectRaw(): " << "object position (" << p.X << "," << p.Y << "," << p.Z << ") outside maximum range" << std::endl; if (object->environmentDeletes()) -- cgit v1.2.3 From 9aabfd5188ce42c98f5ccbe275776dee5ace057d Mon Sep 17 00:00:00 2001 From: paramat Date: Wed, 15 Feb 2017 00:14:11 +0000 Subject: Cavegen: Place correct biome surface in tunnel entrances Previously in tunnel entrance floors only a single biome 'top' node was placed and 'filler' nodes were missing. Place 'top' and 'filler' nodes in tunnel entrance floors with depths defined by the biome. In tunnel entrances under rivers 'riverbed' nodes are placed to the biome-defined depth. --- src/cavegen.cpp | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/cavegen.cpp b/src/cavegen.cpp index bb6aa25a6..1005640c1 100644 --- a/src/cavegen.cpp +++ b/src/cavegen.cpp @@ -74,19 +74,23 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); v3s16 em = vm->m_area.getExtent(); - u32 index2d = 0; + u32 index2d = 0; // Biomemap index for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { bool column_is_open = false; // Is column open to overground bool is_under_river = false; // Is column under river water - bool is_tunnel = false; // Is tunnel or tunnel floor + bool is_under_tunnel = false; // Is tunnel or is under tunnel + // Indexes at column top u32 vi = vm->m_area.index(x, nmax.Y, z); u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride + - (x - nmin.X); + (x - nmin.X); // 3D noise index // Biome of column Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]); - + u16 depth_top = biome->depth_top; + u16 base_filler = depth_top + biome->depth_filler; + u16 depth_riverbed = biome->depth_riverbed; + u16 nplaced = 0; // Don't excavate the overgenerated stone at nmax.Y + 1, // this creates a 'roof' over the tunnel, preventing light in // tunnels at mapchunk borders when generating mapchunks upwards. @@ -112,20 +116,34 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) { // In tunnel and ground content, excavate vm->m_data[vi] = MapNode(CONTENT_AIR); - is_tunnel = true; - } else { - // Not in tunnel or not ground content - if (is_tunnel && column_is_open && - (c == biome->c_filler || c == biome->c_stone)) { - // Tunnel entrance floor - if (is_under_river) + is_under_tunnel = true; + } else if (column_is_open && is_under_tunnel && + (c == biome->c_stone || c == biome->c_filler)) { + // Tunnel entrance floor, place biome surface nodes + if (is_under_river) { + if (nplaced < depth_riverbed) { vm->m_data[vi] = MapNode(biome->c_riverbed); - else - vm->m_data[vi] = MapNode(biome->c_top); + nplaced++; + } else { + // Disable top/filler placement + column_is_open = false; + is_under_river = false; + is_under_tunnel = false; + } + } else if (nplaced < depth_top) { + vm->m_data[vi] = MapNode(biome->c_top); + nplaced++; + } else if (nplaced < base_filler) { + vm->m_data[vi] = MapNode(biome->c_filler); + nplaced++; + } else { + // Disable top/filler placement + column_is_open = false; + is_under_tunnel = false; } - + } else { + // Not tunnel or tunnel entrance floor column_is_open = false; - is_tunnel = false; } } } -- cgit v1.2.3 From d0ce27edd8e9a30330c81a69ccca52b02056c286 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Tue, 14 Feb 2017 15:01:25 -0800 Subject: Revert part of eb49009d023e6e3b5d59a97b8fb5fed5eee83296 (#5230) This reverts the removal of Droid Sans as fallback font. The license for this font used to be GPL2. I've updated the font files to Liberation 2.00, which are SIL, and do not require us to ship source code. I've attempted to fix all the attribution and license strings, and used the strings as provided by redhat for attribution to make sure they're correct. Last, I've removed a bunch of executable bits on files that do not need them. Fixes #5231 --- README.txt | 17 +-- fonts/Arimo-LICENSE.txt | 204 +------------------------------- fonts/Cousine-LICENSE.txt | 204 +------------------------------- fonts/DroidSansFallbackFull-LICENSE.txt | 13 ++ fonts/DroidSansFallbackFull.ttf | Bin 0 -> 4529044 bytes fonts/Tinos-LICENSE.txt | 202 ------------------------------- fonts/Tinos-Regular.ttf | Bin 475996 -> 0 bytes fonts/mono_dejavu_sans_10.xml | Bin fonts/mono_dejavu_sans_100.png | Bin fonts/mono_dejavu_sans_11.xml | Bin fonts/mono_dejavu_sans_110.png | Bin fonts/mono_dejavu_sans_12.xml | Bin fonts/mono_dejavu_sans_120.png | Bin fonts/mono_dejavu_sans_14.xml | Bin fonts/mono_dejavu_sans_140.png | Bin fonts/mono_dejavu_sans_16.xml | Bin fonts/mono_dejavu_sans_160.png | Bin fonts/mono_dejavu_sans_18.xml | Bin fonts/mono_dejavu_sans_180.png | Bin fonts/mono_dejavu_sans_20.xml | Bin fonts/mono_dejavu_sans_200.png | Bin fonts/mono_dejavu_sans_22.xml | Bin fonts/mono_dejavu_sans_220.png | Bin fonts/mono_dejavu_sans_24.xml | Bin fonts/mono_dejavu_sans_240.png | Bin fonts/mono_dejavu_sans_26.xml | Bin fonts/mono_dejavu_sans_260.png | Bin fonts/mono_dejavu_sans_28.xml | Bin fonts/mono_dejavu_sans_280.png | Bin fonts/mono_dejavu_sans_4.xml | Bin fonts/mono_dejavu_sans_40.png | Bin fonts/mono_dejavu_sans_6.xml | Bin fonts/mono_dejavu_sans_60.png | Bin fonts/mono_dejavu_sans_8.xml | Bin fonts/mono_dejavu_sans_80.png | Bin fonts/mono_dejavu_sans_9.xml | Bin fonts/mono_dejavu_sans_90.png | Bin src/defaultsettings.cpp | 2 +- 38 files changed, 23 insertions(+), 619 deletions(-) create mode 100644 fonts/DroidSansFallbackFull-LICENSE.txt create mode 100644 fonts/DroidSansFallbackFull.ttf delete mode 100644 fonts/Tinos-LICENSE.txt delete mode 100644 fonts/Tinos-Regular.ttf mode change 100755 => 100644 fonts/mono_dejavu_sans_10.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_100.png mode change 100755 => 100644 fonts/mono_dejavu_sans_11.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_110.png mode change 100755 => 100644 fonts/mono_dejavu_sans_12.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_120.png mode change 100755 => 100644 fonts/mono_dejavu_sans_14.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_140.png mode change 100755 => 100644 fonts/mono_dejavu_sans_16.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_160.png mode change 100755 => 100644 fonts/mono_dejavu_sans_18.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_180.png mode change 100755 => 100644 fonts/mono_dejavu_sans_20.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_200.png mode change 100755 => 100644 fonts/mono_dejavu_sans_22.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_220.png mode change 100755 => 100644 fonts/mono_dejavu_sans_24.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_240.png mode change 100755 => 100644 fonts/mono_dejavu_sans_26.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_260.png mode change 100755 => 100644 fonts/mono_dejavu_sans_28.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_280.png mode change 100755 => 100644 fonts/mono_dejavu_sans_4.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_40.png mode change 100755 => 100644 fonts/mono_dejavu_sans_6.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_60.png mode change 100755 => 100644 fonts/mono_dejavu_sans_8.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_80.png mode change 100755 => 100644 fonts/mono_dejavu_sans_9.xml mode change 100755 => 100644 fonts/mono_dejavu_sans_90.png diff --git a/README.txt b/README.txt index 4c02763ca..0727d10ea 100644 --- a/README.txt +++ b/README.txt @@ -527,25 +527,18 @@ THE SOFTWARE. Fonts --------------- -DejaVu Sans Mono: - - Fonts are (c) Bitstream (see below). DejaVu changes are in public domain. - Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below) - Bitstream Vera Fonts Copyright: Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is a trademark of Bitstream, Inc. -Arev Fonts Copyright: - - Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved. - -Liberation Fonts Copyright: +Arimo - Apache License, version 2.0 + Digitized data copyright (c) 2010-2012 Google Corporation. - Copyright (c) 2007 Red Hat, Inc. All rights reserved. LIBERATION is a trademark of Red Hat, Inc. +Cousine - Apache License, version 2.0 + Digitized data copyright (c) 2010-2012 Google Corporation. -DroidSansFallback: +DroidSansFallBackFull: Copyright (C) 2008 The Android Open Source Project diff --git a/fonts/Arimo-LICENSE.txt b/fonts/Arimo-LICENSE.txt index 75b52484e..d68578f6a 100644 --- a/fonts/Arimo-LICENSE.txt +++ b/fonts/Arimo-LICENSE.txt @@ -1,202 +1,2 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Arimo - Apache License, version 2.0 +Arimo-Regular.ttf: Digitized data copyright (c) 2010-2012 Google Corporation. diff --git a/fonts/Cousine-LICENSE.txt b/fonts/Cousine-LICENSE.txt index 75b52484e..c84465f01 100644 --- a/fonts/Cousine-LICENSE.txt +++ b/fonts/Cousine-LICENSE.txt @@ -1,202 +1,2 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +Cousine - Apache License, version 2.0 +Cousine-Regular.ttf: Digitized data copyright (c) 2010-2012 Google Corporation. diff --git a/fonts/DroidSansFallbackFull-LICENSE.txt b/fonts/DroidSansFallbackFull-LICENSE.txt new file mode 100644 index 000000000..d3a2eaa8c --- /dev/null +++ b/fonts/DroidSansFallbackFull-LICENSE.txt @@ -0,0 +1,13 @@ +Copyright (C) 2008 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/fonts/DroidSansFallbackFull.ttf b/fonts/DroidSansFallbackFull.ttf new file mode 100644 index 000000000..a9df00585 Binary files /dev/null and b/fonts/DroidSansFallbackFull.ttf differ diff --git a/fonts/Tinos-LICENSE.txt b/fonts/Tinos-LICENSE.txt deleted file mode 100644 index 75b52484e..000000000 --- a/fonts/Tinos-LICENSE.txt +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/fonts/Tinos-Regular.ttf b/fonts/Tinos-Regular.ttf deleted file mode 100644 index ae5030d14..000000000 Binary files a/fonts/Tinos-Regular.ttf and /dev/null differ diff --git a/fonts/mono_dejavu_sans_10.xml b/fonts/mono_dejavu_sans_10.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_100.png b/fonts/mono_dejavu_sans_100.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_11.xml b/fonts/mono_dejavu_sans_11.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_110.png b/fonts/mono_dejavu_sans_110.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_12.xml b/fonts/mono_dejavu_sans_12.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_120.png b/fonts/mono_dejavu_sans_120.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_14.xml b/fonts/mono_dejavu_sans_14.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_140.png b/fonts/mono_dejavu_sans_140.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_16.xml b/fonts/mono_dejavu_sans_16.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_160.png b/fonts/mono_dejavu_sans_160.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_18.xml b/fonts/mono_dejavu_sans_18.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_180.png b/fonts/mono_dejavu_sans_180.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_20.xml b/fonts/mono_dejavu_sans_20.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_200.png b/fonts/mono_dejavu_sans_200.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_22.xml b/fonts/mono_dejavu_sans_22.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_220.png b/fonts/mono_dejavu_sans_220.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_24.xml b/fonts/mono_dejavu_sans_24.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_240.png b/fonts/mono_dejavu_sans_240.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_26.xml b/fonts/mono_dejavu_sans_26.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_260.png b/fonts/mono_dejavu_sans_260.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_28.xml b/fonts/mono_dejavu_sans_28.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_280.png b/fonts/mono_dejavu_sans_280.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_4.xml b/fonts/mono_dejavu_sans_4.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_40.png b/fonts/mono_dejavu_sans_40.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_6.xml b/fonts/mono_dejavu_sans_6.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_60.png b/fonts/mono_dejavu_sans_60.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_8.xml b/fonts/mono_dejavu_sans_8.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_80.png b/fonts/mono_dejavu_sans_80.png old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_9.xml b/fonts/mono_dejavu_sans_9.xml old mode 100755 new mode 100644 diff --git a/fonts/mono_dejavu_sans_90.png b/fonts/mono_dejavu_sans_90.png old mode 100755 new mode 100644 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 4e4c2f92c..b333ff087 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -218,7 +218,7 @@ void set_default_settings(Settings *settings) settings->setDefault("font_shadow", "1"); settings->setDefault("font_shadow_alpha", "127"); settings->setDefault("mono_font_path", porting::getDataPath("fonts" DIR_DELIM "Cousine-Regular.ttf")); - settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "Tinos-Regular.ttf")); + settings->setDefault("fallback_font_path", porting::getDataPath("fonts" DIR_DELIM "DroidSansFallbackFull.ttf")); settings->setDefault("fallback_font_shadow", "1"); settings->setDefault("fallback_font_shadow_alpha", "128"); -- cgit v1.2.3 From 3d25914986845dcb789d372ec7c20d15e310572a Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 18 Feb 2017 11:16:11 +0000 Subject: Add support for the new arguments of `request_shutdown` to the `/shutdown` chatcommand. (#5252) --- builtin/game/chatcommands.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 08dc1bb1d..54cd6c325 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -836,11 +836,13 @@ core.register_chatcommand("days", { core.register_chatcommand("shutdown", { description = "Shutdown server", + params = "[reconnect] [message]", privs = {server=true}, func = function(name, param) core.log("action", name .. " shuts down server") - core.request_shutdown() core.chat_send_all("*** Server shutting down (operator request).") + local reconnect, message = param:match("([^ ]+)(.*)") + core.request_shutdown(message:trim(), minetest.is_yes(reconnect)) end, }) -- cgit v1.2.3 From d988f9b7694de2155425fa109523bb6f44d643ea Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 18 Feb 2017 14:24:49 +0000 Subject: Use the `ARRLEN` macro in more places and remove an unused macro. (#5260) --- src/clientiface.h | 1 - src/keycode.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/clientiface.h b/src/clientiface.h index 551d71bbe..f002f37fb 100644 --- a/src/clientiface.h +++ b/src/clientiface.h @@ -165,7 +165,6 @@ namespace con { class Connection; } -#define CI_ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) // Also make sure to update the ClientInterface::statenames // array when modifying these enums diff --git a/src/keycode.cpp b/src/keycode.cpp index 2e211ad59..66708fb19 100644 --- a/src/keycode.cpp +++ b/src/keycode.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "util/hex.h" #include "util/string.h" +#include "util/basic_macros.h" class UnknownKeycode : public BaseException { @@ -242,11 +243,10 @@ static const struct table_key table[] = { #undef N_ -#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0])) struct table_key lookup_keyname(const char *name) { - for (u16 i = 0; i < ARRAYSIZE(table); i++) { + for (u16 i = 0; i < ARRLEN(table); i++) { if (strcmp(table[i].Name, name) == 0) return table[i]; } @@ -256,7 +256,7 @@ struct table_key lookup_keyname(const char *name) struct table_key lookup_keykey(irr::EKEY_CODE key) { - for (u16 i = 0; i < ARRAYSIZE(table); i++) { + for (u16 i = 0; i < ARRLEN(table); i++) { if (table[i].Key == key) return table[i]; } @@ -268,7 +268,7 @@ struct table_key lookup_keykey(irr::EKEY_CODE key) struct table_key lookup_keychar(wchar_t Char) { - for (u16 i = 0; i < ARRAYSIZE(table); i++) { + for (u16 i = 0; i < ARRLEN(table); i++) { if (table[i].Char == Char) return table[i]; } -- cgit v1.2.3 From 5db41d4d213333fc39845e7b752d7976a24da6af Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 18 Feb 2017 14:36:29 +0000 Subject: Fix not being able to damage players in minimal (#5266) --- games/minimal/mods/default/init.lua | 61 ++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index 2f73b53ef..d36e225a3 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -39,10 +39,15 @@ minetest.register_item(":", { crumbly = {times={[2]=3.00, [3]=0.70}, uses=0, maxlevel=1}, snappy = {times={[3]=0.40}, uses=0, maxlevel=1}, oddly_breakable_by_hand = {times={[1]=7.00,[2]=4.00,[3]=1.40}, uses=0, maxlevel=3}, - } + }, + damage_groups = {fleshy=1}, } }) +-- +-- Picks +-- + minetest.register_tool("default:pick_wood", { description = "Wooden Pickaxe", inventory_image = "default_tool_woodpick.png", @@ -50,7 +55,8 @@ minetest.register_tool("default:pick_wood", { max_drop_level=0, groupcaps={ cracky={times={[2]=2.00, [3]=1.20}, uses=10, maxlevel=1} - } + }, + damage_groups = {fleshy=2}, }, }) minetest.register_tool("default:pick_stone", { @@ -60,7 +66,8 @@ minetest.register_tool("default:pick_stone", { max_drop_level=0, groupcaps={ cracky={times={[1]=2.00, [2]=1.20, [3]=0.80}, uses=20, maxlevel=1} - } + }, + damage_groups = {fleshy=3}, }, }) minetest.register_tool("default:pick_steel", { @@ -70,7 +77,8 @@ minetest.register_tool("default:pick_steel", { max_drop_level=1, groupcaps={ cracky={times={[1]=4.00, [2]=1.60, [3]=1.00}, uses=10, maxlevel=2} - } + }, + damage_groups = {fleshy=4}, }, }) minetest.register_tool("default:pick_mese", { @@ -83,9 +91,15 @@ minetest.register_tool("default:pick_mese", { cracky={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}, crumbly={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3}, snappy={times={[1]=2.0, [2]=1.0, [3]=0.5}, uses=20, maxlevel=3} - } + }, + damage_groups = {fleshy=4}, }, }) + +-- +-- Shovels +-- + minetest.register_tool("default:shovel_wood", { description = "Wooden Shovel", inventory_image = "default_tool_woodshovel.png", @@ -93,7 +107,8 @@ minetest.register_tool("default:shovel_wood", { max_drop_level=0, groupcaps={ crumbly={times={[1]=2.00, [2]=0.80, [3]=0.50}, uses=10, maxlevel=1} - } + }, + damage_groups = {fleshy=2}, }, }) minetest.register_tool("default:shovel_stone", { @@ -103,7 +118,8 @@ minetest.register_tool("default:shovel_stone", { max_drop_level=0, groupcaps={ crumbly={times={[1]=1.20, [2]=0.50, [3]=0.30}, uses=20, maxlevel=1} - } + }, + damage_groups = {fleshy=3}, }, }) minetest.register_tool("default:shovel_steel", { @@ -113,9 +129,15 @@ minetest.register_tool("default:shovel_steel", { max_drop_level=1, groupcaps={ crumbly={times={[1]=1.00, [2]=0.70, [3]=0.60}, uses=10, maxlevel=2} - } + }, + damage_groups = {fleshy=4}, }, }) + +-- +-- Axes +-- + minetest.register_tool("default:axe_wood", { description = "Wooden Axe", inventory_image = "default_tool_woodaxe.png", @@ -124,7 +146,8 @@ minetest.register_tool("default:axe_wood", { groupcaps={ choppy={times={[2]=1.40, [3]=0.80}, uses=10, maxlevel=1}, fleshy={times={[2]=1.50, [3]=0.80}, uses=10, maxlevel=1} - } + }, + damage_groups = {fleshy=2}, }, }) minetest.register_tool("default:axe_stone", { @@ -135,7 +158,8 @@ minetest.register_tool("default:axe_stone", { groupcaps={ choppy={times={[1]=1.50, [2]=1.00, [3]=0.60}, uses=20, maxlevel=1}, fleshy={times={[2]=1.30, [3]=0.70}, uses=20, maxlevel=1} - } + }, + damage_groups = {fleshy=3}, }, }) minetest.register_tool("default:axe_steel", { @@ -146,9 +170,15 @@ minetest.register_tool("default:axe_steel", { groupcaps={ choppy={times={[1]=2.00, [2]=1.60, [3]=1.00}, uses=10, maxlevel=2}, fleshy={times={[2]=1.10, [3]=0.60}, uses=40, maxlevel=1} - } + }, + damage_groups = {fleshy=3}, }, }) + +-- +-- Swords +-- + minetest.register_tool("default:sword_wood", { description = "Wooden Sword", inventory_image = "default_tool_woodsword.png", @@ -159,7 +189,8 @@ minetest.register_tool("default:sword_wood", { fleshy={times={[2]=1.10, [3]=0.60}, uses=10, maxlevel=1}, snappy={times={[2]=1.00, [3]=0.50}, uses=10, maxlevel=1}, choppy={times={[3]=1.00}, uses=20, maxlevel=0} - } + }, + damage_groups = {fleshy=2}, } }) minetest.register_tool("default:sword_stone", { @@ -172,7 +203,8 @@ minetest.register_tool("default:sword_stone", { fleshy={times={[2]=0.80, [3]=0.40}, uses=20, maxlevel=1}, snappy={times={[2]=0.80, [3]=0.40}, uses=20, maxlevel=1}, choppy={times={[3]=0.90}, uses=20, maxlevel=0} - } + }, + damage_groups = {fleshy=4}, } }) minetest.register_tool("default:sword_steel", { @@ -185,7 +217,8 @@ minetest.register_tool("default:sword_steel", { fleshy={times={[1]=2.00, [2]=0.80, [3]=0.40}, uses=10, maxlevel=2}, snappy={times={[2]=0.70, [3]=0.30}, uses=40, maxlevel=1}, choppy={times={[3]=0.70}, uses=40, maxlevel=0} - } + }, + damage_groups = {fleshy=6}, } }) -- cgit v1.2.3 From e9cd7187e88b05a3e972b781cd85ef1e4f2bc10b Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Fri, 10 Feb 2017 17:40:57 +0000 Subject: Statbars.lua: Cache enable_damage setting --- builtin/game/statbars.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/builtin/game/statbars.lua b/builtin/game/statbars.lua index 61a8b9077..4e7781e53 100644 --- a/builtin/game/statbars.lua +++ b/builtin/game/statbars.lua @@ -1,3 +1,5 @@ +-- cache setting +local enable_damage = core.setting_getbool("enable_damage") == true local health_bar_definition = { @@ -42,9 +44,8 @@ local function initialize_builtin_statbars(player) player:hud_set_flags(player:hud_get_flags()) end - if player:hud_get_flags().healthbar and - core.is_yes(core.setting_get("enable_damage")) then - if hud_ids[name].id_healthbar == nil then + if player:hud_get_flags().healthbar and enable_damage then + if hud_ids[name].id_healthbar == nil then health_bar_definition.number = player:get_hp() hud_ids[name].id_healthbar = player:hud_add(health_bar_definition) end @@ -56,8 +57,7 @@ local function initialize_builtin_statbars(player) end if (player:get_breath() < 11) then - if player:hud_get_flags().breathbar and - core.is_yes(core.setting_get("enable_damage")) then + if player:hud_get_flags().breathbar and enable_damage then if hud_ids[name].id_breathbar == nil then hud_ids[name].id_breathbar = player:hud_add(breath_bar_definition) end -- cgit v1.2.3 From d0a6cacd51a25f75b0b2e9133514c7cf40a36805 Mon Sep 17 00:00:00 2001 From: kilbith Date: Thu, 16 Feb 2017 20:14:49 -0800 Subject: Multiplayer menu: fix attempt to open nonexistant image Since local servers and local favorites have no ping value (these are only provided by the server) we shouldn't load a broken image filename. Fixes #5238 --- builtin/mainmenu/common.lua | 3 ++- builtin/mainmenu/tab_multiplayer.lua | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/builtin/mainmenu/common.lua b/builtin/mainmenu/common.lua index 17d910e8b..57950c62c 100644 --- a/builtin/mainmenu/common.lua +++ b/builtin/mainmenu/common.lua @@ -54,7 +54,8 @@ end function image_column(tooltip, flagname) return "image,tooltip=" .. core.formspec_escape(tooltip) .. "," .. "0=" .. core.formspec_escape(defaulttexturedir .. "blank.png") .. "," .. - "1=" .. core.formspec_escape(defaulttexturedir .. "server_flags_" .. flagname .. ".png") .. "," .. + "1=" .. core.formspec_escape(defaulttexturedir .. + (flagname and "server_flags_" .. flagname .. ".png" or "blank.png")) .. "," .. "2=" .. core.formspec_escape(defaulttexturedir .. "server_ping_4.png") .. "," .. "3=" .. core.formspec_escape(defaulttexturedir .. "server_ping_3.png") .. "," .. "4=" .. core.formspec_escape(defaulttexturedir .. "server_ping_2.png") .. "," .. diff --git a/builtin/mainmenu/tab_multiplayer.lua b/builtin/mainmenu/tab_multiplayer.lua index 033ba38d8..0f4921b03 100644 --- a/builtin/mainmenu/tab_multiplayer.lua +++ b/builtin/mainmenu/tab_multiplayer.lua @@ -69,7 +69,7 @@ local function get_formspec(tabview, name, tabdata) --favourites retval = retval .. "tablecolumns[" .. image_column(fgettext("Favorite"), "favorite") .. ";" .. - image_column(fgettext("Ping"), "") .. ",padding=0.25;" .. + image_column(fgettext("Ping")) .. ",padding=0.25;" .. "color,span=3;" .. "text,align=right;" .. -- clients "text,align=center,padding=0.25;" .. -- "/" -- cgit v1.2.3 From 111e7e1cc8316e4812e85fddc579feaeedecbb58 Mon Sep 17 00:00:00 2001 From: paramat Date: Fri, 17 Feb 2017 15:50:51 +0000 Subject: Voxelmanip: Do not emerge or blit to blocks over map gen limit Placing a structure that extends into mapblocks that extend past map_gen_limit causes a crash. For example a sapling growing at the world edge which adds leaves beyond the edge, or placing a structure using the lua voxelmanip, or placing a schematic or l-system tree. Do not run the 'load_if_inexistent' block of code if the mapblock is over limit, this also marks the mapblock with the flag VMANIP_BLOCK_DATA_INEXIST which later prevents blitting back those mapblocks. This fix therefore uses existing functionality by having the same effect as the 'load_if_inexistent' bool being false. --- src/map.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.cpp b/src/map.cpp index ffba77262..a415bda96 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -3161,7 +3161,7 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, if(block_data_inexistent) { - if (load_if_inexistent) { + if (load_if_inexistent && !blockpos_over_limit(p)) { ServerMap *svrmap = (ServerMap *)m_map; block = svrmap->emergeBlock(p, false); if (block == NULL) -- cgit v1.2.3 From 00123ee04d19ecc98e0a6a9255e5697a78167360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Sat, 18 Feb 2017 20:26:19 +0100 Subject: Fixes for colorwallmounted and colorfacedir nodes Correct node placement prediction for attached colorwallmounted nodes. Correct placement direction for colorfacedir and colorwallmounted nodes. Correct detatch mechanism for attached colorwallmounted nodes. --- builtin/game/falling.lua | 3 ++- builtin/game/item.lua | 6 ++++-- src/game.cpp | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 39a74008c..5ef5289be 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -134,7 +134,8 @@ end function builtin_shared.check_attached_node(p, n) local def = core.registered_nodes[n.name] local d = {x = 0, y = 0, z = 0} - if def.paramtype2 == "wallmounted" then + if def.paramtype2 == "wallmounted" or + def.paramtype2 == "colorwallmounted" then -- The fallback vector here is in case 'wallmounted to dir' is nil due -- to voxelmanip placing a wallmounted node without resetting a -- pre-existing param2 value that is out-of-range for wallmounted. diff --git a/builtin/game/item.lua b/builtin/game/item.lua index a8dc51d61..38ef1714f 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -262,7 +262,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Calculate direction for wall mounted stuff like torches and signs if def.place_param2 ~= nil then newnode.param2 = def.place_param2 - elseif def.paramtype2 == 'wallmounted' and not param2 then + elseif (def.paramtype2 == 'wallmounted' or + def.paramtype2 == 'colorwallmounted') and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, @@ -270,7 +271,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) } newnode.param2 = core.dir_to_wallmounted(dir) -- Calculate the direction for furnaces and chests and stuff - elseif def.paramtype2 == 'facedir' and not param2 then + elseif (def.paramtype2 == 'facedir' or + def.paramtype2 == 'colorfacedir') and not param2 then local placer_pos = placer:getpos() if placer_pos then local dir = { diff --git a/src/game.cpp b/src/game.cpp index 1735737de..55b2ccec9 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -884,7 +884,8 @@ bool nodePlacementPrediction(Client &client, }; v3s16 pp; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) + if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || + nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) pp = p + wallmounted_dirs[param2]; else pp = p + v3s16(0, -1, 0); -- cgit v1.2.3 From 01b2d2c66c9095341554400ba67505b137ec1d86 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sun, 19 Feb 2017 16:11:53 +0000 Subject: Fix the documentation for `minetest.is_yes` (#5276) --- doc/lua_api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 0245ee7bd..9c96c41db 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1914,7 +1914,7 @@ Helper functions * `minetest.formspec_escape(string)`: returns a string * escapes the characters "[", "]", "\", "," and ";", which can not be used in formspecs * `minetest.is_yes(arg)` - * returns whether `arg` can be interpreted as yes + * returns true if passed 'y', 'yes', 'true' or a number that isn't zero. * `minetest.get_us_time()` * returns time with microsecond precision. May not return wall time. * `table.copy(table)`: returns a table -- cgit v1.2.3 From 2d1fca51e975a01f84fa3fd9b266435316fbe548 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 19 Feb 2017 20:57:46 +0000 Subject: Fix wrong meta key in item meta on ItemStack construction --- src/script/common/c_content.cpp | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 399aa88d0..a963856b7 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -827,29 +827,13 @@ ItemStack read_item(lua_State* L, int index,Server* srv) ItemStack istack(name, count, wear, idef); - lua_getfield(L, index, "metadata"); - - // Support old metadata format by checking type - int fieldstable = lua_gettop(L); - if (lua_istable(L, fieldstable)) { - lua_pushnil(L); - while (lua_next(L, fieldstable) != 0) { - // key at index -2 and value at index -1 - std::string key = lua_tostring(L, -2); - size_t value_len; - const char *value_cs = lua_tolstring(L, -1, &value_len); - std::string value(value_cs, value_len); - istack.metadata.setString(name, value); - lua_pop(L, 1); // removes value, keeps key for next iteration - } - } else { - // BACKWARDS COMPATIBLITY - std::string value = getstringfield_default(L, index, "metadata", ""); - istack.metadata.setString("", value); - } + // BACKWARDS COMPATIBLITY + std::string value = getstringfield_default(L, index, "metadata", ""); + istack.metadata.setString("", value); + // Get meta lua_getfield(L, index, "meta"); - fieldstable = lua_gettop(L); + int fieldstable = lua_gettop(L); if (lua_istable(L, fieldstable)) { lua_pushnil(L); while (lua_next(L, fieldstable) != 0) { @@ -858,7 +842,7 @@ ItemStack read_item(lua_State* L, int index,Server* srv) size_t value_len; const char *value_cs = lua_tolstring(L, -1, &value_len); std::string value(value_cs, value_len); - istack.metadata.setString(name, value); + istack.metadata.setString(key, value); lua_pop(L, 1); // removes value, keeps key for next iteration } } -- cgit v1.2.3 From 4d634ef675020964413020f6215529670af0091a Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 25 Feb 2017 08:28:25 +0000 Subject: Fix crash that can be caused by the shutdown command. (#5292) --- builtin/game/chatcommands.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 54cd6c325..5d5955972 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -842,7 +842,8 @@ core.register_chatcommand("shutdown", { core.log("action", name .. " shuts down server") core.chat_send_all("*** Server shutting down (operator request).") local reconnect, message = param:match("([^ ]+)(.*)") - core.request_shutdown(message:trim(), minetest.is_yes(reconnect)) + message = message or "" + core.request_shutdown(message:trim(), core.is_yes(reconnect)) end, }) -- cgit v1.2.3 From a901a56859e5beaec6a542eae037dc0b736a20e3 Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 21 Feb 2017 01:56:34 +0000 Subject: Dungeongen: Add and improve parameters Add: Bool for 'only_in_ground'. Min and max corridor length. Min and max room size with X, Y, Z components. Min and max large room size with X, Y, Z components. 'only_in_ground = false' allows core mapgens to create structures in air and water using dungeongen. Corridor length parameters replace a fixed random range. Room size parameters replace the former system where one parameter 'roomsize' was added to fixed random ranges. All parameters are set for no change to current dungeon behaviour. Remove some now-redundant and long-unused code. --- src/dungeongen.cpp | 94 ++++++++++++++++++++++++++---------------------------- src/dungeongen.h | 8 ++++- src/mapgen.cpp | 80 +++++++++++++++++++++++++++------------------- src/mapgen_v6.cpp | 63 +++++++++++++++++++++--------------- 4 files changed, 137 insertions(+), 108 deletions(-) diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp index 7825e9e2c..6cef3f88d 100644 --- a/src/dungeongen.cpp +++ b/src/dungeongen.cpp @@ -52,6 +52,7 @@ DungeonGen::DungeonGen(INodeDefManager *ndef, if (dparams) { memcpy(&dp, dparams, sizeof(dp)); } else { + // Default dungeon parameters dp.seed = 0; dp.c_water = ndef->getId("mapgen_water_source"); @@ -63,14 +64,20 @@ DungeonGen::DungeonGen(INodeDefManager *ndef, if (dp.c_river_water == CONTENT_IGNORE) dp.c_river_water = ndef->getId("mapgen_water_source"); - dp.diagonal_dirs = false; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.rooms_min = 2; - dp.rooms_max = 16; - dp.y_min = -MAX_MAP_GENERATION_LIMIT; - dp.y_max = MAX_MAP_GENERATION_LIMIT; - dp.notifytype = GENNOTIFY_DUNGEON; + dp.diagonal_dirs = false; + dp.only_in_ground = true; + dp.holesize = v3s16(1, 2, 1); + dp.corridor_len_min = 1; + dp.corridor_len_max = 13; + dp.room_size_min = v3s16(4, 4, 4); + dp.room_size_max = v3s16(8, 6, 8); + dp.room_size_large_min = v3s16(8, 8, 8); + dp.room_size_large_max = v3s16(16, 16, 16); + dp.rooms_min = 2; + dp.rooms_max = 16; + dp.y_min = -MAX_MAP_GENERATION_LIMIT; + dp.y_max = MAX_MAP_GENERATION_LIMIT; + dp.notifytype = GENNOTIFY_DUNGEON; dp.np_density = nparams_dungeon_density; dp.np_alt_wall = nparams_dungeon_alt_wall; @@ -97,16 +104,19 @@ void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax) // Dungeon generator doesn't modify places which have this set vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE); - // Set all air and water to be untouchable - // to make dungeons open to caves and open air - for (s16 z = nmin.Z; z <= nmax.Z; z++) { - for (s16 y = nmin.Y; y <= nmax.Y; y++) { - u32 i = vm->m_area.index(nmin.X, y, z); - for (s16 x = nmin.X; x <= nmax.X; x++) { - content_t c = vm->m_data[i].getContent(); - if (c == CONTENT_AIR || c == dp.c_water || c == dp.c_river_water) - vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - i++; + if (dp.only_in_ground) { + // 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 = vm->m_area.index(nmin.X, y, z); + for (s16 x = nmin.X; x <= nmax.X; x++) { + content_t c = vm->m_data[i].getContent(); + if (c == CONTENT_AIR || c == dp.c_water || + c == dp.c_river_water) + vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; + i++; + } } } } @@ -142,21 +152,25 @@ void DungeonGen::makeDungeon(v3s16 start_padding) v3s16 roomplace; /* - Find place for first room + Find place for first room. + There is a 1 in 4 chance of the first room being 'large', + all other rooms are not 'large'. */ bool fits = false; for (u32 i = 0; i < 100 && !fits; i++) { bool is_large_room = ((random.next() & 3) == 1); if (is_large_room) { - roomsize.Z = random.range(8, 16); - roomsize.Y = random.range(8, 16); - roomsize.X = random.range(8, 16); + roomsize.Z = random.range( + dp.room_size_large_min.Z, dp.room_size_large_max.Z); + roomsize.Y = random.range( + dp.room_size_large_min.Y, dp.room_size_large_max.Y); + roomsize.X = random.range( + dp.room_size_large_min.X, dp.room_size_large_max.X); } else { - roomsize.Z = random.range(4, 8); - roomsize.Y = random.range(4, 6); - roomsize.X = random.range(4, 8); + roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z); + roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y); + roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X); } - roomsize += dp.roomsize; // start_padding is used to disallow starting the generation of // a dungeon in a neighboring generation chunk @@ -246,10 +260,9 @@ void DungeonGen::makeDungeon(v3s16 start_padding) makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir); // Find a place for a random sized room - roomsize.Z = random.range(4, 8); - roomsize.Y = random.range(4, 6); - roomsize.X = random.range(4, 8); - roomsize += dp.roomsize; + roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z); + roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y); + roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X); m_pos = corridor_end; m_dir = corridor_end_dir; @@ -397,13 +410,8 @@ void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir, 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 length = random.range(dp.corridor_len_min, dp.corridor_len_max); + u32 partlength = random.range(dp.corridor_len_min, dp.corridor_len_max); u32 partcount = 0; s16 make_stairs = 0; @@ -556,7 +564,6 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, 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)); @@ -569,17 +576,6 @@ bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, 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; diff --git a/src/dungeongen.h b/src/dungeongen.h index 30786b9f3..4bd208330 100644 --- a/src/dungeongen.h +++ b/src/dungeongen.h @@ -48,8 +48,14 @@ struct DungeonParams { content_t c_stair; bool diagonal_dirs; + bool only_in_ground; v3s16 holesize; - v3s16 roomsize; + u16 corridor_len_min; + u16 corridor_len_max; + v3s16 room_size_min; + v3s16 room_size_max; + v3s16 room_size_large_min; + v3s16 room_size_large_max; u16 rooms_min; u16 rooms_max; s16 y_min; diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 00ae917a1..e1e3ccd25 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -845,47 +845,61 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) DungeonParams dp; - dp.seed = seed; - dp.c_water = c_water_source; - dp.c_river_water = c_river_water_source; - dp.rooms_min = 2; - dp.rooms_max = 16; - dp.y_min = -MAX_MAP_GENERATION_LIMIT; - dp.y_max = MAX_MAP_GENERATION_LIMIT; - dp.np_density = nparams_dungeon_density; - dp.np_alt_wall = nparams_dungeon_alt_wall; + dp.seed = seed; + dp.c_water = c_water_source; + dp.c_river_water = c_river_water_source; + + dp.only_in_ground = true; + dp.corridor_len_min = 1; + dp.corridor_len_max = 13; + dp.rooms_min = 2; + dp.rooms_max = 16; + dp.y_min = -MAX_MAP_GENERATION_LIMIT; + dp.y_max = MAX_MAP_GENERATION_LIMIT; + + dp.np_density = nparams_dungeon_density; + dp.np_alt_wall = nparams_dungeon_alt_wall; switch (stone_type) { default: case MGSTONE_STONE: - dp.c_wall = c_cobble; - dp.c_alt_wall = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; + dp.c_wall = c_cobble; + dp.c_alt_wall = c_mossycobble; + dp.c_stair = c_stair_cobble; + + dp.diagonal_dirs = false; + dp.holesize = v3s16(1, 2, 1); + dp.room_size_min = v3s16(4, 4, 4); + dp.room_size_max = v3s16(8, 6, 8); + dp.room_size_large_min = v3s16(8, 8, 8); + dp.room_size_large_max = v3s16(16, 16, 16); + dp.notifytype = GENNOTIFY_DUNGEON; break; case MGSTONE_DESERT_STONE: - dp.c_wall = c_desert_stone; - dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_stair_desert_stone; - - dp.diagonal_dirs = true; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; + dp.c_wall = c_desert_stone; + dp.c_alt_wall = CONTENT_IGNORE; + dp.c_stair = c_stair_desert_stone; + + dp.diagonal_dirs = true; + dp.holesize = v3s16(2, 3, 2); + dp.room_size_min = v3s16(6, 9, 6); + dp.room_size_max = v3s16(10, 11, 10); + dp.room_size_large_min = v3s16(10, 13, 10); + dp.room_size_large_max = v3s16(18, 21, 18); + dp.notifytype = GENNOTIFY_TEMPLE; break; case MGSTONE_SANDSTONE: - dp.c_wall = c_sandstonebrick; - dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_stair_sandstonebrick; - - dp.diagonal_dirs = false; - dp.holesize = v3s16(2, 2, 2); - dp.roomsize = v3s16(2, 0, 2); - dp.notifytype = GENNOTIFY_DUNGEON; + dp.c_wall = c_sandstonebrick; + dp.c_alt_wall = CONTENT_IGNORE; + dp.c_stair = c_stair_sandstonebrick; + + dp.diagonal_dirs = false; + dp.holesize = v3s16(2, 2, 2); + dp.room_size_min = v3s16(6, 4, 6); + dp.room_size_max = v3s16(10, 6, 10); + dp.room_size_large_min = v3s16(10, 8, 10); + dp.room_size_large_max = v3s16(18, 16, 18); + dp.notifytype = GENNOTIFY_DUNGEON; break; } diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 807bbe751..f3e893f58 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -564,34 +564,47 @@ void MapgenV6::makeChunk(BlockMakeData *data) if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) { DungeonParams dp; - dp.seed = seed; - dp.c_water = c_water_source; - dp.c_river_water = c_water_source; - dp.rooms_min = 2; - dp.rooms_max = 16; - dp.y_min = -MAX_MAP_GENERATION_LIMIT; - dp.y_max = MAX_MAP_GENERATION_LIMIT; - dp.np_density = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); - dp.np_alt_wall = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0); + dp.seed = seed; + dp.c_water = c_water_source; + dp.c_river_water = c_water_source; + + dp.only_in_ground = true; + dp.corridor_len_min = 1; + dp.corridor_len_max = 13; + dp.rooms_min = 2; + dp.rooms_max = 16; + dp.y_min = -MAX_MAP_GENERATION_LIMIT; + dp.y_max = MAX_MAP_GENERATION_LIMIT; + + dp.np_density + = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); + dp.np_alt_wall + = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0); if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) { - dp.c_wall = c_desert_stone; - dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_stair_desert_stone; - - dp.diagonal_dirs = true; - dp.holesize = v3s16(2, 3, 2); - dp.roomsize = v3s16(2, 5, 2); - dp.notifytype = GENNOTIFY_TEMPLE; + dp.c_wall = c_desert_stone; + dp.c_alt_wall = CONTENT_IGNORE; + dp.c_stair = c_stair_desert_stone; + + dp.diagonal_dirs = true; + dp.holesize = v3s16(2, 3, 2); + dp.room_size_min = v3s16(6, 9, 6); + dp.room_size_max = v3s16(10, 11, 10); + dp.room_size_large_min = v3s16(10, 13, 10); + dp.room_size_large_max = v3s16(18, 21, 18); + dp.notifytype = GENNOTIFY_TEMPLE; } else { - dp.c_wall = c_cobble; - dp.c_alt_wall = c_mossycobble; - dp.c_stair = c_stair_cobble; - - dp.diagonal_dirs = false; - dp.holesize = v3s16(1, 2, 1); - dp.roomsize = v3s16(0, 0, 0); - dp.notifytype = GENNOTIFY_DUNGEON; + dp.c_wall = c_cobble; + dp.c_alt_wall = c_mossycobble; + dp.c_stair = c_stair_cobble; + + dp.diagonal_dirs = false; + dp.holesize = v3s16(1, 2, 1); + dp.room_size_min = v3s16(4, 4, 4); + dp.room_size_max = v3s16(8, 6, 8); + dp.room_size_large_min = v3s16(8, 8, 8); + dp.room_size_large_max = v3s16(16, 16, 16); + dp.notifytype = GENNOTIFY_DUNGEON; } DungeonGen dgen(ndef, &gennotify, &dp); -- cgit v1.2.3 From 22df9593433ac97a60b7cbf64ba85a262f591758 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 27 Feb 2017 13:22:49 +0100 Subject: Fixed spelling mistakes (#5312) --- README.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.txt b/README.txt index 0727d10ea..203e79601 100644 --- a/README.txt +++ b/README.txt @@ -43,7 +43,7 @@ Default controls - 0-9: Select item - Z: Zoom (needs zoom privilege) - T: Chat -- /: Commad +- /: Command - Esc: Pause menu/abort/exit (pauses only singleplayer game) - R: Enable/disable full range view @@ -85,7 +85,7 @@ $bin = /usr/bin $share = /usr/share/minetest $user = ~/.minetest -OS X: +macOS: $bin = Contents/MacOS $share = Contents/Resources $user = Contents/User OR ~/Library/Application Support/minetest @@ -117,7 +117,7 @@ For Fedora users: $ sudo dnf install make automake gcc gcc-c++ kernel-devel cmake libcurl* openal* libvorbis* libXxf86vm-devel libogg-devel freetype-devel mesa-libGL-devel zlib-devel jsoncpp-devel irrlicht-devel bzip2-libs gmp-devel sqlite-devel luajit-devel leveldb-devel ncurses-devel doxygen spatialindex-devel bzip2-devel You can install git for easily keeping your copy up to date. -If you dont want git, read below on how to get the source without git. +If you don’t want git, read below on how to get the source without git. This is an example for installing git on Debian/Ubuntu: $ sudo apt-get install git @@ -502,7 +502,7 @@ Lua is licensed under the terms of the MIT license reproduced below. This means that Lua is free software and can be used for both academic and commercial purposes at absolutely no cost. -For details and rationale, see http://www.lua.org/license.html . +For details and rationale, see https://www.lua.org/license.html . Copyright (C) 1994-2008 Lua.org, PUC-Rio. -- cgit v1.2.3 From 6de83a2756316653ed67b23bbeb0fb43aa2a68c6 Mon Sep 17 00:00:00 2001 From: adelcoding1 Date: Sat, 4 Mar 2017 10:46:55 +0100 Subject: FormSpec: Add position and anchor elements (#5284) --- doc/lua_api.txt | 10 +++++ src/guiFormSpecMenu.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++++---- src/guiFormSpecMenu.h | 6 +++ 3 files changed, 109 insertions(+), 8 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 9c96c41db..7b956dc74 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1522,6 +1522,16 @@ examples. * `fixed_size`: `true`/`false` (optional) * deprecated: `invsize[,;]` +#### `position[,]` +* Define the position of the formspec +* A value between 0.0 and 1.0 represents a position inside the screen +* The default value is the center of the screen (0.5, 0.5) + +#### `anchor[,]` +* Define the anchor of the formspec +* A value between 0.0 and 1.0 represents an anchor inside the formspec +* The default value is the center of the formspec (0.5, 0.5) + #### `container[,]` * Start of a container block, moves all physical elements in the container by (X, Y) * Must have matching container_end diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 67b3a9ad0..ae3fad7c6 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -1706,6 +1706,74 @@ bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element) return true; } +bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &element) +{ + if (element.empty()) + return false; + + std::vector parts = split(element, '['); + + if (parts.size() != 2) + return false; + + std::string type = trim(parts[0]); + std::string description = trim(parts[1]); + + if (type != "position") + return false; + + parsePosition(data, description); + + return true; +} + +void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ','); + + if (parts.size() == 2) { + data->offset.X = stof(parts[0]); + data->offset.Y = stof(parts[1]); + return; + } + + errorstream << "Invalid position element (" << parts.size() << "): '" << element << "'" << std::endl; +} + +bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &element) +{ + if (element.empty()) + return false; + + std::vector parts = split(element, '['); + + if (parts.size() != 2) + return false; + + std::string type = trim(parts[0]); + std::string description = trim(parts[1]); + + if (type != "anchor") + return false; + + parseAnchor(data, description); + + return true; +} + +void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ','); + + if (parts.size() == 2) { + data->anchor.X = stof(parts[0]); + data->anchor.Y = stof(parts[1]); + return; + } + + errorstream << "Invalid anchor element (" << parts.size() << "): '" << element << "'" << std::endl; +} + void GUIFormSpecMenu::parseElement(parserData* data, std::string element) { //some prechecks @@ -1917,6 +1985,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) mydata.size= v2s32(100,100); mydata.screensize = screensize; + mydata.offset = v2f32(0.5f, 0.5f); + mydata.anchor = v2f32(0.5f, 0.5f); // Base position of contents of form mydata.basepos = getBasePos(); @@ -1983,6 +2053,21 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } } + /* "position" element is always after "size" element if it used */ + for (; i< elements.size(); i++) { + if (!parsePositionDirect(&mydata, elements[i])) { + break; + } + } + + /* "anchor" element is always after "position" (or "size" element) if it used */ + for (; i< elements.size(); i++) { + if (!parseAnchorDirect(&mydata, elements[i])) { + break; + } + } + + if (mydata.explicit_size) { // compute scaling for specified form size if (m_lock) { @@ -2066,10 +2151,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0 ); DesiredRect = mydata.rect = core::rect( - mydata.screensize.X/2 - mydata.size.X/2 + offset.X, - mydata.screensize.Y/2 - mydata.size.Y/2 + offset.Y, - mydata.screensize.X/2 + mydata.size.X/2 + offset.X, - mydata.screensize.Y/2 + mydata.size.Y/2 + offset.Y + (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X, + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y, + (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * (f32)mydata.size.X) + offset.X, + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * (f32)mydata.size.Y) + offset.Y ); } else { // Non-size[] form must consist only of text fields and @@ -2078,10 +2163,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_font = g_fontengine->getFont(); m_btn_height = font_line_height(m_font) * 0.875; DesiredRect = core::rect( - mydata.screensize.X/2 - 580/2, - mydata.screensize.Y/2 - 300/2, - mydata.screensize.X/2 + 580/2, - mydata.screensize.Y/2 + 300/2 + (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * 580.0), + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * 300.0), + (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * 580.0), + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * 300.0) ); } recalculateAbsolutePosition(false); diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h index 2ab7db4f1..bbab9c164 100644 --- a/src/guiFormSpecMenu.h +++ b/src/guiFormSpecMenu.h @@ -447,6 +447,8 @@ private: bool explicit_size; v2f invsize; v2s32 size; + v2f32 offset; + v2f32 anchor; core::rect rect; v2s32 basepos; v2u32 screensize; @@ -502,6 +504,10 @@ private: bool parseVersionDirect(std::string data); bool parseSizeDirect(parserData* data, std::string element); void parseScrollBar(parserData* data, std::string element); + bool parsePositionDirect(parserData *data, const std::string &element); + void parsePosition(parserData *data, const std::string &element); + bool parseAnchorDirect(parserData *data, const std::string &element); + void parseAnchor(parserData *data, const std::string &element); void tryClose(); -- cgit v1.2.3 From c9ac722ea9ab3783bf59e7cb991bfb3a91211490 Mon Sep 17 00:00:00 2001 From: zaoqi Date: Sun, 5 Mar 2017 01:36:37 +0800 Subject: Add minetest.spawn_falling_node(pos) (#5339) * Add minetest.spawn_falling_node(pos) * lua_api.txt: Add minetest.spawn_falling_node(pos) * Update minetest.spawn_falling_node(pos) --- builtin/game/falling.lua | 14 ++++++++++++++ doc/lua_api.txt | 3 +++ 2 files changed, 17 insertions(+) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 5ef5289be..764909361 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -118,6 +118,20 @@ local function spawn_falling_node(p, node) end end +function core.spawn_falling_node(pos) + local node = core.get_node(pos) + if node.name == "air" or node.name == "ignore" then + return false + end + local obj = core.add_entity(pos, "__builtin:falling_node") + if obj then + obj:get_luaentity():set_node(node) + core.remove_node(pos) + return true + end + return false +end + local function drop_attached_node(p) local nn = core.get_node(p).name core.remove_node(p) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 7b956dc74..23aac90d9 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2219,6 +2219,9 @@ and `minetest.auth_reload` call the authetification handler. * Returns `true` if successful, `false` on failure (e.g. protected location) * `minetest.punch_node(pos)` * Punch node with the same effects that a player would cause +* `minetest.spawn_falling_node(pos)` + * Change node into falling node + * Returns `true` if successful, `false` on failure * `minetest.find_nodes_with_meta(pos1, pos2)` * Get a table of positions of nodes that have metadata within a region {pos1, pos2} -- cgit v1.2.3 From 0a6e53bd31d3905b091dd91c9f284288e503fa48 Mon Sep 17 00:00:00 2001 From: Fixer Date: Thu, 16 Feb 2017 23:11:08 +0200 Subject: README.txt: Clarify loading of minetest.conf --- README.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.txt b/README.txt index 203e79601..59bf3adf8 100644 --- a/README.txt +++ b/README.txt @@ -101,7 +101,9 @@ Configuration file: $user/minetest.conf - It is created by Minetest when it is ran the first time. - A specific file can be specified on the command line: - --config + --config +- A run-in-place build will look for the configuration file in + $location_of_exe/../minetest.conf and also $location_of_exe/../../minetest.conf Command-line options: --------------------- -- cgit v1.2.3 From f1ab42fdff7a15d81c8cac0f9192b3d5ff9fb913 Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Sat, 18 Feb 2017 11:40:37 -0800 Subject: Font: attempt fallback font, abort if no fonts found. If you happen to have a font_path setting that is incorrect, minetest will just attempt to start the gui without a valid font which leads to a segfault later on. We can attempt to load the fallback font path fairly easy, but if that fails we should give up with a proper error message and not a weird segfault later. This forces an abort() if the fallback fails as well, and prints a useful error message to the console. --- src/fontengine.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/fontengine.cpp b/src/fontengine.cpp index 55ff001e7..da327c3f6 100644 --- a/src/fontengine.cpp +++ b/src/fontengine.cpp @@ -343,11 +343,31 @@ void FontEngine::initFont(unsigned int basesize, FontMode mode) if (font != NULL) { m_font_cache[mode][basesize] = font; + return; } - else { - errorstream << "FontEngine: failed to load freetype font: " - << font_path << std::endl; + + // try fallback font + errorstream << "FontEngine: failed to load: " << font_path << ", trying to fall back " + "to fallback font" << std::endl; + + font_path = g_settings->get(font_config_prefix + "fallback_font_path"); + + font = gui::CGUITTFont::createTTFont(m_env, + font_path.c_str(), size, true, true, font_shadow, + font_shadow_alpha); + + if (font != NULL) { + m_font_cache[mode][basesize] = font; + return; } + + // give up + errorstream << "FontEngine: failed to load freetype font: " + << font_path << std::endl; + errorstream << "minetest can not continue without a valid font. Please correct " + "the 'font_path' setting or install the font file in the proper " + "location" << std::endl; + abort(); } #endif } -- cgit v1.2.3 From e10e5fd16c2bd55b9b106edc6cedd07530cf103d Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 4 Mar 2017 05:10:10 +0000 Subject: Dungeons: Use 'block' instead of 'brick' for nodebox stairs Affects only sandstone dungeons. Nodebox stairs made from 'sandstone_block' look better because every step is undivided. --- src/mapgen.cpp | 18 +++++++++--------- src/mapgen.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/mapgen.cpp b/src/mapgen.cpp index e1e3ccd25..a0b9990b7 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -601,12 +601,12 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emer c_sandstone = c_stone; //// Content used for dungeon generation - c_cobble = ndef->getId("mapgen_cobble"); - c_mossycobble = ndef->getId("mapgen_mossycobble"); - c_stair_cobble = ndef->getId("mapgen_stair_cobble"); - c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone"); - c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); - c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick"); + c_cobble = ndef->getId("mapgen_cobble"); + c_mossycobble = ndef->getId("mapgen_mossycobble"); + c_stair_cobble = ndef->getId("mapgen_stair_cobble"); + c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone"); + c_sandstonebrick = ndef->getId("mapgen_sandstonebrick"); + c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block"); // Fall back to more basic content if not defined if (c_mossycobble == CONTENT_IGNORE) @@ -617,8 +617,8 @@ MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emer c_stair_desert_stone = c_desert_stone; if (c_sandstonebrick == CONTENT_IGNORE) c_sandstonebrick = c_sandstone; - if (c_stair_sandstonebrick == CONTENT_IGNORE) - c_stair_sandstonebrick = c_sandstonebrick; + if (c_stair_sandstone_block == CONTENT_IGNORE) + c_stair_sandstone_block = c_sandstonebrick; } @@ -891,7 +891,7 @@ void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type) case MGSTONE_SANDSTONE: dp.c_wall = c_sandstonebrick; dp.c_alt_wall = CONTENT_IGNORE; - dp.c_stair = c_stair_sandstonebrick; + dp.c_stair = c_stair_sandstone_block; dp.diagonal_dirs = false; dp.holesize = v3s16(2, 2, 2); diff --git a/src/mapgen.h b/src/mapgen.h index a95e1942a..7aac1e6a0 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -268,7 +268,7 @@ protected: content_t c_mossycobble; content_t c_stair_desert_stone; content_t c_sandstonebrick; - content_t c_stair_sandstonebrick; + content_t c_stair_sandstone_block; int ystride; int zstride; -- cgit v1.2.3 From 0e27b4b978d81504a9e3bf221e6e4691c720e6ef Mon Sep 17 00:00:00 2001 From: Juhani Numminen Date: Tue, 17 Jan 2017 17:26:21 +0200 Subject: Update .appdata and .desktop files --- CMakeLists.txt | 8 ++--- misc/minetest.appdata.xml | 46 --------------------------- misc/minetest.desktop | 17 ---------- misc/net.minetest.minetest.appdata.xml | 58 ++++++++++++++++++++++++++++++++++ misc/net.minetest.minetest.desktop | 17 ++++++++++ 5 files changed, 79 insertions(+), 67 deletions(-) delete mode 100644 misc/minetest.appdata.xml delete mode 100644 misc/minetest.desktop create mode 100644 misc/net.minetest.minetest.appdata.xml create mode 100644 misc/net.minetest.minetest.desktop diff --git a/CMakeLists.txt b/CMakeLists.txt index 99f54ba54..d2568a9ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,7 +82,7 @@ elseif(UNIX) # Linux, BSD etc set(EXAMPLE_CONF_DIR ".") set(MANDIR "unix/man") set(XDG_APPS_DIR "unix/applications") - set(APPDATADIR "unix/appdata") + set(APPDATADIR "unix/metainfo") set(ICONDIR "unix/icons") set(LOCALEDIR "locale") else() @@ -92,7 +92,7 @@ elseif(UNIX) # Linux, BSD etc set(MANDIR "${CMAKE_INSTALL_PREFIX}/share/man") set(EXAMPLE_CONF_DIR ${DOCDIR}) set(XDG_APPS_DIR "${CMAKE_INSTALL_PREFIX}/share/applications") - set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/appdata") + set(APPDATADIR "${CMAKE_INSTALL_PREFIX}/share/metainfo") set(ICONDIR "${CMAKE_INSTALL_PREFIX}/share/icons") set(LOCALEDIR "${CMAKE_INSTALL_PREFIX}/share/${PROJECT_NAME}/locale") endif() @@ -173,8 +173,8 @@ install(FILES "minetest.conf.example" DESTINATION "${EXAMPLE_CONF_DIR}") if(UNIX AND NOT APPLE) install(FILES "doc/minetest.6" "doc/minetestserver.6" DESTINATION "${MANDIR}/man6") - install(FILES "misc/minetest.desktop" DESTINATION "${XDG_APPS_DIR}") - install(FILES "misc/minetest.appdata.xml" DESTINATION "${APPDATADIR}") + install(FILES "misc/net.minetest.minetest.desktop" DESTINATION "${XDG_APPS_DIR}") + install(FILES "misc/net.minetest.minetest.appdata.xml" DESTINATION "${APPDATADIR}") install(FILES "misc/minetest.svg" DESTINATION "${ICONDIR}/hicolor/scalable/apps") install(FILES "misc/minetest-xorg-icon-128.png" DESTINATION "${ICONDIR}/hicolor/128x128/apps" diff --git a/misc/minetest.appdata.xml b/misc/minetest.appdata.xml deleted file mode 100644 index 65acf96ec..000000000 --- a/misc/minetest.appdata.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - minetest.desktop - CC0-1.0 - LGPL-2.1+ and CC-BY-SA-3.0 and MIT and Apache-2.0 - Minetest - Multiplayer infinite-world block sandbox game - -

- Minetest is an infinite-world block sandbox game and game engine. -

- Players can create and destroy various types of blocks in a - three-dimensional open world. This allows forming structures in - every possible creation, on multiplayer servers or in singleplayer. -

- Minetest is designed to be simple, stable, and portable. - It is lightweight enough to run on fairly old hardware. -

- Minetest has many features, including: -

-
    -
  • Ability to walk around, dig, and build in a near-infinite voxel world
  • -
  • Crafting of items from raw materials
  • -
  • Fast and able to run on old and slow hardware
  • -
  • A simple modding API that supports many additions and modifications to the game
  • -
  • Multiplayer support via servers hosted by users
  • -
  • Beautiful lightning-fast map generator
  • -
-
- - http://www.minetest.net/media/gallery/1.jpg - http://www.minetest.net/media/gallery/3.jpg - http://www.minetest.net/media/gallery/5.jpg - - - sandbox - world - mining - multiplayer - - http://minetest.net - http://www.minetest.net/development/#reporting-issues - http://dev.minetest.net/Translation - http://www.minetest.net/development/#donate - sfan5@live.de -
diff --git a/misc/minetest.desktop b/misc/minetest.desktop deleted file mode 100644 index ca493c44e..000000000 --- a/misc/minetest.desktop +++ /dev/null @@ -1,17 +0,0 @@ -[Desktop Entry] -Name=Minetest -GenericName=Minetest -Comment=Multiplayer infinite-world block sandbox -Comment[de]=Mehrspieler-Sandkastenspiel mit unendlichen Blockwelten -Comment[es]=Juego sandbox multijugador con mundos infinitos -Comment[fr]=Jeu multijoueurs de type bac à sable avec des mondes infinis -Comment[ja]=マルチプレイに対応した、無限の世界のブロック型サンドボックスゲームです -Comment[ru]=Игра-песочница с безграничным миром, состоящим из блоков -Comment[tr]=Tek-Çok oyuncuyla küplerden sonsuz dünyalar inşa et -Exec=minetest -Icon=minetest -Terminal=false -Type=Application -Categories=Game;Simulation; -StartupNotify=false -Keywords=sandbox;world;mining;crafting;blocks;nodes;multiplayer;roleplaying; diff --git a/misc/net.minetest.minetest.appdata.xml b/misc/net.minetest.minetest.appdata.xml new file mode 100644 index 000000000..277225d4b --- /dev/null +++ b/misc/net.minetest.minetest.appdata.xml @@ -0,0 +1,58 @@ + + + net.minetest.minetest.desktop + CC0-1.0 + LGPL-2.1+ and CC-BY-SA-3.0 and MIT and Apache-2.0 + Minetest + Multiplayer infinite-world block sandbox game + +

+ Minetest is an infinite-world block sandbox game and game engine. +

+ Players can create and destroy various types of blocks in a + three-dimensional open world. This allows forming structures in + every possible creation, on multiplayer servers or in singleplayer. +

+ Minetest is designed to be simple, stable, and portable. + It is lightweight enough to run on fairly old hardware. +

+ Minetest has many features, including: +

+
    +
  • Ability to walk around, dig, and build in a near-infinite voxel world
  • +
  • Crafting of items from raw materials
  • +
  • Fast and able to run on old and slow hardware
  • +
  • A simple modding API that supports many additions and modifications to the game
  • +
  • Multiplayer support via servers hosted by users
  • +
  • Beautiful lightning-fast map generator
  • +
+
+ + + http://www.minetest.net/media/gallery/1.jpg + + + http://www.minetest.net/media/gallery/3.jpg + + + http://www.minetest.net/media/gallery/5.jpg + + + + sandbox + world + mining + multiplayer + + http://minetest.net + http://www.minetest.net/development/#reporting-issues + http://dev.minetest.net/Translation + http://www.minetest.net/development/#donate + http://wiki.minetest.net/FAQ + http://wiki.minetest.net + + minetest + + minetest + sfan5@live.de +
diff --git a/misc/net.minetest.minetest.desktop b/misc/net.minetest.minetest.desktop new file mode 100644 index 000000000..ca493c44e --- /dev/null +++ b/misc/net.minetest.minetest.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Name=Minetest +GenericName=Minetest +Comment=Multiplayer infinite-world block sandbox +Comment[de]=Mehrspieler-Sandkastenspiel mit unendlichen Blockwelten +Comment[es]=Juego sandbox multijugador con mundos infinitos +Comment[fr]=Jeu multijoueurs de type bac à sable avec des mondes infinis +Comment[ja]=マルチプレイに対応した、無限の世界のブロック型サンドボックスゲームです +Comment[ru]=Игра-песочница с безграничным миром, состоящим из блоков +Comment[tr]=Tek-Çok oyuncuyla küplerden sonsuz dünyalar inşa et +Exec=minetest +Icon=minetest +Terminal=false +Type=Application +Categories=Game;Simulation; +StartupNotify=false +Keywords=sandbox;world;mining;crafting;blocks;nodes;multiplayer;roleplaying; -- cgit v1.2.3 From 9878ce05e75421820115b8eaaf3752ab4bd06e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Mon, 6 Mar 2017 20:34:02 +0100 Subject: CI: Add memleak checking using valgrind (#5350) Add a new step to check memleaks on our current unit tests suite --- .travis.yml | 4 ++++ util/travis/before_install.sh | 3 +++ util/travis/script.sh | 9 ++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 534479efb..3274aedaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,6 +22,10 @@ matrix: - env: PLATFORM=Unix COMPILER=clang compiler: clang os: linux + - env: PLATFORM=Unix COMPILER=clang VALGRIND=1 + compiler: clang + os: linux + dist: trusty - env: PLATFORM=Unix COMPILER=g++-6 compiler: gcc os: linux diff --git a/util/travis/before_install.sh b/util/travis/before_install.sh index 891371984..ea85b3db6 100755 --- a/util/travis/before_install.sh +++ b/util/travis/before_install.sh @@ -18,6 +18,9 @@ if [[ $PLATFORM == "Unix" ]]; then # Linking to LevelDB is broken, use a custom build wget http://minetest.kitsunemimi.pw/libleveldb-1.18-ubuntu12.04.7z sudo 7z x -o/usr libleveldb-1.18-ubuntu12.04.7z + if [[ "$VALGRIND" == "1" ]]; then + sudo apt-get install valgrind + fi else brew update brew install freetype gettext hiredis irrlicht jpeg leveldb libogg libvorbis luajit diff --git a/util/travis/script.sh b/util/travis/script.sh index 24a74d186..84ea578a5 100755 --- a/util/travis/script.sh +++ b/util/travis/script.sh @@ -24,8 +24,15 @@ if [[ $PLATFORM == "Unix" ]]; then -DBUILD_SERVER=TRUE \ $CMAKE_FLAGS .. make -j2 + echo "Running unit tests." - ../bin/minetest --run-unittests && exit 0 + CMD="../bin/minetest --run-unittests" + if [[ "$VALGRIND" == "1" ]]; then + valgrind --leak-check=full --leak-check-heuristics=all --undef-value-errors=no --error-exitcode=9 ${CMD} && exit 0 + else + ${CMD} && exit 0 + fi + elif [[ $PLATFORM == Win* ]]; then [[ $CC == "clang" ]] && exit 1 # Not supposed to happen # We need to have our build directory outside of the minetest directory because -- cgit v1.2.3 From 2baa191a64c4a0f4475bfd12540672dc99405f4f Mon Sep 17 00:00:00 2001 From: kilbith Date: Fri, 10 Mar 2017 13:26:24 +0100 Subject: GUI: Convert loading screen's progress bar to image (#5362) --- src/drawscene.cpp | 48 ++++++++++++++++++--------------- textures/base/pack/progress_bar.png | Bin 0 -> 413 bytes textures/base/pack/progress_bar_bg.png | Bin 0 -> 354 bytes 3 files changed, 27 insertions(+), 21 deletions(-) create mode 100644 textures/base/pack/progress_bar.png create mode 100644 textures/base/pack/progress_bar_bg.png diff --git a/src/drawscene.cpp b/src/drawscene.cpp index 32a078b8e..c4ef3cc51 100644 --- a/src/drawscene.cpp +++ b/src/drawscene.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/timetaker.h" #include "fontengine.h" #include "guiscalingfilter.h" +#include "filesys.h" typedef enum { LEFT = -1, @@ -590,30 +591,35 @@ void draw_load_screen(const std::wstring &text, IrrlichtDevice* device, driver->beginScene(true, true, video::SColor(255, 0, 0, 0)); // draw progress bar - if ((percent >= 0) && (percent <= 100)) - { - v2s32 barsize( - // 342 is (approximately) 256/0.75 to keep bar on same size as - // before with default settings - 342 * porting::getDisplayDensity() * - g_settings->getFloat("gui_scaling"), - g_fontengine->getTextHeight() * 2); - - core::rect barrect(center - barsize / 2, center + barsize / 2); - driver->draw2DRectangle(video::SColor(255, 255, 255, 255),barrect, NULL); // border - driver->draw2DRectangle(video::SColor(255, 64, 64, 64), core::rect ( - barrect.UpperLeftCorner + 1, - barrect.LowerRightCorner-1), NULL); // black inside the bar - driver->draw2DRectangle(video::SColor(255, 128, 128, 128), core::rect ( - barrect.UpperLeftCorner + 1, - core::vector2d( - barrect.LowerRightCorner.X - - (barsize.X - 1) + percent * (barsize.X - 2) / 100, - barrect.LowerRightCorner.Y - 1)), NULL); // the actual progress + if ((percent >= 0) && (percent <= 100)) { + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_share + DIR_DELIM + "textures"); + video::ITexture *progress_img = driver->getTexture( + (gamepath + "/base/pack/progress_bar.png").c_str()); + video::ITexture *progress_img_bg = driver->getTexture( + (gamepath + "/base/pack/progress_bar_bg.png").c_str()); + + const core::dimension2d &img_size = progress_img_bg->getSize(); + u32 imgW = MYMAX(208, img_size.Width); + u32 imgH = MYMAX(24, img_size.Height); + v2s32 img_pos((screensize.X - imgW) / 2, (screensize.Y - imgH) / 2); + + draw2DImageFilterScaled( + driver, progress_img_bg, + core::rect(img_pos.X, img_pos.Y, img_pos.X + imgW, img_pos.Y + imgH), + core::rect(0, 0, img_size.Width, img_size.Height), + 0, 0, true); + + draw2DImageFilterScaled( + driver, progress_img, + core::rect(img_pos.X, img_pos.Y, + img_pos.X + (percent * imgW) / 100, img_pos.Y + imgH), + core::rect(0, 0, ((percent * img_size.Width) / 100), img_size.Height), + 0, 0, true); } + guienv->drawAll(); driver->endScene(); - guitext->remove(); //return guitext; diff --git a/textures/base/pack/progress_bar.png b/textures/base/pack/progress_bar.png new file mode 100644 index 000000000..e80367191 Binary files /dev/null and b/textures/base/pack/progress_bar.png differ diff --git a/textures/base/pack/progress_bar_bg.png b/textures/base/pack/progress_bar_bg.png new file mode 100644 index 000000000..4e30ae889 Binary files /dev/null and b/textures/base/pack/progress_bar_bg.png differ -- cgit v1.2.3 From d785456b3fa35faf47cb972fde9e8668382c5e22 Mon Sep 17 00:00:00 2001 From: tenplus1 Date: Thu, 23 Feb 2017 20:03:18 +0100 Subject: Optimize item.lua Replace slow ItemStack get_definitions with registered_nodes one's and cached playername as it's used multiple times. Also removed local item = itemstack:peek_item() as it is never used. --- builtin/game/item.lua | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/builtin/game/item.lua b/builtin/game/item.lua index 38ef1714f..7048dded1 100644 --- a/builtin/game/item.lua +++ b/builtin/game/item.lua @@ -156,7 +156,8 @@ function core.yaw_to_dir(yaw) end function core.get_node_drops(nodename, toolname) - local drop = ItemStack({name=nodename}):get_definition().drop + local def = core.registered_nodes[nodename] + local drop = def and def.drop if drop == nil then -- default drop return {nodename} @@ -205,7 +206,6 @@ function core.get_node_drops(nodename, toolname) end function core.item_place_node(itemstack, placer, pointed_thing, param2) - local item = itemstack:peek_item() local def = itemstack:get_definition() if def.type ~= "node" or pointed_thing.type ~= "node" then return itemstack, false @@ -215,20 +215,21 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) local oldnode_under = core.get_node_or_nil(under) local above = pointed_thing.above local oldnode_above = core.get_node_or_nil(above) + local playername = placer:get_player_name() if not oldnode_under or not oldnode_above then - core.log("info", placer:get_player_name() .. " tried to place" + core.log("info", playername .. " tried to place" .. " node in unloaded position " .. core.pos_to_string(above)) return itemstack, false end - local olddef_under = ItemStack({name=oldnode_under.name}):get_definition() + local olddef_under = core.registered_nodes[oldnode_under.name] olddef_under = olddef_under or core.nodedef_default - local olddef_above = ItemStack({name=oldnode_above.name}):get_definition() + local olddef_above = core.registered_nodes[oldnode_above.name] olddef_above = olddef_above or core.nodedef_default if not olddef_above.buildable_to and not olddef_under.buildable_to then - core.log("info", placer:get_player_name() .. " tried to place" + core.log("info", playername .. " tried to place" .. " node in invalid position " .. core.pos_to_string(above) .. ", replacing " .. oldnode_above.name) return itemstack, false @@ -243,17 +244,17 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) place_to = {x = under.x, y = under.y, z = under.z} end - if core.is_protected(place_to, placer:get_player_name()) and + if core.is_protected(place_to, playername) and not minetest.check_player_privs(placer, "protection_bypass") then - core.log("action", placer:get_player_name() + core.log("action", playername .. " tried to place " .. def.name .. " at protected position " .. core.pos_to_string(place_to)) - core.record_protection_violation(place_to, placer:get_player_name()) + core.record_protection_violation(place_to, playername) return itemstack end - core.log("action", placer:get_player_name() .. " places node " + core.log("action", playername .. " places node " .. def.name .. " at " .. core.pos_to_string(place_to)) local oldnode = core.get_node(place_to) @@ -262,8 +263,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) -- Calculate direction for wall mounted stuff like torches and signs if def.place_param2 ~= nil then newnode.param2 = def.place_param2 - elseif (def.paramtype2 == 'wallmounted' or - def.paramtype2 == 'colorwallmounted') and not param2 then + elseif (def.paramtype2 == "wallmounted" or + def.paramtype2 == "colorwallmounted") and not param2 then local dir = { x = under.x - above.x, y = under.y - above.y, @@ -271,8 +272,8 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) } newnode.param2 = core.dir_to_wallmounted(dir) -- Calculate the direction for furnaces and chests and stuff - elseif (def.paramtype2 == 'facedir' or - def.paramtype2 == 'colorfacedir') and not param2 then + elseif (def.paramtype2 == "facedir" or + def.paramtype2 == "colorfacedir") and not param2 then local placer_pos = placer:getpos() if placer_pos then local dir = { @@ -310,7 +311,6 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2) end -- Run script hook - local _, callback for _, callback in ipairs(core.registered_on_placenodes) do -- Deepcopy pos, node and pointed_thing because callback can modify them local place_to_copy = {x=place_to.x, y=place_to.y, z=place_to.z} @@ -451,8 +451,9 @@ function core.handle_node_drops(pos, drops, digger) end function core.node_dig(pos, node, digger) - local def = ItemStack({name=node.name}):get_definition() - if not def.diggable or (def.can_dig and not def.can_dig(pos,digger)) then + local def = core.registered_nodes[node.name] + if def and (not def.diggable or + (def.can_dig and not def.can_dig(pos, digger))) then core.log("info", digger:get_player_name() .. " tried to dig " .. node.name .. " which is not diggable " .. core.pos_to_string(pos)) @@ -477,7 +478,7 @@ function core.node_dig(pos, node, digger) local wdef = wielded:get_definition() local tp = wielded:get_tool_capabilities() - local dp = core.get_dig_params(def.groups, tp) + local dp = core.get_dig_params(def and def.groups, tp) if wdef and wdef.after_use then wielded = wdef.after_use(wielded, digger, node, dp) or wielded else -- cgit v1.2.3 From ab371cc93491baf0973ecc94b96c3a1fdb4abfd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Juh=C3=A1sz?= Date: Sat, 10 Dec 2016 19:02:44 +0100 Subject: Light calculation: New bulk node lighting code This commit introduces a new bulk node lighting algorithm to minimize lighting bugs during l-system tree generation, schematic placement and non-mapgen-object lua voxelmanip light calculation. If the block above the changed area is not loaded, it gets loaded to avoid lighting bugs. Light is updated as soon as write_to_map is called on a voxel manipulator, therefore update_map does nothing. --- doc/lua_api.txt | 9 +- src/map.cpp | 565 ---------------------------------------- src/map.h | 16 -- src/mg_schematic.cpp | 10 +- src/mg_schematic.h | 3 +- src/script/lua_api/l_mapgen.cpp | 4 +- src/script/lua_api/l_vmanip.cpp | 46 ++-- src/treegen.cpp | 7 +- src/voxelalgorithms.cpp | 371 +++++++++++++++++++++++++- src/voxelalgorithms.h | 13 + 10 files changed, 408 insertions(+), 636 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 23aac90d9..484a5848c 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3282,9 +3282,6 @@ format as produced by get_data() et al. and is *not required* to be a table retr Once the internal VoxelManip state has been modified to your liking, the changes can be committed back to the map by calling `VoxelManip:write_to_map()`. -Finally, a call to `VoxelManip:update_map()` is required to re-calculate lighting and set the blocks -as being modified so that connected clients are sent the updated parts of map. - ##### Flat array format Let @@ -3349,8 +3346,6 @@ but with a few differences: will also update the Mapgen VoxelManip object's internal state active on the current thread. * After modifying the Mapgen VoxelManip object's internal buffer, it may be necessary to update lighting information using either: `VoxelManip:calc_lighting()` or `VoxelManip:set_lighting()`. -* `VoxelManip:update_map()` does not need to be called after `write_to_map()`. The map update is performed - automatically after all on_generated callbacks have been run for that generated block. ##### Other API functions operating on a VoxelManip If any VoxelManip contents were set to a liquid node, `VoxelManip:update_liquids()` must be called @@ -3393,9 +3388,7 @@ will place the schematic inside of the VoxelManip. * returns raw node data in the form of an array of node content IDs * if the param `buffer` is present, this table will be used to store the result instead * `set_data(data)`: Sets the data contents of the `VoxelManip` object -* `update_map()`: Update map after writing chunk back to map. - * To be used only by `VoxelManip` objects created by the mod itself; - not a `VoxelManip` that was retrieved from `minetest.get_mapgen_object` +* `update_map()`: Does nothing, kept for compatibility. * `set_lighting(light, [p1, p2])`: Set the lighting within the `VoxelManip` to a uniform value * `light` is a table, `{day=<0...15>, night=<0...15>}` * To be used only by a `VoxelManip` object from `minetest.get_mapgen_object` diff --git a/src/map.cpp b/src/map.cpp index a415bda96..a1502befa 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -235,571 +235,6 @@ void Map::setNode(v3s16 p, MapNode & n) block->setNodeNoCheck(relpos, n); } -/* - Goes recursively through the neighbours of the node. - - Alters only transparent nodes. - - If the lighting of the neighbour is lower than the lighting of - the node was (before changing it to 0 at the step before), the - lighting of the neighbour is set to 0 and then the same stuff - repeats for the neighbour. - - The ending nodes of the routine are stored in light_sources. - This is useful when a light is removed. In such case, this - routine can be called for the light node and then again for - light_sources to re-light the area without the removed light. - - values of from_nodes are lighting values. -*/ -void Map::unspreadLight(enum LightBank bank, - std::map & from_nodes, - std::set & light_sources, - std::map & modified_blocks) -{ - v3s16 dirs[6] = { - v3s16(0,0,1), // back - v3s16(0,1,0), // top - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - - if(from_nodes.empty()) - return; - - u32 blockchangecount = 0; - - std::map unlighted_nodes; - - /* - Initialize block cache - */ - v3s16 blockpos_last; - MapBlock *block = NULL; - // Cache this a bit, too - bool block_checked_in_modified = false; - - for(std::map::iterator j = from_nodes.begin(); - j != from_nodes.end(); ++j) - { - v3s16 pos = j->first; - v3s16 blockpos = getNodeBlockPos(pos); - - // Only fetch a new block if the block position has changed - try{ - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) - { - continue; - } - - if(block->isDummy()) - continue; - - // Calculate relative position in block - //v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE; - - // Get node straight from the block - //MapNode n = block->getNode(relpos); - - u8 oldlight = j->second; - - // Loop through 6 neighbors - for(u16 i=0; i<6; i++) - { - // Get the position of the neighbor node - v3s16 n2pos = pos + dirs[i]; - - // Get the block where the node is located - v3s16 blockpos, relpos; - getNodeBlockPosWithOffset(n2pos, blockpos, relpos); - - // Only fetch a new block if the block position has changed - try { - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) { - continue; - } - - // Get node straight from the block - bool is_valid_position; - MapNode n2 = block->getNode(relpos, &is_valid_position); - if (!is_valid_position) - continue; - - bool changed = false; - - //TODO: Optimize output by optimizing light_sources? - - /* - If the neighbor is dimmer than what was specified - as oldlight (the light of the previous node) - */ - if(n2.getLight(bank, m_nodedef) < oldlight) - { - /* - And the neighbor is transparent and it has some light - */ - if(m_nodedef->get(n2).light_propagates - && n2.getLight(bank, m_nodedef) != 0) - { - /* - Set light to 0 and add to queue - */ - - u8 current_light = n2.getLight(bank, m_nodedef); - n2.setLight(bank, 0, m_nodedef); - block->setNode(relpos, n2); - - unlighted_nodes[n2pos] = current_light; - changed = true; - - /* - Remove from light_sources if it is there - NOTE: This doesn't happen nearly at all - */ - /*if(light_sources.find(n2pos)) - { - infostream<<"Removed from light_sources"< & from_nodes, - std::map & modified_blocks) -{ - const v3s16 dirs[6] = { - v3s16(0,0,1), // back - v3s16(0,1,0), // top - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(0,-1,0), // bottom - v3s16(-1,0,0), // left - }; - - if(from_nodes.empty()) - return; - - u32 blockchangecount = 0; - - std::set lighted_nodes; - - /* - Initialize block cache - */ - v3s16 blockpos_last; - MapBlock *block = NULL; - // Cache this a bit, too - bool block_checked_in_modified = false; - - for(std::set::iterator j = from_nodes.begin(); - j != from_nodes.end(); ++j) - { - v3s16 pos = *j; - v3s16 blockpos, relpos; - - getNodeBlockPosWithOffset(pos, blockpos, relpos); - - // Only fetch a new block if the block position has changed - try { - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) { - continue; - } - - if(block->isDummy()) - continue; - - // Get node straight from the block - bool is_valid_position; - MapNode n = block->getNode(relpos, &is_valid_position); - - u8 oldlight = is_valid_position ? n.getLight(bank, m_nodedef) : 0; - u8 newlight = diminish_light(oldlight); - - // Loop through 6 neighbors - for(u16 i=0; i<6; i++){ - // Get the position of the neighbor node - v3s16 n2pos = pos + dirs[i]; - - // Get the block where the node is located - v3s16 blockpos, relpos; - getNodeBlockPosWithOffset(n2pos, blockpos, relpos); - - // Only fetch a new block if the block position has changed - try { - if(block == NULL || blockpos != blockpos_last){ - block = getBlockNoCreate(blockpos); - blockpos_last = blockpos; - - block_checked_in_modified = false; - blockchangecount++; - } - } - catch(InvalidPositionException &e) { - continue; - } - - // Get node straight from the block - MapNode n2 = block->getNode(relpos, &is_valid_position); - if (!is_valid_position) - continue; - - bool changed = false; - /* - If the neighbor is brighter than the current node, - add to list (it will light up this node on its turn) - */ - if(n2.getLight(bank, m_nodedef) > undiminish_light(oldlight)) - { - lighted_nodes.insert(n2pos); - changed = true; - } - /* - If the neighbor is dimmer than how much light this node - would spread on it, add to list - */ - if(n2.getLight(bank, m_nodedef) < newlight) - { - if(m_nodedef->get(n2).light_propagates) - { - n2.setLight(bank, newlight, m_nodedef); - block->setNode(relpos, n2); - lighted_nodes.insert(n2pos); - changed = true; - } - } - - // 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) == modified_blocks.end()) - { - modified_blocks[blockpos] = block; - } - block_checked_in_modified = true; - } - } - } - - /*infostream<<"spreadLight(): Changed block " - < & a_blocks, - std::map & modified_blocks) -{ - /*m_dout<<"Map::updateLighting(): " - < blocks_to_update; - - std::set light_sources; - - std::map unlight_from; - - int num_bottom_invalid = 0; - - { - //TimeTaker t("first stuff"); - - for(std::map::iterator i = a_blocks.begin(); - i != a_blocks.end(); ++i) - { - MapBlock *block = i->second; - - for(;;) - { - // Don't bother with dummy blocks. - if(block->isDummy()) - break; - - v3s16 pos = block->getPos(); - v3s16 posnodes = block->getPosRelative(); - modified_blocks[pos] = block; - //blocks_to_update[pos] = block; - - /* - Clear all light from block - */ - for(s16 z=0; zgetNode(p, &is_valid_position); - if (!is_valid_position) { - /* This would happen when dealing with a - dummy block. - */ - infostream<<"updateLighting(): InvalidPositionException" - <setNode(p, n); - - // If node sources light, add to list - u8 source = m_nodedef->get(n).light_source; - if(source != 0) - light_sources.insert(p + posnodes); - - // Collect borders for unlighting - if((x==0 || x == MAP_BLOCKSIZE-1 - || y==0 || y == MAP_BLOCKSIZE-1 - || z==0 || z == MAP_BLOCKSIZE-1) - && oldlight != 0) - { - v3s16 p_map = p + posnodes; - unlight_from[p_map] = oldlight; - } - - - } - - if(bank == LIGHTBANK_DAY) - { - bool bottom_valid = block->propagateSunlight(light_sources); - - if(!bottom_valid) - num_bottom_invalid++; - - // If bottom is valid, we're done. - if(bottom_valid) - break; - } - else if(bank == LIGHTBANK_NIGHT) - { - // For night lighting, sunlight is not propagated - break; - } - else - { - assert("Invalid lighting bank" == NULL); - } - - /*infostream<<"Bottom for sunlight-propagated block (" - <get("")) - { - core::map::Iterator i; - i = blocks_to_update.getIterator(); - for(; i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - v3s16 p = block->getPos(); - block->setLightingExpired(false); - } - return; - } -#endif - -#if 1 - { - //TimeTaker timer("unspreadLight"); - unspreadLight(bank, unlight_from, light_sources, modified_blocks); - } - - /*if(debug) - { - u32 diff = modified_blocks.size() - count_was; - count_was = modified_blocks.size(); - infostream<<"unspreadLight modified "<::Iterator i; - i = blocks_to_update.getIterator(); - for(; i.atEnd() == false; i++) - { - MapBlock *block = i.getNode()->getValue(); - v3s16 p = block->getPos(); - - // Add all surrounding blocks - vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1)); - - /* - Add all surrounding blocks that have up-to-date lighting - NOTE: This doesn't quite do the job (not everything - appropriate is lighted) - */ - /*for(s16 z=-1; z<=1; z++) - for(s16 y=-1; y<=1; y++) - for(s16 x=-1; x<=1; x++) - { - v3s16 p2 = p + v3s16(x,y,z); - MapBlock *block = getBlockNoCreateNoEx(p2); - if(block == NULL) - continue; - if(block->isDummy()) - continue; - if(block->getLightingExpired()) - continue; - vmanip.initialEmerge(p2, p2); - }*/ - - // Lighting of block will be updated completely - block->setLightingExpired(false); - } - } - - { - //TimeTaker timer("unSpreadLight"); - vmanip.unspreadLight(bank, unlight_from, light_sources, nodemgr); - } - { - //TimeTaker timer("spreadLight"); - vmanip.spreadLight(bank, light_sources, nodemgr); - } - { - //TimeTaker timer("blitBack"); - vmanip.blitBack(modified_blocks); - } - /*infostream<<"emerge_time="< & a_blocks, - std::map & modified_blocks) -{ - updateLighting(LIGHTBANK_DAY, a_blocks, modified_blocks); - updateLighting(LIGHTBANK_NIGHT, a_blocks, modified_blocks); - - /* - Update information about whether day and night light differ - */ - for(std::map::iterator - i = modified_blocks.begin(); - i != modified_blocks.end(); ++i) - { - MapBlock *block = i->second; - block->expireDayNightDiff(); - } -} - void Map::addNodeAndUpdate(v3s16 p, MapNode n, std::map &modified_blocks, bool remove_metadata) diff --git a/src/map.h b/src/map.h index 19c94ee80..c4181a49f 100644 --- a/src/map.h +++ b/src/map.h @@ -208,22 +208,6 @@ public: // position is valid, otherwise false MapNode getNodeNoEx(v3s16 p, bool *is_valid_position = NULL); - void unspreadLight(enum LightBank bank, - std::map & from_nodes, - std::set & light_sources, - std::map & modified_blocks); - - void spreadLight(enum LightBank bank, - std::set & from_nodes, - std::map & modified_blocks); - - void updateLighting(enum LightBank bank, - std::map & a_blocks, - std::map & modified_blocks); - - void updateLighting(std::map & a_blocks, - std::map & modified_blocks); - /* These handle lighting but not faces. */ diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp index 3d08d86fa..92e138df4 100644 --- a/src/mg_schematic.cpp +++ b/src/mg_schematic.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "serialization.h" #include "filesys.h" +#include "voxelalgorithms.h" /////////////////////////////////////////////////////////////////////////////// @@ -202,7 +203,7 @@ bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1))); } -void Schematic::placeOnMap(Map *map, v3s16 p, u32 flags, +void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags, Rotation rot, bool force_place) { std::map lighting_modified_blocks; @@ -238,15 +239,10 @@ void Schematic::placeOnMap(Map *map, v3s16 p, u32 flags, blitToVManip(&vm, p, rot, force_place); - vm.blitBackAll(&modified_blocks); + voxalgo::blit_back_with_light(map, &vm, &modified_blocks); //// Carry out post-map-modification actions - //// Update lighting - // TODO: Optimize this by using Mapgen::calcLighting() instead - lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); - map->updateLighting(lighting_modified_blocks, modified_blocks); - //// Create & dispatch map modification events to observers MapEditEvent event; event.type = MEET_OTHER; diff --git a/src/mg_schematic.h b/src/mg_schematic.h index 1d46e6ac4..2f60c843b 100644 --- a/src/mg_schematic.h +++ b/src/mg_schematic.h @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" class Map; +class ServerMap; class Mapgen; class MMVManip; class PseudoRandom; @@ -108,7 +109,7 @@ public: void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place); bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place); - void placeOnMap(Map *map, v3s16 p, u32 flags, Rotation rot, bool force_place); + void placeOnMap(ServerMap *map, v3s16 p, u32 flags, Rotation rot, bool force_place); void applyProbabilities(v3s16 p0, std::vector > *plist, diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index bc1c32f03..0bc9e25f7 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -1357,7 +1357,9 @@ int ModApiMapgen::l_place_schematic(lua_State *L) { MAP_LOCK_REQUIRED; - Map *map = &(getEnv(L)->getMap()); + GET_ENV_PTR; + + ServerMap *map = &(env->getServerMap()); SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; //// Read position diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index bdf720f0a..5f129d2af 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "map.h" #include "server.h" #include "mapgen.h" +#include "voxelalgorithms.h" // garbage collector int LuaVoxelManip::gc_object(lua_State *L) @@ -109,10 +110,24 @@ int LuaVoxelManip::l_write_to_map(lua_State *L) MAP_LOCK_REQUIRED; LuaVoxelManip *o = checkobject(L, 1); - MMVManip *vm = o->vm; + GET_ENV_PTR; + ServerMap *map = &(env->getServerMap()); + if (o->is_mapgen_vm) { + o->vm->blitBackAll(&(o->modified_blocks)); + } else { + voxalgo::blit_back_with_light(map, o->vm, + &(o->modified_blocks)); + } - vm->blitBackAll(&o->modified_blocks); + MapEditEvent event; + event.type = MEET_OTHER; + for (std::map::iterator it = o->modified_blocks.begin(); + it != o->modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + map->dispatchEvent(&event); + + o->modified_blocks.clear(); return 0; } @@ -322,33 +337,6 @@ int LuaVoxelManip::l_set_param2_data(lua_State *L) int LuaVoxelManip::l_update_map(lua_State *L) { - GET_ENV_PTR; - - LuaVoxelManip *o = checkobject(L, 1); - if (o->is_mapgen_vm) - return 0; - - Map *map = &(env->getMap()); - - // TODO: Optimize this by using Mapgen::calcLighting() instead - std::map lighting_mblocks; - std::map *mblocks = &o->modified_blocks; - - lighting_mblocks.insert(mblocks->begin(), mblocks->end()); - - map->updateLighting(lighting_mblocks, *mblocks); - - MapEditEvent event; - event.type = MEET_OTHER; - for (std::map::iterator - it = mblocks->begin(); - it != mblocks->end(); ++it) - event.modified_blocks.insert(it->first); - - map->dispatchEvent(&event); - - mblocks->clear(); - return 0; } diff --git a/src/treegen.cpp b/src/treegen.cpp index 4df574f34..505954e8e 100644 --- a/src/treegen.cpp +++ b/src/treegen.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverenvironment.h" #include "nodedef.h" #include "treegen.h" +#include "voxelalgorithms.h" namespace treegen { @@ -125,12 +126,8 @@ treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0, if (e != SUCCESS) return e; - vmanip.blitBackAll(&modified_blocks); + voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks); - // update lighting - std::map 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; diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index 3c32bc125..411369bd4 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -542,6 +542,21 @@ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, } } +struct SunlightPropagationUnit{ + v2s16 relative_pos; + bool is_sunlit; + + SunlightPropagationUnit(v2s16 relpos, bool sunlit): + relative_pos(relpos), + is_sunlit(sunlit) + {} +}; + +struct SunlightPropagationData{ + std::vector data; + v3s16 target_block; +}; + /*! * Returns true if the node gets sunlight from the * node above it. @@ -753,7 +768,7 @@ void update_lighting_nodes(Map *map, for (u8 i = 0; i <= LIGHT_SUN; i++) { const std::vector &lights = light_sources.lights[i]; for (std::vector::const_iterator it = lights.begin(); - it < lights.end(); it++) { + it < lights.end(); ++it) { MapNode n = it->block->getNodeNoCheck(it->rel_position, &is_valid_position); n.setLight(bank, i, ndef); @@ -817,8 +832,10 @@ void update_block_border_lighting(Map *map, MapBlock *block, bool is_valid_position; for (s32 i = 0; i < 2; i++) { LightBank bank = banks[i]; - UnlightQueue disappearing_lights(256); - ReLightQueue light_sources(256); + // Since invalid light is not common, do not allocate + // memory if not needed. + UnlightQueue disappearing_lights(0); + ReLightQueue light_sources(0); // Get incorrect lights for (direction d = 0; d < 6; d++) { // For each direction @@ -873,7 +890,7 @@ void update_block_border_lighting(Map *map, MapBlock *block, for (u8 i = 0; i <= LIGHT_SUN; i++) { const std::vector &lights = light_sources.lights[i]; for (std::vector::const_iterator it = lights.begin(); - it < lights.end(); it++) { + it < lights.end(); ++it) { MapNode n = it->block->getNodeNoCheck(it->rel_position, &is_valid_position); n.setLight(bank, i, ndef); @@ -885,6 +902,352 @@ void update_block_border_lighting(Map *map, MapBlock *block, } } +/*! + * Resets the lighting of the given VoxelManipulator to + * complete darkness and full sunlight. + * Operates in one map sector. + * + * \param offset contains the least x and z node coordinates + * of the map sector. + * \param light incoming sunlight, light[x][z] is true if there + * is sunlight above the voxel manipulator at the given x-z coordinates. + * The array's indices are relative node coordinates in the sector. + * After the procedure returns, this contains outgoing light at + * the bottom of the voxel manipulator. + */ +void fill_with_sunlight(MMVManip *vm, INodeDefManager *ndef, v2s16 offset, + bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE]) +{ + // Distance in array between two nodes on top of each other. + s16 ystride = vm->m_area.getExtent().X; + // Cache the ignore node. + MapNode ignore = MapNode(CONTENT_IGNORE); + // For each column of nodes: + for (s16 z = 0; z < MAP_BLOCKSIZE; z++) + for (s16 x = 0; x < MAP_BLOCKSIZE; x++) { + // Position of the column on the map. + v2s16 realpos = offset + v2s16(x, z); + // Array indices in the voxel manipulator + s32 maxindex = vm->m_area.index(realpos.X, vm->m_area.MaxEdge.Y, + realpos.Y); + s32 minindex = vm->m_area.index(realpos.X, vm->m_area.MinEdge.Y, + realpos.Y); + // True if the current node has sunlight. + bool lig = light[z][x]; + // For each node, downwards: + for (s32 i = maxindex; i >= minindex; i -= ystride) { + MapNode *n; + if (vm->m_flags[i] & VOXELFLAG_NO_DATA) + n = &ignore; + else + n = &vm->m_data[i]; + // Ignore IGNORE nodes, these are not generated yet. + if(n->getContent() == CONTENT_IGNORE) + continue; + const ContentFeatures &f = ndef->get(n->getContent()); + if (lig && !f.sunlight_propagates) + // Sunlight is stopped. + lig = false; + // Reset light + n->setLight(LIGHTBANK_DAY, lig ? 15 : 0, f); + n->setLight(LIGHTBANK_NIGHT, 0, f); + } + // Output outgoing light. + light[z][x] = lig; + } +} + +/*! + * Returns incoming sunlight for one map block. + * If block above is not found, it is loaded. + * + * \param pos position of the map block that gets the sunlight. + * \param light incoming sunlight, light[z][x] is true if there + * is sunlight above the block at the given z-x relative + * node coordinates. + */ +void is_sunlight_above_block(ServerMap *map, mapblock_v3 pos, + INodeDefManager *ndef, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE]) +{ + mapblock_v3 source_block_pos = pos + v3s16(0, 1, 0); + // Get or load source block. + // It might take a while to load, but correcting incorrect + // sunlight may be even slower. + MapBlock *source_block = map->emergeBlock(source_block_pos, false); + // Trust only generated blocks. + if (source_block == NULL || source_block->isDummy() + || !source_block->isGenerated()) { + // But if there is no block above, then use heuristics + bool sunlight = true; + MapBlock *node_block = map->getBlockNoCreateNoEx(pos); + if (node_block == NULL) + // This should not happen. + sunlight = false; + else + sunlight = !node_block->getIsUnderground(); + for (s16 z = 0; z < MAP_BLOCKSIZE; z++) + for (s16 x = 0; x < MAP_BLOCKSIZE; x++) + light[z][x] = sunlight; + } else { + // Dummy boolean, the position is valid. + bool is_valid_position; + // For each column: + for (s16 z = 0; z < MAP_BLOCKSIZE; z++) + for (s16 x = 0; x < MAP_BLOCKSIZE; x++) { + // Get the bottom block. + MapNode above = source_block->getNodeNoCheck(x, 0, z, + &is_valid_position); + light[z][x] = above.getLight(LIGHTBANK_DAY, ndef) == LIGHT_SUN; + } + } +} + +/*! + * Propagates sunlight down in a given map block. + * + * \param data contains incoming sunlight and shadow and + * the coordinates of the target block. + * \param unlight propagated shadow is inserted here + * \param relight propagated sunlight is inserted here + * + * \returns true if the block was modified, false otherwise. + */ +bool propagate_block_sunlight(Map *map, INodeDefManager *ndef, + SunlightPropagationData *data, UnlightQueue *unlight, ReLightQueue *relight) +{ + bool modified = false; + // Get the block. + MapBlock *block = map->getBlockNoCreateNoEx(data->target_block); + if (block == NULL || block->isDummy()) { + // The work is done if the block does not contain data. + data->data.clear(); + return false; + } + // Dummy boolean + bool is_valid; + // For each changing column of nodes: + size_t index; + for (index = 0; index < data->data.size(); index++) { + SunlightPropagationUnit it = data->data[index]; + // Relative position of the currently inspected node. + relative_v3 current_pos(it.relative_pos.X, MAP_BLOCKSIZE - 1, + it.relative_pos.Y); + if (it.is_sunlit) { + // Propagate sunlight. + // For each node downwards: + for (; current_pos.Y >= 0; current_pos.Y--) { + MapNode n = block->getNodeNoCheck(current_pos, &is_valid); + const ContentFeatures &f = ndef->get(n); + if (n.getLightRaw(LIGHTBANK_DAY, f) < LIGHT_SUN + && f.sunlight_propagates) { + // This node gets sunlight. + n.setLight(LIGHTBANK_DAY, LIGHT_SUN, f); + block->setNodeNoCheck(current_pos, n); + modified = true; + relight->push(LIGHT_SUN, current_pos, data->target_block, + block, 4); + } else { + // Light already valid, propagation stopped. + break; + } + } + } else { + // Propagate shadow. + // For each node downwards: + for (; current_pos.Y >= 0; current_pos.Y--) { + MapNode n = block->getNodeNoCheck(current_pos, &is_valid); + const ContentFeatures &f = ndef->get(n); + if (n.getLightRaw(LIGHTBANK_DAY, f) == LIGHT_SUN) { + // The sunlight is no longer valid. + n.setLight(LIGHTBANK_DAY, 0, f); + block->setNodeNoCheck(current_pos, n); + modified = true; + unlight->push(LIGHT_SUN, current_pos, data->target_block, + block, 4); + } else { + // Reached shadow, propagation stopped. + break; + } + } + } + if (current_pos.Y >= 0) { + // Propagation stopped, remove from data. + data->data[index] = data->data.back(); + data->data.pop_back(); + index--; + } + } + return modified; +} + +/*! + * Borders of a map block in relative node coordinates. + * The areas do not overlap. + * Compatible with type 'direction'. + */ +const VoxelArea block_pad[] = { + VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+ + VoxelArea(v3s16(1, 15, 0), v3s16(14, 15, 15)), //Y+ + VoxelArea(v3s16(1, 1, 15), v3s16(14, 14, 15)), //Z+ + VoxelArea(v3s16(1, 1, 0), v3s16(14, 14, 0)), //Z- + VoxelArea(v3s16(1, 0, 0), v3s16(14, 0, 15)), //Y- + VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X- +}; + +void blit_back_with_light(ServerMap *map, MMVManip *vm, + std::map *modified_blocks) +{ + INodeDefManager *ndef = map->getNodeDefManager(); + mapblock_v3 minblock = getNodeBlockPos(vm->m_area.MinEdge); + mapblock_v3 maxblock = getNodeBlockPos(vm->m_area.MaxEdge); + // First queue is for day light, second is for night light. + UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) }; + ReLightQueue relight[] = { ReLightQueue(256), ReLightQueue(256) }; + // Will hold sunlight data. + bool lights[MAP_BLOCKSIZE][MAP_BLOCKSIZE]; + SunlightPropagationData data; + // Dummy boolean. + bool is_valid; + + // --- STEP 1: reset everything to sunlight + + // For each map block: + for (s16 x = minblock.X; x <= maxblock.X; x++) + for (s16 z = minblock.Z; z <= maxblock.Z; z++) { + // Extract sunlight above. + is_sunlight_above_block(map, v3s16(x, maxblock.Y, z), ndef, lights); + v2s16 offset(x, z); + offset *= MAP_BLOCKSIZE; + // Reset the voxel manipulator. + fill_with_sunlight(vm, ndef, offset, lights); + // Copy sunlight data + data.target_block = v3s16(x, minblock.Y - 1, z); + for (s16 z = 0; z < MAP_BLOCKSIZE; z++) + for (s16 x = 0; x < MAP_BLOCKSIZE; x++) + data.data.push_back( + SunlightPropagationUnit(v2s16(x, z), lights[z][x])); + // Propagate sunlight and shadow below the voxel manipulator. + while (!data.data.empty()) { + if (propagate_block_sunlight(map, ndef, &data, &unlight[0], + &relight[0])) + (*modified_blocks)[data.target_block] = + map->getBlockNoCreateNoEx(data.target_block); + // Step downwards. + data.target_block.Y--; + } + } + + // --- STEP 2: Get nodes from borders to unlight + + // In case there are unloaded holes in the voxel manipulator + // unlight each block. + // For each block: + for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++) + for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++) + for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) { + v3s16 blockpos(b_x, b_y, b_z); + MapBlock *block = map->getBlockNoCreateNoEx(blockpos); + if (!block || block->isDummy()) + // Skip not existing blocks. + continue; + v3s16 offset = block->getPosRelative(); + // For each border of the block: + for (direction d = 0; d < 6; d++) { + VoxelArea a = block_pad[d]; + // For each node of the border: + for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++) + for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) + for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { + v3s16 relpos(x, y, z); + // Get old and new node + MapNode oldnode = block->getNodeNoCheck(x, y, z, &is_valid); + const ContentFeatures &oldf = ndef->get(oldnode); + MapNode newnode = vm->getNodeNoExNoEmerge(relpos + offset); + const ContentFeatures &newf = ndef->get(newnode); + // For each light bank + for (size_t b = 0; b < 2; b++) { + LightBank bank = banks[b]; + u8 oldlight = oldf.param_type == CPT_LIGHT ? + oldnode.getLightNoChecks(bank, &oldf): + LIGHT_SUN; // no light information, force unlighting + u8 newlight = newf.param_type == CPT_LIGHT ? + newnode.getLightNoChecks(bank, &newf): + newf.light_source; + // If the new node is dimmer, unlight. + if (oldlight > newlight) { + unlight[b].push( + oldlight, relpos, blockpos, block, 6); + } + } // end of banks + } // end of nodes + } // end of borders + } // end of blocks + + // --- STEP 3: All information extracted, overwrite + + vm->blitBackAll(modified_blocks, true); + + // --- STEP 4: Do unlighting + + for (size_t bank = 0; bank < 2; bank++) { + LightBank b = banks[bank]; + unspread_light(map, ndef, b, unlight[bank], relight[bank], + *modified_blocks); + } + + // --- STEP 5: Get all newly inserted light sources + + // For each block: + for (s16 b_x = minblock.X; b_x <= maxblock.X; b_x++) + for (s16 b_y = minblock.Y; b_y <= maxblock.Y; b_y++) + for (s16 b_z = minblock.Z; b_z <= maxblock.Z; b_z++) { + v3s16 blockpos(b_x, b_y, b_z); + MapBlock *block = map->getBlockNoCreateNoEx(blockpos); + if (!block || block->isDummy()) + // Skip not existing blocks + continue; + // For each node in the block: + for (s32 x = 0; x < MAP_BLOCKSIZE; x++) + for (s32 z = 0; z < MAP_BLOCKSIZE; z++) + for (s32 y = 0; y < MAP_BLOCKSIZE; y++) { + v3s16 relpos(x, y, z); + MapNode node = block->getNodeNoCheck(x, y, z, &is_valid); + const ContentFeatures &f = ndef->get(node); + // For each light bank + for (size_t b = 0; b < 2; b++) { + LightBank bank = banks[b]; + u8 light = f.param_type == CPT_LIGHT ? + node.getLightNoChecks(bank, &f): + f.light_source; + if (light > 1) + relight[b].push(light, relpos, blockpos, block, 6); + } // end of banks + } // end of nodes + } // end of blocks + + // --- STEP 6: do light spreading + + // For each light bank: + for (size_t b = 0; b < 2; b++) { + LightBank bank = banks[b]; + // Sunlight is already initialized. + u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN; + // Initialize light values for light spreading. + for (u8 i = 0; i <= maxlight; i++) { + const std::vector &lights = relight[b].lights[i]; + for (std::vector::const_iterator it = lights.begin(); + it < lights.end(); ++it) { + MapNode n = it->block->getNodeNoCheck(it->rel_position, + &is_valid); + n.setLight(bank, i, ndef); + it->block->setNodeNoCheck(it->rel_position, n); + } + } + // Spread lights. + spread_light(map, ndef, bank, relight[b], *modified_blocks); + } +} + VoxelLineIterator::VoxelLineIterator( const v3f &start_position, const v3f &line_vector) : diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index bf1638fa3..cdffe86c8 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -26,7 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/cpp11_container.h" class Map; +class ServerMap; class MapBlock; +class MMVManip; namespace voxalgo { @@ -84,6 +86,17 @@ void update_lighting_nodes( void update_block_border_lighting(Map *map, MapBlock *block, std::map &modified_blocks); +/*! + * Copies back nodes from a voxel manipulator + * to the map and updates lighting. + * For server use only. + * + * \param modified_blocks output, contains all map blocks that + * the function modified + */ +void blit_back_with_light(ServerMap *map, MMVManip *vm, + std::map *modified_blocks); + /*! * This class iterates trough voxels that intersect with * a line. The collision detection does not see nodeboxes, -- cgit v1.2.3 From 25a24c0cdf1467df51c41d0b24e2ab6318ba1850 Mon Sep 17 00:00:00 2001 From: number Zero Date: Thu, 23 Feb 2017 16:04:39 +0300 Subject: Minimap: Optimise --- src/minimap.cpp | 122 +++++++++++++++++++++-------------------------------- src/minimap.h | 7 +-- src/util/numeric.h | 10 +++++ 3 files changed, 59 insertions(+), 80 deletions(-) diff --git a/src/minimap.cpp b/src/minimap.cpp index a2e501751..cfc2c34b0 100644 --- a/src/minimap.cpp +++ b/src/minimap.cpp @@ -120,89 +120,63 @@ void MinimapUpdateThread::doUpdate() } if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) { - getMap(data->pos, data->map_size, data->scan_height, data->is_radar); + getMap(data->pos, data->map_size, data->scan_height); data->map_invalidated = false; } } -MinimapPixel *MinimapUpdateThread::getMinimapPixel(v3s16 pos, - s16 scan_height, s16 *pixel_height) +void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height) { - s16 height = scan_height - MAP_BLOCKSIZE; - v3s16 blockpos_max, blockpos_min, relpos; - - getNodeBlockPosWithOffset( - v3s16(pos.X, pos.Y - scan_height / 2, pos.Z), - blockpos_min, relpos); - getNodeBlockPosWithOffset( - v3s16(pos.X, pos.Y + scan_height / 2, pos.Z), - blockpos_max, relpos); - - for (s16 i = blockpos_max.Y; i > blockpos_min.Y - 1; i--) { - std::map::iterator it = - m_blocks_cache.find(v3s16(blockpos_max.X, i, blockpos_max.Z)); - if (it != m_blocks_cache.end()) { - MinimapMapblock *mmblock = it->second; - MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X]; - if (pixel->n.param0 != CONTENT_AIR) { - *pixel_height = height + pixel->height; - return pixel; - } - } - - height -= MAP_BLOCKSIZE; + v3s16 region(size, 0, size); + v3s16 pos_min(pos.X - size / 2, pos.Y - height / 2, pos.Z - size / 2); + v3s16 pos_max(pos_min.X + size - 1, pos.Y + height / 2, pos_min.Z + size - 1); + v3s16 blockpos_min = getNodeBlockPos(pos_min); + v3s16 blockpos_max = getNodeBlockPos(pos_max); + +// clear the map + for (int z = 0; z < size; z++) + for (int x = 0; x < size; x++) { + MinimapPixel &mmpixel = data->minimap_scan[x + z * size]; + mmpixel.air_count = 0; + mmpixel.height = 0; + mmpixel.n = MapNode(CONTENT_AIR); } - return NULL; -} - -s16 MinimapUpdateThread::getAirCount(v3s16 pos, s16 height) -{ - s16 air_count = 0; - v3s16 blockpos_max, blockpos_min, relpos; - - getNodeBlockPosWithOffset( - v3s16(pos.X, pos.Y - height / 2, pos.Z), - blockpos_min, relpos); - getNodeBlockPosWithOffset( - v3s16(pos.X, pos.Y + height / 2, pos.Z), - blockpos_max, relpos); - - for (s16 i = blockpos_max.Y; i > blockpos_min.Y - 1; i--) { - std::map::iterator it = - m_blocks_cache.find(v3s16(blockpos_max.X, i, blockpos_max.Z)); - if (it != m_blocks_cache.end()) { - MinimapMapblock *mmblock = it->second; - MinimapPixel *pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X]; - air_count += pixel->air_count; - } - } - - return air_count; -} - -void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool is_radar) -{ - v3s16 p = v3s16(pos.X - size / 2, pos.Y, pos.Z - size / 2); - - for (s16 x = 0; x < size; x++) - for (s16 z = 0; z < size; z++) { - MapNode n(CONTENT_AIR); - MinimapPixel *mmpixel = &data->minimap_scan[x + z * size]; - - if (!is_radar) { - s16 pixel_height = 0; - MinimapPixel *cached_pixel = - getMinimapPixel(v3s16(p.X + x, p.Y, p.Z + z), height, &pixel_height); - if (cached_pixel) { - n = cached_pixel->n; - mmpixel->height = pixel_height; +// draw the map + v3s16 blockpos; + for (blockpos.Z = blockpos_min.Z; blockpos.Z <= blockpos_max.Z; ++blockpos.Z) + for (blockpos.Y = blockpos_min.Y; blockpos.Y <= blockpos_max.Y; ++blockpos.Y) + for (blockpos.X = blockpos_min.X; blockpos.X <= blockpos_max.X; ++blockpos.X) { + std::map::const_iterator pblock = + m_blocks_cache.find(blockpos); + if (pblock == m_blocks_cache.end()) + continue; + const MinimapMapblock &block = *pblock->second; + + v3s16 block_node_min(blockpos * MAP_BLOCKSIZE); + v3s16 block_node_max(block_node_min + MAP_BLOCKSIZE - 1); + // clip + v3s16 range_min = componentwise_max(block_node_min, pos_min); + v3s16 range_max = componentwise_min(block_node_max, pos_max); + + v3s16 pos; + pos.Y = range_min.Y; + for (pos.Z = range_min.Z; pos.Z <= range_max.Z; ++pos.Z) + for (pos.X = range_min.X; pos.X <= range_max.X; ++pos.X) { + v3s16 inblock_pos = pos - block_node_min; + const MinimapPixel &in_pixel = + block.data[inblock_pos.Z * MAP_BLOCKSIZE + inblock_pos.X]; + + v3s16 inmap_pos = pos - pos_min; + MinimapPixel &out_pixel = + data->minimap_scan[inmap_pos.X + inmap_pos.Z * size]; + + out_pixel.air_count += in_pixel.air_count; + if (in_pixel.n.param0 != CONTENT_AIR) { + out_pixel.n = in_pixel.n; + out_pixel.height = inmap_pos.Y + in_pixel.height; } - } else { - mmpixel->air_count = getAirCount(v3s16(p.X + x, p.Y, p.Z + z), height); } - - mmpixel->n = n; } } diff --git a/src/minimap.h b/src/minimap.h index 81ed0e49f..60b80d833 100644 --- a/src/minimap.h +++ b/src/minimap.h @@ -96,13 +96,8 @@ public: MinimapUpdateThread() : UpdateThread("Minimap") {} virtual ~MinimapUpdateThread(); - void getMap(v3s16 pos, s16 size, s16 height, bool radar); - MinimapPixel *getMinimapPixel(v3s16 pos, s16 height, s16 *pixel_height); - s16 getAirCount(v3s16 pos, s16 height); - video::SColor getColorFromId(u16 id); - + void getMap(v3s16 pos, s16 size, s16 height); void enqueueBlock(v3s16 pos, MinimapMapblock *data); - bool pushBlockUpdate(v3s16 pos, MinimapMapblock *data); bool popBlockUpdate(QueuedMinimapUpdate *update); diff --git a/src/util/numeric.h b/src/util/numeric.h index 4cdc254c3..760657171 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -149,6 +149,16 @@ inline void sortBoxVerticies(v3s16 &p1, v3s16 &p2) { SWAP(s16, p1.Z, p2.Z); } +inline v3s16 componentwise_min(const v3s16 &a, const v3s16 &b) +{ + return v3s16(MYMIN(a.X, b.X), MYMIN(a.Y, b.Y), MYMIN(a.Z, b.Z)); +} + +inline v3s16 componentwise_max(const v3s16 &a, const v3s16 &b) +{ + return v3s16(MYMAX(a.X, b.X), MYMAX(a.Y, b.Y), MYMAX(a.Z, b.Z)); +} + /** Returns \p f wrapped to the range [-360, 360] * -- cgit v1.2.3 From d34f149bdc4f72db904d948413fa6b3f649dca7b Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 6 Mar 2017 08:35:13 +0000 Subject: Climb speed: Increase default setting from 2 to 3 --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 0e8783f83..4e800e25b 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -799,7 +799,7 @@ movement_acceleration_fast (Fast mode acceleration) float 10 movement_speed_walk (Walking speed) float 4 movement_speed_crouch (Crouch speed) float 1.35 movement_speed_fast (Fast mode speed) float 20 -movement_speed_climb (Climbing speed) float 2 +movement_speed_climb (Climbing speed) float 3 movement_speed_jump (Jumping speed) float 6.5 movement_speed_descend (Descending speed) float 6 movement_liquid_fluidity (Liquid fluidity) float 1 diff --git a/minetest.conf.example b/minetest.conf.example index a90cc7d8e..d53a00fd9 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -965,7 +965,7 @@ # movement_speed_fast = 20 # type: float -# movement_speed_climb = 2 +# movement_speed_climb = 3 # type: float # movement_speed_jump = 6.5 diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index b333ff087..e9ee1135f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -314,7 +314,7 @@ void set_default_settings(Settings *settings) settings->setDefault("movement_speed_walk", "4"); settings->setDefault("movement_speed_crouch", "1.35"); settings->setDefault("movement_speed_fast", "20"); - settings->setDefault("movement_speed_climb", "2"); + settings->setDefault("movement_speed_climb", "3"); settings->setDefault("movement_speed_jump", "6.5"); settings->setDefault("movement_liquid_fluidity", "1"); settings->setDefault("movement_liquid_fluidity_smooth", "0.5"); -- cgit v1.2.3 From f83d8687a7cd15dac61fd257d358eb42337d48e0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 11 Mar 2017 21:39:32 +0100 Subject: database-redis: Support password authentication --- src/database-redis.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/database-redis.cpp b/src/database-redis.cpp index 3df186944..93e6717fa 100644 --- a/src/database-redis.cpp +++ b/src/database-redis.cpp @@ -53,6 +53,18 @@ Database_Redis::Database_Redis(Settings &conf) redisFree(ctx); throw DatabaseException(err); } + if (conf.exists("redis_password")) { + tmp = conf.get("redis_password"); + redisReply *reply = static_cast(redisCommand(ctx, "AUTH %s", tmp.c_str())); + if (!reply) + throw DatabaseException("Redis authentication failed"); + if (reply->type == REDIS_REPLY_ERROR) { + std::string err = "Redis authentication failed: " + std::string(reply->str, reply->len); + freeReplyObject(reply); + throw DatabaseException(err); + } + freeReplyObject(reply); + } } Database_Redis::~Database_Redis() -- cgit v1.2.3 From 6738c7e9a310514fca7d4ddb685800391756626b Mon Sep 17 00:00:00 2001 From: Auke Kok Date: Fri, 10 Mar 2017 23:37:30 -0800 Subject: Do not increase breath if at full breath. Prevents the server from sending TOCLIENT_BREATH packets every 0.5seconds, if there is no reason to. --- src/content_sao.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f87977f80..d4a218505 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -965,7 +965,7 @@ void PlayerSAO::step(float dtime, bool send_recommended) MapNode n = m_env->getMap().getNodeNoEx(p); const ContentFeatures &c = m_env->getGameDef()->ndef()->get(n); // If player is alive & no drowning, breath - if (m_hp > 0 && c.drowning == 0) + if (m_hp > 0 && m_breath < PLAYER_MAX_BREATH && c.drowning == 0) setBreath(m_breath + 1); } -- cgit v1.2.3 From ba4b704ebf24952ab9a84c914b8ad6c45dabfaba Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Mon, 27 Feb 2017 23:06:15 -0800 Subject: Allow server side occlusion culling. --- builtin/settingtypes.txt | 6 +++++ minetest.conf.example | 6 +++++ src/clientiface.cpp | 8 ++++++ src/clientmap.cpp | 67 +----------------------------------------------- src/defaultsettings.cpp | 1 + src/map.cpp | 66 +++++++++++++++++++++++++++++++++++++++++++++++ src/map.h | 4 +++ 7 files changed, 92 insertions(+), 66 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 4e800e25b..ffd872c20 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -864,6 +864,12 @@ liquid_update (Liquid update tick) float 1.0 # Stated in mapblocks (16 nodes) block_send_optimize_distance (block send optimize distance) int 4 2 +# If enabled the server will perform map block occlusion culling based on +# on the eye position of the player. This can reduce the number of blocks +# sent to the client 50-80%. The client will not longer receive most invisible +# so that the utility of noclip mode is reduced. +server_side_occlusion_culling (Server side occlusion culling) bool false + [*Mapgen] # Name of map generator to be used when creating a new world. diff --git a/minetest.conf.example b/minetest.conf.example index d53a00fd9..08d00d62a 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1056,6 +1056,12 @@ # type: int min: 2 # block_send_optimize_distance = 4 +# If enabled the server will perform map block occlusion culling based on +# on the eye position of the player. This can reduce the number of blocks +# sent to the client 50-80%. The client will not longer receive most invisible +# so that the utility of noclip mode is reduced. +server_side_occlusion_culling = false + ## Mapgen # Name of map generator to be used when creating a new world. diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 0eb68c9c1..76a34c392 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -197,6 +197,9 @@ void RemoteClient::GetNextBlocks ( s32 nearest_sent_d = -1; //bool queue_is_full = false; + const v3s16 cam_pos_nodes = floatToInt(camera_pos, BS); + const bool occ_cull = g_settings->getBool("server_side_occlusion_culling"); + s16 d; for(d = d_start; d <= d_max; d++) { /* @@ -298,6 +301,11 @@ void RemoteClient::GetNextBlocks ( if(block->getDayNightDiff() == false) continue; } + + if (occ_cull && !block_is_invalid && + env->getMap().isBlockOccluded(block, cam_pos_nodes)) { + continue; + } } /* diff --git a/src/clientmap.cpp b/src/clientmap.cpp index fb70a97e9..4cae03bf2 100644 --- a/src/clientmap.cpp +++ b/src/clientmap.cpp @@ -109,35 +109,6 @@ void ClientMap::OnRegisterSceneNode() ISceneNode::OnRegisterSceneNode(); } -static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac, - float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr) -{ - float d0 = (float)BS * p0.getDistanceFrom(p1); - v3s16 u0 = p1 - p0; - v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS; - uf.normalize(); - v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS; - u32 count = 0; - for(float s=start_off; sgetNodeNoEx(p); - bool is_transparent = false; - const ContentFeatures &f = nodemgr->get(n); - if(f.solidness == 0) - is_transparent = (f.visual_solidness != 2); - else - is_transparent = (f.solidness != 2); - if(!is_transparent){ - count++; - if(count >= needed_count) - return true; - } - step *= stepfac; - } - return false; -} - void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max) { @@ -273,43 +244,7 @@ void ClientMap::updateDrawList(video::IVideoDriver* driver) /* Occlusion culling */ - v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; - cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2); - float step = BS * 1; - float stepfac = 1.1; - float startoff = BS * 1; - // The occlusion search of 'isOccluded()' must stop short of the target - // point by distance 'endoff' (end offset) to not enter the target mapblock. - // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal - // of a mapblock, because we must consider all view angles. - // sqrt(1^2 + 1^2 + 1^2) = 1.732 - float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569; - v3s16 spn = cam_pos_nodes; - s16 bs2 = MAP_BLOCKSIZE / 2 + 1; - // to reduce the likelihood of falsely occluded blocks - // require at least two solid blocks - // this is a HACK, we should think of a more precise algorithm - u32 needed_count = 2; - if (occlusion_culling_enabled && - // For the central point of the mapblock 'endoff' can be halved - isOccluded(this, spn, cpn, - step, stepfac, startoff, endoff / 2.0f, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef) && - isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2), - step, stepfac, startoff, endoff, needed_count, m_nodedef)) { + if (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes)) { blocks_occlusion_culled++; continue; } diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index e9ee1135f..483c9cff6 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -282,6 +282,7 @@ void set_default_settings(Settings *settings) // This causes frametime jitter on client side, or does it? settings->setDefault("max_block_send_distance", "9"); settings->setDefault("block_send_optimize_distance", "4"); + settings->setDefault("server_side_occlusion_culling", "false"); settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096"); settings->setDefault("time_speed", "72"); settings->setDefault("server_unload_unused_data_timeout", "29"); diff --git a/src/map.cpp b/src/map.cpp index a1502befa..43a49dc2f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1157,6 +1157,72 @@ void Map::removeNodeTimer(v3s16 p) block->m_node_timers.remove(p_rel); } +bool Map::isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac, + float start_off, float end_off, u32 needed_count) +{ + float d0 = (float)BS * p0.getDistanceFrom(p1); + v3s16 u0 = p1 - p0; + v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS; + uf.normalize(); + v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS; + u32 count = 0; + for(float s=start_off; sget(n); + if(f.drawtype == NDT_NORMAL){ + // not transparent, see ContentFeature::updateTextures + count++; + if(count >= needed_count) + return true; + } + step *= stepfac; + } + return false; +} + +bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes) { + v3s16 cpn = block->getPos() * MAP_BLOCKSIZE; + cpn += v3s16(MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2, MAP_BLOCKSIZE / 2); + float step = BS * 1; + float stepfac = 1.1; + float startoff = BS * 1; + // The occlusion search of 'isOccluded()' must stop short of the target + // point by distance 'endoff' (end offset) to not enter the target mapblock. + // For the 8 mapblock corners 'endoff' must therefore be the maximum diagonal + // of a mapblock, because we must consider all view angles. + // sqrt(1^2 + 1^2 + 1^2) = 1.732 + float endoff = -BS * MAP_BLOCKSIZE * 1.732050807569; + v3s16 spn = cam_pos_nodes; + s16 bs2 = MAP_BLOCKSIZE / 2 + 1; + // to reduce the likelihood of falsely occluded blocks + // require at least two solid blocks + // this is a HACK, we should think of a more precise algorithm + u32 needed_count = 2; + + return ( + // For the central point of the mapblock 'endoff' can be halved + isOccluded(spn, cpn, + step, stepfac, startoff, endoff / 2.0f, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,-bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(bs2,-bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,bs2,-bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,-bs2,bs2), + step, stepfac, startoff, endoff, needed_count) && + isOccluded(spn, cpn + v3s16(-bs2,-bs2,-bs2), + step, stepfac, startoff, endoff, needed_count)); +} + /* ServerMap */ diff --git a/src/map.h b/src/map.h index c4181a49f..aeb05c704 100644 --- a/src/map.h +++ b/src/map.h @@ -314,6 +314,7 @@ public: void transforming_liquid_add(v3s16 p); s32 transforming_liquid_size(); + bool isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes); protected: friend class LuaVoxelManip; @@ -335,6 +336,9 @@ protected: // This stores the properties of the nodes on the map. INodeDefManager *m_nodedef; + bool isOccluded(v3s16 p0, v3s16 p1, float step, float stepfac, + float start_off, float end_off, u32 needed_count); + private: f32 m_transforming_liquid_loop_count_multiplier; u32 m_unprocessed_count; -- cgit v1.2.3 From ff8069694748707d4435ebd109c99ff20212826f Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Fri, 3 Mar 2017 12:40:50 -0800 Subject: Enable server side occlusion culling by default. --- builtin/settingtypes.txt | 2 +- minetest.conf.example | 2 +- src/defaultsettings.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ffd872c20..cba03e983 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -868,7 +868,7 @@ block_send_optimize_distance (block send optimize distance) int 4 2 # on the eye position of the player. This can reduce the number of blocks # sent to the client 50-80%. The client will not longer receive most invisible # so that the utility of noclip mode is reduced. -server_side_occlusion_culling (Server side occlusion culling) bool false +server_side_occlusion_culling (Server side occlusion culling) bool true [*Mapgen] diff --git a/minetest.conf.example b/minetest.conf.example index 08d00d62a..8caeeb7d2 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1060,7 +1060,7 @@ # on the eye position of the player. This can reduce the number of blocks # sent to the client 50-80%. The client will not longer receive most invisible # so that the utility of noclip mode is reduced. -server_side_occlusion_culling = false +server_side_occlusion_culling = true ## Mapgen diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 483c9cff6..d67a60b07 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -282,7 +282,7 @@ void set_default_settings(Settings *settings) // This causes frametime jitter on client side, or does it? settings->setDefault("max_block_send_distance", "9"); settings->setDefault("block_send_optimize_distance", "4"); - settings->setDefault("server_side_occlusion_culling", "false"); + settings->setDefault("server_side_occlusion_culling", "true"); settings->setDefault("max_clearobjects_extra_loaded_blocks", "4096"); settings->setDefault("time_speed", "72"); settings->setDefault("server_unload_unused_data_timeout", "29"); -- cgit v1.2.3 From 7a4878cd0b563296b3f2718b79dc9af177dd4fd2 Mon Sep 17 00:00:00 2001 From: Vladislav Tsendrovskii Date: Fri, 17 Feb 2017 04:48:48 +0300 Subject: Save metainfo for falling nodes --- builtin/game/falling.lua | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index 764909361..b1beb1ab0 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -18,9 +18,11 @@ core.register_entity(":__builtin:falling_node", { }, node = {}, + meta = {}, - set_node = function(self, node) + set_node = function(self, node, meta) self.node = node + self.meta = meta or {} self.object:set_properties({ is_visible = true, textures = {node.name}, @@ -28,15 +30,21 @@ core.register_entity(":__builtin:falling_node", { end, get_staticdata = function(self) - return core.serialize(self.node) + local ds = { + node = self.node, + meta = self.meta, + } + return core.serialize(ds) end, on_activate = function(self, staticdata) self.object:set_armor_groups({immortal = 1}) - local node = core.deserialize(staticdata) - if node then - self:set_node(node) + local ds = core.deserialize(staticdata) + if ds and ds.node then + self:set_node(ds.node, ds.meta) + elseif ds then + self:set_node(ds) elseif staticdata ~= "" then self:set_node({name = staticdata}) end @@ -98,6 +106,10 @@ core.register_entity(":__builtin:falling_node", { -- Create node and remove entity if core.registered_nodes[self.node.name] then core.add_node(np, self.node) + if self.meta then + local meta = core.get_meta(np) + meta:from_table(self.meta) + end end self.object:remove() core.check_for_falling(np) @@ -111,10 +123,10 @@ core.register_entity(":__builtin:falling_node", { end }) -local function spawn_falling_node(p, node) +local function spawn_falling_node(p, node, meta) local obj = core.add_entity(p, "__builtin:falling_node") if obj then - obj:get_luaentity():set_node(node) + obj:get_luaentity():set_node(node, meta) end end @@ -189,8 +201,13 @@ function core.check_single_for_falling(p) (not d_bottom.walkable or d_bottom.buildable_to) then n.level = core.get_node_level(p) + local meta = core.get_meta(p) + local metatable = {} + if meta ~= nil then + metatable = meta:to_table() + end core.remove_node(p) - spawn_falling_node(p, n) + spawn_falling_node(p, n, metatable) return true end end -- cgit v1.2.3 From c9492b4d37c11f35cfdc1558f771eef87fc5c972 Mon Sep 17 00:00:00 2001 From: kilbith Date: Mon, 13 Mar 2017 08:07:14 +0100 Subject: GUI: Allow texture packs to customize the progress bar (#5368) --- doc/texture_packs.txt | 3 +++ src/drawscene.cpp | 69 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/doc/texture_packs.txt b/doc/texture_packs.txt index 5c535a9f1..1813c29e4 100644 --- a/doc/texture_packs.txt +++ b/doc/texture_packs.txt @@ -76,6 +76,9 @@ by texture packs. All existing fallback textures can be found in the directory * `player.png`: front texture of the 2D upright sprite player * `player_back.png`: back texture of the 2D upright sprite player +* `progress_bar.png`: foreground texture of the loading screen's progress bar +* `progress_bar_bg.png`: background texture of the loading screen's progress bar + * `moon.png`: texture of the moon. Default texture is generated by Minetest * `moon_tonemap.png`: tonemap to be used when `moon.png` was found * `sun.png`: texture of the sun. Default texture is generated by Minetest diff --git a/src/drawscene.cpp b/src/drawscene.cpp index c4ef3cc51..e3e6301a8 100644 --- a/src/drawscene.cpp +++ b/src/drawscene.cpp @@ -592,30 +592,51 @@ void draw_load_screen(const std::wstring &text, IrrlichtDevice* device, // draw progress bar if ((percent >= 0) && (percent <= 100)) { - std::string gamepath = fs::RemoveRelativePathComponents( - porting::path_share + DIR_DELIM + "textures"); - video::ITexture *progress_img = driver->getTexture( - (gamepath + "/base/pack/progress_bar.png").c_str()); - video::ITexture *progress_img_bg = driver->getTexture( - (gamepath + "/base/pack/progress_bar_bg.png").c_str()); - - const core::dimension2d &img_size = progress_img_bg->getSize(); - u32 imgW = MYMAX(208, img_size.Width); - u32 imgH = MYMAX(24, img_size.Height); - v2s32 img_pos((screensize.X - imgW) / 2, (screensize.Y - imgH) / 2); - - draw2DImageFilterScaled( - driver, progress_img_bg, - core::rect(img_pos.X, img_pos.Y, img_pos.X + imgW, img_pos.Y + imgH), - core::rect(0, 0, img_size.Width, img_size.Height), - 0, 0, true); - - draw2DImageFilterScaled( - driver, progress_img, - core::rect(img_pos.X, img_pos.Y, - img_pos.X + (percent * imgW) / 100, img_pos.Y + imgH), - core::rect(0, 0, ((percent * img_size.Width) / 100), img_size.Height), - 0, 0, true); + const std::string &texture_path = g_settings->get("texture_path"); + std::string tp_progress_bar = texture_path + "/progress_bar.png"; + std::string tp_progress_bar_bg = texture_path + "/progress_bar_bg.png"; + + if (!(fs::PathExists(tp_progress_bar) && + fs::PathExists(tp_progress_bar_bg))) { + std::string gamepath = fs::RemoveRelativePathComponents( + porting::path_share + DIR_DELIM + "textures"); + tp_progress_bar = gamepath + "/base/pack/progress_bar.png"; + tp_progress_bar_bg = gamepath + "/base/pack/progress_bar_bg.png"; + } + + video::ITexture *progress_img = + driver->getTexture(tp_progress_bar.c_str()); + video::ITexture *progress_img_bg = + driver->getTexture(tp_progress_bar_bg.c_str()); + + if (progress_img && progress_img_bg) { + const core::dimension2d &img_size = progress_img_bg->getSize(); + u32 imgW = rangelim(img_size.Width, 200, 600); + u32 imgH = rangelim(img_size.Height, 24, 72); + v2s32 img_pos((screensize.X - imgW) / 2, (screensize.Y - imgH) / 2); + + draw2DImageFilterScaled( + driver, progress_img_bg, + core::rect(img_pos.X, + img_pos.Y, + img_pos.X + imgW, + img_pos.Y + imgH), + core::rect(0, 0, + img_size.Width, + img_size.Height), + 0, 0, true); + + draw2DImageFilterScaled( + driver, progress_img, + core::rect(img_pos.X, + img_pos.Y, + img_pos.X + (percent * imgW) / 100, + img_pos.Y + imgH), + core::rect(0, 0, + (percent * img_size.Width) / 100, + img_size.Height), + 0, 0, true); + } } guienv->drawAll(); -- cgit v1.2.3 From 2efae3ffd720095222c800e016286a45c9fe1e5c Mon Sep 17 00:00:00 2001 From: Loic Blot Date: Sat, 21 Jan 2017 15:02:08 +0100 Subject: [CSM] Client side modding * rename GameScripting to ServerScripting * Make getBuiltinLuaPath static serverside * Add on_shutdown callback * Add on_receiving_chat_message & on_sending_chat_message callbacks * ScriptApiBase: use IGameDef instead of Server This permits to share common attribute between client & server * Enable mod security in client side modding without conditions --- builtin/client/init.lua | 22 +++++++ builtin/client/register.lua | 62 +++++++++++++++++++ builtin/init.lua | 3 + src/client.cpp | 45 +++++++++++--- src/client.h | 10 +++ src/content_abm.cpp | 2 +- src/content_sao.cpp | 2 +- src/emerge.cpp | 2 +- src/environment.cpp | 2 +- src/game.cpp | 2 + src/gamedef.h | 7 ++- src/guiFormSpecMenu.cpp | 2 +- src/inventorymanager.cpp | 2 +- src/network/clientpackethandler.cpp | 6 +- src/network/serverpackethandler.cpp | 2 +- src/script/CMakeLists.txt | 7 ++- src/script/clientscripting.cpp | 54 ++++++++++++++++ src/script/clientscripting.h | 40 ++++++++++++ src/script/cpp_api/CMakeLists.txt | 1 + src/script/cpp_api/s_base.cpp | 21 +++++-- src/script/cpp_api/s_base.h | 14 ++++- src/script/cpp_api/s_client.cpp | 61 ++++++++++++++++++ src/script/cpp_api/s_client.h | 36 +++++++++++ src/script/cpp_api/s_security.cpp | 12 ++-- src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_client.cpp | 33 ++++++++++ src/script/lua_api/l_client.h | 36 +++++++++++ src/script/lua_api/l_env.cpp | 6 +- src/script/lua_api/l_env.h | 2 +- src/script/lua_api/l_object.cpp | 2 +- src/script/scripting_game.cpp | 119 ------------------------------------ src/script/scripting_game.h | 57 ----------------- src/script/serverscripting.cpp | 119 ++++++++++++++++++++++++++++++++++++ src/script/serverscripting.h | 57 +++++++++++++++++ src/server.cpp | 6 +- src/server.h | 10 +-- src/serverenvironment.cpp | 4 +- src/serverenvironment.h | 8 +-- src/unittest/test.cpp | 9 ++- 39 files changed, 655 insertions(+), 231 deletions(-) create mode 100644 builtin/client/init.lua create mode 100644 builtin/client/register.lua create mode 100644 src/script/clientscripting.cpp create mode 100644 src/script/clientscripting.h create mode 100644 src/script/cpp_api/s_client.cpp create mode 100644 src/script/cpp_api/s_client.h create mode 100644 src/script/lua_api/l_client.cpp create mode 100644 src/script/lua_api/l_client.h delete mode 100644 src/script/scripting_game.cpp delete mode 100644 src/script/scripting_game.h create mode 100644 src/script/serverscripting.cpp create mode 100644 src/script/serverscripting.h diff --git a/builtin/client/init.lua b/builtin/client/init.lua new file mode 100644 index 000000000..d14301ade --- /dev/null +++ b/builtin/client/init.lua @@ -0,0 +1,22 @@ +-- Minetest: builtin/client/init.lua +local scriptpath = core.get_builtin_path()..DIR_DELIM +local clientpath = scriptpath.."client"..DIR_DELIM + +dofile(clientpath .. "register.lua") + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_shutdown(function() + print("shutdown client") +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_receiving_chat_messages(function(message) + print("Received message " .. message) + return false +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_sending_chat_messages(function(message) + print("Sending message " .. message) + return false +end) diff --git a/builtin/client/register.lua b/builtin/client/register.lua new file mode 100644 index 000000000..c793195a1 --- /dev/null +++ b/builtin/client/register.lua @@ -0,0 +1,62 @@ + +core.callback_origins = {} + +function core.run_callbacks(callbacks, mode, ...) + assert(type(callbacks) == "table") + local cb_len = #callbacks + if cb_len == 0 then + if mode == 2 or mode == 3 then + return true + elseif mode == 4 or mode == 5 then + return false + end + end + local ret + for i = 1, cb_len do + local cb_ret = callbacks[i](...) + + if mode == 0 and i == 1 or mode == 1 and i == cb_len then + ret = cb_ret + elseif mode == 2 then + if not cb_ret or i == 1 then + ret = cb_ret + end + elseif mode == 3 then + if cb_ret then + return cb_ret + end + ret = cb_ret + elseif mode == 4 then + if (cb_ret and not ret) or i == 1 then + ret = cb_ret + end + elseif mode == 5 and cb_ret then + return cb_ret + end + end + return ret +end + +-- +-- Callback registration +-- + +local function make_registration() + local t = {} + local registerfunc = function(func) + t[#t + 1] = func + core.callback_origins[func] = { + mod = core.get_current_modname() or "??", + name = debug.getinfo(1, "n").name or "??" + } + --local origin = core.callback_origins[func] + --print(origin.name .. ": " .. origin.mod .. " registering cbk " .. tostring(func)) + end + return t, registerfunc +end + +core.registered_on_shutdown, core.register_on_shutdown = make_registration() +core.registered_on_receiving_chat_messages, core.register_on_receiving_chat_messages = make_registration() +core.registered_on_sending_chat_messages, core.register_on_sending_chat_messages = make_registration() + + diff --git a/builtin/init.lua b/builtin/init.lua index b34ad14a0..590f7fa8c 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -27,6 +27,7 @@ minetest = core -- Load other files local scriptdir = core.get_builtin_path() .. DIR_DELIM local gamepath = scriptdir .. "game" .. DIR_DELIM +local clientpath = scriptdir .. "client" .. DIR_DELIM local commonpath = scriptdir .. "common" .. DIR_DELIM local asyncpath = scriptdir .. "async" .. DIR_DELIM @@ -45,6 +46,8 @@ elseif INIT == "mainmenu" then end elseif INIT == "async" then dofile(asyncpath .. "init.lua") +elseif INIT == "client" then + dofile(clientpath .. "init.lua") else error(("Unrecognized builtin initialization type %s!"):format(tostring(INIT))) end diff --git a/src/client.cpp b/src/client.cpp index 30058a2b0..faf454b35 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -32,28 +32,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client.h" #include "network/clientopcodes.h" #include "filesys.h" -#include "porting.h" #include "mapblock_mesh.h" #include "mapblock.h" #include "minimap.h" -#include "settings.h" +#include "mods.h" #include "profiler.h" #include "gettext.h" -#include "log.h" -#include "nodemetadata.h" -#include "itemdef.h" -#include "shader.h" #include "clientmap.h" #include "clientmedia.h" -#include "sound.h" -#include "IMeshCache.h" -#include "config.h" #include "version.h" #include "drawscene.h" #include "database-sqlite3.h" #include "serialization.h" #include "guiscalingfilter.h" -#include "raycast.h" +#include "script/clientscripting.h" extern gui::IGUIEnvironment* guienv; @@ -269,10 +261,36 @@ Client::Client( m_cache_use_tangent_vertices = m_cache_enable_shaders && ( g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion")); + + m_script = new ClientScripting(this); +} + +void Client::initMods() +{ + std::string script_path = getBuiltinLuaPath() + DIR_DELIM "init.lua"; + + m_script->loadMod(script_path, BUILTIN_MOD_NAME); +} + +const std::string Client::getBuiltinLuaPath() +{ + return porting::path_share + DIR_DELIM + "builtin"; +} + +const std::vector& Client::getMods() const +{ + static std::vector client_modspec_temp; + return client_modspec_temp; +} + +const ModSpec* Client::getModSpec(const std::string &modname) const +{ + return NULL; } void Client::Stop() { + m_script->on_shutdown(); //request all client managed threads to stop m_mesh_update_thread.stop(); // Save local server map @@ -280,6 +298,8 @@ void Client::Stop() infostream << "Local map saving ended." << std::endl; m_localdb->endSave(); } + + delete m_script; } bool Client::isShutdown() @@ -1553,6 +1573,11 @@ void Client::typeChatMessage(const std::wstring &message) if(message == L"") return; + // If message was ate by script API, don't send it to server + if (m_script->on_sending_message(wide_to_utf8(message))) { + return; + } + // Send to others sendChatMessage(message); diff --git a/src/client.h b/src/client.h index b33358d94..2fdade61a 100644 --- a/src/client.h +++ b/src/client.h @@ -305,6 +305,8 @@ private: std::map m_packets; }; +class ClientScripting; + class Client : public con::PeerHandler, public InventoryManager, public IGameDef { public: @@ -328,6 +330,8 @@ public: ~Client(); + void initMods(); + /* request all threads managed by client to be stopped */ @@ -428,6 +432,10 @@ public: ClientEnvironment& getEnv() { return m_env; } ITextureSource *tsrc() { return getTextureSource(); } ISoundManager *sound() { return getSoundManager(); } + static const std::string getBuiltinLuaPath(); + + virtual const std::vector &getMods() const; + virtual const ModSpec* getModSpec(const std::string &modname) const; // Causes urgent mesh updates (unlike Map::add/removeNodeWithEvent) void removeNode(v3s16 p); @@ -692,6 +700,8 @@ private: bool m_cache_enable_shaders; bool m_cache_use_tangent_vertices; + ClientScripting *m_script; + DISABLE_CLASS_COPY(Client); }; diff --git a/src/content_abm.cpp b/src/content_abm.cpp index ee444ae77..2ab3a968c 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "mapblock.h" // For getNodeBlockPos #include "map.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "log.h" void add_legacy_abms(ServerEnvironment *env, INodeDefManager *nodedef) { diff --git a/src/content_sao.cpp b/src/content_sao.cpp index d4a218505..93662b035 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "remoteplayer.h" #include "server.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "genericobject.h" std::map ServerActiveObject::m_types; diff --git a/src/emerge.cpp b/src/emerge.cpp index 1c9719c48..8719a9eb3 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mg_schematic.h" #include "nodedef.h" #include "profiler.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "server.h" #include "serverobject.h" #include "settings.h" diff --git a/src/environment.cpp b/src/environment.cpp index 8c1aad9d3..737d93ecd 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "collision.h" #include "serverobject.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "server.h" #include "daynightratio.h" #include "emerge.h" diff --git a/src/game.cpp b/src/game.cpp index 55b2ccec9..9868142f7 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -2222,6 +2222,8 @@ bool Game::connectToServer(const std::string &playername, fps_control.last_time = device->getTimer()->getTime(); + client->initMods(); + while (device->run()) { limitFps(&fps_control, &dtime); diff --git a/src/gamedef.h b/src/gamedef.h index cb624bd6a..16b53e24f 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -39,6 +39,7 @@ namespace irr { namespace scene { class ISceneManager; }} +struct ModSpec; /* An interface for fetching game-global definitions like tool and mapnode properties @@ -68,7 +69,11 @@ public: ICraftDefManager *cdef() { return getCraftDefManager(); } MtEventManager *event() { return getEventManager(); } - IRollbackManager *rollback() { return getRollbackManager();} + IRollbackManager *rollback() { return getRollbackManager(); } + + virtual const std::vector &getMods() const = 0; + virtual const ModSpec* getModSpec(const std::string &modname) const = 0; + virtual std::string getWorldPath() const { return ""; } }; #endif diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index ae3fad7c6..19cac6241 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "gettime.h" #include "gettext.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "porting.h" #include "settings.h" #include "client.h" diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 469e7396b..6ebc2994b 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" #include "log.h" #include "serverenvironment.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "serverobject.h" #include "settings.h" #include "craftdef.h" diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index b11f73e86..f1c44c7d8 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "util/strfnd.h" #include "network/clientopcodes.h" +#include "script/clientscripting.h" #include "util/serialize.h" #include "util/srp.h" #include "tileanimation.h" @@ -411,7 +412,10 @@ void Client::handleCommand_ChatMessage(NetworkPacket* pkt) message += (wchar_t)read_wchar; } - m_chat_queue.push(message); + // If chat message not consummed by client lua API + if (!m_script->on_receiving_message(wide_to_utf8(message))) { + m_chat_queue.push(message); + } } void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt) diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index ac428e8ed..b707c6fad 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "player.h" #include "rollback_interface.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "settings.h" #include "tool.h" #include "version.h" diff --git a/src/script/CMakeLists.txt b/src/script/CMakeLists.txt index 5ef672ca9..c96ccc816 100644 --- a/src/script/CMakeLists.txt +++ b/src/script/CMakeLists.txt @@ -3,16 +3,17 @@ add_subdirectory(cpp_api) add_subdirectory(lua_api) # Used by server and client -set(common_SCRIPT_SRCS - ${CMAKE_CURRENT_SOURCE_DIR}/scripting_game.cpp +set(common_SCRIPT_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/serverscripting.cpp ${common_SCRIPT_COMMON_SRCS} ${common_SCRIPT_CPP_API_SRCS} ${common_SCRIPT_LUA_API_SRCS} PARENT_SCOPE) # Used by client only -set(client_SCRIPT_SRCS +set(client_SCRIPT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/scripting_mainmenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/clientscripting.cpp ${client_SCRIPT_COMMON_SRCS} ${client_SCRIPT_CPP_API_SRCS} ${client_SCRIPT_LUA_API_SRCS} diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp new file mode 100644 index 000000000..43bc6f94e --- /dev/null +++ b/src/script/clientscripting.cpp @@ -0,0 +1,54 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +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 "clientscripting.h" +#include "client.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_client.h" +#include "lua_api/l_util.h" + +ClientScripting::ClientScripting(Client *client): + ScriptApiBase() +{ + setGameDef(client); + + SCRIPTAPI_PRECHECKHEADER + + // Security is mandatory client side + initializeSecurity(); + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "client"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized client game modules" << std::endl; +} + +void ClientScripting::InitializeModApi(lua_State *L, int top) +{ + ModApiUtil::Initialize(L, top); + ModApiClient::Initialize(L, top); +} diff --git a/src/script/clientscripting.h b/src/script/clientscripting.h new file mode 100644 index 000000000..e2a91f695 --- /dev/null +++ b/src/script/clientscripting.h @@ -0,0 +1,40 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef CLIENT_SCRIPTING_H_ +#define CLIENT_SCRIPTING_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_client.h" +#include "cpp_api/s_security.h" + +class Client; +class ClientScripting: + virtual public ScriptApiBase, + public ScriptApiSecurity, + public ScriptApiClient +{ +public: + ClientScripting(Client *client); + +private: + virtual void InitializeModApi(lua_State *L, int top); +}; +#endif diff --git a/src/script/cpp_api/CMakeLists.txt b/src/script/cpp_api/CMakeLists.txt index be4d0131e..4b13356a8 100644 --- a/src/script/cpp_api/CMakeLists.txt +++ b/src/script/cpp_api/CMakeLists.txt @@ -13,6 +13,7 @@ set(common_SCRIPT_CPP_API_SRCS PARENT_SCOPE) set(client_SCRIPT_CPP_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/s_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/s_mainmenu.cpp PARENT_SCOPE) diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index cbe5735a7..6a843810f 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -23,12 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_object.h" #include "common/c_converter.h" #include "serverobject.h" -#include "debug.h" #include "filesys.h" -#include "log.h" #include "mods.h" #include "porting.h" #include "util/string.h" +#include "server.h" +#ifndef SERVER +#include "client.h" +#endif extern "C" { @@ -69,7 +71,8 @@ public: */ ScriptApiBase::ScriptApiBase() : - m_luastackmutex() + m_luastackmutex(), + m_gamedef(NULL) { #ifdef SCRIPTAPI_LOCK_DEBUG m_lock_recursion_count = 0; @@ -113,7 +116,6 @@ ScriptApiBase::ScriptApiBase() : // Default to false otherwise m_secure = false; - m_server = NULL; m_environment = NULL; m_guiengine = NULL; } @@ -333,3 +335,14 @@ void ScriptApiBase::objectrefGet(lua_State *L, u16 id) lua_remove(L, -2); // object_refs lua_remove(L, -2); // core } + +Server* ScriptApiBase::getServer() +{ + return dynamic_cast(m_gamedef); +} +#ifndef SERVER +Client* ScriptApiBase::getClient() +{ + return dynamic_cast(m_gamedef); +} +#endif diff --git a/src/script/cpp_api/s_base.h b/src/script/cpp_api/s_base.h index c27235255..19d71df65 100644 --- a/src/script/cpp_api/s_base.h +++ b/src/script/cpp_api/s_base.h @@ -55,6 +55,10 @@ extern "C" { setOriginFromTableRaw(index, __FUNCTION__) class Server; +#ifndef SERVER +class Client; +#endif +class IGameDef; class Environment; class GUIEngine; class ServerActiveObject; @@ -75,7 +79,11 @@ public: void addObjectReference(ServerActiveObject *cobj); void removeObjectReference(ServerActiveObject *cobj); - Server* getServer() { return m_server; } + IGameDef *getGameDef() { return m_gamedef; } + Server* getServer(); +#ifndef SERVER + Client* getClient(); +#endif std::string getOrigin() { return m_last_run_mod; } void setOriginDirect(const char *origin); @@ -98,7 +106,7 @@ protected: void scriptError(int result, const char *fxn); void stackDump(std::ostream &o); - void setServer(Server* server) { m_server = server; } + void setGameDef(IGameDef* gamedef) { m_gamedef = gamedef; } Environment* getEnv() { return m_environment; } void setEnv(Environment* env) { m_environment = env; } @@ -122,7 +130,7 @@ private: lua_State* m_luastack; - Server* m_server; + IGameDef* m_gamedef; Environment* m_environment; GUIEngine* m_guiengine; }; diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp new file mode 100644 index 000000000..08af8ebdc --- /dev/null +++ b/src/script/cpp_api/s_client.cpp @@ -0,0 +1,61 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +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 "s_client.h" +#include "s_internal.h" + +void ScriptApiClient::on_shutdown() +{ + SCRIPTAPI_PRECHECKHEADER + + // Get registered shutdown hooks + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_shutdown"); + // Call callbacks + runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); +} + +bool ScriptApiClient::on_sending_message(const std::string &message) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_chat_messages + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_sending_chat_messages"); + // Call callbacks + lua_pushstring(L, message.c_str()); + runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC); + bool ate = lua_toboolean(L, -1); + return ate; +} + +bool ScriptApiClient::on_receiving_message(const std::string &message) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_chat_messages + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_receiving_chat_messages"); + // Call callbacks + lua_pushstring(L, message.c_str()); + runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC); + bool ate = lua_toboolean(L, -1); + return ate; +} diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h new file mode 100644 index 000000000..08fdd8fc0 --- /dev/null +++ b/src/script/cpp_api/s_client.h @@ -0,0 +1,36 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef S_CLIENT_H_ +#define S_CLIENT_H_ + +#include "cpp_api/s_base.h" + +class ScriptApiClient: virtual public ScriptApiBase +{ +public: + // Calls on_shutdown handlers + void on_shutdown(); + + // Chat message handlers + bool on_sending_message(const std::string &message); + bool on_receiving_message(const std::string &message); +}; +#endif diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index be2b884cc..f85cd0c9c 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -382,9 +382,9 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_SCRIPTAPI); ScriptApiBase *script = (ScriptApiBase *) lua_touserdata(L, -1); lua_pop(L, 1); - const Server *server = script->getServer(); - - if (!server) return false; + const IGameDef *gamedef = script->getGameDef(); + if (!gamedef) + return false; // Get mod name lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); @@ -400,7 +400,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, // Allow paths in mod path // Don't bother if write access isn't important, since it will be handled later if (write_required || write_allowed != NULL) { - const ModSpec *mod = server->getModSpec(mod_name); + const ModSpec *mod = gamedef->getModSpec(mod_name); if (mod) { str = fs::AbsolutePath(mod->path); if (!str.empty() && fs::PathStartsWith(abs_path, str)) { @@ -414,7 +414,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, // Allow read-only access to all mod directories if (!write_required) { - const std::vector mods = server->getMods(); + const std::vector mods = gamedef->getMods(); for (size_t i = 0; i < mods.size(); ++i) { str = fs::AbsolutePath(mods[i].path); if (!str.empty() && fs::PathStartsWith(abs_path, str)) { @@ -423,7 +423,7 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, } } - str = fs::AbsolutePath(server->getWorldPath()); + str = fs::AbsolutePath(gamedef->getWorldPath()); if (!str.empty()) { // Don't allow access to other paths in the world mod/game path. // These have to be blocked so you can't override a trusted mod diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index e82560696..ea3d75ffa 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -23,5 +23,6 @@ set(common_SCRIPT_LUA_API_SRCS PARENT_SCOPE) set(client_SCRIPT_LUA_API_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/l_client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_mainmenu.cpp PARENT_SCOPE) diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp new file mode 100644 index 000000000..9c478602a --- /dev/null +++ b/src/script/lua_api/l_client.cpp @@ -0,0 +1,33 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +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 "l_client.h" +#include "l_internal.h" + +int ModApiClient::l_get_current_modname(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + return 1; +} + +void ModApiClient::Initialize(lua_State *L, int top) +{ + API_FCT(get_current_modname); +} diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h new file mode 100644 index 000000000..332f00132 --- /dev/null +++ b/src/script/lua_api/l_client.h @@ -0,0 +1,36 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017 nerzhul, Loic Blot + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef L_CLIENT_H_ +#define L_CLIENT_H_ + +#include "lua_api/l_base.h" + +class ModApiClient : public ModApiBase +{ +private: + // get_current_modname() + static int l_get_current_modname(lua_State *L); + +public: + static void Initialize(lua_State *L, int top); +}; + +#endif diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 2722e35a4..442c4b99a 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_vmanip.h" #include "common/c_converter.h" #include "common/c_content.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "environment.h" #include "server.h" #include "nodedef.h" @@ -49,7 +49,7 @@ struct EnumString ModApiEnvMod::es_ClearObjectsMode[] = void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, u32 active_object_count, u32 active_object_count_wider) { - GameScripting *scriptIface = env->getScriptIface(); + ServerScripting *scriptIface = env->getScriptIface(); scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); @@ -92,7 +92,7 @@ void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, MapNode n) { - GameScripting *scriptIface = env->getScriptIface(); + ServerScripting *scriptIface = env->getScriptIface(); scriptIface->realityCheck(); lua_State *L = scriptIface->getStack(); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 21b235f84..322959411 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -242,7 +242,7 @@ public: }; struct ScriptCallbackState { - GameScripting *script; + ServerScripting *script; int callback_ref; int args_ref; unsigned int refcount; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 9352812ab..be454ad45 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "server.h" #include "hud.h" -#include "scripting_game.h" +#include "serverscripting.h" struct EnumString es_HudElementType[] = { diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp deleted file mode 100644 index 4da752263..000000000 --- a/src/script/scripting_game.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include "scripting_game.h" -#include "server.h" -#include "log.h" -#include "settings.h" -#include "cpp_api/s_internal.h" -#include "lua_api/l_areastore.h" -#include "lua_api/l_base.h" -#include "lua_api/l_craft.h" -#include "lua_api/l_env.h" -#include "lua_api/l_inventory.h" -#include "lua_api/l_item.h" -#include "lua_api/l_itemstackmeta.h" -#include "lua_api/l_mapgen.h" -#include "lua_api/l_nodemeta.h" -#include "lua_api/l_nodetimer.h" -#include "lua_api/l_noise.h" -#include "lua_api/l_object.h" -#include "lua_api/l_particles.h" -#include "lua_api/l_rollback.h" -#include "lua_api/l_server.h" -#include "lua_api/l_util.h" -#include "lua_api/l_vmanip.h" -#include "lua_api/l_settings.h" -#include "lua_api/l_http.h" -#include "lua_api/l_storage.h" - -extern "C" { -#include "lualib.h" -} - -GameScripting::GameScripting(Server* server) -{ - setServer(server); - - // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() - // once the environment has been created - - SCRIPTAPI_PRECHECKHEADER - - if (g_settings->getBool("secure.enable_security")) { - initializeSecurity(); - } - - lua_getglobal(L, "core"); - int top = lua_gettop(L); - - lua_newtable(L); - lua_setfield(L, -2, "object_refs"); - - lua_newtable(L); - lua_setfield(L, -2, "luaentities"); - - // Initialize our lua_api modules - InitializeModApi(L, top); - lua_pop(L, 1); - - // Push builtin initialization type - lua_pushstring(L, "game"); - lua_setglobal(L, "INIT"); - - infostream << "SCRIPTAPI: Initialized game modules" << std::endl; -} - -void GameScripting::InitializeModApi(lua_State *L, int top) -{ - // Initialize mod api modules - ModApiCraft::Initialize(L, top); - ModApiEnvMod::Initialize(L, top); - ModApiInventory::Initialize(L, top); - ModApiItemMod::Initialize(L, top); - ModApiMapgen::Initialize(L, top); - ModApiParticles::Initialize(L, top); - ModApiRollback::Initialize(L, top); - ModApiServer::Initialize(L, top); - ModApiUtil::Initialize(L, top); - ModApiHttp::Initialize(L, top); - ModApiStorage::Initialize(L, top); - - // Register reference classes (userdata) - InvRef::Register(L); - ItemStackMetaRef::Register(L); - LuaAreaStore::Register(L); - LuaItemStack::Register(L); - LuaPerlinNoise::Register(L); - LuaPerlinNoiseMap::Register(L); - LuaPseudoRandom::Register(L); - LuaPcgRandom::Register(L); - LuaSecureRandom::Register(L); - LuaVoxelManip::Register(L); - NodeMetaRef::Register(L); - NodeTimerRef::Register(L); - ObjectRef::Register(L); - LuaSettings::Register(L); - StorageRef::Register(L); -} - -void log_deprecated(const std::string &message) -{ - log_deprecated(NULL, message); -} diff --git a/src/script/scripting_game.h b/src/script/scripting_game.h deleted file mode 100644 index 970b3e80d..000000000 --- a/src/script/scripting_game.h +++ /dev/null @@ -1,57 +0,0 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#ifndef SCRIPTING_GAME_H_ -#define SCRIPTING_GAME_H_ - -#include "cpp_api/s_base.h" -#include "cpp_api/s_entity.h" -#include "cpp_api/s_env.h" -#include "cpp_api/s_inventory.h" -#include "cpp_api/s_node.h" -#include "cpp_api/s_player.h" -#include "cpp_api/s_server.h" -#include "cpp_api/s_security.h" - -/*****************************************************************************/ -/* Scripting <-> Game Interface */ -/*****************************************************************************/ - -class GameScripting : - virtual public ScriptApiBase, - public ScriptApiDetached, - public ScriptApiEntity, - public ScriptApiEnv, - public ScriptApiNode, - public ScriptApiPlayer, - public ScriptApiServer, - public ScriptApiSecurity -{ -public: - GameScripting(Server* server); - - // use ScriptApiBase::loadMod() to load mods - -private: - void InitializeModApi(lua_State *L, int top); -}; - -void log_deprecated(const std::string &message); - -#endif /* SCRIPTING_GAME_H_ */ diff --git a/src/script/serverscripting.cpp b/src/script/serverscripting.cpp new file mode 100644 index 000000000..215b2cfd7 --- /dev/null +++ b/src/script/serverscripting.cpp @@ -0,0 +1,119 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "serverscripting.h" +#include "server.h" +#include "log.h" +#include "settings.h" +#include "cpp_api/s_internal.h" +#include "lua_api/l_areastore.h" +#include "lua_api/l_base.h" +#include "lua_api/l_craft.h" +#include "lua_api/l_env.h" +#include "lua_api/l_inventory.h" +#include "lua_api/l_item.h" +#include "lua_api/l_itemstackmeta.h" +#include "lua_api/l_mapgen.h" +#include "lua_api/l_nodemeta.h" +#include "lua_api/l_nodetimer.h" +#include "lua_api/l_noise.h" +#include "lua_api/l_object.h" +#include "lua_api/l_particles.h" +#include "lua_api/l_rollback.h" +#include "lua_api/l_server.h" +#include "lua_api/l_util.h" +#include "lua_api/l_vmanip.h" +#include "lua_api/l_settings.h" +#include "lua_api/l_http.h" +#include "lua_api/l_storage.h" + +extern "C" { +#include "lualib.h" +} + +ServerScripting::ServerScripting(Server* server) +{ + setGameDef(server); + + // setEnv(env) is called by ScriptApiEnv::initializeEnvironment() + // once the environment has been created + + SCRIPTAPI_PRECHECKHEADER + + if (g_settings->getBool("secure.enable_security")) { + initializeSecurity(); + } + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + lua_newtable(L); + lua_setfield(L, -2, "object_refs"); + + lua_newtable(L); + lua_setfield(L, -2, "luaentities"); + + // Initialize our lua_api modules + InitializeModApi(L, top); + lua_pop(L, 1); + + // Push builtin initialization type + lua_pushstring(L, "game"); + lua_setglobal(L, "INIT"); + + infostream << "SCRIPTAPI: Initialized game modules" << std::endl; +} + +void ServerScripting::InitializeModApi(lua_State *L, int top) +{ + // Initialize mod api modules + ModApiCraft::Initialize(L, top); + ModApiEnvMod::Initialize(L, top); + ModApiInventory::Initialize(L, top); + ModApiItemMod::Initialize(L, top); + ModApiMapgen::Initialize(L, top); + ModApiParticles::Initialize(L, top); + ModApiRollback::Initialize(L, top); + ModApiServer::Initialize(L, top); + ModApiUtil::Initialize(L, top); + ModApiHttp::Initialize(L, top); + ModApiStorage::Initialize(L, top); + + // Register reference classes (userdata) + InvRef::Register(L); + ItemStackMetaRef::Register(L); + LuaAreaStore::Register(L); + LuaItemStack::Register(L); + LuaPerlinNoise::Register(L); + LuaPerlinNoiseMap::Register(L); + LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); + LuaSecureRandom::Register(L); + LuaVoxelManip::Register(L); + NodeMetaRef::Register(L); + NodeTimerRef::Register(L); + ObjectRef::Register(L); + LuaSettings::Register(L); + StorageRef::Register(L); +} + +void log_deprecated(const std::string &message) +{ + log_deprecated(NULL, message); +} diff --git a/src/script/serverscripting.h b/src/script/serverscripting.h new file mode 100644 index 000000000..fd97ea40b --- /dev/null +++ b/src/script/serverscripting.h @@ -0,0 +1,57 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef SERVER_SCRIPTING_H_ +#define SERVER_SCRIPTING_H_ + +#include "cpp_api/s_base.h" +#include "cpp_api/s_entity.h" +#include "cpp_api/s_env.h" +#include "cpp_api/s_inventory.h" +#include "cpp_api/s_node.h" +#include "cpp_api/s_player.h" +#include "cpp_api/s_server.h" +#include "cpp_api/s_security.h" + +/*****************************************************************************/ +/* Scripting <-> Server Game Interface */ +/*****************************************************************************/ + +class ServerScripting: + virtual public ScriptApiBase, + public ScriptApiDetached, + public ScriptApiEntity, + public ScriptApiEnv, + public ScriptApiNode, + public ScriptApiPlayer, + public ScriptApiServer, + public ScriptApiSecurity +{ +public: + ServerScripting(Server* server); + + // use ScriptApiBase::loadMod() to load mods + +private: + void InitializeModApi(lua_State *L, int top); +}; + +void log_deprecated(const std::string &message); + +#endif /* SCRIPTING_GAME_H_ */ diff --git a/src/server.cpp b/src/server.cpp index 8b9f46f85..3adbf40cc 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -38,7 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "log.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -269,7 +269,7 @@ Server::Server( // Initialize scripting infostream<<"Server: Initializing Lua"< &modlist) modlist.push_back(it->name); } -std::string Server::getBuiltinLuaPath() +const std::string Server::getBuiltinLuaPath() { return porting::path_share + DIR_DELIM + "builtin"; } diff --git a/src/server.h b/src/server.h index 3eee67b78..417d31bd8 100644 --- a/src/server.h +++ b/src/server.h @@ -53,7 +53,7 @@ class PlayerSAO; class IRollbackManager; struct RollbackAction; class EmergeManager; -class GameScripting; +class ServerScripting; class ServerEnvironment; struct SimpleSoundSpec; class ServerThread; @@ -274,7 +274,7 @@ public: Inventory* createDetachedInventory(const std::string &name, const std::string &player=""); // Envlock and conlock should be locked when using scriptapi - GameScripting *getScriptIface(){ return m_script; } + ServerScripting *getScriptIface(){ return m_script; } // actions: time-reversed list // Return value: success/failure @@ -295,8 +295,8 @@ public: IWritableNodeDefManager* getWritableNodeDefManager(); IWritableCraftDefManager* getWritableCraftDefManager(); - const std::vector &getMods() const { return m_mods; } - const ModSpec* getModSpec(const std::string &modname) const; + virtual const std::vector &getMods() const { return m_mods; } + virtual const ModSpec* getModSpec(const std::string &modname) const; void getModNames(std::vector &modlist); std::string getBuiltinLuaPath(); inline const std::string &getWorldPath() const { return m_path_world; } @@ -540,7 +540,7 @@ private: // Scripting // Envlock and conlock should be locked when using Lua - GameScripting *m_script; + ServerScripting *m_script; // Item definition manager IWritableItemDefManager *m_itemdef; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index f3f489092..ecc7c3150 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "raycast.h" #include "remoteplayer.h" -#include "scripting_game.h" +#include "serverscripting.h" #include "server.h" #include "voxelalgorithms.h" #include "util/serialize.h" @@ -352,7 +352,7 @@ void ActiveBlockList::update(std::vector &active_positions, */ ServerEnvironment::ServerEnvironment(ServerMap *map, - GameScripting *scriptIface, Server *server, + ServerScripting *scriptIface, Server *server, const std::string &path_world) : m_map(map), m_script(scriptIface), diff --git a/src/serverenvironment.h b/src/serverenvironment.h index b7056c00c..b7796b5f1 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -33,7 +33,7 @@ class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; class Server; -class GameScripting; +class ServerScripting; /* {Active, Loading} block modifier interface. @@ -194,7 +194,7 @@ typedef UNORDERED_MAP ActiveObjectMap; class ServerEnvironment : public Environment { public: - ServerEnvironment(ServerMap *map, GameScripting *scriptIface, + ServerEnvironment(ServerMap *map, ServerScripting *scriptIface, Server *server, const std::string &path_world); ~ServerEnvironment(); @@ -203,7 +203,7 @@ public: ServerMap & getServerMap(); //TODO find way to remove this fct! - GameScripting* getScriptIface() + ServerScripting* getScriptIface() { return m_script; } Server *getGameDef() @@ -381,7 +381,7 @@ private: // The map ServerMap *m_map; // Lua state - GameScripting* m_script; + ServerScripting* m_script; // Server definition Server *m_server; // World path diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 41ccf0d2d..9beb0afa6 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -19,10 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "test.h" -#include "log.h" #include "nodedef.h" #include "itemdef.h" #include "gamedef.h" +#include "mods.h" content_t t_CONTENT_STONE; content_t t_CONTENT_GRASS; @@ -59,6 +59,13 @@ public: void defineSomeNodes(); + virtual const std::vector &getMods() const + { + static std::vector testmodspec; + return testmodspec; + } + virtual const ModSpec* getModSpec(const std::string &modname) const { return NULL; } + private: IItemDefManager *m_itemdef; INodeDefManager *m_nodedef; -- cgit v1.2.3 From cb3a61f8db6b7020dd69f7786a1086f6fe014dfc Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 21 Jan 2017 21:44:37 +0000 Subject: [CSM] Add method that display chat to client-sided lua. (#5089) (#5091) * squashed: [Client-sided scripting] Don't register functions that don't work. (#5091) --- src/client.cpp | 6 +++--- src/client.h | 5 +++++ src/network/clientpackethandler.cpp | 4 ++-- src/script/clientscripting.cpp | 2 +- src/script/lua_api/l_base.cpp | 6 ++++++ src/script/lua_api/l_base.h | 8 ++++++++ src/script/lua_api/l_client.cpp | 12 ++++++++++++ src/script/lua_api/l_client.h | 1 + src/script/lua_api/l_util.cpp | 26 ++++++++++++++++++++++++++ src/script/lua_api/l_util.h | 2 ++ 10 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index faf454b35..0af6b4595 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1584,7 +1584,7 @@ void Client::typeChatMessage(const std::wstring &message) // Show locally if (message[0] == L'/') { - m_chat_queue.push((std::wstring)L"issued command: " + message); + pushToChatQueue((std::wstring)L"issued command: " + message); } else { @@ -1593,7 +1593,7 @@ void Client::typeChatMessage(const std::wstring &message) LocalPlayer *player = m_env.getLocalPlayer(); assert(player != NULL); std::wstring name = narrow_to_wide(player->getName()); - m_chat_queue.push((std::wstring)L"<" + name + L"> " + message); + pushToChatQueue((std::wstring)L"<" + name + L"> " + message); } } } @@ -1867,7 +1867,7 @@ void Client::makeScreenshot(IrrlichtDevice *device) } else { sstr << "Failed to save screenshot '" << filename << "'"; } - m_chat_queue.push(narrow_to_wide(sstr.str())); + pushToChatQueue(narrow_to_wide(sstr.str())); infostream << sstr.str() << std::endl; image->drop(); } diff --git a/src/client.h b/src/client.h index 2fdade61a..584b13a90 100644 --- a/src/client.h +++ b/src/client.h @@ -557,6 +557,11 @@ public: void makeScreenshot(IrrlichtDevice *device); + inline void pushToChatQueue(const std::wstring &input) + { + m_chat_queue.push(input); + } + private: // Virtual methods from con::PeerHandler diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index f1c44c7d8..d0642c86a 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -142,7 +142,7 @@ void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt) } void Client::handleCommand_DenySudoMode(NetworkPacket* pkt) { - m_chat_queue.push(L"Password change denied. Password NOT changed."); + pushToChatQueue(L"Password change denied. Password NOT changed."); // reset everything and be sad deleteAuthData(); } @@ -414,7 +414,7 @@ void Client::handleCommand_ChatMessage(NetworkPacket* pkt) // If chat message not consummed by client lua API if (!m_script->on_receiving_message(wide_to_utf8(message))) { - m_chat_queue.push(message); + pushToChatQueue(message); } } diff --git a/src/script/clientscripting.cpp b/src/script/clientscripting.cpp index 43bc6f94e..9bf93eb83 100644 --- a/src/script/clientscripting.cpp +++ b/src/script/clientscripting.cpp @@ -49,6 +49,6 @@ ClientScripting::ClientScripting(Client *client): void ClientScripting::InitializeModApi(lua_State *L, int top) { - ModApiUtil::Initialize(L, top); + ModApiUtil::InitializeClient(L, top); ModApiClient::Initialize(L, top); } diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index 515a7d933..f2703718a 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -37,6 +37,12 @@ Server *ModApiBase::getServer(lua_State *L) return getScriptApiBase(L)->getServer(); } +#ifndef SERVER +Client *ModApiBase::getClient(lua_State *L) +{ + return getScriptApiBase(L)->getClient(); +} +#endif Environment *ModApiBase::getEnv(lua_State *L) { return getScriptApiBase(L)->getEnv(); diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index 641013dfd..dc1b1b226 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -28,6 +28,10 @@ extern "C" { #include } +#ifndef SERVER +#include "client.h" +#endif + class ScriptApiBase; class Server; class Environment; @@ -38,6 +42,10 @@ class ModApiBase { public: static ScriptApiBase* getScriptApiBase(lua_State *L); static Server* getServer(lua_State *L); + #ifndef SERVER + static Client* getClient(lua_State *L); + #endif // !SERVER + static Environment* getEnv(lua_State *L); static GUIEngine* getGuiEngine(lua_State *L); // When we are not loading the mod, this function returns "." diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index 9c478602a..f4c3812ac 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "l_client.h" #include "l_internal.h" +#include "util/string.h" int ModApiClient::l_get_current_modname(lua_State *L) { @@ -27,7 +28,18 @@ int ModApiClient::l_get_current_modname(lua_State *L) return 1; } +// display_chat_message(message) +int ModApiClient::l_display_chat_message(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + std::string message = luaL_checkstring(L, 1); + getClient(L)->pushToChatQueue(utf8_to_wide(message)); + return 1; +} + void ModApiClient::Initialize(lua_State *L, int top) { API_FCT(get_current_modname); + API_FCT(display_chat_message); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index 332f00132..b4a57cb61 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -28,6 +28,7 @@ class ModApiClient : public ModApiBase private: // get_current_modname() static int l_get_current_modname(lua_State *L); + static int l_display_chat_message(lua_State *L); public: static void Initialize(lua_State *L, int top); diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index c26791646..277a874bf 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -526,6 +526,32 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(get_version); } +void ModApiUtil::InitializeClient(lua_State *L, int top) +{ + API_FCT(log); + + API_FCT(setting_set); + API_FCT(setting_get); + API_FCT(setting_setbool); + API_FCT(setting_getbool); + API_FCT(setting_save); + + API_FCT(parse_json); + API_FCT(write_json); + + API_FCT(is_yes); + + API_FCT(get_builtin_path); + + API_FCT(compress); + API_FCT(decompress); + + API_FCT(encode_base64); + API_FCT(decode_base64); + + API_FCT(get_version); +} + void ModApiUtil::InitializeAsync(AsyncEngine& engine) { ASYNC_API_FCT(log); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 9910704b3..eef32c0a1 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -110,6 +110,8 @@ private: public: static void Initialize(lua_State *L, int top); + static void InitializeClient(lua_State *L, int top); + static void InitializeAsync(AsyncEngine& engine); }; -- cgit v1.2.3 From 9978f5af828550d819890fed1fc56d65838a2c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sun, 22 Jan 2017 00:20:55 +0100 Subject: [CSM] Add on_death, on_hp_modification & oh_damage_taken callbacks (#5093) * Add on_death callback * Add on_hp_modification & on_damage_taken callbacks * move preview code to preview.lua --- builtin/client/init.lua | 17 +++-------------- builtin/client/preview.lua | 24 ++++++++++++++++++++++++ builtin/client/register.lua | 3 +++ src/client.h | 2 ++ src/game.cpp | 7 +++---- src/network/clientpackethandler.cpp | 2 ++ src/script/cpp_api/s_client.cpp | 35 +++++++++++++++++++++++++++++++++++ src/script/cpp_api/s_client.h | 4 ++++ 8 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 builtin/client/preview.lua diff --git a/builtin/client/init.lua b/builtin/client/init.lua index d14301ade..e06dfc995 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -3,20 +3,9 @@ local scriptpath = core.get_builtin_path()..DIR_DELIM local clientpath = scriptpath.."client"..DIR_DELIM dofile(clientpath .. "register.lua") +dofile(clientpath .. "preview.lua") --- This is an example function to ensure it's working properly, should be removed before merge -core.register_on_shutdown(function() - print("shutdown client") +core.register_on_death(function() + core.display_chat_message("You died.") end) --- This is an example function to ensure it's working properly, should be removed before merge -core.register_on_receiving_chat_messages(function(message) - print("Received message " .. message) - return false -end) - --- This is an example function to ensure it's working properly, should be removed before merge -core.register_on_sending_chat_messages(function(message) - print("Sending message " .. message) - return false -end) diff --git a/builtin/client/preview.lua b/builtin/client/preview.lua new file mode 100644 index 000000000..4b277b0c6 --- /dev/null +++ b/builtin/client/preview.lua @@ -0,0 +1,24 @@ +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_shutdown(function() + print("shutdown client") +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_receiving_chat_messages(function(message) + print("[PREVIEW] Received message " .. message) + return false +end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_on_sending_chat_messages(function(message) + print("[PREVIEW] Sending message " .. message) + return false +end) + +core.register_on_hp_modification(function(hp) + print("[PREVIEW] HP modified " .. hp) +end) + +core.register_on_damage_taken(function(hp) + print("[PREVIEW] Damage taken " .. hp) +end) diff --git a/builtin/client/register.lua b/builtin/client/register.lua index c793195a1..ddaf4f424 100644 --- a/builtin/client/register.lua +++ b/builtin/client/register.lua @@ -58,5 +58,8 @@ end core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_receiving_chat_messages, core.register_on_receiving_chat_messages = make_registration() core.registered_on_sending_chat_messages, core.register_on_sending_chat_messages = make_registration() +core.registered_on_death, core.register_on_death = make_registration() +core.registered_on_hp_modification, core.register_on_hp_modification = make_registration() +core.registered_on_damage_taken, core.register_on_damage_taken = make_registration() diff --git a/src/client.h b/src/client.h index 584b13a90..21aeb0575 100644 --- a/src/client.h +++ b/src/client.h @@ -562,6 +562,8 @@ public: m_chat_queue.push(input); } + ClientScripting *getScript() { return m_script; } + private: // Virtual methods from con::PeerHandler diff --git a/src/game.cpp b/src/game.cpp index 9868142f7..2e2a8e0c1 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -41,7 +41,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiKeyChangeMenu.h" #include "guiPasswordChange.h" #include "guiVolumeChange.h" -#include "hud.h" #include "mainmenumanager.h" #include "mapblock.h" #include "nodedef.h" // Needed for determining pointing to nodes @@ -61,6 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "minimap.h" #include "mapblock_mesh.h" +#include "script/clientscripting.h" #include "sound.h" @@ -3240,8 +3240,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) if (event.type == CE_PLAYER_DAMAGE && client->getHP() != 0) { - //u16 damage = event.player_damage.amount; - //infostream<<"Player damage: "<getScript()->on_damage_taken(event.player_damage.amount); *damage_flash += 95.0 + 3.2 * event.player_damage.amount; *damage_flash = MYMIN(*damage_flash, 127.0); @@ -3259,7 +3258,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) show_deathscreen(¤t_formspec, client, texture_src, device, &input->joystick); - chat_backend->addMessage(L"", L"You died."); + client->getScript()->on_death(); /* Handle visualization */ *damage_flash = 0; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index d0642c86a..97fa93e95 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -526,6 +526,8 @@ void Client::handleCommand_HP(NetworkPacket* pkt) player->hp = hp; + m_script->on_hp_modification(hp); + if (hp < oldhp) { // Add to ClientEvent queue ClientEvent event; diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index 08af8ebdc..f0676f4c2 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -59,3 +59,38 @@ bool ScriptApiClient::on_receiving_message(const std::string &message) bool ate = lua_toboolean(L, -1); return ate; } + +void ScriptApiClient::on_damage_taken(int32_t damage_amount) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_chat_messages + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_damage_taken"); + // Call callbacks + lua_pushinteger(L, damage_amount); + runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC); +} + +void ScriptApiClient::on_hp_modification(int32_t newhp) +{ + SCRIPTAPI_PRECHECKHEADER + + // Get core.registered_on_chat_messages + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_hp_modification"); + // Call callbacks + lua_pushinteger(L, newhp); + runCallbacks(1, RUN_CALLBACKS_MODE_OR_SC); +} + +void ScriptApiClient::on_death() +{ + SCRIPTAPI_PRECHECKHEADER + + // Get registered shutdown hooks + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_on_death"); + // Call callbacks + runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); +} diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h index 08fdd8fc0..3155c7e67 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -32,5 +32,9 @@ public: // Chat message handlers bool on_sending_message(const std::string &message); bool on_receiving_message(const std::string &message); + + void on_damage_taken(int32_t damage_amount); + void on_hp_modification(int32_t newhp); + void on_death(); }; #endif -- cgit v1.2.3 From d7bc346981e189851e490f2417ed015a38bca79b Mon Sep 17 00:00:00 2001 From: red-001 Date: Sun, 22 Jan 2017 08:05:09 +0000 Subject: [CSM] Add client-sided chat commands (#5092) --- builtin/client/chatcommands.lua | 28 ++++++++++++++++++++++++++++ builtin/client/init.lua | 1 + builtin/client/preview.lua | 7 +++++++ builtin/common/chatcommands.lua | 30 ++++++++++++++++++++++++++++++ builtin/game/chatcommands.lua | 29 +---------------------------- builtin/game/init.lua | 1 + src/client.cpp | 6 +----- src/script/lua_api/l_client.cpp | 23 +++++++++++++++++++++++ src/script/lua_api/l_client.h | 8 ++++++++ 9 files changed, 100 insertions(+), 33 deletions(-) create mode 100644 builtin/client/chatcommands.lua create mode 100644 builtin/common/chatcommands.lua diff --git a/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua new file mode 100644 index 000000000..b49c222ef --- /dev/null +++ b/builtin/client/chatcommands.lua @@ -0,0 +1,28 @@ +-- Minetest: builtin/client/chatcommands.lua + + +core.register_on_sending_chat_messages(function(message) + if not (message:sub(1,1) == "/") then + return false + end + + core.display_chat_message("issued command: " .. message) + + local cmd, param = string.match(message, "^/([^ ]+) *(.*)") + if not param then + param = "" + end + + local cmd_def = core.registered_chatcommands[cmd] + + if cmd_def then + core.set_last_run_mod(cmd_def.mod_origin) + local success, message = cmd_def.func(param) + if message then + core.display_chat_message(message) + end + return true + end + + return false +end) \ No newline at end of file diff --git a/builtin/client/init.lua b/builtin/client/init.lua index e06dfc995..4797ac4b6 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -1,6 +1,7 @@ -- Minetest: builtin/client/init.lua local scriptpath = core.get_builtin_path()..DIR_DELIM local clientpath = scriptpath.."client"..DIR_DELIM +local commonpath = scriptpath.."common"..DIR_DELIM dofile(clientpath .. "register.lua") dofile(clientpath .. "preview.lua") diff --git a/builtin/client/preview.lua b/builtin/client/preview.lua index 4b277b0c6..c421791f5 100644 --- a/builtin/client/preview.lua +++ b/builtin/client/preview.lua @@ -22,3 +22,10 @@ end) core.register_on_damage_taken(function(hp) print("[PREVIEW] Damage taken " .. hp) end) + +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_chatcommand("dump", { + func = function(name, param) + return true, dump(_G) + end, +}) \ No newline at end of file diff --git a/builtin/common/chatcommands.lua b/builtin/common/chatcommands.lua new file mode 100644 index 000000000..ef3a24410 --- /dev/null +++ b/builtin/common/chatcommands.lua @@ -0,0 +1,30 @@ +-- Minetest: builtin/common/chatcommands.lua + +core.registered_chatcommands = {} + +function core.register_chatcommand(cmd, def) + def = def or {} + def.params = def.params or "" + def.description = def.description or "" + def.privs = def.privs or {} + def.mod_origin = core.get_current_modname() or "??" + core.registered_chatcommands[cmd] = def +end + +function core.unregister_chatcommand(name) + if core.registered_chatcommands[name] then + core.registered_chatcommands[name] = nil + else + core.log("warning", "Not unregistering chatcommand " ..name.. + " because it doesn't exist.") + end +end + +function core.override_chatcommand(name, redefinition) + local chatcommand = core.registered_chatcommands[name] + assert(chatcommand, "Attempt to override non-existent chatcommand "..name) + for k, v in pairs(redefinition) do + rawset(chatcommand, k, v) + end + core.registered_chatcommands[name] = chatcommand +end \ No newline at end of file diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index 5d5955972..745b012e6 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -1,37 +1,10 @@ --- Minetest: builtin/chatcommands.lua +-- Minetest: builtin/game/chatcommands.lua -- -- Chat command handler -- -core.registered_chatcommands = {} core.chatcommands = core.registered_chatcommands -- BACKWARDS COMPATIBILITY -function core.register_chatcommand(cmd, def) - def = def or {} - def.params = def.params or "" - def.description = def.description or "" - def.privs = def.privs or {} - def.mod_origin = core.get_current_modname() or "??" - core.registered_chatcommands[cmd] = def -end - -function core.unregister_chatcommand(name) - if core.registered_chatcommands[name] then - core.registered_chatcommands[name] = nil - else - core.log("warning", "Not unregistering chatcommand " ..name.. - " because it doesn't exist.") - end -end - -function core.override_chatcommand(name, redefinition) - local chatcommand = core.registered_chatcommands[name] - assert(chatcommand, "Attempt to override non-existent chatcommand "..name) - for k, v in pairs(redefinition) do - rawset(chatcommand, k, v) - end - core.registered_chatcommands[name] = chatcommand -end core.register_on_chat_message(function(name, message) local cmd, param = string.match(message, "^/([^ ]+) *(.*)") diff --git a/builtin/game/init.lua b/builtin/game/init.lua index b5e2f7cca..793d9fe2b 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -22,6 +22,7 @@ dofile(gamepath.."deprecated.lua") dofile(gamepath.."misc.lua") dofile(gamepath.."privileges.lua") dofile(gamepath.."auth.lua") +dofile(commonpath .. "chatcommands.lua") dofile(gamepath.."chatcommands.lua") dofile(gamepath.."static_spawn.lua") dofile(gamepath.."detached_inventory.lua") diff --git a/src/client.cpp b/src/client.cpp index 0af6b4595..3a3e0673e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1582,11 +1582,7 @@ void Client::typeChatMessage(const std::wstring &message) sendChatMessage(message); // Show locally - if (message[0] == L'/') - { - pushToChatQueue((std::wstring)L"issued command: " + message); - } - else + if (message[0] != L'/') { // compatibility code if (m_proto_ver < 29) { diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index f4c3812ac..7eb340d78 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "l_client.h" #include "l_internal.h" #include "util/string.h" +#include "cpp_api/s_base.h" int ModApiClient::l_get_current_modname(lua_State *L) { @@ -28,6 +29,26 @@ int ModApiClient::l_get_current_modname(lua_State *L) return 1; } +// get_last_run_mod() +int ModApiClient::l_get_last_run_mod(lua_State *L) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); + const char *current_mod = lua_tostring(L, -1); + if (current_mod == NULL || current_mod[0] == '\0') { + lua_pop(L, 1); + lua_pushstring(L, getScriptApiBase(L)->getOrigin().c_str()); + } + return 1; +} + +// set_last_run_mod(modname) +int ModApiClient::l_set_last_run_mod(lua_State *L) +{ + const char *mod = lua_tostring(L, 1); + getScriptApiBase(L)->setOriginDirect(mod); + return 0; +} + // display_chat_message(message) int ModApiClient::l_display_chat_message(lua_State *L) { @@ -42,4 +63,6 @@ void ModApiClient::Initialize(lua_State *L, int top) { API_FCT(get_current_modname); API_FCT(display_chat_message); + API_FCT(set_last_run_mod); + API_FCT(get_last_run_mod); } diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index b4a57cb61..e3106e742 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -28,8 +28,16 @@ class ModApiClient : public ModApiBase private: // get_current_modname() static int l_get_current_modname(lua_State *L); + + // display_chat_message(message) static int l_display_chat_message(lua_State *L); + // get_last_run_mod(n) + static int l_get_last_run_mod(lua_State *L); + + // set_last_run_mod(modname) + static int l_set_last_run_mod(lua_State *L); + public: static void Initialize(lua_State *L, int top); }; -- cgit v1.2.3 From 2c19d51409ca903021e0b508e5bc15299c4e51dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Blot?= Date: Sun, 22 Jan 2017 11:17:41 +0100 Subject: [CSM] sound_play & sound_stop support + client_lua_api doc (#5096) * squashed: CSM: Implement register_globalstep * Re-use fatal error mechanism from server to disconnect client on CSM error * Little client functions cleanups * squashed: CSM: add core.after function * core.after is shared code between client & server * ModApiUtil get_us_time feature enabled for client --- builtin/client/init.lua | 3 +- builtin/client/preview.lua | 15 +- builtin/client/register.lua | 1 + builtin/common/after.lua | 43 +++ builtin/game/init.lua | 1 + builtin/game/misc.lua | 44 --- doc/client_lua_api.txt | 787 ++++++++++++++++++++++++++++++++++++++ src/client.cpp | 1 + src/client.h | 16 +- src/clientenvironment.cpp | 4 + src/clientenvironment.h | 3 + src/guiEngine.h | 1 + src/script/clientscripting.cpp | 2 + src/script/cpp_api/s_client.cpp | 18 + src/script/cpp_api/s_client.h | 2 + src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_client.h | 3 +- src/script/lua_api/l_mainmenu.cpp | 31 -- src/script/lua_api/l_mainmenu.h | 7 +- src/script/lua_api/l_sound.cpp | 62 +++ src/script/lua_api/l_sound.h | 36 ++ src/script/lua_api/l_util.cpp | 2 + src/script/scripting_mainmenu.cpp | 4 +- 23 files changed, 996 insertions(+), 91 deletions(-) create mode 100644 builtin/common/after.lua create mode 100644 doc/client_lua_api.txt create mode 100644 src/script/lua_api/l_sound.cpp create mode 100644 src/script/lua_api/l_sound.h diff --git a/builtin/client/init.lua b/builtin/client/init.lua index 4797ac4b6..dd218aab6 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -4,9 +4,10 @@ local clientpath = scriptpath.."client"..DIR_DELIM local commonpath = scriptpath.."common"..DIR_DELIM dofile(clientpath .. "register.lua") +dofile(commonpath .. "after.lua") +dofile(commonpath .. "chatcommands.lua") dofile(clientpath .. "preview.lua") core.register_on_death(function() core.display_chat_message("You died.") end) - diff --git a/builtin/client/preview.lua b/builtin/client/preview.lua index c421791f5..22e8bb97f 100644 --- a/builtin/client/preview.lua +++ b/builtin/client/preview.lua @@ -1,6 +1,6 @@ -- This is an example function to ensure it's working properly, should be removed before merge core.register_on_shutdown(function() - print("shutdown client") + print("[PREVIEW] shutdown client") end) -- This is an example function to ensure it's working properly, should be removed before merge @@ -15,17 +15,28 @@ core.register_on_sending_chat_messages(function(message) return false end) +-- This is an example function to ensure it's working properly, should be removed before merge core.register_on_hp_modification(function(hp) print("[PREVIEW] HP modified " .. hp) end) +-- This is an example function to ensure it's working properly, should be removed before merge core.register_on_damage_taken(function(hp) print("[PREVIEW] Damage taken " .. hp) end) +-- This is an example function to ensure it's working properly, should be removed before merge +core.register_globalstep(function(dtime) + -- print("[PREVIEW] globalstep " .. dtime) +end) + -- This is an example function to ensure it's working properly, should be removed before merge core.register_chatcommand("dump", { func = function(name, param) return true, dump(_G) end, -}) \ No newline at end of file +}) + +core.after(2, function() + print("After 2") +end) diff --git a/builtin/client/register.lua b/builtin/client/register.lua index ddaf4f424..8b60c1222 100644 --- a/builtin/client/register.lua +++ b/builtin/client/register.lua @@ -55,6 +55,7 @@ local function make_registration() return t, registerfunc end +core.registered_globalsteps, core.register_globalstep = make_registration() core.registered_on_shutdown, core.register_on_shutdown = make_registration() core.registered_on_receiving_chat_messages, core.register_on_receiving_chat_messages = make_registration() core.registered_on_sending_chat_messages, core.register_on_sending_chat_messages = make_registration() diff --git a/builtin/common/after.lua b/builtin/common/after.lua new file mode 100644 index 000000000..30a9c7bad --- /dev/null +++ b/builtin/common/after.lua @@ -0,0 +1,43 @@ +local jobs = {} +local time = 0.0 +local last = core.get_us_time() / 1000000 + +core.register_globalstep(function(dtime) + local new = core.get_us_time() / 1000000 + if new > last then + time = time + (new - last) + else + -- Overflow, we may lose a little bit of time here but + -- only 1 tick max, potentially running timers slightly + -- too early. + time = time + new + end + last = new + + if #jobs < 1 then + return + end + + -- Iterate backwards so that we miss any new timers added by + -- a timer callback, and so that we don't skip the next timer + -- in the list if we remove one. + for i = #jobs, 1, -1 do + local job = jobs[i] + if time >= job.expire then + core.set_last_run_mod(job.mod_origin) + job.func(unpack(job.arg)) + table.remove(jobs, i) + end + end +end) + +function core.after(after, func, ...) + assert(tonumber(after) and type(func) == "function", + "Invalid core.after invocation") + jobs[#jobs + 1] = { + func = func, + expire = time + after, + arg = {...}, + mod_origin = core.get_last_run_mod() + } +end diff --git a/builtin/game/init.lua b/builtin/game/init.lua index 793d9fe2b..3e192a30a 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -17,6 +17,7 @@ if core.setting_getbool("profiler.load") then profiler = dofile(scriptpath.."profiler"..DIR_DELIM.."init.lua") end +dofile(commonpath .. "after.lua") dofile(gamepath.."item_entity.lua") dofile(gamepath.."deprecated.lua") dofile(gamepath.."misc.lua") diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index 3419c1980..25376c180 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -4,50 +4,6 @@ -- Misc. API functions -- -local jobs = {} -local time = 0.0 -local last = core.get_us_time() / 1000000 - -core.register_globalstep(function(dtime) - local new = core.get_us_time() / 1000000 - if new > last then - time = time + (new - last) - else - -- Overflow, we may lose a little bit of time here but - -- only 1 tick max, potentially running timers slightly - -- too early. - time = time + new - end - last = new - - if #jobs < 1 then - return - end - - -- Iterate backwards so that we miss any new timers added by - -- a timer callback, and so that we don't skip the next timer - -- in the list if we remove one. - for i = #jobs, 1, -1 do - local job = jobs[i] - if time >= job.expire then - core.set_last_run_mod(job.mod_origin) - job.func(unpack(job.arg)) - table.remove(jobs, i) - end - end -end) - -function core.after(after, func, ...) - assert(tonumber(after) and type(func) == "function", - "Invalid core.after invocation") - jobs[#jobs + 1] = { - func = func, - expire = time + after, - arg = {...}, - mod_origin = core.get_last_run_mod() - } -end - function core.check_player_privs(name, ...) local arg_type = type(name) if (arg_type == "userdata" or arg_type == "table") and diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt new file mode 100644 index 000000000..8ce487bf8 --- /dev/null +++ b/doc/client_lua_api.txt @@ -0,0 +1,787 @@ +Minetest Lua Modding API Reference 0.4.15 +========================================= +* More information at +* Developer Wiki: + +Introduction +------------ +Content and functionality can be added to Minetest 0.4 by using Lua +scripting in run-time loaded mods. + +A mod is a self-contained bunch of scripts, textures and other related +things that is loaded by and interfaces with Minetest. + +Mods are contained and ran solely on the server side. Definitions and media +files are automatically transferred to the client. + +If you see a deficiency in the API, feel free to attempt to add the +functionality in the engine and API. You can send such improvements as +source code patches on GitHub (https://github.com/minetest/minetest). + +Programming in Lua +------------------ +If you have any difficulty in understanding this, please read +[Programming in Lua](http://www.lua.org/pil/). + +Startup +------- +Mods are loaded during client startup from the mod load paths by running +the `init.lua` scripts in a shared environment. + +Paths +----- +* `RUN_IN_PLACE=1` (Windows release, local build) + * `$path_user`: + * Linux: `` + * Windows: `` + * `$path_share` + * Linux: `` + * Windows: `` +* `RUN_IN_PLACE=0`: (Linux release) + * `$path_share` + * Linux: `/usr/share/minetest` + * Windows: `/minetest-0.4.x` + * `$path_user`: + * Linux: `$HOME/.minetest` + * Windows: `C:/users//AppData/minetest` (maybe) + +Mod load path +------------- +Generic: + +* `$path_share/clientmods/` +* `$path_user/clientmods/` (User-installed mods) + +In a run-in-place version (e.g. the distributed windows version): + +* `minetest-0.4.x/clientmods/` (User-installed mods) + +On an installed version on Linux: + +* `/usr/share/minetest/clientmods/` +* `$HOME/.minetest/clientmods/` (User-installed mods) + +Modpack support +---------------- +Mods can be put in a subdirectory, if the parent directory, which otherwise +should be a mod, contains a file named `modpack.txt`. This file shall be +empty, except for lines starting with `#`, which are comments. + +Mod directory structure +------------------------ + + mods + |-- modname + | |-- depends.txt + | |-- screenshot.png + | |-- description.txt + | |-- settingtypes.txt + | |-- init.lua + | |-- models + | |-- textures + | | |-- modname_stuff.png + | | `-- modname_something_else.png + | |-- sounds + | |-- media + | `-- + `-- another + + +### modname +The location of this directory can be fetched by using +`minetest.get_modpath(modname)`. + +### `depends.txt` +List of mods that have to be loaded before loading this mod. + +A single line contains a single modname. + +Optional dependencies can be defined by appending a question mark +to a single modname. Their meaning is that if the specified mod +is missing, that does not prevent this mod from being loaded. + +### `screenshot.png` +A screenshot shown in the mod manager within the main menu. It should +have an aspect ratio of 3:2 and a minimum size of 300×200 pixels. + +### `description.txt` +A File containing description to be shown within mainmenu. + +### `settingtypes.txt` +A file in the same format as the one in builtin. It will be parsed by the +settings menu and the settings will be displayed in the "Mods" category. + +### `init.lua` +The main Lua script. Running this script should register everything it +wants to register. Subsequent execution depends on minetest calling the +registered callbacks. + +`minetest.setting_get(name)` and `minetest.setting_getbool(name)` can be used +to read custom or existing settings at load time, if necessary. + +### `sounds` +Media files (sounds) that will be transferred to the +client and will be available for use by the mod. + +Naming convention for registered textual names +---------------------------------------------- +Registered names should generally be in this format: + + "modname:" ( can have characters a-zA-Z0-9_) + +This is to prevent conflicting names from corrupting maps and is +enforced by the mod loader. + +### Example +In the mod `experimental`, there is the ideal item/node/entity name `tnt`. +So the name should be `experimental:tnt`. + +Enforcement can be overridden by prefixing the name with `:`. This can +be used for overriding the registrations of some other mod. + +Example: Any mod can redefine `experimental:tnt` by using the name + + :experimental:tnt + +when registering it. +(also that mod is required to have `experimental` as a dependency) + +The `:` prefix can also be used for maintaining backwards compatibility. + +### Aliases +Aliases can be added by using `minetest.register_alias(name, convert_to)` or +`minetest.register_alias_force(name, convert_to). + +This will make Minetest to convert things called name to things called +`convert_to`. + +The only difference between `minetest.register_alias` and +`minetest.register_alias_force` is that if an item called `name` exists, +`minetest.register_alias` will do nothing while +`minetest.register_alias_force` will unregister it. + +This can be used for maintaining backwards compatibility. + +This can be also used for setting quick access names for things, e.g. if +you have an item called `epiclylongmodname:stuff`, you could do + + minetest.register_alias("stuff", "epiclylongmodname:stuff") + +and be able to use `/giveme stuff`. + +Sounds +------ +Only Ogg Vorbis files are supported. + +For positional playing of sounds, only single-channel (mono) files are +supported. Otherwise OpenAL will play them non-positionally. + +Mods should generally prefix their sounds with `modname_`, e.g. given +the mod name "`foomod`", a sound could be called: + + foomod_foosound.ogg + +Sounds are referred to by their name with a dot, a single digit and the +file extension stripped out. When a sound is played, the actual sound file +is chosen randomly from the matching sounds. + +When playing the sound `foomod_foosound`, the sound is chosen randomly +from the available ones of the following files: + +* `foomod_foosound.ogg` +* `foomod_foosound.0.ogg` +* `foomod_foosound.1.ogg` +* (...) +* `foomod_foosound.9.ogg` + +Examples of sound parameter tables: + + -- Play locationless on all clients + { + gain = 1.0, -- default + } + -- Play locationless to one player + { + to_player = name, + gain = 1.0, -- default + } + -- Play locationless to one player, looped + { + to_player = name, + gain = 1.0, -- default + loop = true, + } + -- Play in a location + { + pos = {x = 1, y = 2, z = 3}, + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + } + -- Play connected to an object, looped + { + object = , + gain = 1.0, -- default + max_hear_distance = 32, -- default, uses an euclidean metric + loop = true, + } + +Looped sounds must either be connected to an object or played locationless to +one player using `to_player = name,` + +### `SimpleSoundSpec` +* e.g. `""` +* e.g. `"default_place_node"` +* e.g. `{}` +* e.g. `{name = "default_place_node"}` +* e.g. `{name = "default_place_node", gain = 1.0}` + +Representations of simple things +-------------------------------- + +### Position/vector + + {x=num, y=num, z=num} + +For helper functions see "Vector helpers". + +### `pointed_thing` +* `{type="nothing"}` +* `{type="node", under=pos, above=pos}` +* `{type="object", ref=ObjectRef}` + +Flag Specifier Format +--------------------- +Flags using the standardized flag specifier format can be specified in either of +two ways, by string or table. + +The string format is a comma-delimited set of flag names; whitespace and +unrecognized flag fields are ignored. Specifying a flag in the string sets the +flag, and specifying a flag prefixed by the string `"no"` explicitly +clears the flag from whatever the default may be. + +In addition to the standard string flag format, the schematic flags field can +also be a table of flag names to boolean values representing whether or not the +flag is set. Additionally, if a field with the flag name prefixed with `"no"` +is present, mapped to a boolean of any value, the specified flag is unset. + +E.g. A flag field of value + + {place_center_x = true, place_center_y=false, place_center_z=true} + +is equivalent to + + {place_center_x = true, noplace_center_y=true, place_center_z=true} + +which is equivalent to + + "place_center_x, noplace_center_y, place_center_z" + +or even + + "place_center_x, place_center_z" + +since, by default, no schematic attributes are set. + +Formspec +-------- +Formspec defines a menu. Currently not much else than inventories are +supported. It is a string, with a somewhat strange format. + +Spaces and newlines can be inserted between the blocks, as is used in the +examples. + +### Examples + +#### Chest + + size[8,9] + list[context;main;0,0;8,4;] + list[current_player;main;0,5;8,4;] + +#### Furnace + + size[8,9] + list[context;fuel;2,3;1,1;] + list[context;src;2,1;1,1;] + list[context;dst;5,1;2,2;] + list[current_player;main;0,5;8,4;] + +#### Minecraft-like player inventory + + size[8,7.5] + image[1,0.6;1,2;player.png] + list[current_player;main;0,3.5;8,4;] + list[current_player;craft;3,0;3,3;] + list[current_player;craftpreview;7,1;1,1;] + +### Elements + +#### `size[,,]` +* Define the size of the menu in inventory slots +* `fixed_size`: `true`/`false` (optional) +* deprecated: `invsize[,;]` + +#### `container[,]` +* Start of a container block, moves all physical elements in the container by (X, Y) +* Must have matching container_end +* Containers can be nested, in which case the offsets are added + (child containers are relative to parent containers) + +#### `container_end[]` +* End of a container, following elements are no longer relative to this container + +#### `list[;;,;,;]` +* Show an inventory list + +#### `list[;;,;,;]` +* Show an inventory list + +#### `listring[;]` +* Allows to create a ring of inventory lists +* Shift-clicking on items in one element of the ring + will send them to the next inventory list inside the ring +* The first occurrence of an element inside the ring will + determine the inventory where items will be sent to + +#### `listring[]` +* Shorthand for doing `listring[;]` + for the last two inventory lists added by list[...] + +#### `listcolors[;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering + +#### `listcolors[;;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering +* Sets color of slots border + +#### `listcolors[;;;;]` +* Sets background color of slots as `ColorString` +* Sets background color of slots on mouse hovering +* Sets color of slots border +* Sets default background color of tooltips +* Sets default font color of tooltips + +#### `tooltip[;;,]` +* Adds tooltip for an element +* `` tooltip background color as `ColorString` (optional) +* `` tooltip font color as `ColorString` (optional) + +#### `image[,;,;]` +* Show an image +* Position and size units are inventory slots + +#### `item_image[,;,;]` +* Show an inventory image of registered item/node +* Position and size units are inventory slots + +#### `bgcolor[;]` +* Sets background color of formspec as `ColorString` +* If `true`, the background color is drawn fullscreen (does not effect the size of the formspec) + +#### `background[,;,;]` +* Use a background. Inventory rectangles are not drawn then. +* Position and size units are inventory slots +* Example for formspec 8x4 in 16x resolution: image shall be sized + 8 times 16px times 4 times 16px. + +#### `background[,;,;;]` +* Use a background. Inventory rectangles are not drawn then. +* Position and size units are inventory slots +* Example for formspec 8x4 in 16x resolution: + image shall be sized 8 times 16px times 4 times 16px +* If `true` the background is clipped to formspec size + (`x` and `y` are used as offset values, `w` and `h` are ignored) + +#### `pwdfield[,;,;;