diff --git a/CHANGELOG.md b/CHANGELOG.md index be3403866..a43fe4d90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ Core: fast reliable network protocol, fast multi-threaded server, utf8 chat, bui Changelog ====== +### 5.7.0.0 (?) + * Tree growth + * Weather tuned + + ### 5.6.1.0 (?) * auth_kv removed, use instead in world.mt: auth_backend = leveldbfm diff --git a/CMakeLists.txt b/CMakeLists.txt index 1273d41b0..e5e812ba9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,8 +117,15 @@ endif() MESSAGE(STATUS "Build type: ${CMAKE_BUILD_TYPE}") -if (NOT NO_LTO AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) # AND NOT CMAKE_SYSTEM_NAME STREQUAL "Emscripten" - # include(CheckIPOSupported) + +if(NOT NO_LTO AND NOT DEVELOPMENT_BUILD AND(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) + option(USE_LTO "Use LTO" ON) +else() + option(USE_LTO "Use LTO" OFF) +endif() + +if (USE_LTO) + # include(CheckIPOSupported) # check_ipo_supported(RESULT supported OUTPUT error) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) message(STATUS "Using lto") diff --git a/build_tools/debian_ogles.sh b/build_tools/build.sh similarity index 53% rename from build_tools/debian_ogles.sh rename to build_tools/build.sh index 0559de2bf..641ba7f64 100755 --- a/build_tools/debian_ogles.sh +++ b/build_tools/build.sh @@ -1,15 +1,28 @@ +#!/usr/bin/env bash # script for fast installing on raspberry pi, odroid and other arm boards with debian +set -e set -x # There's no package available, you have to compile it from source. # you can place this text to freeminer.sh file and run it # or -# curl https://raw.githubusercontent.com/freeminer/freeminer/master/build_tools/debian_ogles.sh | sh +# curl https://raw.githubusercontent.com/freeminer/freeminer/master/build_tools/build.sh | sh #1. To compile need to install packages: -sudo apt install -y git subversion build-essential cmake ninja-build ccache libbz2-dev libzstd-dev "libpng12-dev|libpng-dev" libjpeg-dev libfreetype6-dev libxxf86vm-dev libsqlite3-dev libvorbis-dev libopenal-dev libcurl4-openssl-dev libluajit-5.1-dev libleveldb-dev libsnappy-dev libgettextpo0 libmsgpack-dev libgl1-mesa-dev "libgles1-mesa-dev|libgles2-mesa-dev" libgles2-mesa-dev libboost-system-dev libunwind-dev libc++-dev libc++abi-dev +DIST=${DIST=`lsb_release --short --id`} +DIST=${DIST=`cat /etc/issue /etc/issue.net | head -n1 | cut -d " " -f1`} +if [ "$DIST" = "Debian" ] || [ "$DIST" = "Ubuntu" ]; then + sudo apt install -y git subversion build-essential cmake ninja-build ccache libbz2-dev libzstd-dev libjpeg-dev libfreetype6-dev libxxf86vm-dev libxi-dev libsqlite3-dev libhiredis-dev libvorbis-dev libopenal-dev libcurl4-openssl-dev libluajit-5.1-dev libleveldb-dev libsnappy-dev libgettextpo0 libmsgpack-dev libboost-system-dev clang lld llvm libc++-dev libc++abi-dev + for PACKAGE in libpng12-dev libpng-dev libgles1-mesa-dev libgles2-mesa-dev libgl1-mesa-dev libunwind-dev ; do + sudo apt install -y $PACKAGE ||: + done +else + echo Todo +fi + + if [ -n "" ]; then #2. get and compile irrlicht with oppengl es support: @@ -29,10 +42,10 @@ fi [ -s ../src/CMakeLists.txt ] && mkdir -p ../build && cd ../build #update if second+ run -git pull +git pull --rebase #compile -cmake .. -GNinja -DENABLE_GLES=1 # -DIRRLICHT_INCLUDE_DIR=../irrlicht/include -DIRRLICHT_LIBRARY=../irrlicht/lib/Linux/libIrrlicht.a +cmake .. -GNinja -DENABLE_GLES=1 -DCMAKE_C_COMPILER=`which clang` -DCMAKE_CXX_COMPILER=`which clang++` # -DIRRLICHT_INCLUDE_DIR=../irrlicht/include -DIRRLICHT_LIBRARY=../irrlicht/lib/Linux/libIrrlicht.a nice cmake --build . # link dir with /Shaders/ diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index 79fda5472..bfb2fbd06 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -312,7 +312,7 @@ local function main_button_handler(tabview, fields, name, tabdata) if gamedata.address and gamedata.port then core.settings:set("address", gamedata.address) core.settings:set("remote_port", gamedata.port) - core.settings:set("remote_proto", gamedata.proto) + core.settings:set("remote_proto", gamedata.proto or "mt") core.start() end return true diff --git a/games/default b/games/default index 6ccd7c671..56322b15d 160000 --- a/games/default +++ b/games/default @@ -1 +1 @@ -Subproject commit 6ccd7c6718a44802cd51aaf37ed293a369f41750 +Subproject commit 56322b15d2648690992c4c8de44b79a35fe40433 diff --git a/lib/irrlichtmt b/lib/irrlichtmt index ce0d29df9..1d43ea17c 160000 --- a/lib/irrlichtmt +++ b/lib/irrlichtmt @@ -1 +1 @@ -Subproject commit ce0d29df933fd03c5de218e159976629113257b9 +Subproject commit 1d43ea17ca8ee9a01262e9e34c5c89579b97397f diff --git a/src/client/client.cpp b/src/client/client.cpp index 7f03a2d3f..e8793a9ca 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -166,6 +166,7 @@ Client::Client( m_cache_save_interval = g_settings->getU16("server_map_save_interval"); m_mesh_grid = { g_settings->getU16("client_mesh_chunk") }; + control.cell_size = m_mesh_grid.cell_size; } void Client::migrateModStorage() diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index d1df2ec41..d9c2a2646 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -332,8 +332,6 @@ void ClientMap::updateDrawList(float dtime, unsigned int max_cycle_ms) u32 blocks_in_range_with_mesh = 0; - - { for (auto & [block_coord, block] : m_blocks) { //auto block = getBlockNoCreateNoEx(block_coord); int mesh_step = getFarmeshStep(m_control, getNodeBlockPos(cam_pos_nodes), block_coord); @@ -362,6 +360,7 @@ void ClientMap::updateDrawList(float dtime, unsigned int max_cycle_ms) for (MapBlock *block : sectorblocks) { MapBlockMesh *mesh = block->mesh; */ + { // Calculate the coordinates for range and frustum culling v3f mesh_sphere_center; @@ -740,6 +739,8 @@ void ClientMap::updateDrawListFm(float dtime, unsigned int max_cycle_ms) if (!max_cycle_ms) max_cycle_ms = 300/getControl().fps_wanted; + auto is_frustum_culled = m_client->getCamera()->getFrustumCuller(); + //v3f camera_position = m_camera_position; //f32 camera_fov = m_camera_fov; @@ -827,7 +828,16 @@ void ClientMap::updateDrawListFm(float dtime, unsigned int max_cycle_ms) const int maxq = 1000; - bool occlusion_culling_enabled = true; + + // Number of blocks frustum culled + u32 blocks_frustum_culled = 0; + + MeshGrid mesh_grid = m_client->getMeshGrid(); + // No occlusion culling when free_move is on and camera is inside ground + // No occlusion culling for chunk sizes of 4 and above + // because the current occlusion culling test is highly inefficient at these sizes + bool occlusion_culling_enabled = mesh_grid.cell_size < 4; + if (m_control.allow_noclip) { MapNode n = getNode(cam_pos_nodes); if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2) @@ -860,7 +870,7 @@ void ClientMap::updateDrawListFm(float dtime, unsigned int max_cycle_ms) */ auto mesh = block->getMesh(mesh_step); - + { blocks_in_range++; const int smesh_size = block->getMeshSize(mesh_step); @@ -877,7 +887,7 @@ void ClientMap::updateDrawListFm(float dtime, unsigned int max_cycle_ms) continue; } if(mesh_step == mesh->step && block->getTimestamp() <= mesh->timestamp && !smesh_size) { - blocks_in_range_without_mesh++; + ++blocks_in_range_without_mesh; continue; } } @@ -929,13 +939,54 @@ void ClientMap::updateDrawListFm(float dtime, unsigned int max_cycle_ms) continue; } */ - // Occlusion culling - if ((!m_control.range_all && d > m_control.wanted_range * BS) || - (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes))) { - blocks_occlusion_culled++; - continue; - } + // Calculate the coordinates for range and frustum culling + v3opos_t mesh_sphere_center; + f32 mesh_sphere_radius; + + v3pos_t block_pos_nodes = block->getPosRelative(); + + if (mesh) { + mesh_sphere_center = intToFloat(block_pos_nodes, BS) + + mesh->getBoundingSphereCenter(); + mesh_sphere_radius = mesh->getBoundingRadius(); + } else { + mesh_sphere_center = intToFloat(block_pos_nodes, BS) + + v3opos_t((MAP_BLOCKSIZE * 0.5f - 0.5f) * BS); + mesh_sphere_radius = 0.0f; + } + + // First, perform a simple distance check. + if (!m_control.range_all && + mesh_sphere_center.getDistanceFrom(m_camera_position) > + m_control.wanted_range * BS + mesh_sphere_radius) + continue; // Out of range, skip. + + // Keep the block alive as long as it is in range. + block->resetUsageTimer(); + blocks_in_range_with_mesh++; + + /* + + // Frustum culling + // Only do coarse culling here, to account for fast camera movement. + // This is needed because this function is not called every frame. + float frustum_cull_extra_radius = 300.0f; + if (is_frustum_culled(mesh_sphere_center, + mesh_sphere_radius + frustum_cull_extra_radius)) { + blocks_frustum_culled++; + continue; + } + + */ + + // Raytraced occlusion culling - send rays from the camera to the block's corners + if (!m_control.range_all && occlusion_culling_enabled && m_enable_raytraced_culling && + mesh && + isMeshOccluded(block, mesh_grid.cell_size, cam_pos_nodes)) { + blocks_occlusion_culled++; + continue; + } // This block is in range. Reset usage timer. @@ -979,6 +1030,7 @@ void ClientMap::updateDrawListFm(float dtime, unsigned int max_cycle_ms) if (porting::getTimeMs() > end_ms) { break; } + } } m_drawlist_last = draw_nearest.size(); diff --git a/src/client/clientmap.h b/src/client/clientmap.h index ce03075f8..0020c5c0b 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -37,9 +37,10 @@ struct MapDrawControl // freeminer: float farmesh = 0; int farmesh_step = 1; + int cell_size = 1; float fps = 30; - float fps_avg =30; + float fps_avg = 30; float fps_wanted = 30; float drawtime_avg = 30; diff --git a/src/client/game.cpp b/src/client/game.cpp index 8e69a3476..2325848f5 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3112,7 +3112,7 @@ static void pauseNodeAnimation(PausedNodesList &paused, scene::ISceneNode *node) float speed = animated_node->getAnimationSpeed(); if (!speed) return; - paused.push_back({grab(animated_node), speed}); + paused.emplace_back(grab(animated_node), speed); animated_node->setAnimationSpeed(0.0f); } diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index a0c296393..2f5785798 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -36,19 +36,27 @@ along with Freeminer. If not, see . #include #include -int getFarmeshStep(MapDrawControl& draw_control, const v3pos_t & playerblockpos, const v3pos_t & blockpos) { - - -return 1; // TODO FIX MESHMAKE - - +int getFarmeshStep(MapDrawControl &draw_control, const v3bpos_t &playerblockpos, + const v3bpos_t &blockpos) +{ int range = radius_box(playerblockpos, blockpos); if (draw_control.farmesh) { - const pos_t nearest = 256/MAP_BLOCKSIZE; - if (range >= std::min(nearest*8, draw_control.farmesh+draw_control.farmesh_step*4)) return 16; - else if (range >= std::min(nearest*4, draw_control.farmesh+draw_control.farmesh_step*2)) return 8; - else if (range >= std::min(nearest*2, draw_control.farmesh+draw_control.farmesh_step)) return 4; - else if (range >= std::min(nearest, draw_control.farmesh)) return 2; + + const pos_t nearest = std::max(draw_control.cell_size * 2, 256 / MAP_BLOCKSIZE); + // DUMP(draw_control.farmesh, range, nearest, draw_control.cell_size, draw_control.farmesh + draw_control.farmesh_step * 4, draw_control.farmesh + draw_control.farmesh_step * 2, draw_control.farmesh + draw_control.farmesh_step, draw_control.farmesh); + const auto farmesh_cells = std::max(draw_control.cell_size * 2, + draw_control.farmesh / draw_control.cell_size); + if (range >= std::min( + nearest * 8, farmesh_cells + draw_control.farmesh_step * 4)) + return 16; + else if (range >= std::min(nearest * 4, + farmesh_cells + draw_control.farmesh_step * 2)) + return 8; + else if (range >= + std::min(nearest * 2, farmesh_cells + draw_control.farmesh_step)) + return 4; + else if (range >= std::min(nearest, farmesh_cells)) + return 2; } return 1; }; @@ -63,6 +71,7 @@ MeshMakeData::MeshMakeData(Client *client, bool use_shaders, int step): m_client(client), m_use_shaders(use_shaders) + , side_length_data(MAP_BLOCKSIZE * m_mesh_grid.cell_size) , step{step} //, map{map_}, draw_control{draw_control_} /*#if defined(MESH_ZEROCOPY) @@ -149,7 +158,7 @@ void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) m_vmanip.clear(); VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE, - blockpos_nodes + v3s16(1,1,1) * (side_length + MAP_BLOCKSIZE /* extra layer of blocks around the mesh */) - v3s16(1,1,1)); + blockpos_nodes + v3s16(1,1,1) * (side_length_data + MAP_BLOCKSIZE /* extra layer of blocks around the mesh */) - v3s16(1,1,1)); m_vmanip.addArea(voxel_area); } @@ -1306,10 +1315,10 @@ void PartialMeshBuffer::afterDraw() const MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): step(data->step), - no_draw(data->no_draw), + //no_draw(data->no_draw), m_tsrc(data->m_client->getTextureSource()), m_shdrsrc(data->m_client->getShaderSource()), - m_bounding_sphere_center((data->side_length * 0.5f - 0.5f) * BS), + m_bounding_sphere_center((data->side_length_data * 0.5f - 0.5f) * BS), m_animation_force_timer(0), // force initial animation m_last_crack(-1), m_last_daynight_ratio((u32) -1) diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index b1f604cb7..71f55e5c6 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -44,7 +44,7 @@ class IShaderSource; Mesh making stuff */ -int getFarmeshStep(MapDrawControl& draw_control, const v3pos_t & playerblockpos, const v3pos_t & block_pos); +int getFarmeshStep(MapDrawControl& draw_control, const v3bpos_t & playerblockpos, const v3bpos_t & block_pos); class MapBlock; struct MinimapMapblock; @@ -66,6 +66,7 @@ struct MeshMakeData bool m_use_shaders; // fm: + u16 side_length_data; int step = 1; int range = 1; bool no_draw = false; @@ -260,7 +261,7 @@ class MapBlockMesh } int step = 1; - bool no_draw = 0; + //bool no_draw = 0; unsigned int timestamp = 0; u32 m_usage_timer = 0; // === diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index ad61f2c0f..be6e1f678 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -242,7 +242,7 @@ void MeshUpdateWorkerThread::doUpdate() sleep_ms(m_generation_interval); ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)"); - MapBlock::mesh_type mesh_new {new MapBlockMesh(q->data, *m_camera_offset)}; + MapBlock::mesh_type mesh_new = std::make_shared(q->data, *m_camera_offset); MeshUpdateResult r; r.p = q->p; r.mesh = mesh_new; diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index ebdd1d2af..4e4ffbd08 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -70,6 +70,9 @@ ShadowRenderer::~ShadowRenderer() delete m_shadow_mix_cb; m_shadow_node_array.clear(); m_light_list.clear(); + + if (m_screen_quad) + delete m_screen_quad; } void ShadowRenderer::disable() diff --git a/src/content_abm.cpp b/src/content_abm.cpp index 7738128df..ad789fe31 100644 --- a/src/content_abm.cpp +++ b/src/content_abm.cpp @@ -1,6 +1,5 @@ /* -content_abm.cpp -Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2013-2023 proller */ /* diff --git a/src/content_abm_grow_tree.cpp b/src/content_abm_grow_tree.cpp index 26f87bc66..46682f60d 100644 --- a/src/content_abm_grow_tree.cpp +++ b/src/content_abm_grow_tree.cpp @@ -1,16 +1,44 @@ +/* +Copyright (C) 2023 proller +*/ + +/* +This file is part of Freeminer. + +Freeminer is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +Freeminer 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Freeminer. If not, see . +*/ + +#include +#include #include "mapnode.h" #include "nodedef.h" #include "server.h" #include "serverenvironment.h" +constexpr auto grow_debug = false; +constexpr auto grow_debug_fast_default = 0; +//constexpr auto grow_debug_no_die = false; + // Trees use param2 for rotation, level1 is free -inline uint8_t get_tree_water_level(const MapNode &n) +inline uint8_t get_tree_water_level(const MapNode &n, bool use_param2) { - return n.getParam1(); + return use_param2 ? n.getParam2() : n.getParam1(); } -inline void set_tree_water_level(MapNode &n, const uint8_t level) + +inline void set_tree_water_level(MapNode &n, const uint8_t level, bool use_param2) { - n.setParam1(level); + use_param2 ? n.setParam2(level) : n.setParam1(level); } // Leaves use param1 for light, level2 is free @@ -31,8 +59,9 @@ inline auto getLight(const auto &ndef, const auto &n) n.getLight(LIGHTBANK_NIGHT, lightingFlags)); } -const v3pos_t leaves_grow_dirs[] = { +const v3pos_t leaves_look_dirs[] = { // +right, +top, +back + v3pos_t{0, 0, 0}, // 0 self v3pos_t{0, 1, 0}, // 1 top v3pos_t{0, 0, 1}, // 2 back v3pos_t{0, 0, -1}, // 3 front @@ -41,8 +70,17 @@ const v3pos_t leaves_grow_dirs[] = { v3pos_t{0, -1, 0}, // 6 bottom }; +constexpr auto D_SELF = 0; +constexpr auto D_TOP = 1; +constexpr auto D_BACK = 2; +constexpr auto D_FRONT = 3; +constexpr auto D_RIGHT = 4; +constexpr auto D_LEFT = 5; +constexpr auto D_BOTTOM = 6; + struct GrowParams { + int tree_water_param2 = 0; int tree_water_max = 50; // todo: depend on humidity 10-100 int tree_grow_water_min = 20; int tree_grow_heat_min = 7; @@ -50,12 +88,14 @@ struct GrowParams int tree_grow_light_max = 12; // grow more leaves around before grow tree up int tree_get_water_from_humidity = 70; // rain start int tree_get_water_max_from_humidity = 30; // max level to get from air + int tree_grow_bottom = 1; int tree_grow_chance = 10; + int tree_width_to_height = 1; int leaves_water_max = 20; // todo: depend on humidity 2-20 int leaves_grow_light_min = 8; - int leaves_grow_water_min_top = 3; - int leaves_grow_water_min_bottom = 4; - int leaves_grow_water_min_side = 2; + int leaves_grow_water_min_top = 4; + int leaves_grow_water_min_bottom = 5; + int leaves_grow_water_min_side = 3; int leaves_grow_heat_max = 40; int leaves_grow_heat_min = 3; int leaves_grow_prefer_top = 0; @@ -64,13 +104,15 @@ struct GrowParams int leaves_die_heat_min = 55; int leaves_die_chance = 5; int leaves_die_from_liquid = 1; - int leaves_to_fruit_water_min = 8; + int leaves_to_fruit_water_min = 9; int leaves_to_fruit_heat_min = 15; - int leaves_to_fruit_light_min = 9; + int leaves_to_fruit_light_min = 10; int leaves_to_fruit_chance = 10; - GrowParams(const ContentFeatures &cf, bool grow_debug_fast = false) + GrowParams(const ContentFeatures &cf, int16_t grow_debug_fast = 0) { + if (cf.groups.contains("tree_water_param2")) + tree_water_param2 = cf.groups.at("tree_water_param2"); if (cf.groups.contains("tree_water_max")) tree_water_max = cf.groups.at("tree_water_max"); if (cf.groups.contains("tree_grow_water_min")) @@ -81,8 +123,12 @@ struct GrowParams tree_grow_heat_max = cf.groups.at("tree_grow_heat_max"); if (cf.groups.contains("tree_grow_light_max")) tree_grow_light_max = cf.groups.at("tree_grow_light_max"); + if (cf.groups.contains("tree_grow_bottom")) + tree_grow_bottom = cf.groups.at("tree_grow_bottom"); if (cf.groups.contains("tree_grow_chance")) - tree_grow_chance = grow_debug_fast ? 0 : cf.groups.at("tree_grow_chance"); + tree_grow_chance = cf.groups.at("tree_grow_chance"); + if (cf.groups.contains("tree_width_to_height")) + tree_width_to_height = cf.groups.at("tree_width_to_height"); if (cf.groups.contains("tree_get_water_from_humidity")) tree_get_water_from_humidity = cf.groups.at("tree_get_water_from_humidity"); if (cf.groups.contains("tree_get_water_max_from_humidity")) @@ -111,7 +157,7 @@ struct GrowParams if (cf.groups.contains("leaves_die_heat_min")) leaves_die_heat_min = cf.groups.at("leaves_die_heat_min"); if (cf.groups.contains("leaves_die_chance")) - leaves_die_chance = grow_debug_fast ? 0 : cf.groups.at("leaves_die_chance"); + leaves_die_chance = cf.groups.at("leaves_die_chance"); if (cf.groups.contains("leaves_die_from_liquid")) leaves_die_from_liquid = cf.groups.at("leaves_die_from_liquid"); if (cf.groups.contains("leaves_to_fruit_water_min")) @@ -122,29 +168,44 @@ struct GrowParams leaves_to_fruit_light_min = cf.groups.at("leaves_to_fruit_light_min"); if (cf.groups.contains("leaves_to_fruit_chance")) leaves_to_fruit_chance = cf.groups.at("leaves_to_fruit_chance"); + + if (grow_debug_fast) { + tree_grow_chance = leaves_die_chance = 0; + } + if (grow_debug_fast > 1) { + tree_grow_heat_min = tree_grow_heat_max = leaves_grow_heat_min = + leaves_grow_heat_max = 0; + } } }; class GrowTree : public ActiveBlockModifier { - std::unordered_map tree_to_leaves; + std::unordered_map tree_to_leaves; //, tree_to_fruit; std::unordered_map type_params; - bool grow_debug_fast = false; + int16_t grow_debug_fast = grow_debug_fast_default; // bool grow_debug = false; public: GrowTree(ServerEnvironment *env, NodeDefManager *ndef) { // g_settings->getBoolNoEx("grow_debug", grow_debug); - g_settings->getBoolNoEx("grow_debug_fast", grow_debug_fast); + g_settings->getS16NoEx("grow_debug_fast", grow_debug_fast); std::vector ids; ndef->getIds("group:grow_tree", ids); - for (const auto &id : ids) { - const auto &cf = ndef->get(id); - type_params.emplace(id, GrowParams(cf, grow_debug_fast)); - if (!cf.liquid_alternative_source.empty()) - tree_to_leaves[id] = ndef->getId(cf.liquid_alternative_source); + for (const auto &id_tree : ids) { + const auto &cf_tree = ndef->get(id_tree); + type_params.emplace(id_tree, GrowParams(cf_tree, grow_debug_fast)); + if (!cf_tree.liquid_alternative_source.empty()) { + const auto id_leaves = ndef->getId(cf_tree.liquid_alternative_source); + tree_to_leaves[id_tree] = id_leaves; + + const auto &cf_leaves = ndef->get(id_leaves); + type_params.emplace(id_leaves, GrowParams(cf_leaves, grow_debug_fast)); + // if (!cf_leaves.liquid_alternative_source.empty()) + // tree_to_fruit[id_tree] = ndef->getId(cf_leaves.liquid_alternative_source); + } } } virtual const std::vector getTriggerContents() const override @@ -162,296 +223,565 @@ class GrowTree : public ActiveBlockModifier bool getSimpleCatchUp() override { return true; } virtual pos_t getMinY() override { return -MAX_MAP_GENERATION_LIMIT; }; virtual pos_t getMaxY() override { return MAX_MAP_GENERATION_LIMIT; }; - virtual void trigger(ServerEnvironment *env, v3pos_t p, MapNode n, + virtual void trigger(ServerEnvironment *env, v3pos_t pos, MapNode n, u32 active_object_count, u32 active_object_count_wider, v3pos_t, bool activate) override { ServerMap *map = &env->getServerMap(); const auto *ndef = env->getGameDef()->ndef(); - float heat = map->updateBlockHeat(env, p); - int16_t n_water_level = get_tree_water_level(n); - const auto c = n.getContent(); - const auto params = type_params.at(c); + float heat = map->updateBlockHeat(env, pos); - const auto n_water_level_orig = n_water_level; + // dont grow to sides if can grow up + bool top_is_not_tree{false}; + bool around_all_is_tree{true}; + int8_t near_tree{0}; + int8_t near_soil{0}; + int8_t near_liquid{0}; + content_t leaves_content{CONTENT_IGNORE}; + //content_t fruit_content{CONTENT_IGNORE}; + + struct Neighbor + { + MapNode node{}; + content_t content{CONTENT_IGNORE}; + bool is_liquid{false}; + bool is_my_leaves{false}; + bool is_any_leaves{false}; + //bool is_fruit{false}; + bool is_tree{false}; + bool is_other_tree{false}; + bool is_soil{false}; + bool side{false}; + bool top{false}; + bool bottom{false}; + bool self{false}; + uint8_t light{0}; + bool allow_grow_by_rotation{false}; + uint8_t facedir{0}; + ContentFeatures *cf{nullptr}; + int16_t water_level{0}; + v3pos_t pos{}; + }; + + Neighbor nbh[7]{}; + { + size_t i = 0; + for (const auto &dir : leaves_look_dirs) { + auto &nb = nbh[i]; + const bool is_self = i == D_SELF; + nb.pos = pos + dir; + + nb.node = is_self ? n : map->getNodeTry(nb.pos); + if (!nb.node) { + return; + } + + nb.content = nb.node.getContent(); + const auto ¶ms = type_params.at(nbh[D_SELF].content); + + nb.cf = (ContentFeatures *)&ndef->get(nb.content); + nb.light = getLight(ndef, nb.node); + nb.top = i == D_TOP; + nb.bottom = i == D_BOTTOM; + nb.side = !nb.bottom && !nb.top; + nb.self = i == D_SELF; + nb.is_tree = is_self || nbh[D_SELF].content == nb.content; + + if (!nb.is_tree) { + nb.is_tree = nb.is_other_tree = nb.cf->groups.contains("tree"); + } + + if (is_self) { + leaves_content = tree_to_leaves.contains(nb.content) + ? tree_to_leaves.at(nb.content) + : CONTENT_IGNORE; + //fruit_content = tree_to_fruit.contains(nb.content) ? tree_to_fruit.at(nb.content) : CONTENT_IGNORE; + } + //DUMP(is_self, leaves_content); + + if (!is_self) { + if (!nb.top && !nb.bottom && around_all_is_tree && !nb.is_tree) { + around_all_is_tree = false; + } + + nb.is_my_leaves = nb.content == leaves_content; + nb.is_any_leaves = + nb.is_my_leaves || nb.cf->groups.contains("leaves"); + //nb.is_fruit = nb.content == fruit_content; + // DUMP(is_self, nb.is_leaves, "=", nb.content, "==", (int)leaves_content); + nb.is_liquid = nb.cf->groups.contains("liquid"); + near_liquid += nb.is_liquid; + ///has_liquids.emplace_back(nb.pos); + + if (nb.top && !nb.is_tree) { + top_is_not_tree = true; + } + + nb.is_soil = nb.cf->groups.contains("soil"); + near_soil += nb.is_soil; + + if (!nb.top && !nb.bottom && nb.is_tree) { + ++near_tree; + } + } + + nb.water_level = nb.is_my_leaves ? get_leaves_water_level(nb.node) + : nb.is_tree ? get_tree_water_level( + nb.node, params.tree_water_param2) + : 0; + + nb.facedir = nb.node.getFaceDir(ndef); + + const auto &self_facedir = nbh[D_SELF].facedir; + + if (nb.self) { + // Can self grow to up/down? + nb.allow_grow_by_rotation = + ((self_facedir >= 0) && (self_facedir <= 3)) || + ((self_facedir >= 20) && (self_facedir <= 23)); + } else if (nb.top || nb.bottom) { + nb.allow_grow_by_rotation = nbh[D_SELF].allow_grow_by_rotation; + } else if (i == D_FRONT || i == D_BACK) { + // Can self grow this sides? + nb.allow_grow_by_rotation = self_facedir == 7 || self_facedir == 9; + } else if (i == D_LEFT || i == D_RIGHT) { + nb.allow_grow_by_rotation = self_facedir == 18 || self_facedir == 12; + } + + // if (grow_debug) DUMP(i, nb.allow_grow_by_rotation, nb.facedir, self_facedir); + ++i; + } + } + + const auto ¶ms = type_params.at(nbh[D_SELF].content); + const auto &content = nbh[D_SELF].content; + + const auto &self_allow_grow_by_rotation = nbh[D_SELF].allow_grow_by_rotation; + int16_t &self_water_level = nbh[D_SELF].water_level; + + const auto self_water_level_orig = self_water_level; const auto save = [&]() { - if (n_water_level_orig != n_water_level) { - set_tree_water_level(n, n_water_level); - map->setNode(p, n); + if (self_water_level_orig != self_water_level) { + set_tree_water_level(n, self_water_level, params.tree_water_param2); + map->setNode(pos, n); } }; const auto decrease = [&](auto &level, int amount = 1) -> auto { - if (level <= amount) + if (level <= amount) { return false; + } level -= amount; return true; }; - const content_t leaves_c = - tree_to_leaves.contains(c) ? tree_to_leaves.at(c) : CONTENT_IGNORE; - - const auto facedir = n.getFaceDir(ndef); - bool allow_grow_up_by_rotation = - (facedir >= 0 && facedir <= 3) || (facedir >= 20 && facedir <= 23); - - // dont grow to sides if can grow up - bool allow_grow_tree = allow_grow_up_by_rotation; - bool allow_grow_leaves = allow_grow_up_by_rotation; - bool top_is_not_tree = false; - bool have_tree_near = false; - bool all_is_tree = true; - size_t has_soil = 0; - std::vector has_liquids; - - size_t i = 0; - for (const auto &dir : leaves_grow_dirs) { - const auto p_dir = p + dir; - auto n_dir = map->getNodeTry(p_dir); - if (!n_dir) { - all_is_tree = false; - // if (grow_debug) DUMP("getfail tr", p_dir.X, p_dir.Y, p_dir.Z); - break; + if (params.tree_get_water_from_humidity && + self_water_level < params.tree_water_max && + self_water_level < params.tree_get_water_max_from_humidity - 1 && + near_soil && self_allow_grow_by_rotation && !near_liquid) { + float humidity = map->updateBlockHumidity(env, pos); + if (humidity >= params.tree_get_water_from_humidity) { + if (grow_debug_fast) { + self_water_level = params.tree_get_water_max_from_humidity > + params.tree_water_max + ? params.tree_water_max + : params.tree_get_water_max_from_humidity; + } else { + // TODO: depend on += ceil( max_from_air * (params.tree_get_water_from_humidity)/(100-humidity)) + ++self_water_level; + } + // if (grow_debug)DUMP("absorbair", self_water_level,self_water_level_orig, params.tree_get_water_max_from_humidity, humidity,grow_debug_fast); } + } - auto c_dir = n_dir.getContent(); - const auto &cf = ndef->get(c_dir); - const auto light_dir = getLight(ndef, n_dir); - bool top = !i; - bool bottom = i + 1 == sizeof(leaves_grow_dirs) / sizeof(leaves_grow_dirs[0]); - - bool is_leaves = c_dir == leaves_c; + for (int i = D_SELF + 1; i <= D_BOTTOM; ++i) { + auto &nb = nbh[i]; + const bool allow_grow_by_light = + !nb.top || nb.light <= params.tree_grow_light_max; + bool up_all_leaves = true; + //DUMP("gr", i, nb.top, nb.bottom, allow_grow_by_light, nb.water_level, nb.is_leaves, nb.is_tree, nb.is_liquid, nb.is_soil); - bool is_liquid = cf.groups.contains("liquid"); - if (is_liquid) - has_liquids.emplace_back(p_dir); + // Absorb water from near water blocks, leave one level + // DUMP("absorb?", nb.pos.Y, self_water_level, params.tree_water_max, (int)near_soil, (int)near_liquid, allow_grow_up_by_rotation, nb.is_liquid); + if (self_water_level < params.tree_water_max - 1 && near_soil && + self_allow_grow_by_rotation && nb.is_liquid) { + // TODO: cached and random + auto level = nb.node.getLevel(ndef); + + // TODO: allow get all water if bottom of water != water + if (level > 1) { + //auto amount = grow_debug_fast ? level - 1 : 1; + auto amount = level - 1; + if (self_water_level + amount > params.tree_water_max) { + amount = params.tree_water_max - self_water_level; + } - bool is_tree = cf.groups.contains("tree"); - if (all_is_tree && !is_tree) - all_is_tree = false; + level -= amount; - if (top && !is_tree) - top_is_not_tree = true; + if (!grow_debug_fast) { + nb.node.setLevel(ndef, level); + map->setNode(nb.pos, nb.node); + } - bool is_soil = cf.groups.contains("soil"); - has_soil += is_soil; + self_water_level += amount; - if (!top && !bottom && is_tree) { - have_tree_near = true; + } } - const auto n_dir_facedir = n_dir.getFaceDir(ndef); - bool dir_allow_grow_up_by_rotation = - (n_dir_facedir >= 0 && n_dir_facedir <= 3) || - (n_dir_facedir >= 20 && n_dir_facedir <= 23); - - const bool allow_grow_by_light = light_dir <= params.tree_grow_light_max; - bool up_all_leaves = true; - // light recalc sometimes too rare - if (top && !allow_grow_by_light) { + // Light recalc sometimes too rare + if (nb.top && !allow_grow_by_light && leaves_content != CONTENT_IGNORE) { + // DUMP(i, p.Y, top, allow_grow_by_light, up_all_leaves); for (pos_t li = 1; li <= LIGHT_SUN - params.tree_grow_light_max; ++li) { - const auto p_up = p + v3pos_t{0, li, 0}; - const auto n_up = map->getNodeTry(p_up); + const auto p_up = pos + v3pos_t{0, li, 0}; + const auto n_up = li == 1 ? nbh[D_TOP].node : map->getNodeTry(p_up); + // DUMP(top, allow_grow_by_light, up_all_leaves, n_up.getContent()); if (!n_up || n_up.getContent() == CONTENT_AIR) { up_all_leaves = false; break; } } } - bool can_grow_replace_leaves = - allow_grow_up_by_rotation && (allow_grow_by_light || up_all_leaves); - - // DUMP(i, p.Y, top, allow_grow_by_light, up_all_leaves, can_grow_replace_leaves, up_all_leaves); - - if (c != c_dir && - (!params.tree_grow_heat_min || heat > params.tree_grow_heat_min) && - (!params.tree_grow_heat_max || heat < params.tree_grow_heat_max) && - n_water_level >= params.tree_grow_water_min && - (((allow_grow_tree && - ((c_dir == leaves_c && can_grow_replace_leaves) || - ((top || bottom) && is_liquid))) || - (top && light_dir <= params.leaves_die_light_max)) || - (bottom && - (is_liquid || is_soil || cf.groups.contains("sand"))) -#if 0 -// TODO: directional grow - ||(!top && !bottom && n_water_level >= max_water_in_tree && - light_dir <= 1 - //&& c_dir == CONTENT_AIR - && cf.groups.contains("soil")) -#endif - ) && - !myrand_range(0, params.tree_grow_chance)) { + + // DUMP(i, pos.Y, self_water_level, nb.top, nb.water_level, allow_grow_by_light, up_all_leaves, nb.is_my_leaves, nb.is_any_leaves, up_all_leaves, nb.light, params.leaves_die_light_max, nb.allow_grow_by_rotation, nb.is_liquid, nb.cf->name); + auto tree_grow = [&]() { + if (content == nb.content) { + return false; + } + + if ((params.tree_grow_heat_min && heat < params.tree_grow_heat_min) || + (params.tree_grow_heat_max && heat > params.tree_grow_heat_max)) { + return false; + } + + if (self_water_level < params.tree_grow_water_min) { + return false; + } + + if (!nb.allow_grow_by_rotation) { + return false; + } + + if (!(nb.is_any_leaves || nb.cf->buildable_to || nb.is_liquid || + nb.is_soil || nb.cf->groups.contains("sand") || + nb.cf->groups.contains("sapling") || + nb.cf->groups.contains("fruit"))) { + // || nb.is_fruit + return false; + } + + if (nb.top) { + if (nb.content == CONTENT_AIR && params.leaves_grow_light_min && + nb.light < params.leaves_grow_light_min) { + return false; + } + + //if (nb.is_any_leaves) return false; + + if (!(allow_grow_by_light || up_all_leaves)) { + return false; + } + } + // dont grow too deep in liquid - if (bottom && is_liquid && light_dir <= 0) - continue; - if (bottom && have_tree_near) - continue; - if (!decrease(n_water_level)) - break; + if (nb.bottom) { + if (!params.tree_grow_bottom) { + return false; + } + if (nb.is_liquid && nb.light <= 1) { + return false; + } - // if (grow_debug) DUMP("tr->tr", p_dir.Y, c_dir, c,n_water_level, n_water_level_orig, light_dir); + if (near_tree >= 1) { + return false; + } + } - map->setNode(p_dir, {c, 1}); - } else if (((!top && !bottom && c_dir == c) || is_leaves)) { - auto wl_dir = c_dir == leaves_c ? get_leaves_water_level(n_dir) - : get_tree_water_level(n_dir); + if (!(grow_debug_fast || activate || + !myrand_range( + 0, params.tree_grow_chance * (nb.bottom ? 3 : 1)))) { + return false; + } - if (!is_leaves || (is_leaves && (!allow_grow_up_by_rotation || - (!top && top_is_not_tree)))) + if (!decrease(self_water_level)) { + return true; + } + + //if (grow_debug) DUMP("tr->tr", i, nb.pos.Y, nb.top, nb.bottom, nb.content, content, self_water_level, self_water_level_orig, nb.light); + + auto node = + nbh[D_SELF].node; //{content, 1, nbh[D_SELF].node.getParam2()}; + set_tree_water_level(node, 1, params.tree_water_param2); + map->setNode(nb.pos, node); - if (wl_dir < (is_leaves ? params.leaves_water_max - : params.tree_water_max) && - n_water_level > wl_dir + return true; + }; + + if (tree_grow()) { + break; + } + + auto water_pump = [&]() { + if (!(((!nb.top || nb.is_other_tree) && !nb.bottom && nb.is_tree && + !around_all_is_tree) || + nb.is_my_leaves)) { + return false; + } + + if (nb.side && nb.is_tree && self_allow_grow_by_rotation) { + // DUMP("skip tr side pump", water_level, nb.is_tree, allow_grow_up_by_rotation), + return false; + } + + // DUMP(nb.node, self_water_level, water_level); + //??if (is_tree && dir_allow_grow_up_by_rotation && n_water_level >= params.tree_water_max) continue; + + if (!(!nb.is_my_leaves || + (nb.is_my_leaves && (nb.top || !self_allow_grow_by_rotation || + (!nb.top && top_is_not_tree))))) { + return false; + } + + auto water_level = + nb.content == leaves_content + ? get_leaves_water_level(nb.node) + : get_tree_water_level(nb.node, params.tree_water_param2); + //DUMP(water_level, nb.is_leaves, allow_grow_up_by_rotation, nb.top, top_is_not_tree); + + if (water_level >= (nb.is_my_leaves ? params.leaves_water_max + : params.tree_water_max)) { + return false; + } + + if (self_water_level <= water_level + (nb.top ? 2 : 1) /* !!! n_water_level > wl_dir + (top ? -1 :bottom ? 1 : 0) */ ) { + return false; + } - { - if (!decrease(n_water_level)) { - // if (grow_debug) DUMP("pumpfail", - // n_water_level, n_water_level_orig, - // wl_dir, top, bottom, c_dir, c); - break; - } - ++wl_dir; - is_leaves ? set_leaves_water_level(n_dir, wl_dir) - : set_tree_water_level(n_dir, wl_dir); - } - - map->setNode(p_dir, n_dir); + if (!decrease(self_water_level)) { + // if (grow_debug) DUMP("pumpfail", n_water_level, n_water_level_orig, wl_dir, top, bottom, nb.content, c); + return true; } - } - if ((top && is_leaves) || c_dir == c) { - allow_grow_tree = false; - } - if (top && c_dir == c) { - allow_grow_leaves = false; + //if (grow_debug)DUMP("tr pump", pos.Y, self_water_level,self_water_level_orig, water_level, nb.top,nb.bottom, nb.content, content); + ++water_level; + nb.is_my_leaves ? set_leaves_water_level(nb.node, water_level) + : set_tree_water_level( + nb.node, water_level, params.tree_water_param2); + + map->setNode(nb.pos, nb.node); + return true; + }; + if (water_pump()) { + break; } - if (allow_grow_leaves && leaves_c != CONTENT_IGNORE && - heat >= params.leaves_grow_heat_min && - heat <= params.leaves_grow_heat_max && - (n_water_level >= (top ? params.leaves_grow_water_min_top - : params.leaves_grow_water_min_side)) && - // can_grow_leaves(n_water_level, top, bottom) && - light_dir >= params.leaves_grow_light_min) { + // Dont grow after top + //if ((nb.top && nb.is_my_leaves) || nb.content == content) { + //allow_grow_tree = false; + //} - if (cf.buildable_to && !is_liquid) { - if (!decrease(n_water_level)) - break; - map->setNode(p_dir, {leaves_c, n_dir.param1, 1}); + //DUMP(allow_grow_leaves, leaves_c, heat , params.leaves_grow_heat_min, params.leaves_grow_heat_max, n_water_level, light_dir); - if (const auto block = map->getBlock(getNodeBlockPos(p_dir)); block) { - block->setLightingExpired(true); - } + auto leaves_grow = [&]() { + if (!nb.allow_grow_by_rotation) { + // if (grow_debug) DUMP(nb.allow_grow_by_rotation, nb.top, nb.bottom, (int)nb.facedir); + return false; } - } + if (nbh[D_TOP].content == content) // TODO not top, by grow direction + { + return false; + } + if (leaves_content == CONTENT_IGNORE) { + return false; + } + if ((params.leaves_grow_heat_min && heat < params.leaves_grow_heat_min) || + (params.leaves_grow_heat_max && + heat > params.leaves_grow_heat_max)) { + return false; + } + if (!(self_water_level >= (nb.top ? params.leaves_grow_water_min_top + : params.leaves_grow_water_min_side))) { + return false; + } + if (nb.light < params.leaves_grow_light_min) { + return false; + } + if (!nb.cf->buildable_to || nb.is_liquid) { + return false; + } + if (!decrease(self_water_level)) { + return true; + } + //if (grow_debug)DUMP("tr->lv", nb.pos, self_water_level, self_water_level_orig,nb.light_dir); - ++i; + map->setNode(nb.pos, {leaves_content, nb.node.param1, 1}); + + if (const auto block = map->getBlock(getNodeBlockPos(nb.pos)); block) { + block->setLightingComplete(0); + } + + return true; + }; + + if (leaves_grow()) { + break; + } } // up-down distribute of rest - { - int16_t total_level = n_water_level; + + if (self_allow_grow_by_rotation) { + int16_t total_level = self_water_level; int8_t have_liquid = 1; - bool have_top = false, have_bottom = false; - const auto p_bottom = p + v3pos_t{0, -1, 0}; - auto n_bottom = map->getNodeTry(p_bottom); - int16_t wl_bottom = 0; - if (n_bottom.getContent() == c) { - have_bottom = true; - wl_bottom = get_tree_water_level(n_bottom); - total_level += wl_bottom; + //auto &n_bottom = nbh[D_BOTTOM].node; + if (nbh[D_BOTTOM].content == content) { + total_level += nbh[D_BOTTOM].water_level; ++have_liquid; - // if (grow_debug) DUMP("get bot", wl_bottom, total_level, (int)have_liquid, have_bottom); + //if (grow_debug)DUMP("get bot", nbh[D_BOTTOM].water_level, total_level,(int)have_liquid); } - const auto p_top = p + v3pos_t{0, 1, 0}; - auto n_top = map->getNodeTry(p_top); - int16_t wl_top = 0; - if (n_top.getContent() == c) { - have_top = true; - wl_top = get_tree_water_level(n_top); - total_level += wl_top; + //auto &n_top = nbh[D_TOP].node; + if (nbh[D_TOP].content == content) { + total_level += nbh[D_TOP].water_level; // wl_top; ++have_liquid; - // if (grow_debug) DUMP("get top", wl_top, total_level, (int)have_liquid, have_top); + //if (grow_debug)DUMP("get top", nbh[D_TOP].water_level, total_level,(int)have_liquid); } + const auto total_level_orig = total_level; + + /* +tot + avg/3 + b s t +3 1 = 2 1 0 +4 1.3 = 2 1 1 +5 1.6 = 2 2 1 +6 2 = 3 2 1 +7 2.3 = 3 2 2 +8 2.6 = 3 3 2 +9 3 = 4 3 2 +10 3.3 = 4 3 3 + +bottom = floor(avg + 1) +self = round(avg) +top = ceil(avg - 1) + +*/ + const auto float_avg_level = (float)total_level / have_liquid; + const auto fill_bottom = [&](bool prefer = false) { + if (nbh[D_BOTTOM].content != content) + return; + if (total_level <= 1) + return; + //const auto float_avg_level = (float)total_level / have_liquid; + /* + const auto float_avg_level = (float)total_level / have_liquid; + //if (grow_debug)DUMP(avg_level_for_bottom, (int)have_liquid, total_level); + const auto avg_level = prefer ? ceil(float_avg_level + 0.1) + : floor(float_avg_level); // -1 + const auto want_level = + avg_level < params.tree_water_max + ? avg_level + (avg_level >= (total_level ? 0 : 1)) + : params.tree_water_max; +*/ + //const auto float_avg_level = (float)total_level / have_liquid; + auto avg_level = + prefer ? floor(float_avg_level + 1) : ceil(float_avg_level - 1); + if (avg_level < 1) + avg_level = 1; + auto want_level = std::min(avg_level, params.tree_water_max); + + // dont grow down + if (want_level > nbh[D_BOTTOM].water_level) { + want_level = nbh[D_BOTTOM].water_level; + } - if (have_bottom) { - const auto avg_level_for_bottom = ceil((float)total_level / have_liquid); - // if (grow_debug) DUMP(avg_level_for_bottom, (int)have_liquid, total_level, have_bottom, have_top); - const auto bottom_level = - avg_level_for_bottom < params.tree_water_max - ? avg_level_for_bottom + - (avg_level_for_bottom >= total_level ? 0 : 1) - : params.tree_water_max; - total_level -= bottom_level; - --have_liquid; - if (wl_bottom != bottom_level) { - // if (grow_debug) DUMP("setbot", bottom_level, total_level, avg_level_for_bottom); - set_tree_water_level(n_bottom, bottom_level); - map->setNode(p_bottom, n_bottom); - } - } - if (have_top) { - auto float_avg_level_for_top = (float)total_level / have_liquid; - const int16_t avg_level_for_top = - all_is_tree ? int(float_avg_level_for_top) - : floor(float_avg_level_for_top); - const auto top_level = avg_level_for_top; //- 1; - total_level -= top_level; - if (wl_top != top_level) { - // if (grow_debug) DUMP("settop", top_level, total_level, avg_level_for_top); - set_tree_water_level(n_top, top_level); - map->setNode(p_top, n_top); - } - } - // if (grow_debug) DUMP("total res self:", (int)total_level); - n_water_level = total_level; - } + total_level -= want_level; - if (has_soil) { - n_water_level = grow_debug_fast ? params.tree_water_max - : 1; // TODO depend on humidity - // tODO: absorb water from water here - } + --have_liquid; + if (have_liquid == 1 && total_level > params.tree_water_max && + want_level < params.tree_water_max) { + --total_level; + ++want_level; + } - if (n_water_level < params.tree_water_max && has_soil) { - if (!has_liquids.empty()) { - // TODO: cached and random - const auto neighbor_pos = has_liquids.front(); - auto neighbor = map->getNodeTry(neighbor_pos); - auto level = neighbor.getLevel(ndef); + if (nbh[D_BOTTOM].water_level != want_level) { + //if (grow_debug)DUMP("setbot", bottom_level, total_level,avg_level_for_bottom); + set_tree_water_level( + nbh[D_BOTTOM].node, want_level, params.tree_water_param2); + map->setNode(nbh[D_BOTTOM].pos, nbh[D_BOTTOM].node); + } + }; - // TODO: allow get all water if bottom of water != - // water - if (level <= 1) + const auto fill_top = [&](bool prefer = false) { + if (nbh[D_TOP].content != content) return; - auto amount = grow_debug_fast ? level - 1 : 1; - if (n_water_level + amount > params.tree_water_max) - amount = params.tree_water_max - n_water_level; - level -= amount; - - neighbor.setLevel(ndef, level); - - if (!grow_debug_fast) - map->setNode(neighbor_pos, neighbor); - set_tree_water_level(n, n_water_level += amount); - map->setNode(p, n); - // if (grow_debug) DUMP("absorbwater", n_water_level, level, amount); - } else { - float humidity = map->updateBlockHumidity(env, p); - if (params.tree_get_water_from_humidity && - humidity >= params.tree_get_water_from_humidity && - n_water_level < params.tree_get_water_max_from_humidity) { - // if (grow_debug) DUMP("absorbair", n_water_level, humidity); + if (total_level <= 1) + return; + //const auto float_avg_level = (float)total_level / have_liquid; + /* + const auto float_avg_level = (float)total_level / have_liquid; + //const int16_t avg_level_for_top = + const auto avg_level = prefer ? ceil(float_avg_level + 0.1) + : floor(float_avg_level); // -1 + const auto want_level = + avg_level < params.tree_water_max + ? avg_level + (avg_level >= (total_level ? 0 : 1)) + : params.tree_water_max; +*/ + auto avg_level = + prefer ? floor(float_avg_level + 1) : ceil(float_avg_level - 1); + if (avg_level < 1) + avg_level = 1; + auto want_level = std::min(avg_level, params.tree_water_max); + + total_level -= want_level; + --have_liquid; - ++n_water_level; + if (have_liquid == 1 && total_level > params.tree_water_max && + want_level < params.tree_water_max) { + --total_level; + ++want_level; + } + if (nbh[D_TOP].water_level != want_level) { + //if (grow_debug) DUMP("settop", top_level, total_level, avg_level_for_top,around_all_is_tree); + // if (all_is_tree && n_water_level>= params.tree_water_max) DUMP(top_level, total_level, float_avg_level_for_top, avg_level_for_top); + set_tree_water_level( + nbh[D_TOP].node, want_level, params.tree_water_param2); + map->setNode(nbh[D_TOP].pos, nbh[D_TOP].node); + } + }; + + /* + 1 + 1 2 + 1 1 1 1 2 3 + 1 2 2 2 2 3 4 + 1 2 3 3 3 3 4 5 + 1 2 3 4 4 4 4 5 6 + 1 2 3 4 5 5 5 5 6 71 + 1 2 3 4 5 6 6 6 6 71 82 + 1 2 3 4 5 6 7 7 7 71 182 193 + 1 2 3 4 5 6 7 8 8 81 182 293 284 +1 2 3 4 5 6 7 8 9 91 192 293 384 395 +S S S S S S S S S SSS SSS SSS SSS SSS + +*/ + // Yggdrasil mode + if (near_tree >= 4 && params.tree_width_to_height + //&&((nbh[D_BOTTOM].water_level >= params.tree_get_water_from_humidity / 2) || // params.tree_water_max + //(nbh[D_SELF].water_level >= params.tree_get_water_from_humidity / 2)) + ) { + fill_top(true); + fill_bottom(); + } else { + fill_bottom(true); + fill_top(); } - } + + self_water_level = total_level; } save(); @@ -462,16 +792,17 @@ class GrowLeaves : public ActiveBlockModifier { std::unordered_map leaves_to_fruit; std::unordered_map type_params; - bool grow_debug_fast = false; - // bool grow_debug = false; + int16_t grow_debug_fast = grow_debug_fast_default; static bool can_grow_leaves( GrowParams params, int8_t level, bool is_top, bool is_bottom) { - if (is_top) + if (is_top) { return level >= params.leaves_grow_water_min_top; - if (is_bottom) + } + if (is_bottom) { return level >= params.leaves_grow_water_min_bottom; + } return level >= params.leaves_grow_water_min_side; } @@ -479,15 +810,16 @@ class GrowLeaves : public ActiveBlockModifier GrowLeaves(ServerEnvironment *env, NodeDefManager *ndef) { // g_settings->getBoolNoEx("grow_debug", grow_debug); - g_settings->getBoolNoEx("grow_debug_fast", grow_debug_fast); + g_settings->getS16NoEx("grow_debug_fast", grow_debug_fast); std::vector ids; ndef->getIds("group:grow_leaves", ids); for (const auto &id : ids) { const auto &cf = ndef->get(id); - type_params.emplace(id, GrowParams(cf)); - if (!cf.liquid_alternative_source.empty()) + type_params.emplace(id, GrowParams(cf, grow_debug_fast)); + if (!cf.liquid_alternative_source.empty()) { leaves_to_fruit[id] = ndef->getId(cf.liquid_alternative_source); + } } } virtual const std::vector getTriggerContents() const override @@ -505,52 +837,121 @@ class GrowLeaves : public ActiveBlockModifier bool getSimpleCatchUp() override { return true; } virtual pos_t getMinY() override { return -MAX_MAP_GENERATION_LIMIT; }; virtual pos_t getMaxY() override { return MAX_MAP_GENERATION_LIMIT; }; - virtual void trigger(ServerEnvironment *env, v3pos_t p, MapNode n, + virtual void trigger(ServerEnvironment *env, v3pos_t pos, MapNode n, u32 active_object_count, u32 active_object_count_wider, v3pos_t, bool activate) override { ServerMap *map = &env->getServerMap(); const auto *ndef = env->getGameDef()->ndef(); - float heat = map->updateBlockHeat(env, p); - const auto c = n.getContent(); - const auto params = type_params.at(c); + float heat = map->updateBlockHeat(env, pos); - int n_water_level = get_leaves_water_level(n); - const auto n_water_level_orig = n_water_level; + content_t c_fruit{}; + bool top_is_full_liquid = false; + bool have_tree_or_soil = false; + uint8_t have_not_leaves = 0; + bool allow_grow_fruit = false; - const auto l = getLight(ndef, n); + struct Neighbor + { + MapNode node{}; + content_t content{CONTENT_IGNORE}; + bool is_liquid{false}; + bool is_my_leaves{false}; + bool is_any_leaves{false}; + bool is_tree{false}; + bool top{false}; + bool bottom{false}; + uint8_t light{0}; + ContentFeatures *cf{nullptr}; + int16_t water_level{0}; + v3pos_t pos{}; + }; - uint8_t i = 0; + Neighbor nbh[7]{}; + { + size_t i = 0; + for (const auto &dir : leaves_look_dirs) { + auto &nb = nbh[i]; + const bool is_self = i == D_SELF; + + nb.top = i == D_TOP; + nb.bottom = i == D_BOTTOM; + nb.pos = pos + dir; + nb.node = is_self ? n : map->getNodeTry(nb.pos); + if (!nb.node) { + have_tree_or_soil = true; // dont remove when map busy + allow_grow_fruit = false; + ++have_not_leaves; + goto NEXT; + } + { + nb.light = getLight(ndef, nb.node); + nb.content = nb.node.getContent(); + const auto params = type_params.at(nbh[D_SELF].content); + nb.is_my_leaves = is_self || nb.content == nbh[D_SELF].content; + nb.cf = (ContentFeatures *)&ndef->get(nb.content); + nb.is_tree = nb.cf->groups.contains("tree"); + nb.is_any_leaves = nb.cf->groups.contains("leaves"); + nb.is_liquid = nb.cf->groups.contains("liquid"); + nb.water_level = nb.is_my_leaves ? get_leaves_water_level(nb.node) + : nb.is_tree ? get_tree_water_level(nb.node, + params.tree_water_param2) + : 0; + top_is_full_liquid = + nb.top && nb.is_liquid && + nb.node.getMaxLevel(ndef) == nb.node.getLevel(ndef); + + if (is_self) { + allow_grow_fruit = leaves_to_fruit.contains(nbh[D_SELF].content); + + c_fruit = allow_grow_fruit + ? leaves_to_fruit.at(nbh[D_SELF].content) + : CONTENT_IGNORE; + } else { + if ((nb.content == c_fruit) || + (!nb.top && !nb.bottom && !nb.is_any_leaves)) { + allow_grow_fruit = false; + } - bool top_is_full_liquid = false; - bool have_tree_or_soil = false; - bool have_air = false; - bool allow_grow_fruit = leaves_to_fruit.contains(c); - const content_t c_fruit = - allow_grow_fruit ? leaves_to_fruit.at(c) : CONTENT_IGNORE; - // TODO: choose pump and grow direction by leaves type - for (const auto &dir : leaves_grow_dirs) { - const auto p_dir = p + dir; - auto n_dir = map->getNodeTry(p_dir); - if (!n_dir) { - have_tree_or_soil = true; // dont remove when map busy - allow_grow_fruit = false; - have_air = false; - continue; + if (nb.is_tree) // no fruit near tree + { + allow_grow_fruit = false; + } + + if (!have_tree_or_soil) { + have_tree_or_soil = nb.is_tree || nb.is_any_leaves || + nb.cf->groups.contains("soil") || + nb.is_liquid; + } + if (!have_not_leaves && !nb.is_any_leaves) { + ++have_not_leaves; + } + } + } + NEXT: + ++i; } - const auto light_dir = getLight(ndef, n_dir); + } + + const auto params = type_params.at(nbh[D_SELF].content); + + auto &n_water_level = nbh[D_SELF].water_level; + const auto n_water_level_orig = n_water_level; + + // TODO: choose pump and grow direction by leaves type + + std::array grow_order{ + D_TOP, D_BACK, D_FRONT, D_RIGHT, D_LEFT, D_BOTTOM}; - auto c_dir = n_dir.getContent(); + std::sort(grow_order.begin(), grow_order.end(), + [&](auto &a, auto &b) { return nbh[a].light > nbh[b].light; }); - const auto &cf = ndef->get(c_dir); - bool is_tree = cf.groups.contains("tree"); - bool is_leaves = cf.groups.contains("leaves"); - bool is_liquid = cf.groups.contains("liquid"); - bool top = !i; - bool bottom = i + 1 == sizeof(leaves_grow_dirs) / sizeof(leaves_grow_dirs[0]); + for (auto &i : grow_order) { + auto &nb = nbh[i]; - top_is_full_liquid = - top && is_liquid && n_dir.getMaxLevel(ndef) == n_dir.getLevel(ndef); + if (!nb.node) { + continue; + } /*todo: shapes: o sphere @@ -559,82 +960,98 @@ class GrowLeaves : public ActiveBlockModifier \ / */ - if ((c_dir == c_fruit) || (!top && !bottom && !is_leaves)) - allow_grow_fruit = false; - - if (!have_tree_or_soil) - have_tree_or_soil = - is_tree || is_leaves || cf.groups.contains("soil") || is_liquid; - if (!have_air) - have_air = c_dir == CONTENT_AIR; + //DUMP("lv?", (int)i, n_water_level, params.leaves_grow_heat_min, heat, params.leaves_grow_heat_min, nb.light, nb.is_liquid, nb.cf->buildable_to); if ((!params.leaves_grow_heat_min || heat >= params.leaves_grow_heat_min) && (!params.leaves_grow_heat_max || heat <= params.leaves_grow_heat_max) && - can_grow_leaves(params, n_water_level, top, bottom) && - light_dir > params.leaves_grow_light_min && cf.buildable_to && - !is_liquid) { - // if (grow_debug) DUMP("lv->lv ", p.X, p.Y, p.Z, c_dir, c, l, n_water_level, n_water_level_orig, l, ndef->get(c_dir).name); - map->setNode(p_dir, {c, n_dir.getParam1(), 1}); - --n_water_level; - - if (!myrand_range(0, 10)) - if (const auto block = map->getBlock(getNodeBlockPos(p_dir)); block) { - block->setLightingExpired(true); + can_grow_leaves(params, n_water_level, nb.top, nb.bottom) && + nb.light >= params.leaves_grow_light_min && nb.cf->buildable_to && + !nb.is_liquid) { + uint8_t water_transfer = 0; + // Leaves grow should cost 2 wates, nobody knows why + if (n_water_level > 1) { + ++water_transfer; + --n_water_level; } + if (n_water_level > 1) { + ++water_transfer; + --n_water_level; + } + if (water_transfer <= 0) { + break; + } + //if (grow_debug)DUMP("lv->lv ", p.X, p.Y, p.Z, nb.content, c, l, n_water_level,n_water_level_orig, water_transfer, ndef->get(nb.content).name); + map->setNode(nb.pos, + {nbh[D_SELF].content, nb.node.getParam1(), water_transfer}); - } else if (c_dir == c) { - const auto l_dir = getLight(ndef, n_dir); - - auto wl_dir = get_leaves_water_level(n_dir); - if (n_water_level > 1 && wl_dir < params.leaves_water_max && l_dir >= l && + if (!myrand_range(0, 10)) + if (const auto block = map->getBlock(getNodeBlockPos(nb.pos)); + block) { + block->setLightingComplete(0); + } + break; + } else if (nb.content == nbh[D_SELF].content) { + if (n_water_level > 1 && nb.water_level < params.leaves_water_max && + nb.light >= nbh[D_SELF].light && // todo: all up by type? - wl_dir < n_water_level - 1 //(top ? -1 : - // bottom ? 1 : -2) + nb.water_level < n_water_level - 1 //(top ? -1 : + // bottom ? 1 : -2) ) { --n_water_level; - set_leaves_water_level(n_dir, ++wl_dir); - map->setNode(p_dir, n_dir); + set_leaves_water_level(nb.node, ++nb.water_level); + map->setNode(nb.pos, nb.node); - // if (grow_debug) DUMP("lv pumpup2", p.Y, - // n_water_level, n_water_level_orig, wl_dir, top, - // bottom, c_dir); + //if (grow_debug)DUMP("lv pumpup2", p.Y, n_water_level, n_water_level_orig, wl_dir,top, bottom, nb.content); + break; // Prefer pump up // todo: its like cypress, by settings - if (top && params.leaves_grow_prefer_top) { - break; - } + // if (nb.top && params.leaves_grow_prefer_top)break; } } - if (n_water_level != n_water_level_orig) { - set_leaves_water_level(n, n_water_level); - map->setNode(p, n); - } ++i; } - // DUMP(allow_grow_fruit, n_water_level, leaves_to_fruit_water_min, heat , - // leaves_to_fruit_heat_min); + // Slowly evaporate water and kill leaves with water_level==1 + const auto can_decay = !have_not_leaves && nbh[D_SELF].light < LIGHT_SUN - 1; + if (n_water_level > 1 && can_decay && + (!myrand_range(0, 10 * (grow_debug_fast ? 1 : 10)))) { + float humidity = map->updateBlockHumidity(env, pos); + if (humidity < params.tree_get_water_from_humidity) { + --n_water_level; + } + } + + // DUMP(allow_grow_fruit, n_water_level, leaves_to_fruit_water_min, heat, leaves_to_fruit_heat_min); if (allow_grow_fruit && n_water_level >= params.leaves_to_fruit_water_min && heat >= params.leaves_to_fruit_heat_min && - l >= params.leaves_to_fruit_light_min && + nbh[D_SELF].light >= params.leaves_to_fruit_light_min && (grow_debug_fast || !myrand_range(0, params.leaves_to_fruit_chance))) { - map->setNode(p, {c_fruit}); + map->setNode(pos, {c_fruit}); } else if ( + (n_water_level == 1 && can_decay && + (!myrand_range(0, 30 * (grow_debug_fast ? 1 : 10)))) || (n_water_level >= 1 && // dont touch old static trees - have_air && - ((l < params.leaves_die_light_max && - (l > 0 || !myrand_range(0, params.leaves_die_chance))) || + !have_not_leaves && + ((nbh[D_SELF].light < params.leaves_die_light_max && + (nbh[D_SELF].light > 0 || activate || + !myrand_range(0, params.leaves_die_chance))) || ((params.leaves_die_heat_max && heat < params.leaves_die_heat_max) || (params.leaves_die_heat_min && heat > params.leaves_die_heat_min)))) || ((!have_tree_or_soil || - (params.leaves_die_from_liquid && top_is_full_liquid)) && - !myrand_range(0, 10))) { - map->removeNodeWithEvent(p, false); + (params.leaves_die_from_liquid && top_is_full_liquid)) && + (activate || !myrand_range(0, 10)))) { + //if (grow_debug) DUMP("lv die", p.X, p.Y, p.Z, have_tree_or_soil, n_water_level, l, heat); + //if (!grow_debug_no_die) + map->removeNodeWithEvent(pos, false); + } else if (n_water_level != n_water_level_orig) { + // save if self level changed + set_leaves_water_level(n, n_water_level); + map->setNode(pos, n); } } }; diff --git a/src/debug/iostream_debug_helpers.h b/src/debug/iostream_debug_helpers.h index f31f1b8a6..28bb2e52c 100644 --- a/src/debug/iostream_debug_helpers.h +++ b/src/debug/iostream_debug_helpers.h @@ -6,6 +6,7 @@ #include "getThreadId.h" #include "magic_enum.hpp" #include +#include #include #include #include @@ -87,8 +88,12 @@ dumpImpl(Out & out, T && x) /// string and const char * - output not as container or pointer. template -std::enable_if_t, std::string> || std::is_same_v, const char *>), Out> & -dumpImpl(Out & out, T && x) +std::enable_if_t, std::string> || + std::is_same_v, std::string_view> || + std::is_same_v, const char *>), + Out> & +dumpImpl(Out &out, T &&x) { out << std::quoted(x); return out; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 45c73d7fd..297cb3daf 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -176,6 +176,8 @@ void fm_set_default_settings(Settings *settings) { //settings->setDefault("enable_vbo", win ? "false" : "true"); settings->setDefault("light_ambient", "false"); settings->setDefault("enable_dynamic_shadows", "1"); + settings->setDefault("enable_bloom", "true"); + settings->setDefault("client_mesh_chunk", std::to_string(std::max(1, Thread::getNumberOfProcessors() / 4))); // Liquid settings->setDefault("liquid_real", "true"); diff --git a/src/fm_clientiface.cpp b/src/fm_clientiface.cpp index 8715729f1..58bdbd0bc 100644 --- a/src/fm_clientiface.cpp +++ b/src/fm_clientiface.cpp @@ -466,14 +466,8 @@ int RemoteClient::GetNextBlocks(ServerEnvironment *env, EmergeManager *emerge, block->resetUsageTimer(); const auto complete = block->getLightingComplete(); - if (block->getLightingExpired()) { - // env->getServerMap().lighting_modified_blocks.set(p, nullptr); - if (complete) // complete = 0 means lighting calc in progress - env->getServerMap().lighting_modified_add(p, d); - if (!complete) { - continue; - } - + if (!complete){ + env->getServerMap().lighting_modified_add(p, d); if (block_sent && can_skip) { continue; } diff --git a/src/fm_liquid.cpp b/src/fm_liquid.cpp index 8f5d34e12..fc0419c2f 100644 --- a/src/fm_liquid.cpp +++ b/src/fm_liquid.cpp @@ -1,3 +1,7 @@ +/* +Copyright (C) 2013-2023 proller +*/ + /* This file is part of Freeminer. @@ -326,7 +330,7 @@ NEXT_LIQUID:; #endif goto NEXT_LIQUID; } - } catch (InvalidPositionException &e) { + } catch (const InvalidPositionException &e) { verbosestream << "transformLiquidsReal: weight: setNode() failed:" << nb.pos << ":" << e.what() << std::endl; @@ -843,7 +847,7 @@ NEXT_LIQUID:; getBlockNoCreateNoEx(blockpos, true); // remove true if light bugs if (!block) continue; - block->setLightingExpired(true); + block->setLightingComplete(0); // modified_blocks[blockpos] = block; // if(!nodemgr->get(neighbors[i].node).light_propagates || // nodemgr->get(neighbors[i].node).light_source) // better to update always diff --git a/src/fm_map.cpp b/src/fm_map.cpp index 6bead3cad..2031ba4bd 100644 --- a/src/fm_map.cpp +++ b/src/fm_map.cpp @@ -718,7 +718,7 @@ void ServerMap::unspreadLight(enum LightBank bank, std::map &from_n { */ //++block->lighting_broken; - block->setLightingExpired(true); + block->setLightingComplete(0); modified_blocks[blockpos] = block; /* @@ -877,9 +877,12 @@ void ServerMap::spreadLight(enum LightBank bank, std::set &from_nodes, <<" for "< end_ms) { + return; + } + + if (!lighted_nodes.empty()) { + /* infostream<<"spreadLight(): recursive("<lighting_broken = 0; } // infostream<< " ablocks_aft="<add("Server: light blocks", loopcount); + + return ret; +} +#endif + +u32 ServerMap::updateLighting(lighting_map_t &a_blocks, + unordered_map_v3pos &processed, unsigned int max_cycle_ms) +{ + std::map modified_blocks; + + int ret = 0; + int loopcount = 0; + + TimeTaker timer("updateLighting"); + for (auto i = a_blocks.begin(); i != a_blocks.end();) { + auto block = getBlockNoCreateNoEx(i->first); + if (!block) { + i = a_blocks.erase(i); + continue; + } + if (!voxalgo::repair_block_light(this, block, &modified_blocks)) { + processed[block->getPos()] = block->getPos().Y; + } + ++loopcount; + ++i; + } + + for (const auto &i : modified_blocks) { + a_blocks.erase(i.first); + } + for (const auto &i : processed) { + a_blocks.erase(i.first); + } g_profiler->add("Server: light blocks", loopcount); return ret; @@ -1231,7 +1256,7 @@ bool ServerMap::propagateSunlight( void ServerMap::lighting_modified_add(const v3pos_t &pos, int range) { MutexAutoLock lock(m_lighting_modified_mutex); - if (m_lighting_modified_blocks.count(pos)) { + if (m_lighting_modified_blocks.contains(pos)) { auto old_range = m_lighting_modified_blocks[pos]; if (old_range <= range) return; @@ -1257,9 +1282,7 @@ unsigned int ServerMap::updateLightingQueue(unsigned int max_cycle_ms, int &loop ++loopcount; range = r->first; blocks = r->second; - // infostream <<" go light range="<< r->first << " size="<raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_EXPIRE_DAYNIGHTDIFF); else - block->setLightingExpired(true); + block->setLightingComplete(0); } /* diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 8d94807cb..6f4f92f11 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -753,7 +753,6 @@ void MapBlock::deSerializeNetworkSpecific(std::istream &is) } if (light == modified_light_yes) { setLightingComplete(0); - setLightingExpired(true); } } diff --git a/src/mapblock.h b/src/mapblock.h index a501be79a..32137ea44 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -250,17 +250,6 @@ class MapBlock return (m_lighting_complete & (1 << direction)) != 0; } - inline void setLightingExpired(bool expired) - { - m_lighting_expired = expired; - } - - inline bool getLightingExpired() const - { - return m_lighting_expired; - } - - inline bool isGenerated() { return m_generated; @@ -362,6 +351,12 @@ class MapBlock return data[p.Z*zstride + p.Y*ystride + p.X]; } + inline void setNodeNoLock(v3pos_t p, MapNode n, bool important = false) + { + data[p.Z * zstride + p.Y * ystride + p.X] = n; + raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK, important); + } + //// //// Non-checking variants of the above //// diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index aa624c171..013bbce94 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -720,7 +720,7 @@ void MapgenBasic::generateBiomes() // nplaced to stone level by setting a number exceeding any possible filler depth. u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; - s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x,node_max.Y,z), NULL, &heat_cache) : 0; + const auto heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x,node_max.Y,z), NULL, &heat_cache) : 0; for (s16 y = node_max.Y; y >= node_min.Y; y--) { content_t c = vm->m_data[vi].getContent(); diff --git a/src/mapgen/mapgen_indev.cpp b/src/mapgen/mapgen_indev.cpp index 986d2e3d3..5e857894c 100644 --- a/src/mapgen/mapgen_indev.cpp +++ b/src/mapgen/mapgen_indev.cpp @@ -165,10 +165,10 @@ MapgenIndev::MapgenIndev(MapgenIndevParams *params, EmergeParams *emerge) floatland_ywater = params->floatland_ywater; - noise_layers = new Noise(&sp->np_layers, seed, csize.X, csize.Y + y_offset * 2, csize.Z); + noise_layers = new Noise(&sp->np_layers, seed, csize.X, csize.Y + y_offset * 2 + 2, csize.Z); layers_init(emerge, sp->paramsj); - noise_cave_indev = new Noise(&sp->np_cave_indev, seed, csize.X, csize.Y + y_offset * 2, csize.Z); + noise_cave_indev = new Noise(&sp->np_cave_indev, seed, csize.X, csize.Y + y_offset * 2 + 2, csize.Z); if (spflags & MGV6_FLOATLANDS) { @@ -242,12 +242,12 @@ void MapgenIndevParams::readParams(const Settings *settings) { paramsj = mg_params; //settings->getS16NoEx("mg_float_islands", float_islands); - settings->getS16NoEx("mgindev_floatland_ymin", floatland_ymin); - settings->getS16NoEx("mgindev_floatland_ymax", floatland_ymax); - settings->getS16NoEx("mgindev_floatland_taper", floatland_taper); + settings->getPosNoEx("mgindev_floatland_ymin", floatland_ymin); + settings->getPosNoEx("mgindev_floatland_ymax", floatland_ymax); + settings->getPosNoEx("mgindev_floatland_taper", floatland_taper); settings->getFloatNoEx("mgindev_float_taper_exp", float_taper_exp); settings->getFloatNoEx("mgindev_floatland_density", floatland_density); - settings->getS16NoEx("mgindev_floatland_ywater", floatland_ywater); + settings->getPosNoEx("mgindev_floatland_ywater", floatland_ywater); settings->getNoiseParamsFromGroup("mgindev_np_terrain_base", np_terrain_base); settings->getNoiseParamsFromGroup("mgindev_np_terrain_higher", np_terrain_higher); @@ -271,12 +271,12 @@ void MapgenIndevParams::writeParams(Settings *settings) const { //settings->setS16("mg_float_islands", float_islands); - settings->setS16("mgindev_floatland_ymin", floatland_ymin); - settings->setS16("mgindev_floatland_ymax", floatland_ymax); - settings->setS16("mgindev_floatland_taper", floatland_taper); + settings->setPos("mgindev_floatland_ymin", floatland_ymin); + settings->setPos("mgindev_floatland_ymax", floatland_ymax); + settings->setPos("mgindev_floatland_taper", floatland_taper); settings->setFloat("mgindev_float_taper_exp", float_taper_exp); settings->setFloat("mgindev_floatland_density", floatland_density); - settings->setS16("mgindev_floatland_ywater", floatland_ywater); + settings->setPos("mgindev_floatland_ywater", floatland_ywater); settings->setNoiseParams("mgindev_np_terrain_base", np_terrain_base); settings->setNoiseParams("mgindev_np_terrain_higher", np_terrain_higher); @@ -318,7 +318,7 @@ void MapgenIndev::generateCaves(int max_stone_y) { u32 bruises_count = 1; PseudoRandom ps(blockseed + 21343); PseudoRandom ps2(blockseed + 1032); - + if (ps.range(1, 6) == 1) bruises_count = ps.range(0, ps.range(0, 2)); @@ -419,7 +419,7 @@ int Mapgen_features::float_islands_generate(const v3pos_t & node_min, const v3po int generated = 0; if (node_min.Y < min_y) return generated; // originally from http://forum.minetest.net/viewtopic.php?id=4776 - float RAR = 0.8 * farscale(0.4, node_min.Y); // 0.4; // Island rarity in chunk layer. -0.4 = thick layer with holes, 0 = 50%, 0.4 = desert rarity, 0.7 = very rare. + float RAR = 0.8 * farscale(0.4f, node_min.Y); // 0.4; // Island rarity in chunk layer. -0.4 = thick layer with holes, 0 = 50%, 0.4 = desert rarity, 0.7 = very rare. float AMPY = 24; // 24; // Amplitude of island centre y variation. float TGRAD = 24; // 24; // Noise gradient to create top surface. Tallness of island top. float BGRAD = 24; // 24; // Noise gradient to create bottom surface. Tallness of island bottom. @@ -478,8 +478,8 @@ int MapgenIndev::generateGround() { bool gen_floatlands = false; u8 cache_index = 0; // Y values where floatland tapering starts - s16 float_taper_ymax = floatland_ymax - floatland_taper; - s16 float_taper_ymin = floatland_ymin + floatland_taper; + pos_t float_taper_ymax = floatland_ymax - floatland_taper; + pos_t float_taper_ymin = floatland_ymin + floatland_taper; if ((spflags & MGV6_FLOATLANDS) && node_max.Y >= floatland_ymin && node_min.Y <= floatland_ymax) { @@ -488,7 +488,7 @@ int MapgenIndev::generateGround() { noise_floatland->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); // Cache floatland noise offset values, for floatland tapering - for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++, cache_index++) { + for (pos_t y = node_min.Y - 1; y <= node_max.Y + 1; y++, cache_index++) { float float_offset = 0.0f; if (y > float_taper_ymax) { float_offset = std::pow((y - float_taper_ymax) / (float)floatland_taper, @@ -505,10 +505,10 @@ int MapgenIndev::generateGround() { int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index = 0; - for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 x = node_min.X; x <= node_max.X; x++, index++) { + for (pos_t z = node_min.Z; z <= node_max.Z; z++) + for (pos_t x = node_min.X; x <= node_max.X; x++, index++) { // Surface height - s16 surface_y = (s16)baseTerrainLevelFromMap(index); + pos_t surface_y = (pos_t)baseTerrainLevelFromMap(index); // Log it if (surface_y > stone_surface_max_y) @@ -516,7 +516,7 @@ int MapgenIndev::generateGround() { auto bt = getBiome(index, v3pos_t(x, surface_y, z)); - s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x,node_max.Y,z), nullptr, &heat_cache) : 0; + const auto heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x,node_max.Y,z), nullptr, &heat_cache) : 0; // Fill ground with stone v3pos_t em = vm->m_area.getExtent(); @@ -525,7 +525,7 @@ int MapgenIndev::generateGround() { cache_index = 0; u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X); - for (s16 y = node_min.Y; y <= node_max.Y; + for (pos_t y = node_min.Y; y <= node_max.Y; y++, index3d += ystride, cache_index++) { if (!vm->m_data[i]) { diff --git a/src/mapgen/mapgen_indev.h b/src/mapgen/mapgen_indev.h index 64b9c574b..bb78296e1 100644 --- a/src/mapgen/mapgen_indev.h +++ b/src/mapgen/mapgen_indev.h @@ -75,12 +75,12 @@ class Mapgen_features { struct MapgenIndevParams : public MapgenV6Params { //s16 float_islands; - s16 floatland_ymin = 1024; - s16 floatland_ymax = mapgen_limit; - s16 floatland_taper = 256; + pos_t floatland_ymin = 1024; + pos_t floatland_ymax = mapgen_limit; + pos_t floatland_taper = 256; float float_taper_exp = 2.0f; float floatland_density = -0.6f; - s16 floatland_ywater = 10000; + pos_t floatland_ywater = 10000; /* NoiseParams np_float_islands1; @@ -103,10 +103,10 @@ struct MapgenIndevParams : public MapgenV6Params { class MapgenIndev : public MapgenV6, public Mapgen_features { Noise *noise_floatland = nullptr; - s16 floatland_taper; + pos_t floatland_taper; float float_taper_exp; float floatland_density; - s16 floatland_ywater; + pos_t floatland_ywater; float *float_offset_cache = nullptr; diff --git a/src/mapgen/mapgen_math.cpp b/src/mapgen/mapgen_math.cpp index 64a9e1005..67d853c82 100644 --- a/src/mapgen/mapgen_math.cpp +++ b/src/mapgen/mapgen_math.cpp @@ -651,7 +651,7 @@ int MapgenMath::generateTerrain() { for (pos_t z = node_min.Z; z <= node_max.Z; z++) { for (pos_t x = node_min.X; x <= node_max.X; x++, index++) { - s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x, node_max.Y, z), nullptr, &heat_cache) : 0; + const auto heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x, node_max.Y, z), nullptr, &heat_cache) : 0; u32 i = vm->m_area.index(x, node_min.Y, z); for (pos_t y = node_min.Y; y <= node_max.Y; y++) { diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp index bc266017b..a229c10fe 100644 --- a/src/mapgen/mapgen_v5.cpp +++ b/src/mapgen/mapgen_v5.cpp @@ -78,7 +78,7 @@ MapgenV5::MapgenV5(MapgenV5Params *params, EmergeParams *emerge) noise_float_islands2 = new Noise(¶ms->np_float_islands2, seed, csize.X, csize.Y + y_offset * 2, csize.Z); noise_float_islands3 = new Noise(¶ms->np_float_islands3, seed, csize.X, csize.Z); - noise_layers = new Noise(¶ms->np_layers, seed, csize.X, csize.Y + y_offset * 2, csize.Z); + noise_layers = new Noise(¶ms->np_layers, seed, csize.X, csize.Y + y_offset * 2 + 2, csize.Z); layers_init(emerge, params->paramsj); //noise_cave_indev = new Noise(&sp->np_cave_indev, seed, csize.X, csize.Y + y_offset * 2, csize.Z); diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index c0386bc98..7c29157c3 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -713,7 +713,7 @@ int MapgenV6::generateGround() stone_surface_max_y = surface_y; BiomeV6Type bt = getBiome(v3pos_t(x, node_min.Y, z)); - s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x,node_max.Y,z), nullptr, &heat_cache) : 0; + const auto heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x,node_max.Y,z), nullptr, &heat_cache) : 0; // Fill ground with stone const v3s16 &em = vm->m_area.getExtent(); @@ -1123,7 +1123,7 @@ void MapgenV6::growGrass() // Add surface nodes u32 i = vm->m_area.index(x, surface_y, z); content_t c = vm->m_data[i].getContent(); if (m_emerge->env->m_use_weather && c == c_dirt) { - int heat = m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x, surface_y, z), nullptr, &heat_cache); + const auto heat = m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3pos_t(x, surface_y, z), nullptr, &heat_cache); vm->m_data[i] = (heat < -10 ? n_dirt_with_snow : (heat < -5 || heat > 50) ? n_dirt : n_dirt_with_grass); } else if (surface_y >= water_level - 20) { diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index 3c690c1e4..3fc3de017 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -131,9 +131,9 @@ MapgenV7::MapgenV7(MapgenV7Params *params, EmergeParams *emerge) //noise_float_islands2 = new Noise(¶ms->np_float_islands2, seed, csize.X, csize.Y + y_offset * 2, csize.Z); //noise_float_islands3 = new Noise(¶ms->np_float_islands3, seed, csize.X, csize.Z); - noise_layers = new Noise(¶ms->np_layers, seed, csize.X, csize.Y + y_offset * 2, csize.Z); + noise_layers = new Noise(¶ms->np_layers, seed, csize.X, csize.Y + y_offset * 2 + 2, csize.Z); layers_init(emerge, params->paramsj); - noise_cave_indev = new Noise(¶ms->np_cave_indev, seed, csize.X, csize.Y + y_offset * 2, csize.Z); + noise_cave_indev = new Noise(¶ms->np_cave_indev, seed, csize.X, csize.Y + y_offset * 2 + 2, csize.Z); //========== diff --git a/src/network/fm_connection_multi.cpp b/src/network/fm_connection_multi.cpp index c7a92b02b..d2cf40afa 100644 --- a/src/network/fm_connection_multi.cpp +++ b/src/network/fm_connection_multi.cpp @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_connection_multi.h b/src/network/fm_connection_multi.h index 7c5dcb544..369a80ac4 100644 --- a/src/network/fm_connection_multi.h +++ b/src/network/fm_connection_multi.h @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_connection_sctp.cpp b/src/network/fm_connection_sctp.cpp index b76dcf315..53e17533c 100644 --- a/src/network/fm_connection_sctp.cpp +++ b/src/network/fm_connection_sctp.cpp @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_connection_sctp.h b/src/network/fm_connection_sctp.h index 649a8abc5..637652950 100644 --- a/src/network/fm_connection_sctp.h +++ b/src/network/fm_connection_sctp.h @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_connection_use.h b/src/network/fm_connection_use.h index d2bcafddf..d914e6fa7 100644 --- a/src/network/fm_connection_use.h +++ b/src/network/fm_connection_use.h @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_connection_websocket_sctp.cpp b/src/network/fm_connection_websocket_sctp.cpp index 27c98d7d9..b4b8b7efa 100644 --- a/src/network/fm_connection_websocket_sctp.cpp +++ b/src/network/fm_connection_websocket_sctp.cpp @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_connection_websocket_sctp.h b/src/network/fm_connection_websocket_sctp.h index 267a4469a..5a98c43e6 100644 --- a/src/network/fm_connection_websocket_sctp.h +++ b/src/network/fm_connection_websocket_sctp.h @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/fm_lan.cpp b/src/network/fm_lan.cpp index a9529befc..efdffd216 100644 --- a/src/network/fm_lan.cpp +++ b/src/network/fm_lan.cpp @@ -1,6 +1,8 @@ /* -Copyright (C) 2016 proller +Copyright (C) 2016 proller +*/ +/* This file is part of Freeminer. Freeminer is free software: you can redistribute it and/or modify diff --git a/src/network/fm_lan.h b/src/network/fm_lan.h index 9431b6209..ee64e6756 100644 --- a/src/network/fm_lan.h +++ b/src/network/fm_lan.h @@ -1,5 +1,5 @@ /* -Copyright (C) 2016 proller +Copyright (C) 2016 proller This file is part of Freeminer. diff --git a/src/network/wssocket.cpp b/src/network/wssocket.cpp index 9733ad8c2..0d4375541 100644 --- a/src/network/wssocket.cpp +++ b/src/network/wssocket.cpp @@ -1,3 +1,7 @@ +/* +Copyright (C) 2023 proller +*/ + /* This file is part of Freeminer. diff --git a/src/network/wssocket.h b/src/network/wssocket.h index 2deecdf50..3e84ac893 100644 --- a/src/network/wssocket.h +++ b/src/network/wssocket.h @@ -1,6 +1,5 @@ /* -database-dummy.cpp -Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2023 proller */ /* diff --git a/src/noise.cpp b/src/noise.cpp index 78cf8d9c3..de85528a3 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -765,16 +765,3 @@ void Noise::updateResults(float g, float *gmap, } } } - -float farscale(float scale, float z) { - return ( 1 + ( 1 - (FARSCALE_LIMIT * 1 - (fabs(z)) ) / (FARSCALE_LIMIT * 1) ) * (scale - 1) ); -} - -float farscale(float scale, float x, float z) { - return ( 1 + ( 1 - (FARSCALE_LIMIT * 2 - (fabs(x) + fabs(z)) ) / (FARSCALE_LIMIT * 2) ) * (scale - 1) ); -} - -float farscale(float scale, float x, float y, float z) { - return ( 1 + ( 1 - (FARSCALE_LIMIT * 3 - (fabs(x) + fabs(y) + fabs(z)) ) / (FARSCALE_LIMIT * 3) ) * (scale - 1) ); -} - diff --git a/src/noise.h b/src/noise.h index d7c7cb392..ca3937bfe 100644 --- a/src/noise.h +++ b/src/noise.h @@ -25,6 +25,7 @@ along with Freeminer. If not, see . #include +#include "constants.h" #include "irr_v3d.h" #include "exceptions.h" #include "util/string.h" @@ -38,9 +39,21 @@ along with Freeminer. If not, see . extern FlagDesc flagdesc_noiseparams[]; -float farscale(float scale, float z); -float farscale(float scale, float x, float z); -float farscale(float scale, float x, float y, float z); +template +inline type_scale farscale(type_scale scale, type_coord z) { + return ( 1 + ( 1 - (FARSCALE_LIMIT * 1 - (fabs(z)) ) / (FARSCALE_LIMIT * 1) ) * (scale - 1) ); +} + +template +inline type_scale farscale(type_scale scale, type_coord x, type_coord z) { + return ( 1 + ( 1 - (FARSCALE_LIMIT * 2 - (fabs(x) + fabs(z)) ) / (FARSCALE_LIMIT * 2) ) * (scale - 1) ); +} + +template +inline type_scale farscale(type_scale scale, type_coord x, type_coord y, type_coord z) { + return ( 1 + ( 1 - (FARSCALE_LIMIT * 3 - (fabs(x) + fabs(y) + fabs(z)) ) / (FARSCALE_LIMIT * 3) ) * (scale - 1) ); +} + // Note: this class is not polymorphic so that its high level of // optimizability may be preserved in the common use case @@ -123,10 +136,10 @@ struct NoiseParams { u32 flags = NOISE_FLAG_DEFAULTS; // fm: - float far_scale = 1; - float far_spread = 1; - float far_persist = 1; - float far_lacunarity = 1; + opos_t far_scale = 1; + opos_t far_spread = 1; + opos_t far_persist = 1; + opos_t far_lacunarity = 1; NoiseParams() = default; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index d6c872e10..9172217d2 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -1703,7 +1703,7 @@ void read_groups(lua_State *L, int index, ItemGroupList &result) std::string name = luaL_checkstring(L, -2); int rating = luaL_checkinteger(L, -1); // zero rating indicates not in the group - if (rating != 0) + //fm: wtf? if (rating != 0) result[name] = rating; // removes value, keeps key for next iteration lua_pop(L, 1); diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 14aa0ca2d..a1d91f631 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -562,6 +562,20 @@ bool getfloatfield(lua_State *L, int table, return got; } +bool getfloatfield(lua_State *L, int table, + const char *fieldname, double &result) +{ + lua_getfield(L, table, fieldname); + bool got = false; + + if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) { + result = lua_tonumber(L, -1); + got = true; + } + lua_pop(L, 1); + return got; +} + bool getboolfield(lua_State *L, int table, const char *fieldname, bool &result) { diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index 57e266fd9..21dc0dca9 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -81,6 +81,8 @@ bool getboolfield(lua_State *L, int table, const char *fieldname, bool &result); bool getfloatfield(lua_State *L, int table, const char *fieldname, float &result); +bool getfloatfield(lua_State *L, int table, + const char *fieldname, double &result); void setstringfield(lua_State *L, int table, const char *fieldname, const std::string &value); diff --git a/src/server.cpp b/src/server.cpp index 369187019..e425fa039 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -264,7 +264,6 @@ Server::Server( CONNECTION_TIMEOUT, m_bind_addr.isIPv6(), this)), - stat(path_world), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), @@ -272,6 +271,7 @@ Server::Server( m_clients(m_con), m_admin_chat(iface), m_on_shutdown_errmsg(on_shutdown_errmsg), + stat(path_world), m_modchannel_mgr(new ModChannelMgr()) { #if ENABLE_THREADS @@ -4248,8 +4248,10 @@ v3f Server::findSpawnPos() for (s32 ii = (find > 0) ? 0 : find - 50; ii < find; ii++) { v3bpos_t blockpos = getNodeBlockPos(nodepos); - if (!map.emergeBlock(blockpos, false)) + if (!map.emergeBlock(blockpos, false)) { + nodeposf = intToFloat(nodepos, BS); break; + } content_t c = map.getNode(nodepos).getContent(); // In generated mapblocks allow spawn in all 'airlike' drawtype nodes. diff --git a/src/settings.cpp b/src/settings.cpp index 560e16ee1..05fc52e72 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -778,6 +778,16 @@ bool Settings::getFloatNoEx(const std::string &name, float &val) const } } +bool Settings::getFloatNoEx(const std::string &name, double &val) const +{ + try { + val = getFloat(name); + return true; + } catch (SettingNotFoundException &e) { + return false; + } +} + bool Settings::getU16NoEx(const std::string &name, u16 &val) const { @@ -820,6 +830,14 @@ bool Settings::getS32NoEx(const std::string &name, s32 &val) const } } +bool Settings::getPosNoEx(const std::string &name, pos_t &val) const +{ +#if USE_POS32 + return getS32NoEx(name, val); +#else + return getS16NoEx(name, val); +#endif +} bool Settings::getU64NoEx(const std::string &name, u64 &val) const { @@ -956,6 +974,10 @@ bool Settings::setS32(const std::string &name, s32 value) return set(name, itos(value)); } +bool Settings::setPos(const std::string &name, pos_t value) +{ + return set(name, itos(value)); +} bool Settings::setU64(const std::string &name, uint64_t value) { diff --git a/src/settings.h b/src/settings.h index b27736277..fdf23be9a 100644 --- a/src/settings.h +++ b/src/settings.h @@ -204,7 +204,9 @@ class Settings { bool getU32NoEx(const std::string &name, u32 &val) const; bool getS32NoEx(const std::string &name, s32 &val) const; bool getU64NoEx(const std::string &name, u64 &val) const; + bool getPosNoEx(const std::string &name, pos_t &val) const; bool getFloatNoEx(const std::string &name, float &val) const; + bool getFloatNoEx(const std::string &name, double &val) const; bool getV2FNoEx(const std::string &name, v2f &val) const; bool getV3FNoEx(const std::string &name, v3f &val) const; @@ -233,6 +235,7 @@ class Settings { bool setU16(const std::string &name, u16 value); bool setS32(const std::string &name, s32 value); bool setU64(const std::string &name, uint64_t value); + bool setPos(const std::string &name, pos_t value); bool setFloat(const std::string &name, float value); bool setV2F(const std::string &name, v2f value); bool setV3F(const std::string &name, v3f value); diff --git a/src/threading/thread_pool.cpp b/src/threading/thread_pool.cpp index 5d28dc02c..ba1a44a2b 100644 --- a/src/threading/thread_pool.cpp +++ b/src/threading/thread_pool.cpp @@ -37,7 +37,7 @@ void thread_pool::start (int n) { #endif requeststop = false; for(int i = 0; i < n; ++i) - workers.emplace_back(std::thread(&thread_pool::func, this)); + workers.emplace_back(&thread_pool::func, this); } void thread_pool::stop () { diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index 12d750c28..177eb36cd 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -294,8 +294,14 @@ void unspread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank, } else { neighbor_block = current.block; } + + auto lock = neighbor_block->try_lock_unique_rec(); + if (!lock->owns_lock()) { + continue; // may cause dark areas + } + // Get the neighbor itself - MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos); + MapNode neighbor = neighbor_block->getNodeNoLock(neighbor_rel_pos); ContentLightingFlags neighbor_f = nodemgr->getLightingFlags( neighbor.getContent()); u8 neighbor_light = neighbor.getLightRaw(bank, neighbor_f); @@ -306,7 +312,7 @@ void unspread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank, // Unlight, but only if the node has light. if (neighbor_light > 0) { neighbor.setLight(bank, 0, neighbor_f); - neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor); + neighbor_block->setNodeNoLock(neighbor_rel_pos, neighbor); from_nodes.push(neighbor_light, neighbor_rel_pos, neighbor_block_pos, neighbor_block, i); // The current node was modified earlier, so its block @@ -381,15 +387,18 @@ void spread_light(Map *map, const NodeDefManager *nodemgr, LightBank bank, } else { neighbor_block = current.block; } + + auto lock = neighbor_block->lock_unique_rec(); + // Get the neighbor itself - MapNode neighbor = neighbor_block->getNodeNoCheck(neighbor_rel_pos); + MapNode neighbor = neighbor_block->getNodeNoLock(neighbor_rel_pos); ContentLightingFlags f = nodemgr->getLightingFlags(neighbor); if (f.light_propagates) { // Light up the neighbor, if it has less light than it should. u8 neighbor_light = neighbor.getLightRaw(bank, f); if (neighbor_light < spreading_light) { neighbor.setLight(bank, spreading_light, f); - neighbor_block->setNodeNoCheck(neighbor_rel_pos, neighbor); + neighbor_block->setNodeNoLock(neighbor_rel_pos, neighbor); light_sources.push(spreading_light, neighbor_rel_pos, neighbor_block_pos, neighbor_block, i); // The current node was modified earlier, so its block @@ -635,7 +644,7 @@ void update_lighting_nodes(Map *map, } for (const auto & block : modified_blocks) { - block.second->setLightingExpired(false); + block.second->setLightingComplete(static_cast(0xFFFF)); } } @@ -758,7 +767,7 @@ void update_block_border_lighting(Map *map, MapBlock *block, } for (const auto & block : modified_blocks) { - block.second->setLightingExpired(false); + block.second->setLightingComplete(static_cast(0xFFFF)); } } @@ -855,6 +864,38 @@ void is_sunlight_above_block(Map *map, mapblock_v3 pos, MapNode above = source_block->getNodeNoCheck(x, 0, z); ContentLightingFlags above_f = ndef->getLightingFlags(above); light[z][x] = above.getLight(LIGHTBANK_DAY, above_f) == LIGHT_SUN; + + if (!light[z][x]) { + v3pos_t p; + bool go = false; + if (x == 0) { + p = v3pos_t(x - 1, 0, z); + go = true; + } else if (z == 0) { + p = v3pos_t(x, 0, z - 1); + go = true; + } else if (z == MAP_BLOCKSIZE - 1) { + p = v3pos_t(x, 0, z + 1); + go = true; + } else if (x == MAP_BLOCKSIZE - 1) { + p = v3pos_t(x + 1, 0, z); + go = true; + } + if (go) { + const auto n = map->getNode(p + source_block->getPosRelative()); + if (n.getLight(LIGHTBANK_DAY, ndef->getLightingFlags(n)) == LIGHT_SUN) + light[z][x] = true; + } + } else { + if (z > 0) + light[z - 1][x] = true; + if (x > 0) + light[z][x - 1] = true; + if (z < MAP_BLOCKSIZE - 1) + light[z + 1][x] = true; + if (x < MAP_BLOCKSIZE - 1) + light[z][x + 1] = true; + } } } } @@ -880,6 +921,9 @@ bool propagate_block_sunlight(Map *map, const NodeDefManager *ndef, data->data.clear(); return false; } + + auto lock = block->lock_unique_rec(); + // For each changing column of nodes: size_t index; for (index = 0; index < data->data.size(); index++) { @@ -891,13 +935,13 @@ bool propagate_block_sunlight(Map *map, const NodeDefManager *ndef, // Propagate sunlight. // For each node downwards: for (; current_pos.Y >= 0; current_pos.Y--) { - MapNode n = block->getNodeNoCheck(current_pos); + MapNode n = block->getNodeNoLock(current_pos); ContentLightingFlags f = ndef->getLightingFlags(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); + block->setNodeNoLock(current_pos, n); modified = true; relight->push(LIGHT_SUN, current_pos, data->target_block, block, 4); @@ -910,12 +954,12 @@ bool propagate_block_sunlight(Map *map, const NodeDefManager *ndef, // Propagate shadow. // For each node downwards: for (; current_pos.Y >= 0; current_pos.Y--) { - MapNode n = block->getNodeNoCheck(current_pos); + MapNode n = block->getNodeNoLock(current_pos); ContentLightingFlags f = ndef->getLightingFlags(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); + block->setNodeNoLock(current_pos, n); modified = true; unlight->push(LIGHT_SUN, current_pos, data->target_block, block, 4); @@ -1036,7 +1080,7 @@ void finish_bulk_light_update(Map *map, mapblock_v3 minblock, } for (const auto & block : *modified_blocks) { - block.second->setLightingExpired(false); + block.second->setLightingComplete(static_cast(0xFFFF)); } } @@ -1157,7 +1201,7 @@ void fill_with_sunlight(MapBlock *block, const NodeDefManager *ndef, bool lig = light[z][x]; // For each node, downwards: for (s16 y = MAP_BLOCKSIZE - 1; y >= 0; y--) { - MapNode n = block->getNodeNoCheck(x, y, z); + MapNode n = block->getNodeNoLock({x, y, z}); // Ignore IGNORE nodes, these are not generated yet. if (n.getContent() == CONTENT_IGNORE) continue; @@ -1169,18 +1213,18 @@ void fill_with_sunlight(MapBlock *block, const NodeDefManager *ndef, // Reset light n.setLight(LIGHTBANK_DAY, lig ? 15 : 0, f); n.setLight(LIGHTBANK_NIGHT, 0, f); - block->setNodeNoCheck(x, y, z, n); + block->setNodeNoLock({x, y, z}, n); } // Output outgoing light. light[z][x] = lig; } } -void repair_block_light(Map *map, MapBlock *block, +bool repair_block_light(Map *map, MapBlock *block, std::map *modified_blocks) { if (!block) - return; + return false; const NodeDefManager *ndef = map->getNodeDefManager(); // First queue is for day light, second is for night light. UnlightQueue unlight[] = { UnlightQueue(256), UnlightQueue(256) }; @@ -1196,8 +1240,14 @@ void repair_block_light(Map *map, MapBlock *block, // For each map block: // Extract sunlight above. is_sunlight_above_block(map, blockpos, ndef, lights); + + { + + auto lock = block->lock_unique_rec(); + // Reset the voxel manipulator. fill_with_sunlight(block, ndef, lights); + } // Copy sunlight data data.target_block = v3s16(blockpos.X, blockpos.Y - 1, blockpos.Z); for (s16 z = 0; z < MAP_BLOCKSIZE; z++) @@ -1214,6 +1264,10 @@ void repair_block_light(Map *map, MapBlock *block, data.target_block.Y--; } + + { + auto lock = block->lock_shared_rec(); + // --- STEP 2: Get nodes from borders to unlight // For each border of the block: @@ -1225,7 +1279,7 @@ void repair_block_light(Map *map, MapBlock *block, for (relpos.Y = a.MinEdge.Y; relpos.Y <= a.MaxEdge.Y; relpos.Y++) { // Get node - MapNode node = block->getNodeNoCheck(relpos); + MapNode node = block->getNodeNoLock(relpos); ContentLightingFlags f = ndef->getLightingFlags(node); // For each light bank for (size_t b = 0; b < 2; b++) { @@ -1243,11 +1297,13 @@ void repair_block_light(Map *map, MapBlock *block, } // end of banks } // end of nodes } // end of borders - + } // STEP 3: Remove and spread light finish_bulk_light_update(map, blockpos, blockpos, unlight, relight, modified_blocks); + + return false; } VoxelLineIterator::VoxelLineIterator(const v3f &start_position, const v3f &line_vector) : diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index b0d46b6b1..faa4d7836 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -79,7 +79,7 @@ void blit_back_with_light(Map *map, MMVManip *vm, * * \param block the block to update */ -void repair_block_light(Map *map, MapBlock *block, +bool repair_block_light(Map *map, MapBlock *block, std::map *modified_blocks); /*! diff --git a/util/autotest/auto.pl b/util/autotest/auto.pl index 5cd4711c4..d541c25c4 100755 --- a/util/autotest/auto.pl +++ b/util/autotest/auto.pl @@ -376,7 +376,7 @@ () $D{USE_LIBCXX} = $config->{cmake_libcxx} if defined $config->{cmake_libcxx}; $D{USE_TOUCHSCREENGUI} = $config->{cmake_touchscreen} if defined $config->{cmake_touchscreen}; $D{USE_GPERF} = $config->{cmake_gperf} if defined $config->{cmake_gperf}; - $D{NO_LTO} = $config->{cmake_no_lto} // 1; + $D{USE_LTO} = $config->{cmake_lto} if defined $config->{cmake_lto}; $D{EXCEPTION_DEBUG} = $config->{cmake_exception_debug} if defined $config->{cmake_exception_debug}; $D{USE_DEBUG_HELPERS} = 1; @@ -413,7 +413,7 @@ () sy qq{$config->{env} $config->{runner} @_ ./freeminer --run-unittests --logfile $config->{logdir}/autotest.$g->{task_name}.test.log } . options_make([qw(verbose trace)]); }, set_bot => {'----bot' => 1, '----bot_random' => 1}, - run_bot => ['set_bot', 'run_single'], + run_bot => ['set_bot', 'set_client', 'run_single'], run_single_tsan => sub { local $config->{options_display} = 'software' if $config->{tsan_opengl_fix} and !$config->{options_display}; local $config->{cmake_leveldb} //= 0 if $config->{tsan_leveldb_fix}; @@ -488,8 +488,8 @@ () fail => sub { warn 'fail:', join ' ', @_; }, - set_client => [{-no_build_client => 0, -no_build_server => 1,}], - set_server => [{-no_build_client => 1, -no_build_server => 0, -options_add => 'no_exit'}], + set_client => [{'---no_build_client' => 0, '---no_build_server' => 1,}], + set_server => [{'---no_build_client' => 1, '---no_build_server' => 0, -options_add => 'no_exit'}], }; our $tasks = { @@ -693,6 +693,7 @@ () ++$g->{keep_config}; $config->{runner} = $config->{runner} + . ' ASAN_OPTIONS=abort_on_error=1 ' . $config->{gdb} . q{ -ex 'run' -ex 't a a bt' } . ($config->{gdb_stay} ? '' : q{ -ex 'cont' -ex 'quit' })