2010-11-27 00:02:21 +01:00
|
|
|
/*
|
2013-02-24 18:40:43 +01:00
|
|
|
Minetest
|
2013-02-24 19:38:45 +01:00
|
|
|
Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
2010-11-29 19:13:04 +01:00
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
2012-06-05 16:56:56 +02:00
|
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2.1 of the License, or
|
2010-11-29 19:13:04 +01:00
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2012-06-05 16:56:56 +02:00
|
|
|
GNU Lesser General Public License for more details.
|
2010-11-29 19:13:04 +01:00
|
|
|
|
2012-06-05 16:56:56 +02:00
|
|
|
You should have received a copy of the GNU Lesser General Public License along
|
2010-11-29 19:13:04 +01:00
|
|
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "map.h"
|
2011-06-25 23:03:58 +02:00
|
|
|
#include "mapsector.h"
|
|
|
|
#include "mapblock.h"
|
2010-11-27 00:02:21 +01:00
|
|
|
#include "filesys.h"
|
2010-11-29 09:52:07 +01:00
|
|
|
#include "voxel.h"
|
2016-10-20 21:41:38 +02:00
|
|
|
#include "voxelalgorithms.h"
|
2010-12-22 02:33:58 +01:00
|
|
|
#include "porting.h"
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "serialization.h"
|
2011-06-26 01:34:36 +02:00
|
|
|
#include "nodemetadata.h"
|
2011-10-12 12:53:38 +02:00
|
|
|
#include "settings.h"
|
2011-10-16 13:57:53 +02:00
|
|
|
#include "log.h"
|
2011-10-17 16:06:28 +02:00
|
|
|
#include "profiler.h"
|
2011-11-14 20:41:30 +01:00
|
|
|
#include "nodedef.h"
|
|
|
|
#include "gamedef.h"
|
2012-06-17 01:40:36 +02:00
|
|
|
#include "util/directiontables.h"
|
2016-12-13 23:16:26 +01:00
|
|
|
#include "util/basic_macros.h"
|
2012-07-26 21:06:45 +02:00
|
|
|
#include "rollback_interface.h"
|
2013-08-11 04:09:45 +02:00
|
|
|
#include "environment.h"
|
2016-11-27 21:51:40 +01:00
|
|
|
#include "reflowscan.h"
|
2013-02-14 04:43:15 +01:00
|
|
|
#include "emerge.h"
|
2017-11-08 23:56:20 +01:00
|
|
|
#include "mapgen/mapgen_v6.h"
|
|
|
|
#include "mapgen/mg_biome.h"
|
2013-09-10 18:16:36 +02:00
|
|
|
#include "config.h"
|
2013-09-17 04:52:24 +02:00
|
|
|
#include "server.h"
|
2017-11-08 23:56:20 +01:00
|
|
|
#include "database/database.h"
|
|
|
|
#include "database/database-dummy.h"
|
|
|
|
#include "database/database-sqlite3.h"
|
2017-04-25 19:38:08 +02:00
|
|
|
#include "script/scripting_server.h"
|
2015-01-13 15:19:54 +01:00
|
|
|
#include <deque>
|
2015-08-10 22:24:47 +02:00
|
|
|
#include <queue>
|
2012-10-22 23:18:44 +02:00
|
|
|
#if USE_LEVELDB
|
2017-11-08 23:56:20 +01:00
|
|
|
#include "database/database-leveldb.h"
|
2012-10-22 23:18:44 +02:00
|
|
|
#endif
|
2014-04-08 21:39:21 +02:00
|
|
|
#if USE_REDIS
|
2017-11-08 23:56:20 +01:00
|
|
|
#include "database/database-redis.h"
|
2014-04-08 21:39:21 +02:00
|
|
|
#endif
|
2016-05-14 11:00:42 +02:00
|
|
|
#if USE_POSTGRESQL
|
2017-11-08 23:56:20 +01:00
|
|
|
#include "database/database-postgresql.h"
|
2016-05-14 11:00:42 +02:00
|
|
|
#endif
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2010-11-29 09:52:07 +01:00
|
|
|
/*
|
|
|
|
Map
|
|
|
|
*/
|
|
|
|
|
2011-11-13 23:19:48 +01:00
|
|
|
Map::Map(std::ostream &dout, IGameDef *gamedef):
|
2010-11-27 00:02:21 +01:00
|
|
|
m_dout(dout),
|
2011-11-13 23:19:48 +01:00
|
|
|
m_gamedef(gamedef),
|
2017-06-17 19:11:28 +02:00
|
|
|
m_nodedef(gamedef->ndef())
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Map::~Map()
|
|
|
|
{
|
|
|
|
/*
|
2011-02-23 01:49:57 +01:00
|
|
|
Free all MapSectors
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto §or : m_sectors) {
|
|
|
|
delete sector.second;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-23 01:49:57 +01:00
|
|
|
void Map::addEventReceiver(MapEventReceiver *event_receiver)
|
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
m_event_receivers.insert(event_receiver);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Map::removeEventReceiver(MapEventReceiver *event_receiver)
|
|
|
|
{
|
2012-12-20 18:19:49 +01:00
|
|
|
m_event_receivers.erase(event_receiver);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
|
2019-09-24 19:05:28 +02:00
|
|
|
void Map::dispatchEvent(const MapEditEvent &event)
|
2011-02-23 01:49:57 +01:00
|
|
|
{
|
2017-08-19 09:12:54 +02:00
|
|
|
for (MapEventReceiver *event_receiver : m_event_receivers) {
|
|
|
|
event_receiver->onMapEditEvent(event);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 19:58:27 +02:00
|
|
|
MapSector * Map::getSectorNoGenerateNoLock(v2s16 p)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
|
|
|
if(m_sector_cache != NULL && p == m_sector_cache_p){
|
|
|
|
MapSector * sector = m_sector_cache;
|
|
|
|
return sector;
|
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v2s16, MapSector*>::iterator n = m_sectors.find(p);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
if (n == m_sectors.end())
|
2011-01-30 00:44:54 +01:00
|
|
|
return NULL;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
MapSector *sector = n->second;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
// Cache the last result
|
|
|
|
m_sector_cache_p = p;
|
|
|
|
m_sector_cache = sector;
|
|
|
|
|
|
|
|
return sector;
|
|
|
|
}
|
|
|
|
|
2011-01-30 00:44:54 +01:00
|
|
|
MapSector * Map::getSectorNoGenerate(v2s16 p)
|
|
|
|
{
|
2019-08-13 19:58:27 +02:00
|
|
|
return getSectorNoGenerateNoLock(p);
|
2011-01-30 00:44:54 +01:00
|
|
|
}
|
|
|
|
|
2010-12-21 17:08:24 +01:00
|
|
|
MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d)
|
|
|
|
{
|
2010-11-27 00:02:21 +01:00
|
|
|
v2s16 p2d(p3d.X, p3d.Z);
|
2019-08-13 19:58:27 +02:00
|
|
|
MapSector * sector = getSectorNoGenerate(p2d);
|
2011-06-26 00:31:43 +02:00
|
|
|
if(sector == NULL)
|
2010-12-21 17:08:24 +01:00
|
|
|
return NULL;
|
2011-06-25 23:03:58 +02:00
|
|
|
MapBlock *block = sector->getBlockNoCreateNoEx(p3d.Y);
|
2010-11-27 00:02:21 +01:00
|
|
|
return block;
|
2010-12-21 17:08:24 +01:00
|
|
|
}
|
|
|
|
|
2011-06-25 23:03:58 +02:00
|
|
|
MapBlock * Map::getBlockNoCreate(v3s16 p3d)
|
2012-12-22 06:34:35 +01:00
|
|
|
{
|
2011-06-25 23:03:58 +02:00
|
|
|
MapBlock *block = getBlockNoCreateNoEx(p3d);
|
|
|
|
if(block == NULL)
|
|
|
|
throw InvalidPositionException();
|
2011-02-01 02:06:02 +01:00
|
|
|
return block;
|
2010-12-21 17:08:24 +01:00
|
|
|
}
|
2011-02-01 02:06:02 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
bool Map::isNodeUnderground(v3s16 p)
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
2019-08-07 12:06:45 +02:00
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
2020-04-27 08:31:37 +02:00
|
|
|
return block && block->getIsUnderground();
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2011-06-26 01:34:36 +02:00
|
|
|
bool Map::isValidPosition(v3s16 p)
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
2016-11-27 21:34:16 +01:00
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
2011-06-26 01:34:36 +02:00
|
|
|
return (block != NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a CONTENT_IGNORE node if not found
|
2019-08-10 19:45:44 +02:00
|
|
|
MapNode Map::getNode(v3s16 p, bool *is_valid_position)
|
2011-06-26 01:34:36 +02:00
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
2014-11-14 09:05:34 +01:00
|
|
|
if (block == NULL) {
|
|
|
|
if (is_valid_position != NULL)
|
|
|
|
*is_valid_position = false;
|
2017-08-19 09:12:54 +02:00
|
|
|
return {CONTENT_IGNORE};
|
2014-11-14 09:05:34 +01:00
|
|
|
}
|
|
|
|
|
2011-06-26 01:34:36 +02:00
|
|
|
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
|
2014-11-14 09:05:34 +01:00
|
|
|
bool is_valid_p;
|
|
|
|
MapNode node = block->getNodeNoCheck(relpos, &is_valid_p);
|
|
|
|
if (is_valid_position != NULL)
|
|
|
|
*is_valid_position = is_valid_p;
|
|
|
|
return node;
|
2011-06-26 01:34:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// throws InvalidPositionException if not found
|
|
|
|
void Map::setNode(v3s16 p, MapNode & n)
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
MapBlock *block = getBlockNoCreate(blockpos);
|
|
|
|
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
|
2019-10-24 02:17:00 +02:00
|
|
|
// Never allow placing CONTENT_IGNORE, it causes problems
|
2011-11-25 16:42:41 +01:00
|
|
|
if(n.getContent() == CONTENT_IGNORE){
|
2014-11-14 09:05:34 +01:00
|
|
|
bool temp_bool;
|
2011-11-25 16:42:41 +01:00
|
|
|
errorstream<<"Map::setNode(): Not allowing to place CONTENT_IGNORE"
|
|
|
|
<<" while trying to replace \""
|
2017-01-04 19:18:40 +01:00
|
|
|
<<m_nodedef->get(block->getNodeNoCheck(relpos, &temp_bool)).name
|
2011-11-25 16:42:41 +01:00
|
|
|
<<"\" at "<<PP(p)<<" (block "<<PP(blockpos)<<")"<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2011-06-26 01:34:36 +02:00
|
|
|
block->setNodeNoCheck(relpos, n);
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
2013-11-23 15:35:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> &modified_blocks,
|
|
|
|
bool remove_metadata)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2016-10-20 21:41:38 +02:00
|
|
|
// Collect old node for rollback
|
2012-07-26 21:06:45 +02:00
|
|
|
RollbackNode rollback_oldnode(this, p, m_gamedef);
|
2010-12-18 16:46:00 +01:00
|
|
|
|
2016-10-20 21:41:38 +02:00
|
|
|
// This is needed for updating the lighting
|
2019-08-10 19:45:44 +02:00
|
|
|
MapNode oldnode = getNode(p);
|
2010-12-18 16:46:00 +01:00
|
|
|
|
2016-10-20 21:41:38 +02:00
|
|
|
// Remove node metadata
|
2013-11-23 15:35:49 +01:00
|
|
|
if (remove_metadata) {
|
|
|
|
removeNodeMetadata(p);
|
|
|
|
}
|
2012-06-02 10:01:28 +02:00
|
|
|
|
2016-10-20 21:41:38 +02:00
|
|
|
// Set the node on the map
|
2016-10-23 17:51:13 +02:00
|
|
|
// Ignore light (because calling voxalgo::update_lighting_nodes)
|
2017-01-04 19:18:40 +01:00
|
|
|
n.setLight(LIGHTBANK_DAY, 0, m_nodedef);
|
|
|
|
n.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
|
2010-11-27 00:02:21 +01:00
|
|
|
setNode(p, n);
|
2011-04-04 02:45:08 +02:00
|
|
|
|
2016-10-20 21:41:38 +02:00
|
|
|
// Update lighting
|
2016-10-23 17:51:13 +02:00
|
|
|
std::vector<std::pair<v3s16, MapNode> > oldnodes;
|
2017-08-19 09:12:54 +02:00
|
|
|
oldnodes.emplace_back(p, oldnode);
|
2016-10-27 23:25:44 +02:00
|
|
|
voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto &modified_block : modified_blocks) {
|
|
|
|
modified_block.second->expireDayNightDiff();
|
2010-12-19 15:51:45 +01:00
|
|
|
}
|
2011-01-17 20:15:31 +01:00
|
|
|
|
2016-10-20 21:41:38 +02:00
|
|
|
// Report for rollback
|
2012-07-26 21:06:45 +02:00
|
|
|
if(m_gamedef->rollback())
|
|
|
|
{
|
|
|
|
RollbackNode rollback_newnode(this, p, m_gamedef);
|
|
|
|
RollbackAction action;
|
|
|
|
action.setSetNode(p, rollback_oldnode, rollback_newnode);
|
|
|
|
m_gamedef->rollback()->reportAction(action);
|
|
|
|
}
|
|
|
|
|
2011-01-17 20:15:31 +01:00
|
|
|
/*
|
2016-10-20 21:41:38 +02:00
|
|
|
Add neighboring liquid nodes and this node to transform queue.
|
|
|
|
(it's vital for the node itself to get updated last, if it was removed.)
|
|
|
|
*/
|
2017-08-19 09:12:54 +02:00
|
|
|
|
|
|
|
for (const v3s16 &dir : g_7dirs) {
|
|
|
|
v3s16 p2 = p + dir;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2016-10-20 21:41:38 +02:00
|
|
|
bool is_valid_position;
|
2019-08-10 19:45:44 +02:00
|
|
|
MapNode n2 = getNode(p2, &is_valid_position);
|
2017-01-04 19:18:40 +01:00
|
|
|
if(is_valid_position &&
|
|
|
|
(m_nodedef->get(n2).isLiquid() ||
|
|
|
|
n2.getContent() == CONTENT_AIR))
|
2011-01-17 20:15:31 +01:00
|
|
|
m_transforming_liquid.push_back(p2);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Map::removeNodeAndUpdate(v3s16 p,
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> &modified_blocks)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2016-10-20 21:41:38 +02:00
|
|
|
addNodeAndUpdate(p, MapNode(CONTENT_AIR), modified_blocks, true);
|
2010-12-18 16:46:00 +01:00
|
|
|
}
|
|
|
|
|
2013-11-23 15:35:49 +01:00
|
|
|
bool Map::addNodeWithEvent(v3s16 p, MapNode n, bool remove_metadata)
|
2011-02-23 01:49:57 +01:00
|
|
|
{
|
|
|
|
MapEditEvent event;
|
2013-11-23 15:35:49 +01:00
|
|
|
event.type = remove_metadata ? MEET_ADDNODE : MEET_SWAPNODE;
|
2011-02-23 01:49:57 +01:00
|
|
|
event.p = p;
|
|
|
|
event.n = n;
|
|
|
|
|
|
|
|
bool succeeded = true;
|
|
|
|
try{
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> modified_blocks;
|
2013-11-23 15:35:49 +01:00
|
|
|
addNodeAndUpdate(p, n, modified_blocks, remove_metadata);
|
2011-02-23 01:49:57 +01:00
|
|
|
|
|
|
|
// Copy modified_blocks to event
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto &modified_block : modified_blocks) {
|
|
|
|
event.modified_blocks.insert(modified_block.first);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){
|
|
|
|
succeeded = false;
|
|
|
|
}
|
|
|
|
|
2019-09-24 19:05:28 +02:00
|
|
|
dispatchEvent(event);
|
2011-02-23 01:49:57 +01:00
|
|
|
|
|
|
|
return succeeded;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Map::removeNodeWithEvent(v3s16 p)
|
|
|
|
{
|
|
|
|
MapEditEvent event;
|
|
|
|
event.type = MEET_REMOVENODE;
|
|
|
|
event.p = p;
|
|
|
|
|
|
|
|
bool succeeded = true;
|
|
|
|
try{
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> modified_blocks;
|
2011-02-23 01:49:57 +01:00
|
|
|
removeNodeAndUpdate(p, modified_blocks);
|
|
|
|
|
|
|
|
// Copy modified_blocks to event
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto &modified_block : modified_blocks) {
|
|
|
|
event.modified_blocks.insert(modified_block.first);
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(InvalidPositionException &e){
|
|
|
|
succeeded = false;
|
|
|
|
}
|
|
|
|
|
2019-09-24 19:05:28 +02:00
|
|
|
dispatchEvent(event);
|
2011-02-23 01:49:57 +01:00
|
|
|
|
|
|
|
return succeeded;
|
|
|
|
}
|
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
struct TimeOrderedMapBlock {
|
|
|
|
MapSector *sect;
|
|
|
|
MapBlock *block;
|
|
|
|
|
|
|
|
TimeOrderedMapBlock(MapSector *sect, MapBlock *block) :
|
|
|
|
sect(sect),
|
|
|
|
block(block)
|
|
|
|
{}
|
|
|
|
|
|
|
|
bool operator<(const TimeOrderedMapBlock &b) const
|
|
|
|
{
|
|
|
|
return block->getUsageTimer() < b.block->getUsageTimer();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
|
|
|
Updates usage timers
|
|
|
|
*/
|
2015-08-10 22:24:47 +02:00
|
|
|
void Map::timerUpdate(float dtime, float unload_timeout, u32 max_loaded_blocks,
|
2015-02-17 15:04:08 +01:00
|
|
|
std::vector<v3s16> *unloaded_blocks)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2011-06-26 23:27:17 +02:00
|
|
|
bool save_before_unloading = (mapType() == MAPTYPE_SERVER);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-11-21 12:35:32 +01:00
|
|
|
// Profile modified reasons
|
|
|
|
Profiler modprofiler;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2015-03-04 16:58:04 +01:00
|
|
|
std::vector<v2s16> sector_deletion_queue;
|
2011-06-26 23:27:17 +02:00
|
|
|
u32 deleted_blocks_count = 0;
|
|
|
|
u32 saved_blocks_count = 0;
|
2011-11-25 17:08:47 +01:00
|
|
|
u32 block_count_all = 0;
|
2010-11-27 00:02:21 +01:00
|
|
|
|
2011-09-06 18:16:36 +02:00
|
|
|
beginSave();
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
// If there is no practical limit, we spare creation of mapblock_queue
|
2015-10-04 08:50:04 +02:00
|
|
|
if (max_loaded_blocks == U32_MAX) {
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto §or_it : m_sectors) {
|
|
|
|
MapSector *sector = sector_it.second;
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
bool all_blocks_deleted = true;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
MapBlockVect blocks;
|
|
|
|
sector->getBlocks(blocks);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (MapBlock *block : blocks) {
|
2015-08-10 22:24:47 +02:00
|
|
|
block->incrementUsageTimer(dtime);
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
if (block->refGet() == 0
|
|
|
|
&& block->getUsageTimer() > unload_timeout) {
|
|
|
|
v3s16 p = block->getPos();
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
// Save if modified
|
|
|
|
if (block->getModified() != MOD_STATE_CLEAN
|
|
|
|
&& save_before_unloading) {
|
|
|
|
modprofiler.add(block->getModifiedReasonString(), 1);
|
|
|
|
if (!saveBlock(block))
|
|
|
|
continue;
|
|
|
|
saved_blocks_count++;
|
|
|
|
}
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
// Delete from memory
|
|
|
|
sector->deleteBlock(block);
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2015-08-10 22:24:47 +02:00
|
|
|
if (unloaded_blocks)
|
|
|
|
unloaded_blocks->push_back(p);
|
|
|
|
|
|
|
|
deleted_blocks_count++;
|
|
|
|
} else {
|
|
|
|
all_blocks_deleted = false;
|
|
|
|
block_count_all++;
|
|
|
|
}
|
2011-06-26 23:27:17 +02:00
|
|
|
}
|
2015-08-10 22:24:47 +02:00
|
|
|
|
|
|
|
if (all_blocks_deleted) {
|
2017-08-19 09:12:54 +02:00
|
|
|
sector_deletion_queue.push_back(sector_it.first);
|
2011-06-26 23:27:17 +02:00
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2015-08-10 22:24:47 +02:00
|
|
|
} else {
|
|
|
|
std::priority_queue<TimeOrderedMapBlock> mapblock_queue;
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto §or_it : m_sectors) {
|
|
|
|
MapSector *sector = sector_it.second;
|
2015-08-10 22:24:47 +02:00
|
|
|
|
|
|
|
MapBlockVect blocks;
|
|
|
|
sector->getBlocks(blocks);
|
2011-06-26 23:27:17 +02:00
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (MapBlock *block : blocks) {
|
2015-08-10 22:24:47 +02:00
|
|
|
block->incrementUsageTimer(dtime);
|
|
|
|
mapblock_queue.push(TimeOrderedMapBlock(sector, block));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
block_count_all = mapblock_queue.size();
|
|
|
|
// Delete old blocks, and blocks over the limit from the memory
|
2015-08-13 18:02:48 +02:00
|
|
|
while (!mapblock_queue.empty() && (mapblock_queue.size() > max_loaded_blocks
|
|
|
|
|| mapblock_queue.top().block->getUsageTimer() > unload_timeout)) {
|
2015-08-10 22:24:47 +02:00
|
|
|
TimeOrderedMapBlock b = mapblock_queue.top();
|
|
|
|
mapblock_queue.pop();
|
|
|
|
|
|
|
|
MapBlock *block = b.block;
|
|
|
|
|
|
|
|
if (block->refGet() != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
v3s16 p = block->getPos();
|
|
|
|
|
|
|
|
// Save if modified
|
|
|
|
if (block->getModified() != MOD_STATE_CLEAN && save_before_unloading) {
|
|
|
|
modprofiler.add(block->getModifiedReasonString(), 1);
|
|
|
|
if (!saveBlock(block))
|
|
|
|
continue;
|
|
|
|
saved_blocks_count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete from memory
|
|
|
|
b.sect->deleteBlock(block);
|
|
|
|
|
|
|
|
if (unloaded_blocks)
|
|
|
|
unloaded_blocks->push_back(p);
|
|
|
|
|
|
|
|
deleted_blocks_count++;
|
|
|
|
block_count_all--;
|
|
|
|
}
|
|
|
|
// Delete empty sectors
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto §or_it : m_sectors) {
|
|
|
|
if (sector_it.second->empty()) {
|
|
|
|
sector_deletion_queue.push_back(sector_it.first);
|
2015-08-10 22:24:47 +02:00
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2011-09-06 18:16:36 +02:00
|
|
|
endSave();
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
// Finally delete the empty sectors
|
|
|
|
deleteSectors(sector_deletion_queue);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-26 23:27:17 +02:00
|
|
|
if(deleted_blocks_count != 0)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
PrintInfo(infostream); // ServerMap/ClientMap:
|
|
|
|
infostream<<"Unloaded "<<deleted_blocks_count
|
2011-06-26 23:27:17 +02:00
|
|
|
<<" blocks from memory";
|
|
|
|
if(save_before_unloading)
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<", of which "<<saved_blocks_count<<" were written";
|
2011-11-25 17:08:47 +01:00
|
|
|
infostream<<", "<<block_count_all<<" blocks in memory";
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"."<<std::endl;
|
2011-11-21 12:44:10 +01:00
|
|
|
if(saved_blocks_count != 0){
|
|
|
|
PrintInfo(infostream); // ServerMap/ClientMap:
|
|
|
|
infostream<<"Blocks modified by: "<<std::endl;
|
|
|
|
modprofiler.print(infostream);
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-17 15:04:08 +01:00
|
|
|
void Map::unloadUnreferencedBlocks(std::vector<v3s16> *unloaded_blocks)
|
2013-06-02 15:35:29 +02:00
|
|
|
{
|
2015-08-10 22:24:47 +02:00
|
|
|
timerUpdate(0.0, -1.0, 0, unloaded_blocks);
|
2013-06-02 15:35:29 +02:00
|
|
|
}
|
|
|
|
|
2015-03-04 16:58:04 +01:00
|
|
|
void Map::deleteSectors(std::vector<v2s16> §orList)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2017-08-18 18:18:25 +02:00
|
|
|
for (v2s16 j : sectorList) {
|
|
|
|
MapSector *sector = m_sectors[j];
|
2011-06-26 20:53:11 +02:00
|
|
|
// If sector is in sector cache, remove it from there
|
|
|
|
if(m_sector_cache == sector)
|
|
|
|
m_sector_cache = NULL;
|
|
|
|
// Remove from map and delete
|
2017-08-18 18:18:25 +02:00
|
|
|
m_sectors.erase(j);
|
2011-06-26 20:53:11 +02:00
|
|
|
delete sector;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Map::PrintInfo(std::ostream &out)
|
|
|
|
{
|
|
|
|
out<<"Map: ";
|
|
|
|
}
|
|
|
|
|
2011-01-17 20:15:31 +01:00
|
|
|
#define WATER_DROP_BOOST 4
|
|
|
|
|
2020-05-27 00:17:23 +02:00
|
|
|
const static v3s16 liquid_6dirs[6] = {
|
|
|
|
// order: upper before same level before lower
|
|
|
|
v3s16( 0, 1, 0),
|
|
|
|
v3s16( 0, 0, 1),
|
|
|
|
v3s16( 1, 0, 0),
|
|
|
|
v3s16( 0, 0,-1),
|
|
|
|
v3s16(-1, 0, 0),
|
|
|
|
v3s16( 0,-1, 0)
|
|
|
|
};
|
|
|
|
|
2018-02-11 09:23:25 +01:00
|
|
|
enum NeighborType : u8 {
|
2011-07-16 16:01:37 +02:00
|
|
|
NEIGHBOR_UPPER,
|
|
|
|
NEIGHBOR_SAME_LEVEL,
|
|
|
|
NEIGHBOR_LOWER
|
|
|
|
};
|
2018-02-11 09:23:25 +01:00
|
|
|
|
2011-07-16 16:01:37 +02:00
|
|
|
struct NodeNeighbor {
|
|
|
|
MapNode n;
|
|
|
|
NeighborType t;
|
|
|
|
v3s16 p;
|
2015-01-17 10:19:26 +01:00
|
|
|
|
|
|
|
NodeNeighbor()
|
2018-02-11 09:23:25 +01:00
|
|
|
: n(CONTENT_AIR), t(NEIGHBOR_SAME_LEVEL)
|
2015-01-17 10:19:26 +01:00
|
|
|
{ }
|
|
|
|
|
2018-02-11 09:23:25 +01:00
|
|
|
NodeNeighbor(const MapNode &node, NeighborType n_type, const v3s16 &pos)
|
2015-01-17 10:19:26 +01:00
|
|
|
: n(node),
|
|
|
|
t(n_type),
|
|
|
|
p(pos)
|
|
|
|
{ }
|
2011-07-16 16:01:37 +02:00
|
|
|
};
|
|
|
|
|
2013-02-24 15:39:07 +01:00
|
|
|
void Map::transforming_liquid_add(v3s16 p) {
|
|
|
|
m_transforming_liquid.push_back(p);
|
|
|
|
}
|
|
|
|
|
2017-04-20 08:10:39 +02:00
|
|
|
void Map::transformLiquids(std::map<v3s16, MapBlock*> &modified_blocks,
|
|
|
|
ServerEnvironment *env)
|
2011-01-17 20:15:31 +01:00
|
|
|
{
|
|
|
|
u32 loopcount = 0;
|
|
|
|
u32 initial_size = m_transforming_liquid.size();
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2011-02-08 00:12:55 +01:00
|
|
|
/*if(initial_size != 0)
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;*/
|
2011-01-30 00:44:54 +01:00
|
|
|
|
2011-08-16 19:56:57 +02:00
|
|
|
// list of nodes that due to viscosity have not reached their max level height
|
2015-01-13 15:19:54 +01:00
|
|
|
std::deque<v3s16> must_reflow;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2016-10-23 17:51:13 +02:00
|
|
|
std::vector<std::pair<v3s16, MapNode> > changed_nodes;
|
2011-08-16 19:56:57 +02:00
|
|
|
|
2014-12-17 17:54:43 +01:00
|
|
|
u32 liquid_loop_max = g_settings->getS32("liquid_loop_max");
|
|
|
|
u32 loop_max = liquid_loop_max;
|
|
|
|
|
2014-12-23 00:25:18 +01:00
|
|
|
#if 0
|
2014-12-17 17:54:43 +01:00
|
|
|
|
2014-12-23 00:25:18 +01:00
|
|
|
/* If liquid_loop_max is not keeping up with the queue size increase
|
|
|
|
* loop_max up to a maximum of liquid_loop_max * dedicated_server_step.
|
|
|
|
*/
|
2014-12-17 17:54:43 +01:00
|
|
|
if (m_transforming_liquid.size() > loop_max * 2) {
|
|
|
|
// "Burst" mode
|
|
|
|
float server_step = g_settings->getFloat("dedicated_server_step");
|
|
|
|
if (m_transforming_liquid_loop_count_multiplier - 1.0 < server_step)
|
|
|
|
m_transforming_liquid_loop_count_multiplier *= 1.0 + server_step / 10;
|
|
|
|
} else {
|
|
|
|
m_transforming_liquid_loop_count_multiplier = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
loop_max *= m_transforming_liquid_loop_count_multiplier;
|
2014-12-23 00:25:18 +01:00
|
|
|
#endif
|
2013-06-09 01:42:46 +02:00
|
|
|
|
2015-12-30 05:22:58 +01:00
|
|
|
while (m_transforming_liquid.size() != 0)
|
2011-01-17 20:15:31 +01:00
|
|
|
{
|
2011-08-15 17:49:44 +02:00
|
|
|
// This should be done here so that it is done when continue is used
|
2015-12-30 05:22:58 +01:00
|
|
|
if (loopcount >= initial_size || loopcount >= loop_max)
|
2011-08-15 17:49:44 +02:00
|
|
|
break;
|
|
|
|
loopcount++;
|
|
|
|
|
2011-01-23 16:29:15 +01:00
|
|
|
/*
|
|
|
|
Get a queued transforming liquid node
|
|
|
|
*/
|
2015-01-13 13:28:21 +01:00
|
|
|
v3s16 p0 = m_transforming_liquid.front();
|
|
|
|
m_transforming_liquid.pop_front();
|
2011-01-17 20:15:31 +01:00
|
|
|
|
2019-08-10 19:45:44 +02:00
|
|
|
MapNode n0 = getNode(p0);
|
2011-08-16 06:00:40 +02:00
|
|
|
|
2011-01-17 20:15:31 +01:00
|
|
|
/*
|
2011-07-16 16:01:37 +02:00
|
|
|
Collect information about current node
|
|
|
|
*/
|
|
|
|
s8 liquid_level = -1;
|
2016-10-25 23:04:10 +02:00
|
|
|
// The liquid node which will be placed there if
|
|
|
|
// the liquid flows into this node.
|
2013-01-12 20:20:21 +01:00
|
|
|
content_t liquid_kind = CONTENT_IGNORE;
|
2016-10-25 23:04:10 +02:00
|
|
|
// The node which will be placed there if liquid
|
|
|
|
// can't flow into this node.
|
2015-12-30 05:22:58 +01:00
|
|
|
content_t floodable_node = CONTENT_AIR;
|
2017-01-04 19:18:40 +01:00
|
|
|
const ContentFeatures &cf = m_nodedef->get(n0);
|
2015-12-30 05:22:58 +01:00
|
|
|
LiquidType liquid_type = cf.liquid_type;
|
2011-07-16 16:01:37 +02:00
|
|
|
switch (liquid_type) {
|
|
|
|
case LIQUID_SOURCE:
|
2011-08-16 08:31:33 +02:00
|
|
|
liquid_level = LIQUID_LEVEL_SOURCE;
|
2020-05-20 23:52:10 +02:00
|
|
|
liquid_kind = cf.liquid_alternative_flowing_id;
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
|
|
|
case LIQUID_FLOWING:
|
|
|
|
liquid_level = (n0.param2 & LIQUID_LEVEL_MASK);
|
2011-07-30 18:53:54 +02:00
|
|
|
liquid_kind = n0.getContent();
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
|
|
|
case LIQUID_NONE:
|
2015-12-30 05:22:58 +01:00
|
|
|
// if this node is 'floodable', it *could* be transformed
|
|
|
|
// into a liquid, otherwise, continue with the next node.
|
|
|
|
if (!cf.floodable)
|
2011-07-16 16:01:37 +02:00
|
|
|
continue;
|
2015-12-30 05:22:58 +01:00
|
|
|
floodable_node = n0.getContent();
|
2011-07-16 16:01:37 +02:00
|
|
|
liquid_kind = CONTENT_AIR;
|
|
|
|
break;
|
|
|
|
}
|
2011-08-16 06:00:40 +02:00
|
|
|
|
2011-07-16 16:01:37 +02:00
|
|
|
/*
|
|
|
|
Collect information about the environment
|
|
|
|
*/
|
|
|
|
NodeNeighbor sources[6]; // surrounding sources
|
|
|
|
int num_sources = 0;
|
|
|
|
NodeNeighbor flows[6]; // surrounding flowing liquid nodes
|
|
|
|
int num_flows = 0;
|
|
|
|
NodeNeighbor airs[6]; // surrounding air
|
|
|
|
int num_airs = 0;
|
2011-07-17 13:55:59 +02:00
|
|
|
NodeNeighbor neutrals[6]; // nodes that are solid or another kind of liquid
|
2011-07-16 16:01:37 +02:00
|
|
|
int num_neutrals = 0;
|
|
|
|
bool flowing_down = false;
|
2016-11-27 21:46:14 +01:00
|
|
|
bool ignored_sources = false;
|
2011-07-16 16:01:37 +02:00
|
|
|
for (u16 i = 0; i < 6; i++) {
|
|
|
|
NeighborType nt = NEIGHBOR_SAME_LEVEL;
|
|
|
|
switch (i) {
|
2020-05-27 00:17:23 +02:00
|
|
|
case 0:
|
2011-07-16 16:01:37 +02:00
|
|
|
nt = NEIGHBOR_UPPER;
|
|
|
|
break;
|
2020-05-27 00:17:23 +02:00
|
|
|
case 5:
|
2011-07-16 16:01:37 +02:00
|
|
|
nt = NEIGHBOR_LOWER;
|
|
|
|
break;
|
2018-02-11 09:23:25 +01:00
|
|
|
default:
|
|
|
|
break;
|
2011-07-16 16:01:37 +02:00
|
|
|
}
|
2020-05-27 00:17:23 +02:00
|
|
|
v3s16 npos = p0 + liquid_6dirs[i];
|
2019-08-10 19:45:44 +02:00
|
|
|
NodeNeighbor nb(getNode(npos), nt, npos);
|
2017-01-04 19:18:40 +01:00
|
|
|
const ContentFeatures &cfnb = m_nodedef->get(nb.n);
|
|
|
|
switch (m_nodedef->get(nb.n.getContent()).liquid_type) {
|
2011-07-16 16:01:37 +02:00
|
|
|
case LIQUID_NONE:
|
2015-12-30 05:22:58 +01:00
|
|
|
if (cfnb.floodable) {
|
2011-07-16 16:01:37 +02:00
|
|
|
airs[num_airs++] = nb;
|
2011-08-16 07:24:01 +02:00
|
|
|
// if the current node is a water source the neighbor
|
|
|
|
// should be enqueded for transformation regardless of whether the
|
|
|
|
// current node changes or not.
|
|
|
|
if (nb.t != NEIGHBOR_UPPER && liquid_type != LIQUID_NONE)
|
|
|
|
m_transforming_liquid.push_back(npos);
|
2011-07-17 13:55:59 +02:00
|
|
|
// if the current node happens to be a flowing node, it will start to flow down here.
|
2016-01-17 06:11:35 +01:00
|
|
|
if (nb.t == NEIGHBOR_LOWER)
|
2011-07-16 16:01:37 +02:00
|
|
|
flowing_down = true;
|
|
|
|
} else {
|
|
|
|
neutrals[num_neutrals++] = nb;
|
2016-11-27 21:46:14 +01:00
|
|
|
if (nb.n.getContent() == CONTENT_IGNORE) {
|
|
|
|
// If node below is ignore prevent water from
|
|
|
|
// spreading outwards and otherwise prevent from
|
|
|
|
// flowing away as ignore node might be the source
|
|
|
|
if (nb.t == NEIGHBOR_LOWER)
|
|
|
|
flowing_down = true;
|
|
|
|
else
|
|
|
|
ignored_sources = true;
|
|
|
|
}
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
|
|
|
case LIQUID_SOURCE:
|
2012-12-22 06:34:35 +01:00
|
|
|
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
|
2011-07-16 16:01:37 +02:00
|
|
|
if (liquid_kind == CONTENT_AIR)
|
2020-05-20 23:52:10 +02:00
|
|
|
liquid_kind = cfnb.liquid_alternative_flowing_id;
|
|
|
|
if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
|
2011-07-16 16:01:37 +02:00
|
|
|
neutrals[num_neutrals++] = nb;
|
|
|
|
} else {
|
2011-11-29 17:21:00 +01:00
|
|
|
// Do not count bottom source, it will screw things up
|
2020-05-27 00:17:23 +02:00
|
|
|
if(nt != NEIGHBOR_LOWER)
|
2011-11-29 17:21:00 +01:00
|
|
|
sources[num_sources++] = nb;
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
|
|
|
case LIQUID_FLOWING:
|
2020-05-27 00:17:23 +02:00
|
|
|
if (nb.t != NEIGHBOR_SAME_LEVEL ||
|
|
|
|
(nb.n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK) {
|
|
|
|
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
|
|
|
|
// but exclude falling liquids on the same level, they cannot flow here anyway
|
|
|
|
if (liquid_kind == CONTENT_AIR)
|
|
|
|
liquid_kind = cfnb.liquid_alternative_flowing_id;
|
|
|
|
}
|
2020-05-20 23:52:10 +02:00
|
|
|
if (cfnb.liquid_alternative_flowing_id != liquid_kind) {
|
2011-07-16 16:01:37 +02:00
|
|
|
neutrals[num_neutrals++] = nb;
|
|
|
|
} else {
|
2011-07-17 13:55:59 +02:00
|
|
|
flows[num_flows++] = nb;
|
2011-07-16 16:01:37 +02:00
|
|
|
if (nb.t == NEIGHBOR_LOWER)
|
|
|
|
flowing_down = true;
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
|
|
|
}
|
2011-08-16 06:00:40 +02:00
|
|
|
|
2011-01-17 20:15:31 +01:00
|
|
|
/*
|
2011-07-16 16:01:37 +02:00
|
|
|
decide on the type (and possibly level) of the current node
|
|
|
|
*/
|
2011-08-02 20:51:14 +02:00
|
|
|
content_t new_node_content;
|
2011-07-16 16:01:37 +02:00
|
|
|
s8 new_node_level = -1;
|
2011-08-16 19:56:57 +02:00
|
|
|
s8 max_node_level = -1;
|
2014-12-29 14:53:08 +01:00
|
|
|
|
2017-01-04 19:18:40 +01:00
|
|
|
u8 range = m_nodedef->get(liquid_kind).liquid_range;
|
2015-12-30 05:22:58 +01:00
|
|
|
if (range > LIQUID_LEVEL_MAX + 1)
|
|
|
|
range = LIQUID_LEVEL_MAX + 1;
|
2014-12-29 14:53:08 +01:00
|
|
|
|
2017-01-04 19:18:40 +01:00
|
|
|
if ((num_sources >= 2 && m_nodedef->get(liquid_kind).liquid_renewable) || liquid_type == LIQUID_SOURCE) {
|
2011-07-16 16:01:37 +02:00
|
|
|
// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
|
|
|
|
// or the flowing alternative of the first of the surrounding sources (if it's air), so
|
|
|
|
// it's perfectly safe to use liquid_kind here to determine the new node content.
|
2020-05-20 23:52:10 +02:00
|
|
|
new_node_content = m_nodedef->get(liquid_kind).liquid_alternative_source_id;
|
2012-09-07 18:48:12 +02:00
|
|
|
} else if (num_sources >= 1 && sources[0].t != NEIGHBOR_LOWER) {
|
2011-07-16 16:01:37 +02:00
|
|
|
// liquid_kind is set properly, see above
|
2011-08-16 19:56:57 +02:00
|
|
|
max_node_level = new_node_level = LIQUID_LEVEL_MAX;
|
2015-12-30 05:22:58 +01:00
|
|
|
if (new_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
|
|
|
|
new_node_content = liquid_kind;
|
|
|
|
else
|
|
|
|
new_node_content = floodable_node;
|
2016-11-27 21:46:14 +01:00
|
|
|
} else if (ignored_sources && liquid_level >= 0) {
|
|
|
|
// Maybe there are neighbouring sources that aren't loaded yet
|
|
|
|
// so prevent flowing away.
|
|
|
|
new_node_level = liquid_level;
|
|
|
|
new_node_content = liquid_kind;
|
2011-07-16 16:01:37 +02:00
|
|
|
} else {
|
|
|
|
// no surrounding sources, so get the maximum level that can flow into this node
|
|
|
|
for (u16 i = 0; i < num_flows; i++) {
|
|
|
|
u8 nb_liquid_level = (flows[i].n.param2 & LIQUID_LEVEL_MASK);
|
|
|
|
switch (flows[i].t) {
|
|
|
|
case NEIGHBOR_UPPER:
|
2011-08-16 19:56:57 +02:00
|
|
|
if (nb_liquid_level + WATER_DROP_BOOST > max_node_level) {
|
|
|
|
max_node_level = LIQUID_LEVEL_MAX;
|
2011-08-16 08:31:33 +02:00
|
|
|
if (nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX)
|
2011-08-16 19:56:57 +02:00
|
|
|
max_node_level = nb_liquid_level + WATER_DROP_BOOST;
|
2015-12-30 05:22:58 +01:00
|
|
|
} else if (nb_liquid_level > max_node_level) {
|
2011-08-20 07:54:01 +02:00
|
|
|
max_node_level = nb_liquid_level;
|
2015-12-30 05:22:58 +01:00
|
|
|
}
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
|
|
|
case NEIGHBOR_LOWER:
|
|
|
|
break;
|
|
|
|
case NEIGHBOR_SAME_LEVEL:
|
|
|
|
if ((flows[i].n.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK &&
|
2015-12-30 05:22:58 +01:00
|
|
|
nb_liquid_level > 0 && nb_liquid_level - 1 > max_node_level)
|
2011-08-16 19:56:57 +02:00
|
|
|
max_node_level = nb_liquid_level - 1;
|
2011-07-16 16:01:37 +02:00
|
|
|
break;
|
|
|
|
}
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
2011-08-16 06:00:40 +02:00
|
|
|
|
2017-01-04 19:18:40 +01:00
|
|
|
u8 viscosity = m_nodedef->get(liquid_kind).liquid_viscosity;
|
2011-08-16 20:38:44 +02:00
|
|
|
if (viscosity > 1 && max_node_level != liquid_level) {
|
2011-08-16 19:56:57 +02:00
|
|
|
// amount to gain, limited by viscosity
|
|
|
|
// must be at least 1 in absolute value
|
|
|
|
s8 level_inc = max_node_level - liquid_level;
|
|
|
|
if (level_inc < -viscosity || level_inc > viscosity)
|
|
|
|
new_node_level = liquid_level + level_inc/viscosity;
|
|
|
|
else if (level_inc < 0)
|
|
|
|
new_node_level = liquid_level - 1;
|
|
|
|
else if (level_inc > 0)
|
|
|
|
new_node_level = liquid_level + 1;
|
|
|
|
if (new_node_level != max_node_level)
|
|
|
|
must_reflow.push_back(p0);
|
2015-12-30 05:22:58 +01:00
|
|
|
} else {
|
2011-08-16 19:56:57 +02:00
|
|
|
new_node_level = max_node_level;
|
2015-12-30 05:22:58 +01:00
|
|
|
}
|
2011-08-16 19:56:57 +02:00
|
|
|
|
2015-12-30 05:22:58 +01:00
|
|
|
if (max_node_level >= (LIQUID_LEVEL_MAX + 1 - range))
|
2011-07-16 16:01:37 +02:00
|
|
|
new_node_content = liquid_kind;
|
2011-01-17 20:15:31 +01:00
|
|
|
else
|
2015-12-30 05:22:58 +01:00
|
|
|
new_node_content = floodable_node;
|
2011-08-16 19:56:57 +02:00
|
|
|
|
2011-07-16 16:01:37 +02:00
|
|
|
}
|
2011-08-16 06:00:40 +02:00
|
|
|
|
2011-07-16 16:01:37 +02:00
|
|
|
/*
|
|
|
|
check if anything has changed. if not, just continue with the next node.
|
|
|
|
*/
|
2015-12-30 05:22:58 +01:00
|
|
|
if (new_node_content == n0.getContent() &&
|
2017-01-04 19:18:40 +01:00
|
|
|
(m_nodedef->get(n0.getContent()).liquid_type != LIQUID_FLOWING ||
|
2015-12-30 05:22:58 +01:00
|
|
|
((n0.param2 & LIQUID_LEVEL_MASK) == (u8)new_node_level &&
|
|
|
|
((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)
|
|
|
|
== flowing_down)))
|
2011-07-16 16:01:37 +02:00
|
|
|
continue;
|
2011-08-16 06:00:40 +02:00
|
|
|
|
|
|
|
|
2011-07-16 16:01:37 +02:00
|
|
|
/*
|
|
|
|
update the current node
|
|
|
|
*/
|
2013-07-12 14:45:26 +02:00
|
|
|
MapNode n00 = n0;
|
2011-11-13 09:57:55 +01:00
|
|
|
//bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
|
2017-01-04 19:18:40 +01:00
|
|
|
if (m_nodedef->get(new_node_content).liquid_type == LIQUID_FLOWING) {
|
2011-07-16 16:01:37 +02:00
|
|
|
// set level to last 3 bits, flowing down bit to 4th bit
|
2011-08-16 16:46:55 +02:00
|
|
|
n0.param2 = (flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00) | (new_node_level & LIQUID_LEVEL_MASK);
|
2011-07-16 16:01:37 +02:00
|
|
|
} else {
|
2020-02-12 22:08:43 +01:00
|
|
|
// set the liquid level and flow bits to 0
|
|
|
|
n0.param2 &= ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
|
2011-07-16 16:01:37 +02:00
|
|
|
}
|
2017-04-20 08:10:39 +02:00
|
|
|
|
|
|
|
// change the node.
|
2011-08-16 16:46:55 +02:00
|
|
|
n0.setContent(new_node_content);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2017-04-20 08:10:39 +02:00
|
|
|
// on_flood() the node
|
|
|
|
if (floodable_node != CONTENT_AIR) {
|
|
|
|
if (env->getScriptIface()->node_on_flood(p0, n00, n0))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-10-23 17:51:13 +02:00
|
|
|
// Ignore light (because calling voxalgo::update_lighting_nodes)
|
2017-01-04 19:18:40 +01:00
|
|
|
n0.setLight(LIGHTBANK_DAY, 0, m_nodedef);
|
|
|
|
n0.setLight(LIGHTBANK_NIGHT, 0, m_nodedef);
|
2016-10-23 17:51:13 +02:00
|
|
|
|
2012-07-27 12:24:28 +02:00
|
|
|
// Find out whether there is a suspect for this action
|
|
|
|
std::string suspect;
|
2015-12-30 05:22:58 +01:00
|
|
|
if (m_gamedef->rollback())
|
2012-07-27 14:46:51 +02:00
|
|
|
suspect = m_gamedef->rollback()->getSuspect(p0, 83, 1);
|
2012-07-27 12:24:28 +02:00
|
|
|
|
2015-12-30 05:22:58 +01:00
|
|
|
if (m_gamedef->rollback() && !suspect.empty()) {
|
2012-07-27 12:24:28 +02:00
|
|
|
// Blame suspect
|
|
|
|
RollbackScopeActor rollback_scope(m_gamedef->rollback(), suspect, true);
|
|
|
|
// Get old node for rollback
|
|
|
|
RollbackNode rollback_oldnode(this, p0, m_gamedef);
|
|
|
|
// Set node
|
|
|
|
setNode(p0, n0);
|
|
|
|
// Report
|
2012-07-26 21:06:45 +02:00
|
|
|
RollbackNode rollback_newnode(this, p0, m_gamedef);
|
|
|
|
RollbackAction action;
|
|
|
|
action.setSetNode(p0, rollback_oldnode, rollback_newnode);
|
|
|
|
m_gamedef->rollback()->reportAction(action);
|
2012-07-27 12:24:28 +02:00
|
|
|
} else {
|
|
|
|
// Set node
|
|
|
|
setNode(p0, n0);
|
|
|
|
}
|
2012-07-26 21:06:45 +02:00
|
|
|
|
2011-07-16 16:01:37 +02:00
|
|
|
v3s16 blockpos = getNodeBlockPos(p0);
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
2015-12-30 05:22:58 +01:00
|
|
|
if (block != NULL) {
|
2012-12-20 18:19:49 +01:00
|
|
|
modified_blocks[blockpos] = block;
|
2017-08-19 09:12:54 +02:00
|
|
|
changed_nodes.emplace_back(p0, n00);
|
2011-08-22 12:19:07 +02:00
|
|
|
}
|
2011-08-16 06:00:40 +02:00
|
|
|
|
2011-07-17 04:01:31 +02:00
|
|
|
/*
|
|
|
|
enqueue neighbors for update if neccessary
|
|
|
|
*/
|
2017-01-04 19:18:40 +01:00
|
|
|
switch (m_nodedef->get(n0.getContent()).liquid_type) {
|
2011-07-16 16:01:37 +02:00
|
|
|
case LIQUID_SOURCE:
|
2011-08-16 18:05:28 +02:00
|
|
|
case LIQUID_FLOWING:
|
2011-07-16 16:01:37 +02:00
|
|
|
// make sure source flows into all neighboring nodes
|
|
|
|
for (u16 i = 0; i < num_flows; i++)
|
|
|
|
if (flows[i].t != NEIGHBOR_UPPER)
|
|
|
|
m_transforming_liquid.push_back(flows[i].p);
|
|
|
|
for (u16 i = 0; i < num_airs; i++)
|
|
|
|
if (airs[i].t != NEIGHBOR_UPPER)
|
|
|
|
m_transforming_liquid.push_back(airs[i].p);
|
|
|
|
break;
|
|
|
|
case LIQUID_NONE:
|
|
|
|
// this flow has turned to air; neighboring flows might need to do the same
|
|
|
|
for (u16 i = 0; i < num_flows; i++)
|
|
|
|
m_transforming_liquid.push_back(flows[i].p);
|
|
|
|
break;
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
|
|
|
}
|
2011-10-16 13:57:53 +02:00
|
|
|
//infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
|
2015-01-13 15:19:54 +01:00
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto &iter : must_reflow)
|
|
|
|
m_transforming_liquid.push_back(iter);
|
2015-01-13 15:19:54 +01:00
|
|
|
|
2016-10-27 23:25:44 +02:00
|
|
|
voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks);
|
2014-12-17 17:54:43 +01:00
|
|
|
|
|
|
|
|
2014-12-23 00:25:18 +01:00
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
* Manage the queue so that it does not grow indefinately
|
2014-12-17 17:54:43 +01:00
|
|
|
*/
|
2014-12-23 00:25:18 +01:00
|
|
|
u16 time_until_purge = g_settings->getU16("liquid_queue_purge_time");
|
|
|
|
|
|
|
|
if (time_until_purge == 0)
|
|
|
|
return; // Feature disabled
|
|
|
|
|
|
|
|
time_until_purge *= 1000; // seconds -> milliseconds
|
|
|
|
|
2017-05-26 14:03:36 +02:00
|
|
|
u64 curr_time = porting::getTimeMs();
|
2014-12-17 17:54:43 +01:00
|
|
|
u32 prev_unprocessed = m_unprocessed_count;
|
|
|
|
m_unprocessed_count = m_transforming_liquid.size();
|
|
|
|
|
|
|
|
// if unprocessed block count is decreasing or stable
|
|
|
|
if (m_unprocessed_count <= prev_unprocessed) {
|
|
|
|
m_queue_size_timer_started = false;
|
|
|
|
} else {
|
|
|
|
if (!m_queue_size_timer_started)
|
|
|
|
m_inc_trending_up_start_time = curr_time;
|
|
|
|
m_queue_size_timer_started = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Account for curr_time overflowing
|
|
|
|
if (m_queue_size_timer_started && m_inc_trending_up_start_time > curr_time)
|
|
|
|
m_queue_size_timer_started = false;
|
|
|
|
|
|
|
|
/* If the queue has been growing for more than liquid_queue_purge_time seconds
|
|
|
|
* and the number of unprocessed blocks is still > liquid_loop_max then we
|
|
|
|
* cannot keep up; dump the oldest blocks from the queue so that the queue
|
|
|
|
* has liquid_loop_max items in it
|
|
|
|
*/
|
|
|
|
if (m_queue_size_timer_started
|
|
|
|
&& curr_time - m_inc_trending_up_start_time > time_until_purge
|
|
|
|
&& m_unprocessed_count > liquid_loop_max) {
|
|
|
|
|
|
|
|
size_t dump_qty = m_unprocessed_count - liquid_loop_max;
|
|
|
|
|
|
|
|
infostream << "transformLiquids(): DUMPING " << dump_qty
|
|
|
|
<< " blocks from the queue" << std::endl;
|
|
|
|
|
|
|
|
while (dump_qty--)
|
|
|
|
m_transforming_liquid.pop_front();
|
|
|
|
|
|
|
|
m_queue_size_timer_started = false; // optimistically assume we can keep up now
|
|
|
|
m_unprocessed_count = m_transforming_liquid.size();
|
|
|
|
}
|
2011-01-17 20:15:31 +01:00
|
|
|
}
|
|
|
|
|
2015-05-05 20:30:46 +02:00
|
|
|
std::vector<v3s16> Map::findNodesWithMetadata(v3s16 p1, v3s16 p2)
|
|
|
|
{
|
|
|
|
std::vector<v3s16> positions_with_meta;
|
|
|
|
|
|
|
|
sortBoxVerticies(p1, p2);
|
|
|
|
v3s16 bpmin = getNodeBlockPos(p1);
|
|
|
|
v3s16 bpmax = getNodeBlockPos(p2);
|
|
|
|
|
2015-05-08 19:31:03 +02:00
|
|
|
VoxelArea area(p1, p2);
|
|
|
|
|
2015-05-05 20:30:46 +02:00
|
|
|
for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
|
|
|
|
for (s16 y = bpmin.Y; y <= bpmax.Y; y++)
|
|
|
|
for (s16 x = bpmin.X; x <= bpmax.X; x++) {
|
|
|
|
v3s16 blockpos(x, y, z);
|
|
|
|
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if (!block) {
|
|
|
|
verbosestream << "Map::getNodeMetadata(): Need to emerge "
|
|
|
|
<< PP(blockpos) << std::endl;
|
|
|
|
block = emergeBlock(blockpos, false);
|
|
|
|
}
|
|
|
|
if (!block) {
|
|
|
|
infostream << "WARNING: Map::getNodeMetadata(): Block not found"
|
|
|
|
<< std::endl;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
v3s16 p_base = blockpos * MAP_BLOCKSIZE;
|
|
|
|
std::vector<v3s16> keys = block->m_node_metadata.getAllKeys();
|
2015-05-08 19:31:03 +02:00
|
|
|
for (size_t i = 0; i != keys.size(); i++) {
|
|
|
|
v3s16 p(keys[i] + p_base);
|
|
|
|
if (!area.contains(p))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
positions_with_meta.push_back(p);
|
|
|
|
}
|
2015-05-05 20:30:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return positions_with_meta;
|
|
|
|
}
|
|
|
|
|
2013-11-20 22:11:57 +01:00
|
|
|
NodeMetadata *Map::getNodeMetadata(v3s16 p)
|
2011-04-03 15:21:06 +02:00
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
2011-10-17 00:04:22 +02:00
|
|
|
if(!block){
|
|
|
|
infostream<<"Map::getNodeMetadata(): Need to emerge "
|
|
|
|
<<PP(blockpos)<<std::endl;
|
|
|
|
block = emergeBlock(blockpos, false);
|
|
|
|
}
|
2013-11-20 22:11:57 +01:00
|
|
|
if(!block){
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map::getNodeMetadata(): Block not found"
|
2011-04-04 02:45:08 +02:00
|
|
|
<<std::endl;
|
2011-04-03 15:21:06 +02:00
|
|
|
return NULL;
|
2011-04-04 02:45:08 +02:00
|
|
|
}
|
2012-03-19 01:08:04 +01:00
|
|
|
NodeMetadata *meta = block->m_node_metadata.get(p_rel);
|
2011-04-03 15:21:06 +02:00
|
|
|
return meta;
|
|
|
|
}
|
|
|
|
|
2013-11-20 22:11:57 +01:00
|
|
|
bool Map::setNodeMetadata(v3s16 p, NodeMetadata *meta)
|
2011-04-04 02:45:08 +02:00
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
2011-10-17 00:04:22 +02:00
|
|
|
if(!block){
|
|
|
|
infostream<<"Map::setNodeMetadata(): Need to emerge "
|
|
|
|
<<PP(blockpos)<<std::endl;
|
|
|
|
block = emergeBlock(blockpos, false);
|
|
|
|
}
|
2013-11-20 22:11:57 +01:00
|
|
|
if(!block){
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map::setNodeMetadata(): Block not found"
|
2011-04-04 02:45:08 +02:00
|
|
|
<<std::endl;
|
2013-11-20 22:11:57 +01:00
|
|
|
return false;
|
2011-04-04 02:45:08 +02:00
|
|
|
}
|
2012-03-19 01:08:04 +01:00
|
|
|
block->m_node_metadata.set(p_rel, meta);
|
2013-11-20 22:11:57 +01:00
|
|
|
return true;
|
2011-04-04 02:45:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Map::removeNodeMetadata(v3s16 p)
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if(block == NULL)
|
|
|
|
{
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map::removeNodeMetadata(): Block not found"
|
2011-04-04 02:45:08 +02:00
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2012-03-19 01:08:04 +01:00
|
|
|
block->m_node_metadata.remove(p_rel);
|
2011-04-05 01:56:29 +02:00
|
|
|
}
|
|
|
|
|
2012-07-17 15:00:04 +02:00
|
|
|
NodeTimer Map::getNodeTimer(v3s16 p)
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if(!block){
|
|
|
|
infostream<<"Map::getNodeTimer(): Need to emerge "
|
|
|
|
<<PP(blockpos)<<std::endl;
|
|
|
|
block = emergeBlock(blockpos, false);
|
|
|
|
}
|
2013-11-20 22:11:57 +01:00
|
|
|
if(!block){
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map::getNodeTimer(): Block not found"
|
2012-07-17 15:00:04 +02:00
|
|
|
<<std::endl;
|
|
|
|
return NodeTimer();
|
|
|
|
}
|
|
|
|
NodeTimer t = block->m_node_timers.get(p_rel);
|
2016-03-21 12:58:52 +01:00
|
|
|
NodeTimer nt(t.timeout, t.elapsed, p);
|
|
|
|
return nt;
|
2012-07-17 15:00:04 +02:00
|
|
|
}
|
|
|
|
|
2016-03-21 12:58:52 +01:00
|
|
|
void Map::setNodeTimer(const NodeTimer &t)
|
2012-07-17 15:00:04 +02:00
|
|
|
{
|
2016-03-21 12:58:52 +01:00
|
|
|
v3s16 p = t.position;
|
2012-07-17 15:00:04 +02:00
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if(!block){
|
|
|
|
infostream<<"Map::setNodeTimer(): Need to emerge "
|
|
|
|
<<PP(blockpos)<<std::endl;
|
|
|
|
block = emergeBlock(blockpos, false);
|
|
|
|
}
|
2013-11-20 22:11:57 +01:00
|
|
|
if(!block){
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map::setNodeTimer(): Block not found"
|
2012-07-17 15:00:04 +02:00
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
2016-03-21 12:58:52 +01:00
|
|
|
NodeTimer nt(t.timeout, t.elapsed, p_rel);
|
|
|
|
block->m_node_timers.set(nt);
|
2012-07-17 15:00:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Map::removeNodeTimer(v3s16 p)
|
|
|
|
{
|
|
|
|
v3s16 blockpos = getNodeBlockPos(p);
|
|
|
|
v3s16 p_rel = p - blockpos*MAP_BLOCKSIZE;
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if(block == NULL)
|
|
|
|
{
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map::removeNodeTimer(): Block not found"
|
2012-07-17 15:00:04 +02:00
|
|
|
<<std::endl;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
block->m_node_timers.remove(p_rel);
|
|
|
|
}
|
|
|
|
|
2019-08-23 00:14:45 +02:00
|
|
|
bool Map::determineAdditionalOcclusionCheck(const v3s16 &pos_camera,
|
|
|
|
const core::aabbox3d<s16> &block_bounds, v3s16 &check)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
This functions determines the node inside the target block that is
|
|
|
|
closest to the camera position. This increases the occlusion culling
|
|
|
|
accuracy in straight and diagonal corridors.
|
|
|
|
The returned position will be occlusion checked first in addition to the
|
|
|
|
others (8 corners + center).
|
|
|
|
No position is returned if
|
|
|
|
- the closest node is a corner, corners are checked anyway.
|
|
|
|
- the camera is inside the target block, it will never be occluded.
|
|
|
|
*/
|
|
|
|
#define CLOSEST_EDGE(pos, bounds, axis) \
|
|
|
|
((pos).axis <= (bounds).MinEdge.axis) ? (bounds).MinEdge.axis : \
|
|
|
|
(bounds).MaxEdge.axis
|
|
|
|
|
|
|
|
bool x_inside = (block_bounds.MinEdge.X <= pos_camera.X) &&
|
|
|
|
(pos_camera.X <= block_bounds.MaxEdge.X);
|
|
|
|
bool y_inside = (block_bounds.MinEdge.Y <= pos_camera.Y) &&
|
|
|
|
(pos_camera.Y <= block_bounds.MaxEdge.Y);
|
|
|
|
bool z_inside = (block_bounds.MinEdge.Z <= pos_camera.Z) &&
|
|
|
|
(pos_camera.Z <= block_bounds.MaxEdge.Z);
|
|
|
|
|
|
|
|
if (x_inside && y_inside && z_inside)
|
|
|
|
return false; // Camera inside target mapblock
|
|
|
|
|
|
|
|
// straight
|
|
|
|
if (x_inside && y_inside) {
|
|
|
|
check = v3s16(pos_camera.X, pos_camera.Y, 0);
|
|
|
|
check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
|
|
|
|
return true;
|
|
|
|
} else if (y_inside && z_inside) {
|
|
|
|
check = v3s16(0, pos_camera.Y, pos_camera.Z);
|
|
|
|
check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
|
|
|
|
return true;
|
|
|
|
} else if (x_inside && z_inside) {
|
|
|
|
check = v3s16(pos_camera.X, 0, pos_camera.Z);
|
|
|
|
check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// diagonal
|
|
|
|
if (x_inside) {
|
|
|
|
check = v3s16(pos_camera.X, 0, 0);
|
|
|
|
check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
|
|
|
|
check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
|
|
|
|
return true;
|
|
|
|
} else if (y_inside) {
|
|
|
|
check = v3s16(0, pos_camera.Y, 0);
|
|
|
|
check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
|
|
|
|
check.Z = CLOSEST_EDGE(pos_camera, block_bounds, Z);
|
|
|
|
return true;
|
|
|
|
} else if (z_inside) {
|
|
|
|
check = v3s16(0, 0, pos_camera.Z);
|
|
|
|
check.X = CLOSEST_EDGE(pos_camera, block_bounds, X);
|
|
|
|
check.Y = CLOSEST_EDGE(pos_camera, block_bounds, Y);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Closest node would be a corner, none returned
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-17 15:42:36 +02:00
|
|
|
bool Map::isOccluded(const v3s16 &pos_camera, const v3s16 &pos_target,
|
2019-08-23 21:52:11 +02:00
|
|
|
float step, float stepfac, float offset, float end_offset, u32 needed_count)
|
2017-02-28 08:06:15 +01:00
|
|
|
{
|
2019-08-17 15:42:36 +02:00
|
|
|
v3f direction = intToFloat(pos_target - pos_camera, BS);
|
|
|
|
float distance = direction.getLength();
|
|
|
|
|
|
|
|
// Normalize direction vector
|
|
|
|
if (distance > 0.0f)
|
|
|
|
direction /= distance;
|
|
|
|
|
|
|
|
v3f pos_origin_f = intToFloat(pos_camera, BS);
|
2017-02-28 08:06:15 +01:00
|
|
|
u32 count = 0;
|
2019-08-17 15:42:36 +02:00
|
|
|
bool is_valid_position;
|
|
|
|
|
2019-08-23 21:52:11 +02:00
|
|
|
for (; offset < distance + end_offset; offset += step) {
|
2019-08-17 15:42:36 +02:00
|
|
|
v3f pos_node_f = pos_origin_f + direction * offset;
|
|
|
|
v3s16 pos_node = floatToInt(pos_node_f, BS);
|
|
|
|
|
|
|
|
MapNode node = getNode(pos_node, &is_valid_position);
|
|
|
|
|
|
|
|
if (is_valid_position &&
|
|
|
|
!m_nodedef->get(node).light_propagates) {
|
|
|
|
// Cannot see through light-blocking nodes --> occluded
|
2017-02-28 08:06:15 +01:00
|
|
|
count++;
|
2019-08-17 14:21:22 +02:00
|
|
|
if (count >= needed_count)
|
2017-02-28 08:06:15 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
step *= stepfac;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-17 14:21:22 +02:00
|
|
|
bool Map::isBlockOccluded(MapBlock *block, v3s16 cam_pos_nodes)
|
|
|
|
{
|
2019-08-17 15:42:36 +02:00
|
|
|
// Check occlusion for center and all 8 corners of the mapblock
|
|
|
|
// Overshoot a little for less flickering
|
2019-08-17 14:21:22 +02:00
|
|
|
static const s16 bs2 = MAP_BLOCKSIZE / 2 + 1;
|
2019-08-17 15:42:36 +02:00
|
|
|
static const v3s16 dir9[9] = {
|
|
|
|
v3s16( 0, 0, 0),
|
2019-08-17 14:21:22 +02:00
|
|
|
v3s16( 1, 1, 1) * bs2,
|
|
|
|
v3s16( 1, 1, -1) * bs2,
|
|
|
|
v3s16( 1, -1, 1) * bs2,
|
|
|
|
v3s16( 1, -1, -1) * bs2,
|
|
|
|
v3s16(-1, 1, 1) * bs2,
|
|
|
|
v3s16(-1, 1, -1) * bs2,
|
|
|
|
v3s16(-1, -1, 1) * bs2,
|
|
|
|
v3s16(-1, -1, -1) * bs2,
|
|
|
|
};
|
|
|
|
|
2019-08-23 21:52:11 +02:00
|
|
|
v3s16 pos_blockcenter = block->getPosRelative() + (MAP_BLOCKSIZE / 2);
|
2019-08-17 15:42:36 +02:00
|
|
|
|
|
|
|
// Starting step size, value between 1m and sqrt(3)m
|
|
|
|
float step = BS * 1.2f;
|
2019-08-17 14:21:22 +02:00
|
|
|
// Multiply step by each iteraction by 'stepfac' to reduce checks in distance
|
2019-08-17 15:42:36 +02:00
|
|
|
float stepfac = 1.05f;
|
2019-08-17 14:21:22 +02:00
|
|
|
|
2019-08-17 15:42:36 +02:00
|
|
|
float start_offset = BS * 1.0f;
|
2019-08-17 14:21:22 +02:00
|
|
|
|
2019-08-23 21:52:11 +02:00
|
|
|
// The occlusion search of 'isOccluded()' must stop short of the target
|
|
|
|
// point by distance 'end_offset' to not enter the target mapblock.
|
|
|
|
// For the 8 mapblock corners 'end_offset' must therefore be the maximum
|
|
|
|
// diagonal of a mapblock, because we must consider all view angles.
|
|
|
|
// sqrt(1^2 + 1^2 + 1^2) = 1.732
|
|
|
|
float end_offset = -BS * MAP_BLOCKSIZE * 1.732f;
|
|
|
|
|
2017-02-28 08:06:15 +01:00
|
|
|
// to reduce the likelihood of falsely occluded blocks
|
|
|
|
// require at least two solid blocks
|
|
|
|
// this is a HACK, we should think of a more precise algorithm
|
|
|
|
u32 needed_count = 2;
|
|
|
|
|
2019-08-23 00:14:45 +02:00
|
|
|
// Additional occlusion check, see comments in that function
|
|
|
|
v3s16 check;
|
|
|
|
if (determineAdditionalOcclusionCheck(cam_pos_nodes, block->getBox(), check)) {
|
|
|
|
// node is always on a side facing the camera, end_offset can be lower
|
|
|
|
if (!isOccluded(cam_pos_nodes, check, step, stepfac, start_offset,
|
|
|
|
-1.0f, needed_count))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-08-17 15:42:36 +02:00
|
|
|
for (const v3s16 &dir : dir9) {
|
2019-08-23 21:52:11 +02:00
|
|
|
if (!isOccluded(cam_pos_nodes, pos_blockcenter + dir, step, stepfac,
|
|
|
|
start_offset, end_offset, needed_count))
|
2019-08-17 14:21:22 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2017-02-28 08:06:15 +01:00
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
|
|
|
ServerMap
|
|
|
|
*/
|
2017-04-19 00:36:30 +02:00
|
|
|
ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef,
|
2020-04-27 08:31:37 +02:00
|
|
|
EmergeManager *emerge, MetricsBackend *mb):
|
2011-11-13 23:19:48 +01:00
|
|
|
Map(dout_server, gamedef),
|
2016-06-25 00:15:56 +02:00
|
|
|
settings_mgr(g_settings, savedir + DIR_DELIM + "map_meta.txt"),
|
2017-06-17 19:11:28 +02:00
|
|
|
m_emerge(emerge)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2015-10-14 08:39:30 +02:00
|
|
|
verbosestream<<FUNCTION_NAME<<std::endl;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2016-06-25 00:15:56 +02:00
|
|
|
// Tell the EmergeManager about our MapSettingsManager
|
|
|
|
emerge->map_settings_mgr = &settings_mgr;
|
|
|
|
|
2011-01-16 18:32:14 +01:00
|
|
|
/*
|
|
|
|
Try to load map; if not found, create a new one.
|
|
|
|
*/
|
|
|
|
|
2012-10-22 23:18:44 +02:00
|
|
|
// Determine which database backend to use
|
|
|
|
std::string conf_path = savedir + DIR_DELIM + "world.mt";
|
|
|
|
Settings conf;
|
|
|
|
bool succeeded = conf.readConfigFile(conf_path.c_str());
|
|
|
|
if (!succeeded || !conf.exists("backend")) {
|
|
|
|
// fall back to sqlite3
|
|
|
|
conf.set("backend", "sqlite3");
|
|
|
|
}
|
2014-11-16 21:31:57 +01:00
|
|
|
std::string backend = conf.get("backend");
|
2015-02-27 08:24:07 +01:00
|
|
|
dbase = createDatabase(backend, savedir, conf);
|
2018-07-25 17:54:23 +02:00
|
|
|
if (conf.exists("readonly_backend")) {
|
|
|
|
std::string readonly_dir = savedir + DIR_DELIM + "readonly";
|
|
|
|
dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf);
|
|
|
|
}
|
2014-11-16 21:31:57 +01:00
|
|
|
if (!conf.updateConfigFile(conf_path.c_str()))
|
|
|
|
errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl;
|
2012-10-22 23:18:44 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
m_savedir = savedir;
|
|
|
|
m_map_saving_enabled = false;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2020-04-27 08:31:37 +02:00
|
|
|
m_save_time_counter = mb->addCounter("minetest_core_map_save_time", "Map save time (in nanoseconds)");
|
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
try {
|
2010-11-27 00:02:21 +01:00
|
|
|
// If directory exists, check contents and load if possible
|
2017-08-19 09:12:54 +02:00
|
|
|
if (fs::PathExists(m_savedir)) {
|
2010-11-27 00:02:21 +01:00
|
|
|
// If directory is empty, it is safe to save into it.
|
2017-08-19 09:12:54 +02:00
|
|
|
if (fs::GetDirListing(m_savedir).empty()) {
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"ServerMap: Empty save directory is valid."
|
2010-11-27 00:02:21 +01:00
|
|
|
<<std::endl;
|
|
|
|
m_map_saving_enabled = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-04-10 23:06:07 +02:00
|
|
|
|
2016-06-25 00:15:56 +02:00
|
|
|
if (settings_mgr.loadMapMeta()) {
|
|
|
|
infostream << "ServerMap: Metadata loaded from "
|
|
|
|
<< savedir << std::endl;
|
|
|
|
} else {
|
|
|
|
infostream << "ServerMap: Metadata could not be loaded "
|
|
|
|
"from " << savedir << ", assuming valid save "
|
|
|
|
"directory." << std::endl;
|
|
|
|
}
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
m_map_saving_enabled = true;
|
|
|
|
// Map loaded, not creating new one
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If directory doesn't exist, it is safe to save to it
|
|
|
|
else{
|
|
|
|
m_map_saving_enabled = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(std::exception &e)
|
|
|
|
{
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"ServerMap: Failed to load map from "<<savedir
|
2010-11-27 00:02:21 +01:00
|
|
|
<<", exception: "<<e.what()<<std::endl;
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"Please remove the map or fix it."<<std::endl;
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Map saving will be disabled."<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ServerMap::~ServerMap()
|
|
|
|
{
|
2015-10-14 08:39:30 +02:00
|
|
|
verbosestream<<FUNCTION_NAME<<std::endl;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
try
|
|
|
|
{
|
2017-08-19 11:29:46 +02:00
|
|
|
if (m_map_saving_enabled) {
|
2010-11-27 00:02:21 +01:00
|
|
|
// Save only changed parts
|
2011-11-27 23:45:34 +01:00
|
|
|
save(MOD_STATE_WRITE_AT_UNLOAD);
|
2017-08-19 11:29:46 +02:00
|
|
|
infostream << "ServerMap: Saved map to " << m_savedir << std::endl;
|
|
|
|
} else {
|
|
|
|
infostream << "ServerMap: Map not saved" << std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
catch(std::exception &e)
|
|
|
|
{
|
2012-03-11 03:15:45 +01:00
|
|
|
infostream<<"ServerMap: Failed to save map to "<<m_savedir
|
2010-11-27 00:02:21 +01:00
|
|
|
<<", exception: "<<e.what()<<std::endl;
|
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
/*
|
|
|
|
Close database if it was opened
|
|
|
|
*/
|
2014-02-04 04:42:10 +01:00
|
|
|
delete dbase;
|
2020-03-10 20:30:42 +01:00
|
|
|
delete dbase_ro;
|
2011-09-03 01:07:14 +02:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
#if 0
|
2011-04-03 11:14:23 +02:00
|
|
|
/*
|
|
|
|
Free all MapChunks
|
|
|
|
*/
|
|
|
|
core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
|
|
|
|
for(; i.atEnd() == false; i++)
|
|
|
|
{
|
|
|
|
MapChunk *chunk = i.getNode()->getValue();
|
|
|
|
delete chunk;
|
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
#endif
|
2014-02-04 04:42:10 +01:00
|
|
|
}
|
|
|
|
|
2016-06-25 00:15:56 +02:00
|
|
|
MapgenParams *ServerMap::getMapgenParams()
|
|
|
|
{
|
|
|
|
// getMapgenParams() should only ever be called after Server is initialized
|
|
|
|
assert(settings_mgr.mapgen_params != NULL);
|
|
|
|
return settings_mgr.mapgen_params;
|
|
|
|
}
|
|
|
|
|
2014-02-04 04:42:10 +01:00
|
|
|
u64 ServerMap::getSeed()
|
|
|
|
{
|
2016-06-25 00:15:56 +02:00
|
|
|
return getMapgenParams()->seed;
|
2014-02-04 04:42:10 +01:00
|
|
|
}
|
2013-04-07 21:01:19 +02:00
|
|
|
|
2014-02-04 04:42:10 +01:00
|
|
|
s16 ServerMap::getWaterLevel()
|
|
|
|
{
|
2016-06-25 00:15:56 +02:00
|
|
|
return getMapgenParams()->water_level;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2017-03-23 01:18:59 +01:00
|
|
|
bool ServerMap::blockpos_over_mapgen_limit(v3s16 p)
|
|
|
|
{
|
|
|
|
const s16 mapgen_limit_bp = rangelim(
|
|
|
|
getMapgenParams()->mapgen_limit, 0, MAX_MAP_GENERATION_LIMIT) /
|
|
|
|
MAP_BLOCKSIZE;
|
|
|
|
return p.X < -mapgen_limit_bp ||
|
|
|
|
p.X > mapgen_limit_bp ||
|
|
|
|
p.Y < -mapgen_limit_bp ||
|
|
|
|
p.Y > mapgen_limit_bp ||
|
|
|
|
p.Z < -mapgen_limit_bp ||
|
|
|
|
p.Z > mapgen_limit_bp;
|
|
|
|
}
|
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
2016-06-25 00:15:56 +02:00
|
|
|
s16 csize = getMapgenParams()->chunksize;
|
2015-10-04 08:54:25 +02:00
|
|
|
v3s16 bpmin = EmergeManager::getContainingChunk(blockpos, csize);
|
|
|
|
v3s16 bpmax = bpmin + v3s16(1, 1, 1) * (csize - 1);
|
|
|
|
|
|
|
|
bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
|
|
|
|
EMERGE_DBG_OUT("initBlockMake(): " PP(bpmin) " - " PP(bpmax));
|
2012-02-03 01:23:21 +01:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
v3s16 extra_borders(1, 1, 1);
|
|
|
|
v3s16 full_bpmin = bpmin - extra_borders;
|
|
|
|
v3s16 full_bpmax = bpmax + extra_borders;
|
2012-02-03 01:23:21 +01:00
|
|
|
|
2017-03-13 22:35:29 +01:00
|
|
|
// Do nothing if not inside mapgen limits (+-1 because of neighbors)
|
|
|
|
if (blockpos_over_mapgen_limit(full_bpmin) ||
|
|
|
|
blockpos_over_mapgen_limit(full_bpmax))
|
2013-02-17 08:46:08 +01:00
|
|
|
return false;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2016-06-25 00:15:56 +02:00
|
|
|
data->seed = getSeed();
|
2015-10-04 08:54:25 +02:00
|
|
|
data->blockpos_min = bpmin;
|
|
|
|
data->blockpos_max = bpmax;
|
2012-01-27 00:40:57 +01:00
|
|
|
data->blockpos_requested = blockpos;
|
2017-01-04 19:18:40 +01:00
|
|
|
data->nodedef = m_nodedef;
|
2011-01-15 12:50:13 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
|
|
|
Create the whole area of this and the neighboring blocks
|
|
|
|
*/
|
2015-10-04 08:54:25 +02:00
|
|
|
for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++)
|
|
|
|
for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) {
|
|
|
|
v2s16 sectorpos(x, z);
|
|
|
|
// Sector metadata is loaded from disk if not already loaded.
|
2017-08-19 11:29:46 +02:00
|
|
|
MapSector *sector = createSector(sectorpos);
|
2015-10-04 08:54:25 +02:00
|
|
|
FATAL_ERROR_IF(sector == NULL, "createSector() failed");
|
|
|
|
|
|
|
|
for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) {
|
|
|
|
v3s16 p(x, y, z);
|
2011-07-21 16:00:08 +02:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
MapBlock *block = emergeBlock(p, false);
|
|
|
|
if (block == NULL) {
|
|
|
|
block = createBlock(p);
|
2011-01-08 13:08:48 +01:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
// Block gets sunlight if this is true.
|
|
|
|
// Refer to the map generator heuristics.
|
|
|
|
bool ug = m_emerge->isBlockUnderground(p);
|
|
|
|
block->setIsUnderground(ug);
|
2011-01-08 13:08:48 +01:00
|
|
|
}
|
2010-12-25 15:04:51 +01:00
|
|
|
}
|
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-01-15 12:50:13 +01:00
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Now we have a big empty area.
|
|
|
|
|
|
|
|
Make a ManualMapVoxelManipulator that contains this and the
|
|
|
|
neighboring blocks
|
2011-01-15 12:50:13 +01:00
|
|
|
*/
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2015-01-05 08:42:27 +01:00
|
|
|
data->vmanip = new MMVManip(this);
|
2015-10-04 08:54:25 +02:00
|
|
|
data->vmanip->initialEmerge(full_bpmin, full_bpmax);
|
2011-01-15 12:50:13 +01:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
// Note: we may need this again at some point.
|
|
|
|
#if 0
|
|
|
|
// Ensure none of the blocks to be generated were marked as
|
|
|
|
// containing CONTENT_IGNORE
|
|
|
|
for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
|
2013-02-17 07:47:49 +01:00
|
|
|
for (s16 y = blockpos_min.Y; y <= blockpos_max.Y; y++) {
|
|
|
|
for (s16 x = blockpos_min.X; x <= blockpos_max.X; x++) {
|
|
|
|
core::map<v3s16, u8>::Node *n;
|
|
|
|
n = data->vmanip->m_loaded_blocks.find(v3s16(x, y, z));
|
|
|
|
if (n == NULL)
|
|
|
|
continue;
|
|
|
|
u8 flags = n->getValue();
|
|
|
|
flags &= ~VMANIP_BLOCK_CONTAINS_CIGNORE;
|
|
|
|
n->setValue(flags);
|
|
|
|
}
|
|
|
|
}
|
2015-10-04 08:54:25 +02:00
|
|
|
}
|
|
|
|
#endif
|
2010-12-13 02:19:12 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
// Data is ready now.
|
2013-02-17 08:46:08 +01:00
|
|
|
return true;
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2011-01-15 00:26:29 +01:00
|
|
|
|
2014-06-14 11:36:46 +02:00
|
|
|
void ServerMap::finishBlockMake(BlockMakeData *data,
|
2015-10-04 08:54:25 +02:00
|
|
|
std::map<v3s16, MapBlock*> *changed_blocks)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
2015-10-04 08:54:25 +02:00
|
|
|
v3s16 bpmin = data->blockpos_min;
|
|
|
|
v3s16 bpmax = data->blockpos_max;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
v3s16 extra_borders(1, 1, 1);
|
2012-02-03 01:23:21 +01:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info;
|
|
|
|
EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax));
|
2011-06-26 20:53:11 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Blit generated stuff to map
|
2011-06-25 15:32:09 +02:00
|
|
|
NOTE: blitBackAll adds nearly everything to changed_blocks
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2015-10-04 08:54:25 +02:00
|
|
|
data->vmanip->blitBackAll(changed_blocks);
|
2011-06-26 20:53:11 +02:00
|
|
|
|
2015-10-04 08:54:25 +02:00
|
|
|
EMERGE_DBG_OUT("finishBlockMake: changed_blocks.size()="
|
|
|
|
<< changed_blocks->size());
|
2011-06-26 20:53:11 +02:00
|
|
|
|
2010-11-29 09:52:07 +01:00
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Copy transforming liquid information
|
2010-11-29 09:52:07 +01:00
|
|
|
*/
|
2015-10-04 08:54:25 +02:00
|
|
|
while (data->transforming_liquid.size()) {
|
2015-01-13 13:28:21 +01:00
|
|
|
m_transforming_liquid.push_back(data->transforming_liquid.front());
|
|
|
|
data->transforming_liquid.pop_front();
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto &changed_block : *changed_blocks) {
|
|
|
|
MapBlock *block = changed_block.second;
|
2014-06-14 11:36:46 +02:00
|
|
|
if (!block)
|
|
|
|
continue;
|
2010-12-22 16:58:02 +01:00
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Update day/night difference cache of the MapBlocks
|
2010-12-22 16:58:02 +01:00
|
|
|
*/
|
2012-03-28 23:28:48 +02:00
|
|
|
block->expireDayNightDiff();
|
2010-12-22 16:58:02 +01:00
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Set block as modified
|
2010-12-22 16:58:02 +01:00
|
|
|
*/
|
2011-11-21 12:29:16 +01:00
|
|
|
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
2015-05-18 04:14:26 +02:00
|
|
|
MOD_REASON_EXPIRE_DAYNIGHTDIFF);
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2011-01-25 23:41:06 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
2012-01-27 00:40:57 +01:00
|
|
|
Set central blocks as generated
|
2011-06-25 03:25:14 +02:00
|
|
|
*/
|
2015-10-04 08:54:25 +02:00
|
|
|
for (s16 x = bpmin.X; x <= bpmax.X; x++)
|
|
|
|
for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
|
|
|
|
for (s16 y = bpmin.Y; y <= bpmax.Y; y++) {
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(v3s16(x, y, z));
|
2014-06-14 11:36:46 +02:00
|
|
|
if (!block)
|
|
|
|
continue;
|
2015-10-04 08:54:25 +02:00
|
|
|
|
2012-01-27 00:40:57 +01:00
|
|
|
block->setGenerated(true);
|
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
|
|
|
Save changed parts of map
|
|
|
|
NOTE: Will be saved later.
|
|
|
|
*/
|
2011-11-27 23:45:34 +01:00
|
|
|
//save(MOD_STATE_WRITE_AT_UNLOAD);
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2010-12-22 15:30:23 +01:00
|
|
|
|
2017-08-19 11:29:46 +02:00
|
|
|
MapSector *ServerMap::createSector(v2s16 p2d)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
Check if it exists already in memory
|
|
|
|
*/
|
2019-08-13 19:58:27 +02:00
|
|
|
MapSector *sector = getSectorNoGenerate(p2d);
|
2017-08-19 11:29:46 +02:00
|
|
|
if (sector)
|
2011-06-25 03:25:14 +02:00
|
|
|
return sector;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
2017-03-13 22:35:29 +01:00
|
|
|
Do not create over max mapgen limit
|
2011-06-25 03:25:14 +02:00
|
|
|
*/
|
2017-03-13 22:35:29 +01:00
|
|
|
const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE;
|
|
|
|
if (p2d.X < -max_limit_bp ||
|
|
|
|
p2d.X > max_limit_bp ||
|
|
|
|
p2d.Y < -max_limit_bp ||
|
|
|
|
p2d.Y > max_limit_bp)
|
|
|
|
throw InvalidPositionException("createSector(): pos. over max mapgen limit");
|
2011-06-25 03:25:14 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
Generate blank sector
|
|
|
|
*/
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2017-08-19 11:29:46 +02:00
|
|
|
sector = new MapSector(this, p2d, m_gamedef);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
// Sector position on map in nodes
|
2013-05-20 03:26:08 +02:00
|
|
|
//v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Insert to container
|
2010-11-27 00:02:21 +01:00
|
|
|
*/
|
2012-12-20 18:19:49 +01:00
|
|
|
m_sectors[p2d] = sector;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
return sector;
|
|
|
|
}
|
|
|
|
|
2012-11-26 03:16:48 +01:00
|
|
|
#if 0
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
|
|
|
This is a quick-hand function for calling makeBlock().
|
|
|
|
*/
|
|
|
|
MapBlock * ServerMap::generateBlock(
|
|
|
|
v3s16 p,
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, MapBlock*> &modified_blocks
|
2011-06-25 03:25:14 +02:00
|
|
|
)
|
|
|
|
{
|
2011-10-12 12:53:38 +02:00
|
|
|
bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
|
2011-06-26 20:53:11 +02:00
|
|
|
|
2011-06-25 15:32:09 +02:00
|
|
|
TimeTaker timer("generateBlock");
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
//MapBlock *block = original_dummy;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
v2s16 p2d(p.X, p.Z);
|
|
|
|
v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
/*
|
|
|
|
Do not generate over-limit
|
|
|
|
*/
|
|
|
|
if(blockpos_over_limit(p))
|
|
|
|
{
|
2015-10-14 08:39:30 +02:00
|
|
|
infostream<<FUNCTION_NAME<<": Block position over limit"<<std::endl;
|
2011-06-25 03:25:14 +02:00
|
|
|
throw InvalidPositionException("generateBlock(): pos. over limit");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Create block make data
|
|
|
|
*/
|
2012-11-26 03:16:48 +01:00
|
|
|
BlockMakeData data;
|
2011-06-25 03:25:14 +02:00
|
|
|
initBlockMake(&data, p);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Generate block
|
|
|
|
*/
|
|
|
|
{
|
2011-06-25 17:12:41 +02:00
|
|
|
TimeTaker t("mapgen::make_block()");
|
2012-11-26 03:16:48 +01:00
|
|
|
mapgen->makeChunk(&data);
|
|
|
|
//mapgen::make_block(&data);
|
2011-06-26 20:53:11 +02:00
|
|
|
|
|
|
|
if(enable_mapgen_debug_info == false)
|
|
|
|
t.stop(true); // Hide output
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Blit data back on map, update lighting, add mobs and whatever this does
|
|
|
|
*/
|
|
|
|
finishBlockMake(&data, modified_blocks);
|
|
|
|
|
|
|
|
/*
|
|
|
|
Get central block
|
|
|
|
*/
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(p);
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
Check result
|
|
|
|
*/
|
2011-07-22 00:37:05 +02:00
|
|
|
if(block)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
2011-07-22 00:37:05 +02:00
|
|
|
bool erroneus_content = false;
|
|
|
|
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
|
|
|
|
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
|
|
|
|
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
|
|
|
|
{
|
|
|
|
v3s16 p(x0,y0,z0);
|
|
|
|
MapNode n = block->getNode(p);
|
2011-07-23 15:55:26 +02:00
|
|
|
if(n.getContent() == CONTENT_IGNORE)
|
2011-07-22 00:37:05 +02:00
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"CONTENT_IGNORE at "
|
2011-07-22 00:37:05 +02:00
|
|
|
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
|
|
|
|
<<std::endl;
|
|
|
|
erroneus_content = true;
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(erroneus_content)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
|
|
|
assert(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-01-30 00:44:54 +01:00
|
|
|
#if 0
|
|
|
|
/*
|
2011-06-25 03:25:14 +02:00
|
|
|
Generate a completely empty block
|
2011-01-30 00:44:54 +01:00
|
|
|
*/
|
2011-07-22 00:37:05 +02:00
|
|
|
if(block)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
2011-07-22 00:37:05 +02:00
|
|
|
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
|
|
|
|
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
|
2011-06-25 03:25:14 +02:00
|
|
|
{
|
2011-07-22 00:37:05 +02:00
|
|
|
for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
|
|
|
|
{
|
|
|
|
MapNode n;
|
2011-11-16 00:15:32 +01:00
|
|
|
n.setContent(CONTENT_AIR);
|
2011-07-22 00:37:05 +02:00
|
|
|
block->setNode(v3s16(x0,y0,z0), n);
|
|
|
|
}
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
|
|
|
}
|
2011-01-30 00:44:54 +01:00
|
|
|
#endif
|
|
|
|
|
2011-06-26 20:53:11 +02:00
|
|
|
if(enable_mapgen_debug_info == false)
|
|
|
|
timer.stop(true); // Hide output
|
|
|
|
|
2011-01-30 00:44:54 +01:00
|
|
|
return block;
|
|
|
|
}
|
2012-11-26 03:16:48 +01:00
|
|
|
#endif
|
2011-01-30 00:44:54 +01:00
|
|
|
|
2011-02-01 02:06:02 +01:00
|
|
|
MapBlock * ServerMap::createBlock(v3s16 p)
|
|
|
|
{
|
2011-02-15 00:02:31 +01:00
|
|
|
/*
|
2017-03-13 22:35:29 +01:00
|
|
|
Do not create over max mapgen limit
|
2011-02-15 00:02:31 +01:00
|
|
|
*/
|
2017-03-13 22:35:29 +01:00
|
|
|
if (blockpos_over_max_limit(p))
|
|
|
|
throw InvalidPositionException("createBlock(): pos. over max mapgen limit");
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-02-01 02:06:02 +01:00
|
|
|
v2s16 p2d(p.X, p.Z);
|
|
|
|
s16 block_y = p.Y;
|
|
|
|
/*
|
|
|
|
This will create or load a sector if not found in memory.
|
|
|
|
If block exists on disk, it will be loaded.
|
|
|
|
|
|
|
|
NOTE: On old save formats, this will be slow, as it generates
|
|
|
|
lighting on blocks for them.
|
|
|
|
*/
|
2017-08-19 11:29:46 +02:00
|
|
|
MapSector *sector;
|
2015-03-06 11:21:51 +01:00
|
|
|
try {
|
2017-08-19 11:29:46 +02:00
|
|
|
sector = createSector(p2d);
|
|
|
|
} catch (InvalidPositionException &e) {
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"createBlock: createSector() failed"<<std::endl;
|
2011-02-01 02:06:02 +01:00
|
|
|
throw e;
|
2011-02-15 00:02:31 +01:00
|
|
|
}
|
2011-02-01 02:06:02 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Try to get a block from the sector
|
|
|
|
*/
|
|
|
|
|
|
|
|
MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
|
2017-08-19 11:29:46 +02:00
|
|
|
if (block) {
|
2011-06-25 03:25:14 +02:00
|
|
|
if(block->isDummy())
|
|
|
|
block->unDummify();
|
2011-02-01 02:06:02 +01:00
|
|
|
return block;
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
2011-02-01 02:06:02 +01:00
|
|
|
// Create blank
|
|
|
|
block = sector->createBlankBlock(block_y);
|
2012-11-26 03:16:48 +01:00
|
|
|
|
2011-02-01 02:06:02 +01:00
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2012-11-26 03:16:48 +01:00
|
|
|
MapBlock * ServerMap::emergeBlock(v3s16 p, bool create_blank)
|
2011-01-30 00:44:54 +01:00
|
|
|
{
|
2011-07-01 20:04:40 +02:00
|
|
|
{
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(p);
|
2017-08-18 18:18:25 +02:00
|
|
|
if (block && !block->isDummy())
|
2011-07-01 20:04:40 +02:00
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
MapBlock *block = loadBlock(p);
|
|
|
|
if(block)
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2012-11-26 03:16:48 +01:00
|
|
|
if (create_blank) {
|
2017-08-19 11:29:46 +02:00
|
|
|
MapSector *sector = createSector(v2s16(p.X, p.Z));
|
2012-11-26 03:16:48 +01:00
|
|
|
MapBlock *block = sector->createBlankBlock(p.Y);
|
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
2014-02-09 22:36:30 +01:00
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-02-09 22:36:30 +01:00
|
|
|
MapBlock *ServerMap::getBlockOrEmerge(v3s16 p3d)
|
|
|
|
{
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(p3d);
|
|
|
|
if (block == NULL)
|
|
|
|
m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, p3d, false);
|
|
|
|
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
|
2014-09-01 20:20:31 +02:00
|
|
|
// N.B. This requires no synchronization, since data will not be modified unless
|
|
|
|
// the VoxelManipulator being updated belongs to the same thread.
|
|
|
|
void ServerMap::updateVManip(v3s16 pos)
|
|
|
|
{
|
|
|
|
Mapgen *mg = m_emerge->getCurrentMapgen();
|
|
|
|
if (!mg)
|
|
|
|
return;
|
|
|
|
|
2015-01-05 08:42:27 +01:00
|
|
|
MMVManip *vm = mg->vm;
|
2014-09-01 20:20:31 +02:00
|
|
|
if (!vm)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!vm->m_area.contains(pos))
|
|
|
|
return;
|
|
|
|
|
|
|
|
s32 idx = vm->m_area.index(pos);
|
2019-08-10 19:45:44 +02:00
|
|
|
vm->m_data[idx] = getNode(pos);
|
2014-09-01 20:20:31 +02:00
|
|
|
vm->m_flags[idx] &= ~VOXELFLAG_NO_DATA;
|
|
|
|
|
|
|
|
vm->m_is_dirty = true;
|
|
|
|
}
|
|
|
|
|
2014-01-07 03:17:19 +01:00
|
|
|
s16 ServerMap::findGroundLevel(v2s16 p2d)
|
2011-02-23 01:49:57 +01:00
|
|
|
{
|
2014-01-07 03:17:19 +01:00
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
Uh, just do something random...
|
|
|
|
*/
|
|
|
|
// Find existing map from top to down
|
|
|
|
s16 max=63;
|
|
|
|
s16 min=-64;
|
|
|
|
v3s16 p(p2d.X, max, p2d.Y);
|
|
|
|
for(; p.Y>min; p.Y--)
|
|
|
|
{
|
|
|
|
MapNode n = getNodeNoEx(p);
|
|
|
|
if(n.getContent() != CONTENT_IGNORE)
|
|
|
|
break;
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
2014-01-07 03:17:19 +01:00
|
|
|
if(p.Y == min)
|
|
|
|
goto plan_b;
|
|
|
|
// If this node is not air, go to plan b
|
|
|
|
if(getNodeNoEx(p).getContent() != CONTENT_AIR)
|
|
|
|
goto plan_b;
|
|
|
|
// Search existing walkable and return it
|
|
|
|
for(; p.Y>min; p.Y--)
|
2011-02-23 01:49:57 +01:00
|
|
|
{
|
2014-01-07 03:17:19 +01:00
|
|
|
MapNode n = getNodeNoEx(p);
|
|
|
|
if(content_walkable(n.d) && n.getContent() != CONTENT_IGNORE)
|
|
|
|
return p.Y;
|
2013-05-30 18:14:22 +02:00
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2014-01-07 03:17:19 +01:00
|
|
|
// Move to plan b
|
|
|
|
plan_b:
|
|
|
|
#endif
|
2011-06-25 03:25:14 +02:00
|
|
|
|
2014-01-07 03:17:19 +01:00
|
|
|
/*
|
|
|
|
Determine from map generator noise functions
|
|
|
|
*/
|
|
|
|
|
|
|
|
s16 level = m_emerge->getGroundLevelAtPoint(p2d);
|
2013-05-30 18:14:22 +02:00
|
|
|
return level;
|
2014-01-07 03:17:19 +01:00
|
|
|
|
|
|
|
//double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
|
|
|
|
//return (s16)level;
|
2011-02-23 01:49:57 +01:00
|
|
|
}
|
|
|
|
|
Optimize string (mis)handling (#8128)
* Optimize statbar drawing
The texture name of the statbar is a string passed by value.
That slows down the client and creates litter in the heap
as the content of the string is allocated there. Convert the
offending parameter to a const reference to avoid the
performance hit.
* Optimize texture cache
There is an unnecessary temporary created when the texture
path is being generated. This slows down the cache each time
a new texture is encountered and it needs to be loaded into
the cache. Additionally, the heap litter created by this
unnecessary temporary is particularly troublesome here as
the following code then piles another string (the resulting
full path of the texture) on top of it, followed by the
texture itself, which both are quite long term objects as
they are subsequently inserted into the cache where they can
remain for quite a while (especially if the texture turns
out to be a common one like dirt, grass or stone).
Use std::string.append to get rid of the temporary which
solves both issues (speed and heap fragmentation).
* Optimize animations in client
Each time an animated node is updated, an unnecessary copy of
the texture name is created, littering the heap with lots of
fragments. This can be specifically troublesome when looking
at oceans or large lava lakes as both of these nodes are
usually animated (the lava animation is pretty visible).
Convert the parameter of GenericCAO::updateTextures to a
const reference to get rid of the unnecessary copy.
There is a comment stating "std::string copy is mandatory as
mod can be a class member and there is a swap on those class
members ... do NOT pass by reference", reinforcing the
belief that the unnecessary copy is in fact necessary.
However one of the first things the code of the method does
is to assign the parameter to its class member, creating
another copy. By rearranging the code a little bit this
"another copy" can then be used by the subsequent code,
getting rid of the need to pass the parameter by value and
thus saving that copying effort.
* Optimize chat console history handling
The GUIChatConsole::replaceAndAddToHistory was getting the
line to work on by value which turns out to be unnecessary.
Get rid of that unnecessary copy by converting the parameter
to a const reference.
* Optimize gui texture setting
The code used to set the texture for GUI components was
getting the name of the texture by value, creating
unnecessary performance bottleneck for mods/games with
heavily textured GUIs. Get rid of the bottleneck by passing
the texture name as a const reference.
* Optimize sound playing code in GUIEngine
The GUIEngine's code receives the specification of the sound
to be played by value, which turns out to be most likely a
mistake as the underlying sound manager interface receives
the same thing by reference. Convert the offending parameter
to a const reference to get rid of the rather bulky copying
effort and the associated performance hit.
* Silence CLANG TIDY warnings for unit tests
Change "std::string" to "const std::string &" to avoid an
unnecessary local value copy, silencing the CLANG TIDY
process.
* Optimize formspec handling
The "formspec prepend" parameter was passed to the formspec
handling code by value, creating unnecessary copy of
std::string and slowing down the game if mods add things like
textured backgrounds for the player inventory and/or other
forms. Get rid of that performance bottleneck by converting
the parameter to a const reference.
* Optimize hotbar image handling
The code that sets the background images for the hotbar is
getting the name of the image by value, creating an
unnecessary std::string copying effort. Fix that by
converting the relevant parameters to const references.
* Optimize inventory deserialization
The inventory manager deserialization code gets the
serialized version of the inventory by value, slowing the
server and the client down when there are inventory updates.
This can get particularly troublesome with pipeworks which
adds nodes that can mess around with inventories
automatically or with mods that have mobs with inventories
that actively use them.
* Optimize texture scaling cache
There is an io::path parameter passed by value in the
procedure used to add images converted from textures,
leading to slowdown when the image is not yet created and
the conversion is thus needed. The performance hit is
quite significant as io::path is similar to std::string
so convert the parameter to a const reference to get rid of
it.
* Optimize translation file loader
Use "std::string::append" when calculating the final index
for the translation table to avoid unnecessary temporary
strings. This speeds the translation file loader up
significantly as std::string uses heap allocation which
tends to be rather slow. Additionally, the heap is no
longer being littered by these unnecessary string
temporaries, increasing performance of code that gets
executed after the translation file loader finishes.
* Optimize server map saving
When the directory structure for the world data is created
during server map saving, an unnecessary value passing of
the directory name slows things down. Remove that overhead
by converting the offending parameter to a const reference.
2019-05-18 17:19:13 +02:00
|
|
|
void ServerMap::createDirs(const std::string &path)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2017-08-18 18:18:25 +02:00
|
|
|
if (!fs::CreateAllDirs(path)) {
|
2015-10-14 07:26:03 +02:00
|
|
|
m_dout<<"ServerMap: Failed to create directory "
|
2010-11-27 00:02:21 +01:00
|
|
|
<<"\""<<path<<"\""<<std::endl;
|
|
|
|
throw BaseException("ServerMap failed to create directory");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-27 23:45:34 +01:00
|
|
|
void ServerMap::save(ModifiedState save_level)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2017-08-18 18:18:25 +02:00
|
|
|
if (!m_map_saving_enabled) {
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream<<"Not saving map, saving disabled."<<std::endl;
|
2010-11-27 00:02:21 +01:00
|
|
|
return;
|
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2020-04-27 08:31:37 +02:00
|
|
|
u64 start_time = porting::getTimeNs();
|
|
|
|
|
2011-11-27 23:45:34 +01:00
|
|
|
if(save_level == MOD_STATE_CLEAN)
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"ServerMap: Saving whole map, this can take time."
|
2010-11-27 00:02:21 +01:00
|
|
|
<<std::endl;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2016-06-25 00:15:56 +02:00
|
|
|
if (m_map_metadata_changed || save_level == MOD_STATE_CLEAN) {
|
|
|
|
if (settings_mgr.saveMapMeta())
|
|
|
|
m_map_metadata_changed = false;
|
2011-04-25 23:23:38 +02:00
|
|
|
}
|
2011-04-10 21:50:31 +02:00
|
|
|
|
2011-11-21 12:35:32 +01:00
|
|
|
// Profile modified reasons
|
|
|
|
Profiler modprofiler;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
u32 block_count = 0;
|
2011-06-05 17:57:36 +02:00
|
|
|
u32 block_count_all = 0; // Number of blocks in memory
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-11-21 13:13:28 +01:00
|
|
|
// Don't do anything with sqlite unless something is really saved
|
|
|
|
bool save_started = false;
|
|
|
|
|
2017-08-19 09:29:55 +02:00
|
|
|
for (auto §or_it : m_sectors) {
|
2017-08-19 11:29:46 +02:00
|
|
|
MapSector *sector = sector_it.second;
|
2015-02-17 15:28:49 +01:00
|
|
|
|
|
|
|
MapBlockVect blocks;
|
2011-02-05 13:55:16 +01:00
|
|
|
sector->getBlocks(blocks);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (MapBlock *block : blocks) {
|
2011-06-05 17:57:36 +02:00
|
|
|
block_count_all++;
|
|
|
|
|
2015-02-17 15:28:49 +01:00
|
|
|
if(block->getModified() >= (u32)save_level) {
|
2011-11-21 13:13:28 +01:00
|
|
|
// Lazy beginSave()
|
2015-02-17 15:28:49 +01:00
|
|
|
if(!save_started) {
|
2011-11-21 13:13:28 +01:00
|
|
|
beginSave();
|
|
|
|
save_started = true;
|
|
|
|
}
|
|
|
|
|
2015-05-18 04:14:26 +02:00
|
|
|
modprofiler.add(block->getModifiedReasonString(), 1);
|
2011-11-21 13:13:28 +01:00
|
|
|
|
2011-02-05 13:55:16 +01:00
|
|
|
saveBlock(block);
|
|
|
|
block_count++;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-17 15:28:49 +01:00
|
|
|
|
2011-11-21 13:13:28 +01:00
|
|
|
if(save_started)
|
|
|
|
endSave();
|
2010-11-27 00:02:21 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
Only print if something happened or saved whole map
|
|
|
|
*/
|
2017-08-19 11:29:46 +02:00
|
|
|
if(save_level == MOD_STATE_CLEAN
|
2015-02-17 15:28:49 +01:00
|
|
|
|| block_count != 0) {
|
2020-04-08 20:13:23 +02:00
|
|
|
infostream << "ServerMap: Written: "
|
|
|
|
<< block_count << " blocks"
|
|
|
|
<< ", " << block_count_all << " blocks in memory."
|
|
|
|
<< std::endl;
|
2011-11-21 12:35:32 +01:00
|
|
|
PrintInfo(infostream); // ServerMap/ClientMap:
|
|
|
|
infostream<<"Blocks modified by: "<<std::endl;
|
|
|
|
modprofiler.print(infostream);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
2020-04-27 08:31:37 +02:00
|
|
|
|
|
|
|
auto end_time = porting::getTimeNs();
|
|
|
|
m_save_time_counter->increment(end_time - start_time);
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2015-02-17 14:30:32 +01:00
|
|
|
void ServerMap::listAllLoadableBlocks(std::vector<v3s16> &dst)
|
2011-10-17 23:01:50 +02:00
|
|
|
{
|
2013-09-09 18:44:50 +02:00
|
|
|
dbase->listAllLoadableBlocks(dst);
|
2018-07-25 17:54:23 +02:00
|
|
|
if (dbase_ro)
|
|
|
|
dbase_ro->listAllLoadableBlocks(dst);
|
2011-10-17 23:01:50 +02:00
|
|
|
}
|
|
|
|
|
2015-02-17 14:30:32 +01:00
|
|
|
void ServerMap::listAllLoadedBlocks(std::vector<v3s16> &dst)
|
2013-06-02 15:35:29 +02:00
|
|
|
{
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto §or_it : m_sectors) {
|
|
|
|
MapSector *sector = sector_it.second;
|
2013-06-02 15:35:29 +02:00
|
|
|
|
2015-02-17 15:28:49 +01:00
|
|
|
MapBlockVect blocks;
|
2013-06-02 15:35:29 +02:00
|
|
|
sector->getBlocks(blocks);
|
|
|
|
|
2017-08-19 09:12:54 +02:00
|
|
|
for (MapBlock *block : blocks) {
|
|
|
|
v3s16 p = block->getPos();
|
2013-06-02 15:35:29 +02:00
|
|
|
dst.push_back(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-23 14:35:08 +02:00
|
|
|
MapDatabase *ServerMap::createDatabase(
|
2015-10-04 08:54:25 +02:00
|
|
|
const std::string &name,
|
|
|
|
const std::string &savedir,
|
|
|
|
Settings &conf)
|
2015-02-27 08:24:07 +01:00
|
|
|
{
|
|
|
|
if (name == "sqlite3")
|
2017-04-23 14:35:08 +02:00
|
|
|
return new MapDatabaseSQLite3(savedir);
|
2015-02-27 08:24:07 +01:00
|
|
|
if (name == "dummy")
|
|
|
|
return new Database_Dummy();
|
|
|
|
#if USE_LEVELDB
|
2017-08-19 09:12:54 +02:00
|
|
|
if (name == "leveldb")
|
2015-02-27 08:24:07 +01:00
|
|
|
return new Database_LevelDB(savedir);
|
|
|
|
#endif
|
|
|
|
#if USE_REDIS
|
2017-08-19 09:12:54 +02:00
|
|
|
if (name == "redis")
|
2015-02-27 08:24:07 +01:00
|
|
|
return new Database_Redis(conf);
|
|
|
|
#endif
|
2016-05-14 11:00:42 +02:00
|
|
|
#if USE_POSTGRESQL
|
2017-08-19 09:12:54 +02:00
|
|
|
if (name == "postgresql") {
|
|
|
|
std::string connect_string;
|
2017-04-23 14:35:08 +02:00
|
|
|
conf.getNoEx("pgsql_connection", connect_string);
|
|
|
|
return new MapDatabasePostgreSQL(connect_string);
|
|
|
|
}
|
2016-05-14 11:00:42 +02:00
|
|
|
#endif
|
2017-08-19 09:12:54 +02:00
|
|
|
|
|
|
|
throw BaseException(std::string("Database backend ") + name + " not supported.");
|
2015-02-27 08:24:07 +01:00
|
|
|
}
|
|
|
|
|
2020-04-15 08:01:11 +02:00
|
|
|
void ServerMap::pingDatabase()
|
|
|
|
{
|
|
|
|
dbase->pingDatabase();
|
|
|
|
}
|
|
|
|
|
2014-07-07 07:20:25 +02:00
|
|
|
void ServerMap::beginSave()
|
|
|
|
{
|
2012-10-22 23:18:44 +02:00
|
|
|
dbase->beginSave();
|
2011-09-03 01:07:14 +02:00
|
|
|
}
|
|
|
|
|
2014-07-07 07:20:25 +02:00
|
|
|
void ServerMap::endSave()
|
|
|
|
{
|
2012-10-22 23:18:44 +02:00
|
|
|
dbase->endSave();
|
2011-09-03 01:07:14 +02:00
|
|
|
}
|
|
|
|
|
2014-07-07 07:20:25 +02:00
|
|
|
bool ServerMap::saveBlock(MapBlock *block)
|
2010-11-27 00:02:21 +01:00
|
|
|
{
|
2014-07-08 20:04:37 +02:00
|
|
|
return saveBlock(block, dbase);
|
|
|
|
}
|
|
|
|
|
2017-04-23 14:35:08 +02:00
|
|
|
bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db)
|
2014-07-08 20:04:37 +02:00
|
|
|
{
|
|
|
|
v3s16 p3d = block->getPos();
|
|
|
|
|
|
|
|
// Dummy blocks are not written
|
|
|
|
if (block->isDummy()) {
|
2015-10-14 07:26:03 +02:00
|
|
|
warningstream << "saveBlock: Not writing dummy block "
|
2014-07-08 20:04:37 +02:00
|
|
|
<< PP(p3d) << std::endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Format used for writing
|
|
|
|
u8 version = SER_FMT_VER_HIGHEST_WRITE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
[0] u8 serialization version
|
|
|
|
[1] data
|
|
|
|
*/
|
|
|
|
std::ostringstream o(std::ios_base::binary);
|
|
|
|
o.write((char*) &version, 1);
|
|
|
|
block->serialize(o, version, true);
|
|
|
|
|
2017-08-19 11:29:46 +02:00
|
|
|
bool ret = db->saveBlock(p3d, o.str());
|
2014-11-16 21:31:57 +01:00
|
|
|
if (ret) {
|
2014-07-08 20:04:37 +02:00
|
|
|
// We just wrote it to the disk so clear modified flag
|
|
|
|
block->resetModified();
|
|
|
|
}
|
|
|
|
return ret;
|
2010-11-27 00:02:21 +01:00
|
|
|
}
|
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
std::istringstream is(*blob, std::ios_base::binary);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
u8 version = SER_FMT_VER_INVALID;
|
|
|
|
is.read((char*)&version, 1);
|
|
|
|
|
|
|
|
if(is.fail())
|
|
|
|
throw SerializationError("ServerMap::loadBlock(): Failed"
|
|
|
|
" to read MapBlock version");
|
|
|
|
|
|
|
|
MapBlock *block = NULL;
|
|
|
|
bool created_new = false;
|
|
|
|
block = sector->getBlockNoCreateNoEx(p3d.Y);
|
|
|
|
if(block == NULL)
|
|
|
|
{
|
|
|
|
block = sector->createBlankBlockNoInsert(p3d.Y);
|
|
|
|
created_new = true;
|
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
// Read basic data
|
2012-01-21 00:11:44 +01:00
|
|
|
block->deSerialize(is, version, true);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
// If it's a new block, insert it to the map
|
2016-11-27 21:51:40 +01:00
|
|
|
if (created_new) {
|
2011-09-03 01:07:14 +02:00
|
|
|
sector->insertBlock(block);
|
2016-11-27 21:51:40 +01:00
|
|
|
ReflowScan scanner(this, m_emerge->ndef);
|
|
|
|
scanner.scan(block, &m_transforming_liquid);
|
|
|
|
}
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
/*
|
|
|
|
Save blocks loaded in old format in new format
|
|
|
|
*/
|
|
|
|
|
2013-08-01 22:51:36 +02:00
|
|
|
//if(version < SER_FMT_VER_HIGHEST_READ || save_after_load)
|
2011-11-16 12:03:28 +01:00
|
|
|
// Only save if asked to; no need to update version
|
|
|
|
if(save_after_load)
|
2011-09-03 01:07:14 +02:00
|
|
|
saveBlock(block);
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-09-03 01:07:14 +02:00
|
|
|
// We just loaded it from, so it's up-to-date.
|
|
|
|
block->resetModified();
|
|
|
|
}
|
|
|
|
catch(SerializationError &e)
|
|
|
|
{
|
2012-06-04 21:34:22 +02:00
|
|
|
errorstream<<"Invalid block data in database"
|
|
|
|
<<" ("<<p3d.X<<","<<p3d.Y<<","<<p3d.Z<<")"
|
|
|
|
<<" (SerializationError): "<<e.what()<<std::endl;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2012-06-04 21:34:22 +02:00
|
|
|
// TODO: Block should be marked as invalid in memory so that it is
|
|
|
|
// not touched but the game can run
|
2011-09-03 01:07:14 +02:00
|
|
|
|
2012-06-04 21:34:22 +02:00
|
|
|
if(g_settings->getBool("ignore_world_load_errors")){
|
|
|
|
errorstream<<"Ignoring block load error. Duck and cover! "
|
|
|
|
<<"(ignore_world_load_errors)"<<std::endl;
|
|
|
|
} else {
|
|
|
|
throw SerializationError("Invalid block data in database");
|
|
|
|
}
|
2011-09-03 01:07:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
MapBlock* ServerMap::loadBlock(v3s16 blockpos)
|
|
|
|
{
|
2016-10-27 23:25:44 +02:00
|
|
|
bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL);
|
|
|
|
|
2011-06-25 03:25:14 +02:00
|
|
|
v2s16 p2d(blockpos.X, blockpos.Z);
|
|
|
|
|
2014-07-08 20:04:37 +02:00
|
|
|
std::string ret;
|
2016-05-14 12:23:15 +02:00
|
|
|
dbase->loadBlock(blockpos, &ret);
|
2017-08-19 09:12:54 +02:00
|
|
|
if (!ret.empty()) {
|
2014-07-08 20:04:37 +02:00
|
|
|
loadBlock(&ret, blockpos, createSector(p2d), false);
|
2018-07-25 17:54:23 +02:00
|
|
|
} else if (dbase_ro) {
|
|
|
|
dbase_ro->loadBlock(blockpos, &ret);
|
|
|
|
if (!ret.empty()) {
|
|
|
|
loadBlock(&ret, blockpos, createSector(p2d), false);
|
|
|
|
}
|
2016-10-27 23:25:44 +02:00
|
|
|
} else {
|
2019-10-21 23:59:58 +02:00
|
|
|
return NULL;
|
2016-10-27 23:25:44 +02:00
|
|
|
}
|
2017-08-19 11:29:46 +02:00
|
|
|
|
2016-10-27 23:25:44 +02:00
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if (created_new && (block != NULL)) {
|
|
|
|
std::map<v3s16, MapBlock*> modified_blocks;
|
|
|
|
// Fix lighting if necessary
|
|
|
|
voxalgo::update_block_border_lighting(this, block, modified_blocks);
|
|
|
|
if (!modified_blocks.empty()) {
|
|
|
|
//Modified lighting, send event
|
|
|
|
MapEditEvent event;
|
|
|
|
event.type = MEET_OTHER;
|
|
|
|
std::map<v3s16, MapBlock *>::iterator it;
|
|
|
|
for (it = modified_blocks.begin();
|
|
|
|
it != modified_blocks.end(); ++it)
|
|
|
|
event.modified_blocks.insert(it->first);
|
2019-09-24 19:05:28 +02:00
|
|
|
dispatchEvent(event);
|
2016-10-27 23:25:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return block;
|
2011-06-25 03:25:14 +02:00
|
|
|
}
|
|
|
|
|
2015-01-15 22:20:05 +01:00
|
|
|
bool ServerMap::deleteBlock(v3s16 blockpos)
|
|
|
|
{
|
|
|
|
if (!dbase->deleteBlock(blockpos))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
MapBlock *block = getBlockNoCreateNoEx(blockpos);
|
|
|
|
if (block) {
|
|
|
|
v2s16 p2d(blockpos.X, blockpos.Z);
|
2019-08-13 19:58:27 +02:00
|
|
|
MapSector *sector = getSectorNoGenerate(p2d);
|
2015-01-15 22:20:05 +01:00
|
|
|
if (!sector)
|
|
|
|
return false;
|
|
|
|
sector->deleteBlock(block);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-11-27 00:02:21 +01:00
|
|
|
void ServerMap::PrintInfo(std::ostream &out)
|
|
|
|
{
|
|
|
|
out<<"ServerMap: ";
|
|
|
|
}
|
|
|
|
|
2017-03-11 17:07:04 +01:00
|
|
|
bool ServerMap::repairBlockLight(v3s16 blockpos,
|
|
|
|
std::map<v3s16, MapBlock *> *modified_blocks)
|
|
|
|
{
|
|
|
|
MapBlock *block = emergeBlock(blockpos, false);
|
|
|
|
if (!block || !block->isGenerated())
|
|
|
|
return false;
|
|
|
|
voxalgo::repair_block_light(this, block, modified_blocks);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-01-05 08:42:27 +01:00
|
|
|
MMVManip::MMVManip(Map *map):
|
2014-06-09 14:25:35 +02:00
|
|
|
VoxelManipulator(),
|
|
|
|
m_map(map)
|
2011-01-24 15:36:58 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2015-01-05 08:42:27 +01:00
|
|
|
void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
|
|
|
|
bool load_if_inexistent)
|
2011-01-24 15:36:58 +01:00
|
|
|
{
|
2011-02-01 15:17:55 +01:00
|
|
|
TimeTaker timer1("initialEmerge", &emerge_time);
|
2011-01-24 15:36:58 +01:00
|
|
|
|
|
|
|
// Units of these are MapBlocks
|
|
|
|
v3s16 p_min = blockpos_min;
|
|
|
|
v3s16 p_max = blockpos_max;
|
|
|
|
|
|
|
|
VoxelArea block_area_nodes
|
|
|
|
(p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-02-01 15:17:55 +01:00
|
|
|
u32 size_MB = block_area_nodes.getVolume()*4/1000000;
|
|
|
|
if(size_MB >= 1)
|
|
|
|
{
|
2011-10-16 13:57:53 +02:00
|
|
|
infostream<<"initialEmerge: area: ";
|
|
|
|
block_area_nodes.print(infostream);
|
|
|
|
infostream<<" ("<<size_MB<<"MB)";
|
|
|
|
infostream<<std::endl;
|
2011-02-01 15:17:55 +01:00
|
|
|
}
|
2011-01-24 15:36:58 +01:00
|
|
|
|
|
|
|
addArea(block_area_nodes);
|
|
|
|
|
|
|
|
for(s32 z=p_min.Z; z<=p_max.Z; z++)
|
|
|
|
for(s32 y=p_min.Y; y<=p_max.Y; y++)
|
|
|
|
for(s32 x=p_min.X; x<=p_max.X; x++)
|
|
|
|
{
|
2013-02-17 07:47:49 +01:00
|
|
|
u8 flags = 0;
|
|
|
|
MapBlock *block;
|
2011-01-24 15:36:58 +01:00
|
|
|
v3s16 p(x,y,z);
|
2012-12-20 18:19:49 +01:00
|
|
|
std::map<v3s16, u8>::iterator n;
|
2011-01-24 15:36:58 +01:00
|
|
|
n = m_loaded_blocks.find(p);
|
2012-12-20 18:19:49 +01:00
|
|
|
if(n != m_loaded_blocks.end())
|
2011-01-24 15:36:58 +01:00
|
|
|
continue;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-01-24 15:36:58 +01:00
|
|
|
bool block_data_inexistent = false;
|
|
|
|
{
|
2018-03-08 23:42:49 +01:00
|
|
|
TimeTaker timer2("emerge load", &emerge_load_time);
|
2011-01-24 15:36:58 +01:00
|
|
|
|
2019-08-13 20:02:50 +02:00
|
|
|
block = m_map->getBlockNoCreateNoEx(p);
|
|
|
|
if (!block || block->isDummy())
|
2011-01-24 15:36:58 +01:00
|
|
|
block_data_inexistent = true;
|
|
|
|
else
|
|
|
|
block->copyTo(*this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(block_data_inexistent)
|
|
|
|
{
|
2014-06-14 11:36:46 +02:00
|
|
|
|
2017-03-13 22:35:29 +01:00
|
|
|
if (load_if_inexistent && !blockpos_over_max_limit(p)) {
|
2013-06-25 17:49:08 +02:00
|
|
|
ServerMap *svrmap = (ServerMap *)m_map;
|
|
|
|
block = svrmap->emergeBlock(p, false);
|
|
|
|
if (block == NULL)
|
|
|
|
block = svrmap->createBlock(p);
|
2015-01-14 06:43:33 +01:00
|
|
|
block->copyTo(*this);
|
2013-06-25 17:49:08 +02:00
|
|
|
} else {
|
|
|
|
flags |= VMANIP_BLOCK_DATA_INEXIST;
|
2014-06-14 11:36:46 +02:00
|
|
|
|
2013-06-25 17:49:08 +02:00
|
|
|
/*
|
|
|
|
Mark area inexistent
|
|
|
|
*/
|
|
|
|
VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
|
2014-06-09 13:29:22 +02:00
|
|
|
// Fill with VOXELFLAG_NO_DATA
|
2013-06-25 17:49:08 +02:00
|
|
|
for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++)
|
|
|
|
for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++)
|
|
|
|
{
|
|
|
|
s32 i = m_area.index(a.MinEdge.X,y,z);
|
2014-06-09 13:29:22 +02:00
|
|
|
memset(&m_flags[i], VOXELFLAG_NO_DATA, MAP_BLOCKSIZE);
|
2013-06-25 17:49:08 +02:00
|
|
|
}
|
2011-01-24 15:36:58 +01:00
|
|
|
}
|
|
|
|
}
|
2013-02-22 04:26:21 +01:00
|
|
|
/*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
|
2013-02-17 07:47:49 +01:00
|
|
|
{
|
|
|
|
// Mark that block was loaded as blank
|
|
|
|
flags |= VMANIP_BLOCK_CONTAINS_CIGNORE;
|
2013-02-22 04:26:21 +01:00
|
|
|
}*/
|
2011-01-24 15:36:58 +01:00
|
|
|
|
2012-12-20 18:19:49 +01:00
|
|
|
m_loaded_blocks[p] = flags;
|
2011-02-01 02:06:02 +01:00
|
|
|
}
|
2014-09-01 20:20:31 +02:00
|
|
|
|
|
|
|
m_is_dirty = false;
|
2011-02-01 02:06:02 +01:00
|
|
|
}
|
|
|
|
|
2015-01-05 08:42:27 +01:00
|
|
|
void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
|
|
|
|
bool overwrite_generated)
|
2011-02-01 02:06:02 +01:00
|
|
|
{
|
|
|
|
if(m_area.getExtent() == v3s16(0,0,0))
|
|
|
|
return;
|
2012-12-22 06:34:35 +01:00
|
|
|
|
2011-02-01 02:06:02 +01:00
|
|
|
/*
|
|
|
|
Copy data of all blocks
|
|
|
|
*/
|
2017-08-19 09:12:54 +02:00
|
|
|
for (auto &loaded_block : m_loaded_blocks) {
|
|
|
|
v3s16 p = loaded_block.first;
|
2011-02-01 02:06:02 +01:00
|
|
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
2017-08-19 09:12:54 +02:00
|
|
|
bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
|
2017-08-18 18:18:25 +02:00
|
|
|
if (!existed || (block == NULL) ||
|
|
|
|
(!overwrite_generated && block->isGenerated()))
|
2011-02-01 02:06:02 +01:00
|
|
|
continue;
|
|
|
|
|
2013-02-22 04:26:21 +01:00
|
|
|
block->copyFrom(*this);
|
2017-09-23 17:05:55 +02:00
|
|
|
block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
|
2012-12-20 18:19:49 +01:00
|
|
|
|
2013-02-22 04:26:21 +01:00
|
|
|
if(modified_blocks)
|
2012-12-20 18:19:49 +01:00
|
|
|
(*modified_blocks)[p] = block;
|
2011-01-24 15:36:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-11 17:11:03 +01:00
|
|
|
//END
|