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' })