From ca3629637cb20cea318b8811e717e2caab579dc0 Mon Sep 17 00:00:00 2001 From: Lars Hofhansl Date: Wed, 4 Jan 2017 11:11:55 -0800 Subject: Fixes for using std:vector in ABMHander and further perf improvements --- src/mapblock.h | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/mapblock.h b/src/mapblock.h index 5adfcf3fb..c737b4c37 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -305,8 +305,7 @@ public: inline MapNode getNodeNoEx(v3s16 p) { bool is_valid; - MapNode node = getNode(p.X, p.Y, p.Z, &is_valid); - return is_valid ? node : MapNode(CONTENT_IGNORE); + return getNode(p.X, p.Y, p.Z, &is_valid); } inline void setNode(s16 x, s16 y, s16 z, MapNode & n) @@ -341,6 +340,22 @@ public: return getNodeNoCheck(p.X, p.Y, p.Z, valid_position); } + //// + //// Non-checking, unsafe variants of the above + //// MapBlock must be loaded by another function in the same scope/function + //// Caller must ensure that this is not a dummy block (by calling isDummy()) + //// + + inline const MapNode &getNodeUnsafe(s16 x, s16 y, s16 z) + { + return data[z * zstride + y * ystride + x]; + } + + inline const MapNode &getNodeUnsafe(v3s16 &p) + { + return getNodeUnsafe(p.X, p.Y, p.Z); + } + inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode & n) { if (data == NULL) @@ -512,7 +527,6 @@ public: void serializeNetworkSpecific(std::ostream &os, u16 net_proto_version); void deSerializeNetworkSpecific(std::istream &is); - private: /* Private methods -- cgit v1.2.3 From ddcf8422a229c4965233177f6315847a6773a20c Mon Sep 17 00:00:00 2001 From: paramat Date: Tue, 27 Dec 2016 17:00:47 +0000 Subject: Map generation limit: Fix checks for block/sector over-limit Fix the maths that check if any part of a mapblock or sector is over the set map_generation_limit. Therefore avoid the loading of any over-limit blocks that were previously generated when map_generation_limit was larger. The set limit can vary for a world because it is not yet a per-world mapgen parameter, even when it is sometimes it will be changed deliberately. Therefore avoid a player being returned to world centre if they re-enter a world while being over-limit. Fix the createSector() crash caused by a mob spawning over-limit in an over-limit mapblock --- src/map.cpp | 21 ++++++++++++++++----- src/mapblock.h | 29 ++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 14 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/map.cpp b/src/map.cpp index 53657ce6b..d4115887f 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2064,15 +2064,26 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d) return sector; } #endif + /* - Do not create over-limit + Do not create over-limit. + We are checking for any nodes of the mapblocks of the sector being beyond the limit. + A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos. + + At the negative limit we are checking for + block minimum nodepos < -mapgenlimit. + At the positive limit we are checking for + block maximum nodepos > mapgenlimit. + + Block minimum nodepos = blockpos * mapblocksize. + Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. */ const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); - if(p2d.X < -map_gen_limit / MAP_BLOCKSIZE - || p2d.X > map_gen_limit / MAP_BLOCKSIZE - || p2d.Y < -map_gen_limit / MAP_BLOCKSIZE - || p2d.Y > map_gen_limit / MAP_BLOCKSIZE) + if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit + || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit + || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit + || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit) throw InvalidPositionException("createSector(): pos. over limit"); /* diff --git a/src/mapblock.h b/src/mapblock.h index c737b4c37..d46b7b880 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -656,23 +656,34 @@ inline bool objectpos_over_limit(v3f p) const static float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")) * BS; return (p.X < -map_gen_limit_bs - || p.X > map_gen_limit_bs + || p.X > map_gen_limit_bs || p.Y < -map_gen_limit_bs - || p.Y > map_gen_limit_bs + || p.Y > map_gen_limit_bs || p.Z < -map_gen_limit_bs - || p.Z > map_gen_limit_bs); + || p.Z > map_gen_limit_bs); } +/* + We are checking for any node of the mapblock being beyond the limit. + + At the negative limit we are checking for + block minimum nodepos < -mapgenlimit. + At the positive limit we are checking for + block maximum nodepos > mapgenlimit. + + Block minimum nodepos = blockpos * mapblocksize. + Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. +*/ inline bool blockpos_over_limit(v3s16 p) { const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); - return (p.X < -map_gen_limit / MAP_BLOCKSIZE - || p.X > map_gen_limit / MAP_BLOCKSIZE - || p.Y < -map_gen_limit / MAP_BLOCKSIZE - || p.Y > map_gen_limit / MAP_BLOCKSIZE - || p.Z < -map_gen_limit / MAP_BLOCKSIZE - || p.Z > map_gen_limit / MAP_BLOCKSIZE); + return (p.X * MAP_BLOCKSIZE < -map_gen_limit + || (p.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit + || p.Y * MAP_BLOCKSIZE < -map_gen_limit + || (p.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit + || p.Z * MAP_BLOCKSIZE < -map_gen_limit + || (p.Z + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit); } /* -- cgit v1.2.3 From 8c1b4f298e2812598b0350e8c86b6a0788062ed7 Mon Sep 17 00:00:00 2001 From: paramat Date: Sat, 7 Jan 2017 21:24:31 +0000 Subject: Map generation limit: Cache as 'const' not 'const static' --- src/map.cpp | 2 +- src/mapblock.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/map.cpp b/src/map.cpp index d4115887f..0659f66aa 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2078,7 +2078,7 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d) Block minimum nodepos = blockpos * mapblocksize. Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. */ - const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, + const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit diff --git a/src/mapblock.h b/src/mapblock.h index d46b7b880..f80800109 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -653,7 +653,7 @@ typedef std::vector MapBlockVect; inline bool objectpos_over_limit(v3f p) { - const static float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, + const float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")) * BS; return (p.X < -map_gen_limit_bs || p.X > map_gen_limit_bs @@ -676,7 +676,7 @@ inline bool objectpos_over_limit(v3f p) */ inline bool blockpos_over_limit(v3s16 p) { - const static u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, + const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); return (p.X * MAP_BLOCKSIZE < -map_gen_limit || (p.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit -- cgit v1.2.3 From f17c9c45dc30a388675d46418d278a4a029206e2 Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Thu, 27 Oct 2016 23:25:44 +0200 Subject: Lighting: Update lighting at block loading This commit updates mapblocks' light if necessary when they are loaded. This removes ghost lighting. --- src/clientiface.cpp | 6 -- src/map.cpp | 131 +++++++++++++++++++---------------------- src/mapblock.cpp | 21 +++---- src/mapblock.h | 62 ++++++++++++-------- src/serialization.h | 5 +- src/voxelalgorithms.cpp | 152 +++++++++++++++++++++++++++++++++++++++++++++--- src/voxelalgorithms.h | 17 +++++- 7 files changed, 273 insertions(+), 121 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 47730343c..0eb68c9c1 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -283,12 +283,6 @@ void RemoteClient::GetNextBlocks ( surely_not_found_on_disk = true; } - // Block is valid if lighting is up-to-date and data exists - if(block->isValid() == false) - { - block_is_invalid = true; - } - if(block->isGenerated() == false) block_is_invalid = true; diff --git a/src/map.cpp b/src/map.cpp index f2a4b7ffe..ffba77262 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -824,7 +824,7 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n, // Update lighting std::vector > oldnodes; oldnodes.push_back(std::pair(p, oldnode)); - voxalgo::update_lighting_nodes(this, m_nodedef, oldnodes, modified_blocks); + voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks); for(std::map::iterator i = modified_blocks.begin(); @@ -1523,7 +1523,7 @@ void Map::transformLiquids(std::map &modified_blocks) for (std::deque::iterator iter = must_reflow.begin(); iter != must_reflow.end(); ++iter) m_transforming_liquid.push_back(*iter); - voxalgo::update_lighting_nodes(this, m_nodedef, changed_nodes, modified_blocks); + voxalgo::update_lighting_nodes(this, changed_nodes, modified_blocks); /* ---------------------------------------------------------------------- @@ -1955,27 +1955,10 @@ void ServerMap::finishBlockMake(BlockMakeData *data, v3s16 bpmax = data->blockpos_max; v3s16 extra_borders(1, 1, 1); - v3s16 full_bpmin = bpmin - extra_borders; - v3s16 full_bpmax = bpmax + extra_borders; bool enable_mapgen_debug_info = m_emerge->enable_mapgen_debug_info; EMERGE_DBG_OUT("finishBlockMake(): " PP(bpmin) " - " PP(bpmax)); - /* - Set lighting to non-expired state in all of them. - This is cheating, but it is not fast enough if all of them - would actually be updated. - */ - for (s16 x = full_bpmin.X; x <= full_bpmax.X; x++) - for (s16 z = full_bpmin.Z; z <= full_bpmax.Z; z++) - for (s16 y = full_bpmin.Y; y <= full_bpmax.Y; y++) { - MapBlock *block = emergeBlock(v3s16(x, y, z), false); - if (!block) - continue; - - block->setLightingExpired(false); - } - /* Blit generated stuff to map NOTE: blitBackAll adds nearly everything to changed_blocks @@ -2991,7 +2974,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool // We just loaded it from, so it's up-to-date. block->resetModified(); - } catch(SerializationError &e) { @@ -3015,71 +2997,80 @@ MapBlock* ServerMap::loadBlock(v3s16 blockpos) { DSTACK(FUNCTION_NAME); + bool created_new = (getBlockNoCreateNoEx(blockpos) == NULL); + v2s16 p2d(blockpos.X, blockpos.Z); std::string ret; dbase->loadBlock(blockpos, &ret); if (ret != "") { loadBlock(&ret, blockpos, createSector(p2d), false); - return getBlockNoCreateNoEx(blockpos); - } - // Not found in database, try the files - - // The directory layout we're going to load from. - // 1 - original sectors/xxxxzzzz/ - // 2 - new sectors2/xxx/zzz/ - // If we load from anything but the latest structure, we will - // immediately save to the new one, and remove the old. - int loadlayout = 1; - std::string sectordir1 = getSectorDir(p2d, 1); - std::string sectordir; - if(fs::PathExists(sectordir1)) - { - sectordir = sectordir1; - } - else - { - loadlayout = 2; - sectordir = getSectorDir(p2d, 2); - } + } else { + // Not found in database, try the files + + // The directory layout we're going to load from. + // 1 - original sectors/xxxxzzzz/ + // 2 - new sectors2/xxx/zzz/ + // If we load from anything but the latest structure, we will + // immediately save to the new one, and remove the old. + int loadlayout = 1; + std::string sectordir1 = getSectorDir(p2d, 1); + std::string sectordir; + if (fs::PathExists(sectordir1)) { + sectordir = sectordir1; + } else { + loadlayout = 2; + sectordir = getSectorDir(p2d, 2); + } - /* + /* Make sure sector is loaded - */ + */ - MapSector *sector = getSectorNoGenerateNoEx(p2d); - if(sector == NULL) - { - try{ - sector = loadSectorMeta(sectordir, loadlayout != 2); - } - catch(InvalidFilenameException &e) - { - return NULL; - } - catch(FileNotGoodException &e) - { - return NULL; - } - catch(std::exception &e) - { - return NULL; + MapSector *sector = getSectorNoGenerateNoEx(p2d); + if (sector == NULL) { + try { + sector = loadSectorMeta(sectordir, loadlayout != 2); + } catch(InvalidFilenameException &e) { + return NULL; + } catch(FileNotGoodException &e) { + return NULL; + } catch(std::exception &e) { + return NULL; + } } - } - /* + + /* Make sure file exists - */ + */ - std::string blockfilename = getBlockFilename(blockpos); - if(fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false) - return NULL; + std::string blockfilename = getBlockFilename(blockpos); + if (fs::PathExists(sectordir + DIR_DELIM + blockfilename) == false) + return NULL; - /* + /* Load block and save it to the database - */ - loadBlock(sectordir, blockfilename, sector, true); - return getBlockNoCreateNoEx(blockpos); + */ + loadBlock(sectordir, blockfilename, sector, true); + } + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if (created_new && (block != NULL)) { + std::map modified_blocks; + // Fix lighting if necessary + voxalgo::update_block_border_lighting(this, block, modified_blocks); + if (!modified_blocks.empty()) { + //Modified lighting, send event + MapEditEvent event; + event.type = MEET_OTHER; + std::map::iterator it; + for (it = modified_blocks.begin(); + it != modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + dispatchEvent(&event); + } + } + return block; } bool ServerMap::deleteBlock(v3s16 blockpos) diff --git a/src/mapblock.cpp b/src/mapblock.cpp index f8c3197bc..840cb9b39 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -73,7 +73,7 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy): m_modified(MOD_STATE_WRITE_NEEDED), m_modified_reason(MOD_REASON_INITIAL), is_underground(false), - m_lighting_expired(true), + m_lighting_complete(0xFFFF), m_day_night_differs(false), m_day_night_differs_expired(true), m_generated(false), @@ -571,11 +571,12 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk) flags |= 0x01; if(getDayNightDiff()) flags |= 0x02; - if(m_lighting_expired) - flags |= 0x04; if(m_generated == false) flags |= 0x08; writeU8(os, flags); + if (version >= 27) { + writeU16(os, m_lighting_complete); + } /* Bulk node data @@ -672,7 +673,11 @@ void MapBlock::deSerialize(std::istream &is, u8 version, bool disk) u8 flags = readU8(is); is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; - m_lighting_expired = (flags & 0x04) ? true : false; + if (version < 27) { + m_lighting_complete = 0xFFFF; + } else { + m_lighting_complete = readU16(is); + } m_generated = (flags & 0x08) ? false : true; /* @@ -783,7 +788,7 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) // Initialize default flags is_underground = false; m_day_night_differs = false; - m_lighting_expired = false; + m_lighting_complete = 0xFFFF; m_generated = true; // Make a temporary buffer @@ -849,7 +854,6 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) is.read((char*)&flags, 1); is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; - m_lighting_expired = (flags & 0x04) ? true : false; if(version >= 18) m_generated = (flags & 0x08) ? false : true; @@ -1027,10 +1031,7 @@ std::string analyze_block(MapBlock *block) else desc<<"is_ug [ ], "; - if(block->getLightingExpired()) - desc<<"lighting_exp [X], "; - else - desc<<"lighting_exp [ ], "; + desc<<"lighting_complete: "<getLightingComplete()<<", "; if(block->isDummy()) { diff --git a/src/mapblock.h b/src/mapblock.h index f80800109..5a0ec937a 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -105,7 +105,7 @@ public: #define MOD_REASON_INITIAL (1 << 0) #define MOD_REASON_REALLOCATE (1 << 1) #define MOD_REASON_SET_IS_UNDERGROUND (1 << 2) -#define MOD_REASON_SET_LIGHTING_EXPIRED (1 << 3) +#define MOD_REASON_SET_LIGHTING_COMPLETE (1 << 3) #define MOD_REASON_SET_GENERATED (1 << 4) #define MOD_REASON_SET_NODE (1 << 5) #define MOD_REASON_SET_NODE_NO_CHECK (1 << 6) @@ -213,17 +213,42 @@ public: raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_IS_UNDERGROUND); } - inline void setLightingExpired(bool expired) + inline void setLightingComplete(u16 newflags) { - if (expired != m_lighting_expired){ - m_lighting_expired = expired; - raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_EXPIRED); + if (newflags != m_lighting_complete) { + m_lighting_complete = newflags; + raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE); } } - inline bool getLightingExpired() + inline u16 getLightingComplete() { - return m_lighting_expired; + return m_lighting_complete; + } + + inline void setLightingComplete(LightBank bank, u8 direction, + bool is_complete) + { + assert(direction >= 0 && direction <= 5); + if (bank == LIGHTBANK_NIGHT) { + direction += 6; + } + u16 newflags = m_lighting_complete; + if (is_complete) { + newflags |= 1 << direction; + } else { + newflags &= ~(1 << direction); + } + setLightingComplete(newflags); + } + + inline bool isLightingComplete(LightBank bank, u8 direction) + { + assert(direction >= 0 && direction <= 5); + if (bank == LIGHTBANK_NIGHT) { + direction += 6; + } + return (m_lighting_complete & (1 << direction)) != 0; } inline bool isGenerated() @@ -239,15 +264,6 @@ public: } } - inline bool isValid() - { - if (m_lighting_expired) - return false; - if (data == NULL) - return false; - return true; - } - //// //// Position stuff //// @@ -613,14 +629,14 @@ private: */ bool is_underground; - /* - Set to true if changes has been made that make the old lighting - values wrong but the lighting hasn't been actually updated. - - If this is false, lighting is exactly right. - If this is true, lighting might be wrong or right. + /*! + * Each bit indicates if light spreading was finished + * in a direction. (Because the neighbor could also be unloaded.) + * Bits: day X+, day Y+, day Z+, day Z-, day Y-, day X-, + * night X+, night Y+, night Z+, night Z-, night Y-, night X-, + * nothing, nothing, nothing, nothing. */ - bool m_lighting_expired; + u16 m_lighting_complete; // Whether day and night lighting differs bool m_day_night_differs; diff --git a/src/serialization.h b/src/serialization.h index 01d37d363..52c63098e 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -62,13 +62,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 24: 16-bit node ids and node timers (never released as stable) 25: Improved node timer format 26: Never written; read the same as 25 + 27: Added light spreading flags to blocks */ // This represents an uninitialized or invalid format #define SER_FMT_VER_INVALID 255 // Highest supported serialization version -#define SER_FMT_VER_HIGHEST_READ 26 +#define SER_FMT_VER_HIGHEST_READ 27 // Saved on disk version -#define SER_FMT_VER_HIGHEST_WRITE 25 +#define SER_FMT_VER_HIGHEST_WRITE 27 // Lowest supported serialization version #define SER_FMT_VER_LOWEST_READ 0 // Lowest serialization version for writing diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index c20917164..3c32bc125 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -423,6 +423,7 @@ void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) { neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos); if (neighbor_block == NULL) { + current.block->setLightingComplete(bank, i, false); continue; } } else { @@ -486,7 +487,8 @@ void unspread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, * \param modified_blocks output, all modified map blocks are added to this */ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, - LightQueue &light_sources, std::map &modified_blocks) + LightQueue &light_sources, + std::map &modified_blocks) { // The light the current node can provide to its neighbors. u8 spreading_light; @@ -511,6 +513,7 @@ void spread_light(Map *map, INodeDefManager *nodemgr, LightBank bank, if (step_rel_block_pos(i, neighbor_rel_pos, neighbor_block_pos)) { neighbor_block = map->getBlockNoCreateNoEx(neighbor_block_pos); if (neighbor_block == NULL) { + current.block->setLightingComplete(bank, i, false); continue; } } else { @@ -584,10 +587,11 @@ bool is_sunlight_above(Map *map, v3s16 pos, INodeDefManager *ndef) static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT }; -void update_lighting_nodes(Map *map, INodeDefManager *ndef, +void update_lighting_nodes(Map *map, std::vector > &oldnodes, std::map &modified_blocks) { + INodeDefManager *ndef = map->getNodeDefManager(); // For node getter functions bool is_valid_position; @@ -596,6 +600,22 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef, LightBank bank = banks[i]; UnlightQueue disappearing_lights(256); ReLightQueue light_sources(256); + // Nodes that are brighter than the brightest modified node was + // won't change, since they didn't get their light from a + // modified node. + u8 min_safe_light = 0; + for (std::vector >::iterator it = + oldnodes.begin(); it < oldnodes.end(); ++it) { + u8 old_light = it->second.getLight(bank, ndef); + if (old_light > min_safe_light) { + min_safe_light = old_light; + } + } + // If only one node changed, even nodes with the same brightness + // didn't get their light from the changed node. + if (oldnodes.size() > 1) { + min_safe_light++; + } // For each changed node process sunlight and initialize for (std::vector >::iterator it = oldnodes.begin(); it < oldnodes.end(); ++it) { @@ -634,11 +654,9 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef, MapNode n2 = map->getNodeNoEx(p2, &is_valid); if (is_valid) { u8 spread = n2.getLight(bank, ndef); - // If the neighbor is at least as bright as - // this node then its light is not from - // this node. - // Its light can spread to this node. - if (spread > new_light && spread >= old_light) { + // If it is sure that the neighbor won't be + // unlighted, its light can spread to this node. + if (spread > new_light && spread >= min_safe_light) { new_light = spread - 1; } } @@ -747,6 +765,126 @@ void update_lighting_nodes(Map *map, INodeDefManager *ndef, } } +/*! + * Borders of a map block in relative node coordinates. + * Compatible with type 'direction'. + */ +const VoxelArea block_borders[] = { + VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+ + VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+ + VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+ + VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z- + VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y- + VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X- +}; + +/*! + * Returns true if: + * -the node has unloaded neighbors + * -the node doesn't have light + * -the node's light is the same as the maximum of + * its light source and its brightest neighbor minus one. + * . + */ +bool is_light_locally_correct(Map *map, INodeDefManager *ndef, LightBank bank, + v3s16 pos) +{ + bool is_valid_position; + MapNode n = map->getNodeNoEx(pos, &is_valid_position); + const ContentFeatures &f = ndef->get(n); + if (f.param_type != CPT_LIGHT) { + return true; + } + u8 light = n.getLightNoChecks(bank, &f); + assert(f.light_source <= LIGHT_MAX); + u8 brightest_neighbor = f.light_source + 1; + for (direction d = 0; d < 6; ++d) { + MapNode n2 = map->getNodeNoEx(pos + neighbor_dirs[d], + &is_valid_position); + u8 light2 = n2.getLight(bank, ndef); + if (brightest_neighbor < light2) { + brightest_neighbor = light2; + } + } + assert(light <= LIGHT_SUN); + return brightest_neighbor == light + 1; +} + +void update_block_border_lighting(Map *map, MapBlock *block, + std::map &modified_blocks) +{ + INodeDefManager *ndef = map->getNodeDefManager(); + bool is_valid_position; + for (s32 i = 0; i < 2; i++) { + LightBank bank = banks[i]; + UnlightQueue disappearing_lights(256); + ReLightQueue light_sources(256); + // Get incorrect lights + for (direction d = 0; d < 6; d++) { + // For each direction + // Get neighbor block + v3s16 otherpos = block->getPos() + neighbor_dirs[d]; + MapBlock *other = map->getBlockNoCreateNoEx(otherpos); + if (other == NULL) { + continue; + } + // Only update if lighting was not completed. + if (block->isLightingComplete(bank, d) && + other->isLightingComplete(bank, 5 - d)) + continue; + // Reset flags + block->setLightingComplete(bank, d, true); + other->setLightingComplete(bank, 5 - d, true); + // The two blocks and their connecting surfaces + MapBlock *blocks[] = {block, other}; + VoxelArea areas[] = {block_borders[d], block_borders[5 - d]}; + // For both blocks + for (u8 blocknum = 0; blocknum < 2; blocknum++) { + MapBlock *b = blocks[blocknum]; + VoxelArea a = areas[blocknum]; + // For all nodes + for (s32 x = a.MinEdge.X; x <= a.MaxEdge.X; x++) + for (s32 z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) + for (s32 y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { + MapNode n = b->getNodeNoCheck(x, y, z, + &is_valid_position); + u8 light = n.getLight(bank, ndef); + // Sunlight is fixed + if (light < LIGHT_SUN) { + // Unlight if not correct + if (!is_light_locally_correct(map, ndef, bank, + v3s16(x, y, z) + b->getPosRelative())) { + // Initialize for unlighting + n.setLight(bank, 0, ndef); + b->setNodeNoCheck(x, y, z, n); + modified_blocks[b->getPos()]=b; + disappearing_lights.push(light, + relative_v3(x, y, z), b->getPos(), b, + 6); + } + } + } + } + } + // Remove lights + unspread_light(map, ndef, bank, disappearing_lights, light_sources, + modified_blocks); + // Initialize light values for light spreading. + for (u8 i = 0; i <= LIGHT_SUN; i++) { + const std::vector &lights = light_sources.lights[i]; + for (std::vector::const_iterator it = lights.begin(); + it < lights.end(); it++) { + MapNode n = it->block->getNodeNoCheck(it->rel_position, + &is_valid_position); + n.setLight(bank, i, ndef); + it->block->setNodeNoCheck(it->rel_position, n); + } + } + // Spread lights. + spread_light(map, ndef, bank, light_sources, modified_blocks); + } +} + VoxelLineIterator::VoxelLineIterator( const v3f &start_position, const v3f &line_vector) : diff --git a/src/voxelalgorithms.h b/src/voxelalgorithms.h index 5eff8f7ac..bf1638fa3 100644 --- a/src/voxelalgorithms.h +++ b/src/voxelalgorithms.h @@ -22,8 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "mapnode.h" -#include -#include +#include "util/container.h" +#include "util/cpp11_container.h" class Map; class MapBlock; @@ -69,10 +69,21 @@ SunlightPropagateResult propagateSunlight(VoxelManipulator &v, VoxelArea a, */ void update_lighting_nodes( Map *map, - INodeDefManager *ndef, std::vector > &oldnodes, std::map &modified_blocks); +/*! + * Updates borders of the given mapblock. + * Only updates if the block was marked with incomplete + * lighting and the neighbor is also loaded. + * + * \param block the block to update + * \param modified_blocks output, contains all map blocks that + * the function modified + */ +void update_block_border_lighting(Map *map, MapBlock *block, + std::map &modified_blocks); + /*! * This class iterates trough voxels that intersect with * a line. The collision detection does not see nodeboxes, -- cgit v1.2.3 From 3955f512538ca7c6f2d2187f22d5a696da8e84d3 Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 13 Feb 2017 04:37:25 +0000 Subject: Objectpos over limit: Avoid crash caused by sector over limit Reduce the object limit by mapblock size, to avoid objects being added just inside the map generation limit but in a block and sector that extend beyond the map generation limit. Change notification of 'objectpos over limit' from red in-chat ERROR to in-terminal only WARNING, since this will happen often using mob mods near the world's edge. --- src/mapblock.h | 20 ++++++++++++-------- src/serverenvironment.cpp | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/mapblock.h b/src/mapblock.h index 5a0ec937a..bec3b6f56 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -669,14 +669,18 @@ typedef std::vector MapBlockVect; inline bool objectpos_over_limit(v3f p) { - const float map_gen_limit_bs = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")) * BS; - return (p.X < -map_gen_limit_bs - || p.X > map_gen_limit_bs - || p.Y < -map_gen_limit_bs - || p.Y > map_gen_limit_bs - || p.Z < -map_gen_limit_bs - || p.Z > map_gen_limit_bs); + // MAP_BLOCKSIZE must be subtracted to avoid an object being spawned just + // within the map generation limit but in a block and sector that extend + // beyond the map generation limit. + // This avoids crashes caused by sector over limit in createSector(). + const float object_limit = (MYMIN(MAX_MAP_GENERATION_LIMIT, + g_settings->getU16("map_generation_limit")) - MAP_BLOCKSIZE) * BS; + return (p.X < -object_limit + || p.X > object_limit + || p.Y < -object_limit + || p.Y > object_limit + || p.Z < -object_limit + || p.Z > object_limit); } /* diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 61faaace7..f3f489092 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1667,7 +1667,7 @@ u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object, if (objectpos_over_limit(object->getBasePosition())) { v3f p = object->getBasePosition(); - errorstream << "ServerEnvironment::addActiveObjectRaw(): " + warningstream << "ServerEnvironment::addActiveObjectRaw(): " << "object position (" << p.X << "," << p.Y << "," << p.Z << ") outside maximum range" << std::endl; if (object->environmentDeletes()) -- cgit v1.2.3 From f8ad01ab7c4cf012781bd4caa821544e676c9267 Mon Sep 17 00:00:00 2001 From: Loïc Blot Date: Sun, 19 Mar 2017 08:44:29 +0100 Subject: Update server min protocol version to v24 (#5411) * Update server min protocol version to v24 It's based on @sfan5 stats. See https://kitsunemimi.pw/tmp/serverlist_stats_2017-03-17.txt v24 was bumped 25/08/14 and 0.4.11 was released 25/12/14 * Drop protocol v23 and lesser code --- src/content_sao.cpp | 166 +++++++++++++++--------------------- src/itemdef.cpp | 35 +++----- src/map.cpp | 7 -- src/mapblock.cpp | 14 ++- src/mapblock.h | 8 +- src/network/networkprotocol.h | 4 +- src/network/serverpackethandler.cpp | 21 ----- src/nodedef.cpp | 123 +++----------------------- src/server.cpp | 10 +-- src/tool.cpp | 27 +++--- 10 files changed, 116 insertions(+), 299 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/content_sao.cpp b/src/content_sao.cpp index 93662b035..69f80d356 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -470,57 +470,43 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version) { std::ostringstream os(std::ios::binary); - if(protocol_version >= 14) - { - writeU8(os, 1); // version - os< >::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 - int message_count = 4 + m_bone_position.size(); - for (UNORDERED_SET::const_iterator ii = m_attachment_child_ids.begin(); - (ii != m_attachment_child_ids.end()); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), - obj->getClientInitializationData(protocol_version))); - } - } - - msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier)); - message_count++; + // protocol >= 14 + writeU8(os, 1); // version + os << serializeString(""); // name + writeU8(os, 0); // is_player + writeS16(os, getId()); //id + writeV3F1000(os, m_base_position); + writeF1000(os, m_yaw); + writeS16(os, m_hp); - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 + msg_os << serializeLongString(gob_cmd_update_animation( + m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 + for (UNORDERED_MAP >::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } - else - { - writeU8(os, 0); // version - os<::const_iterator ii = m_attachment_child_ids.begin(); + (ii != m_attachment_child_ids.end()); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), + obj->getClientInitializationData(protocol_version))); + } } + msg_os << serializeLongString(gob_cmd_set_texture_mod(m_current_texture_modifier)); + message_count++; + + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + // return result return os.str(); } @@ -877,59 +863,45 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version) { std::ostringstream os(std::ios::binary); - if(protocol_version >= 15) - { - writeU8(os, 1); // version - os<getName()); // name - writeU8(os, 1); // is_player - writeS16(os, getId()); //id - writeV3F1000(os, m_base_position + v3f(0,BS*1,0)); - writeF1000(os, m_yaw); - writeS16(os, getHP()); - - std::ostringstream msg_os(std::ios::binary); - msg_os << serializeLongString(getPropertyPacket()); // message 1 - msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 - msg_os << serializeLongString(gob_cmd_update_animation( - m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 - for (UNORDERED_MAP >::const_iterator - ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { - msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, - (*ii).second.X, (*ii).second.Y)); // m_bone_position.size - } - msg_os << serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, - m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4 - msg_os << serializeLongString(gob_cmd_update_physics_override(m_physics_override_speed, - m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak, - m_physics_override_sneak_glitch)); // 5 - // (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only. - msg_os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 - int message_count = 6 + m_bone_position.size(); - for (UNORDERED_SET::const_iterator ii = m_attachment_child_ids.begin(); - ii != m_attachment_child_ids.end(); ++ii) { - if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { - message_count++; - msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), - obj->getClientInitializationData(protocol_version))); - } - } - - writeU8(os, message_count); - os.write(msg_os.str().c_str(), msg_os.str().size()); + // Protocol >= 15 + writeU8(os, 1); // version + os << serializeString(m_player->getName()); // name + writeU8(os, 1); // is_player + writeS16(os, getId()); //id + writeV3F1000(os, m_base_position + v3f(0,BS*1,0)); + writeF1000(os, m_yaw); + writeS16(os, getHP()); + + std::ostringstream msg_os(std::ios::binary); + msg_os << serializeLongString(getPropertyPacket()); // message 1 + msg_os << serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2 + msg_os << serializeLongString(gob_cmd_update_animation( + m_animation_range, m_animation_speed, m_animation_blend, m_animation_loop)); // 3 + for (UNORDERED_MAP >::const_iterator + ii = m_bone_position.begin(); ii != m_bone_position.end(); ++ii) { + msg_os << serializeLongString(gob_cmd_update_bone_position((*ii).first, + (*ii).second.X, (*ii).second.Y)); // m_bone_position.size } - else - { - writeU8(os, 0); // version - os<getName()); // name - writeU8(os, 1); // is_player - writeV3F1000(os, m_base_position + v3f(0,BS*1,0)); - writeF1000(os, m_yaw); - writeS16(os, getHP()); - writeU8(os, 2); // number of messages stuffed in here - os<::const_iterator ii = m_attachment_child_ids.begin(); + ii != m_attachment_child_ids.end(); ++ii) { + if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) { + message_count++; + msg_os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), + obj->getClientInitializationData(protocol_version))); + } } + writeU8(os, message_count); + os.write(msg_os.str().c_str(), msg_os.str().size()); + // return result return os.str(); } diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 5ba9d8f9a..f43e5c970 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -123,17 +123,13 @@ void ItemDefinition::reset() void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version <= 17) - writeU8(os, 1); // version - else if(protocol_version <= 20) - writeU8(os, 2); // version - else - writeU8(os, 3); // version + + writeU8(os, 3); // version (proto > 20) writeU8(os, type); - os<serialize(tmp_os, protocol_version); tool_capabilities_s = tmp_os.str(); } - os<first); writeS16(os, i->second); } - os< 17){ - //serializeSimpleSoundSpec(sound_place, os); - os< 20) { - writeF1000(os, range); - os << serializeString(sound_place_failed.name); - writeF1000(os, sound_place_failed.gain); - } + os << serializeString(node_placement_prediction); + os << serializeString(sound_place.name); + writeF1000(os, sound_place.gain); + writeF1000(os, range); + os << serializeString(sound_place_failed.name); + writeF1000(os, sound_place_failed.gain); } void ItemDefinition::deSerialize(std::istream &is) diff --git a/src/map.cpp b/src/map.cpp index 43a49dc2f..689112c7d 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -2438,13 +2438,6 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool throw SerializationError("ServerMap::loadBlock(): Failed" " to read MapBlock version"); - /*u32 block_size = MapBlock::serializedLength(version); - SharedBuffer data(block_size); - is.read((char*)*data, block_size);*/ - - // This will always return a sector because we're the server - //MapSector *sector = emergeSector(p2d); - MapBlock *block = NULL; bool created_new = false; block = sector->getBlockNoCreateNoEx(p3d.Y); diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 840cb9b39..1a0b01f2b 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -640,19 +640,15 @@ void MapBlock::serialize(std::ostream &os, u8 version, bool disk) } } -void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version) +void MapBlock::serializeNetworkSpecific(std::ostream &os) { - if(data == NULL) - { + if (!data) { throw SerializationError("ERROR: Not writing dummy block."); } - if(net_proto_version >= 21){ - int version = 1; - writeU8(os, version); - writeF1000(os, 0); // deprecated heat - writeF1000(os, 0); // deprecated humidity - } + writeU8(os, 1); // version + writeF1000(os, 0); // deprecated heat + writeF1000(os, 0); // deprecated humidity } void MapBlock::deSerialize(std::istream &is, u8 version, bool disk) diff --git a/src/mapblock.h b/src/mapblock.h index bec3b6f56..8a7dfc11d 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -541,7 +541,7 @@ public: // unknown blocks from id-name mapping to wndef void deSerialize(std::istream &is, u8 version, bool disk); - void serializeNetworkSpecific(std::ostream &os, u16 net_proto_version); + void serializeNetworkSpecific(std::ostream &os); void deSerializeNetworkSpecific(std::istream &is); private: /* @@ -698,11 +698,11 @@ inline bool blockpos_over_limit(v3s16 p) { const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, g_settings->getU16("map_generation_limit")); - return (p.X * MAP_BLOCKSIZE < -map_gen_limit + return (p.X * MAP_BLOCKSIZE < -map_gen_limit || (p.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit - || p.Y * MAP_BLOCKSIZE < -map_gen_limit + || p.Y * MAP_BLOCKSIZE < -map_gen_limit || (p.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit - || p.Z * MAP_BLOCKSIZE < -map_gen_limit + || p.Z * MAP_BLOCKSIZE < -map_gen_limit || (p.Z + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit); } diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 5301cc91c..ea532d9e0 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -153,14 +153,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #define LATEST_PROTOCOL_VERSION 30 // Server's supported network protocol range -#define SERVER_PROTOCOL_VERSION_MIN 13 +#define SERVER_PROTOCOL_VERSION_MIN 24 #define SERVER_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION // Client's supported network protocol range // The minimal version depends on whether // send_pre_v25_init is enabled or not #define CLIENT_PROTOCOL_VERSION_MIN 25 -#define CLIENT_PROTOCOL_VERSION_MIN_LEGACY 13 +#define CLIENT_PROTOCOL_VERSION_MIN_LEGACY 24 #define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION // Constant that differentiates the protocol from random data and other protocols diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index b707c6fad..e0ea4bf83 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -614,20 +614,6 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) u16 protocol_version = m_clients.getProtocolVersion(pkt->getPeerId()); - ///// begin compatibility code - PlayerSAO* playersao = NULL; - if (protocol_version <= 22) { - playersao = StageTwoClientInit(pkt->getPeerId()); - - if (playersao == NULL) { - actionstream - << "TOSERVER_INIT2 stage 2 client init failed for peer " - << pkt->getPeerId() << std::endl; - return; - } - } - ///// end compatibility code - /* Send some initialization data */ @@ -657,13 +643,6 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) float time_speed = g_settings->getFloat("time_speed"); SendTimeOfDay(pkt->getPeerId(), time, time_speed); - ///// begin compatibility code - if (protocol_version <= 22) { - m_clients.event(pkt->getPeerId(), CSE_SetClientReady); - m_script->on_joinplayer(playersao); - } - ///// end compatibility code - // Warnings about protocol version can be issued here if (getClient(pkt->getPeerId())->net_proto_version < LATEST_PROTOCOL_VERSION) { SendChatMessage(pkt->getPeerId(), L"# Server: WARNING: YOUR CLIENT'S " diff --git a/src/nodedef.cpp b/src/nodedef.cpp index c717b62b9..3532eea1e 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -61,11 +61,10 @@ void NodeBox::reset() void NodeBox::serialize(std::ostream &os, u16 protocol_version) const { - int version = 1; + // Protocol >= 21 + int version = 2; if (protocol_version >= 27) version = 3; - else if (protocol_version >= 21) - version = 2; writeU8(os, version); switch (type) { @@ -195,14 +194,12 @@ void TileDef::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, 3); else if (protocol_version >= 26) writeU8(os, 2); - else if (protocol_version >= 17) - writeU8(os, 1); else - writeU8(os, 0); - os<= 17) - writeU8(os, backface_culling); + writeU8(os, backface_culling); if (protocol_version >= 26) { writeU8(os, tileable_horizontal); writeU8(os, tileable_vertical); @@ -1615,109 +1612,8 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) compatible_visual_scale = sqrt(visual_scale); - if (protocol_version == 13) - { - writeU8(os, 5); // version - os<first); - writeS16(os, i->second); - } - writeU8(os, drawtype); - writeF1000(os, compatible_visual_scale); - writeU8(os, 6); - for (u32 i = 0; i < 6; i++) - tiledef[i].serialize(os, protocol_version); - //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24 - writeU8(os, 2); - for (u32 i = 0; i < 2; i++) - tiledef_special[i].serialize(os, protocol_version); - writeU8(os, alpha); - writeU8(os, post_effect_color.getAlpha()); - writeU8(os, post_effect_color.getRed()); - writeU8(os, post_effect_color.getGreen()); - writeU8(os, post_effect_color.getBlue()); - writeU8(os, param_type); - writeU8(os, compatible_param_type_2); - writeU8(os, is_ground_content); - writeU8(os, light_propagates); - writeU8(os, sunlight_propagates); - writeU8(os, walkable); - writeU8(os, pointable); - writeU8(os, diggable); - writeU8(os, climbable); - writeU8(os, buildable_to); - os< 13 && protocol_version < 24) { - writeU8(os, 6); // version - os<first); - writeS16(os, i->second); - } - writeU8(os, drawtype); - writeF1000(os, compatible_visual_scale); - writeU8(os, 6); - for (u32 i = 0; i < 6; i++) - tiledef[i].serialize(os, protocol_version); - //CF_SPECIAL_COUNT = 2 before cf ver. 7 and protocol ver. 24 - writeU8(os, 2); - for (u32 i = 0; i < 2; i++) - tiledef_special[i].serialize(os, protocol_version); - writeU8(os, alpha); - writeU8(os, post_effect_color.getAlpha()); - writeU8(os, post_effect_color.getRed()); - writeU8(os, post_effect_color.getGreen()); - writeU8(os, post_effect_color.getBlue()); - writeU8(os, param_type); - writeU8(os, compatible_param_type_2); - writeU8(os, is_ground_content); - writeU8(os, light_propagates); - writeU8(os, sunlight_propagates); - writeU8(os, walkable); - writeU8(os, pointable); - writeU8(os, diggable); - writeU8(os, climbable); - writeU8(os, buildable_to); - os<= 24 && protocol_version < 30) { + // Protocol >= 24 + if (protocol_version < 30) { writeU8(os, protocol_version < 27 ? 7 : 8); os << serializeString(name); @@ -1778,9 +1674,10 @@ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const i != connects_to_ids.end(); ++i) writeU16(os, *i); writeU8(os, connect_sides); - } else + } else { throw SerializationError("ContentFeatures::serialize(): " "Unsupported version requested"); + } } void ContentFeatures::deSerializeOld(std::istream &is, int version) diff --git a/src/server.cpp b/src/server.cpp index 9ed2a045d..8e9313464 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2147,14 +2147,6 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, if (client != 0) { pkt << p << n.param0 << n.param1 << n.param2 << (u8) (remove_metadata ? 0 : 1); - - if (!remove_metadata) { - if (client->net_proto_version <= 21) { - // Old clients always clear metadata; fix it - // by sending the full block again. - client->SetBlockNotSent(getNodeBlockPos(p)); - } - } } m_clients.unlock(); @@ -2188,7 +2180,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto std::ostringstream os(std::ios_base::binary); block->serialize(os, ver, false); - block->serializeNetworkSpecific(os, net_proto_version); + block->serializeNetworkSpecific(os); std::string s = os.str(); NetworkPacket pkt(TOCLIENT_BLOCKDATA, 2 + 2 + 2 + 2 + s.size(), peer_id); diff --git a/src/tool.cpp b/src/tool.cpp index 20b71fb31..1877a1cf8 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -27,33 +27,30 @@ with this program; if not, write to the Free Software Foundation, Inc., void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const { - if(protocol_version <= 17) - writeU8(os, 1); // version - else - writeU8(os, 2); // version + writeU8(os, 2); // version (protocol >= 18) writeF1000(os, full_punch_interval); writeS16(os, max_drop_level); writeU32(os, groupcaps.size()); for (ToolGCMap::const_iterator i = groupcaps.begin(); i != groupcaps.end(); ++i) { const std::string *name = &i->first; const ToolGroupCap *cap = &i->second; - os<uses); writeS16(os, cap->maxlevel); writeU32(os, cap->times.size()); for (UNORDERED_MAP::const_iterator - i = cap->times.begin(); i != cap->times.end(); ++i) { - writeS16(os, i->first); - writeF1000(os, i->second); + j = cap->times.begin(); j != cap->times.end(); ++j) { + writeS16(os, j->first); + writeF1000(os, j->second); } } - if(protocol_version > 17){ - writeU32(os, damageGroups.size()); - for (DamageGroup::const_iterator i = damageGroups.begin(); - i != damageGroups.end(); ++i) { - os<first); - writeS16(os, i->second); - } + + writeU32(os, damageGroups.size()); + + for (DamageGroup::const_iterator i = damageGroups.begin(); + i != damageGroups.end(); ++i) { + os << serializeString(i->first); + writeS16(os, i->second); } } -- cgit v1.2.3 From d3131aeae79141961efdeff38446e73d027f13ff Mon Sep 17 00:00:00 2001 From: paramat Date: Mon, 13 Mar 2017 21:35:29 +0000 Subject: Map generation limit: Rewrite The previous implementation applied the setting to blockpos_over_limit(), objectpos_over_limit() and in createSector(), causing many bugs near the world edge. First revert the previous implementation. Rename blockpos_over_limit() to blockpos_over_max_limit() for clarity. Add a new function to mapblock.h called blockpos_over_mapgen_limit() that checks against the map_generation_limit setting, and call this only from the code that decides where mapgen stops. Use MAX_MAP_GENERATION_LIMIT in objectpos_over_limit() to reduce the chance of bugs, there is no need to use map_generation_limit here. --- src/clientiface.cpp | 4 ++-- src/emerge.cpp | 2 +- src/map.cpp | 39 +++++++++++++---------------------- src/mapblock.h | 58 +++++++++++++++++++++++++---------------------------- 4 files changed, 44 insertions(+), 59 deletions(-) (limited to 'src/mapblock.h') diff --git a/src/clientiface.cpp b/src/clientiface.cpp index 11054969c..39223d3eb 100644 --- a/src/clientiface.cpp +++ b/src/clientiface.cpp @@ -238,9 +238,9 @@ void RemoteClient::GetNextBlocks ( continue; /* - Do not go over-limit + Do not go over max mapgen limit */ - if (blockpos_over_limit(p)) + if (blockpos_over_max_limit(p)) continue; // If this is true, inexistent block will be made from scratch diff --git a/src/emerge.cpp b/src/emerge.cpp index a3efb09e7..4c3a83f7e 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -606,7 +606,7 @@ void *EmergeThread::run() continue; } - if (blockpos_over_limit(pos)) + if (blockpos_over_max_limit(pos)) continue; bool allow_gen = bedata.flags & BLOCK_EMERGE_ALLOW_GEN; diff --git a/src/map.cpp b/src/map.cpp index 689112c7d..504760d09 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1380,9 +1380,9 @@ bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) v3s16 full_bpmin = bpmin - extra_borders; v3s16 full_bpmax = bpmax + extra_borders; - // Do nothing if not inside limits (+-1 because of neighbors) - if (blockpos_over_limit(full_bpmin) || - blockpos_over_limit(full_bpmax)) + // Do nothing if not inside mapgen limits (+-1 because of neighbors) + if (blockpos_over_mapgen_limit(full_bpmin) || + blockpos_over_mapgen_limit(full_bpmax)) return false; data->seed = getSeed(); @@ -1549,25 +1549,14 @@ ServerMapSector *ServerMap::createSector(v2s16 p2d) #endif /* - Do not create over-limit. - We are checking for any nodes of the mapblocks of the sector being beyond the limit. - A sector is a vertical column of mapblocks, so sectorpos is like a 2D blockpos. - - At the negative limit we are checking for - block minimum nodepos < -mapgenlimit. - At the positive limit we are checking for - block maximum nodepos > mapgenlimit. - - Block minimum nodepos = blockpos * mapblocksize. - Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. + Do not create over max mapgen limit */ - const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")); - if (p2d.X * MAP_BLOCKSIZE < -map_gen_limit - || (p2d.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit - || p2d.Y * MAP_BLOCKSIZE < -map_gen_limit - || (p2d.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit) - throw InvalidPositionException("createSector(): pos. over limit"); + 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"); /* Generate blank sector @@ -1708,10 +1697,10 @@ MapBlock * ServerMap::createBlock(v3s16 p) FUNCTION_NAME, p.X, p.Y, p.Z); /* - Do not create over-limit + Do not create over max mapgen limit */ - if (blockpos_over_limit(p)) - throw InvalidPositionException("createBlock(): pos. over limit"); + if (blockpos_over_max_limit(p)) + throw InvalidPositionException("createBlock(): pos. over max mapgen limit"); v2s16 p2d(p.X, p.Z); s16 block_y = p.Y; @@ -2655,7 +2644,7 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, if(block_data_inexistent) { - if (load_if_inexistent && !blockpos_over_limit(p)) { + if (load_if_inexistent && !blockpos_over_max_limit(p)) { ServerMap *svrmap = (ServerMap *)m_map; block = svrmap->emergeBlock(p, false); if (block == NULL) diff --git a/src/mapblock.h b/src/mapblock.h index 8a7dfc11d..be2edc791 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -669,41 +669,37 @@ typedef std::vector MapBlockVect; inline bool objectpos_over_limit(v3f p) { - // MAP_BLOCKSIZE must be subtracted to avoid an object being spawned just - // within the map generation limit but in a block and sector that extend - // beyond the map generation limit. - // This avoids crashes caused by sector over limit in createSector(). - const float object_limit = (MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")) - MAP_BLOCKSIZE) * BS; - return (p.X < -object_limit - || p.X > object_limit - || p.Y < -object_limit - || p.Y > object_limit - || p.Z < -object_limit - || p.Z > object_limit); + const float max_limit_bs = MAX_MAP_GENERATION_LIMIT * BS; + return p.X < -max_limit_bs || + p.X > max_limit_bs || + p.Y < -max_limit_bs || + p.Y > max_limit_bs || + p.Z < -max_limit_bs || + p.Z > max_limit_bs; } -/* - We are checking for any node of the mapblock being beyond the limit. - - At the negative limit we are checking for - block minimum nodepos < -mapgenlimit. - At the positive limit we are checking for - block maximum nodepos > mapgenlimit. +inline bool blockpos_over_max_limit(v3s16 p) +{ + const s16 max_limit_bp = MAX_MAP_GENERATION_LIMIT / MAP_BLOCKSIZE; + return p.X < -max_limit_bp || + p.X > max_limit_bp || + p.Y < -max_limit_bp || + p.Y > max_limit_bp || + p.Z < -max_limit_bp || + p.Z > max_limit_bp; +} - Block minimum nodepos = blockpos * mapblocksize. - Block maximum nodepos = (blockpos + 1) * mapblocksize - 1. -*/ -inline bool blockpos_over_limit(v3s16 p) +inline bool blockpos_over_mapgen_limit(v3s16 p) { - const u16 map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")); - return (p.X * MAP_BLOCKSIZE < -map_gen_limit - || (p.X + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit - || p.Y * MAP_BLOCKSIZE < -map_gen_limit - || (p.Y + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit - || p.Z * MAP_BLOCKSIZE < -map_gen_limit - || (p.Z + 1) * MAP_BLOCKSIZE - 1 > map_gen_limit); + const s16 mapgen_limit_bp = rangelim( + g_settings->getS16("map_generation_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; } /* -- cgit v1.2.3 From ec0c4d33db9e0b7b3e541757e34c04c08c3b48c9 Mon Sep 17 00:00:00 2001 From: paramat Date: Thu, 23 Mar 2017 00:18:59 +0000 Subject: Map generation limit: Make per-world The setting limits map generation but affects nothing else. Add 'mapgen_limit' to global mapgen parameters. Move 'blockpos_over_mapgen_limit()' to the only place it is called from: map.cpp. Allow teleportation to any part of the world even if over the set mapgen limit. Simplify the reading of this limit in mgvalleys. Remove the 'map_generation_limit' setting. --- builtin/game/chatcommands.lua | 2 +- builtin/settingtypes.txt | 11 ++++------- minetest.conf.example | 11 ++++------- src/defaultsettings.cpp | 2 +- src/map.cpp | 13 +++++++++++++ src/map.h | 1 + src/mapblock.h | 14 +------------- src/mapgen.cpp | 24 ++++++++++++++---------- src/mapgen.h | 3 +++ src/mapgen_valleys.cpp | 9 +++------ src/mapgen_valleys.h | 2 -- 11 files changed, 45 insertions(+), 47 deletions(-) (limited to 'src/mapblock.h') diff --git a/builtin/game/chatcommands.lua b/builtin/game/chatcommands.lua index b4fa4f828..16f5f3be9 100644 --- a/builtin/game/chatcommands.lua +++ b/builtin/game/chatcommands.lua @@ -303,7 +303,7 @@ core.register_chatcommand("teleport", { p.y = tonumber(p.y) p.z = tonumber(p.z) if p.x and p.y and p.z then - local lm = tonumber(minetest.setting_get("map_generation_limit") or 31000) + local lm = 31000 if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then return false, "Cannot teleport out of map bounds!" end diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index e63697f61..c9e0f7b87 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -893,13 +893,10 @@ water_level (Water level) int 1 # From how far blocks are generated for clients, stated in mapblocks (16 nodes). max_block_generate_distance (Max block generate distance) int 6 -# Where the map generator stops. -# Please note: -# - Limited to 31000 (setting above has no effect) -# - The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks). -# - Those groups have an offset of -32, -32 nodes from the origin. -# - Only groups which are within the map_generation_limit are generated -map_generation_limit (Map generation limit) int 31000 0 31000 +# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). +# Only mapchunks completely within the mapgen limit are generated. +# Value is stored per-world. +mapgen_limit (Map generation limit) int 31000 0 31000 # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees diff --git a/minetest.conf.example b/minetest.conf.example index 78488432f..a45a9948a 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -1092,14 +1092,11 @@ server_side_occlusion_culling = true # type: int # max_block_generate_distance = 6 -# Where the map generator stops. -# Please note: -# - Limited to 31000 (setting above has no effect) -# - The map generator works in groups of 80x80x80 nodes (5x5x5 MapBlocks). -# - Those groups have an offset of -32, -32 nodes from the origin. -# - Only groups which are within the map_generation_limit are generated +# Limit of map generation, in nodes, in all 6 directions from (0, 0, 0). +# Only mapchunks completely within the mapgen limit are generated. +# Value is stored per-world. # type: int min: 0 max: 31000 -# map_generation_limit = 31000 +# mapgen_limit = 31000 # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 5b66c583a..4b50b991b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -333,10 +333,10 @@ void set_default_settings(Settings *settings) // Mapgen settings->setDefault("mg_name", "v7"); settings->setDefault("water_level", "1"); + settings->setDefault("mapgen_limit", "31000"); settings->setDefault("chunksize", "5"); settings->setDefault("mg_flags", "dungeons"); settings->setDefault("fixed_map_seed", ""); - settings->setDefault("map_generation_limit", "31000"); settings->setDefault("max_block_generate_distance", "7"); settings->setDefault("enable_mapgen_debug_info", "false"); diff --git a/src/map.cpp b/src/map.cpp index 504760d09..b690ea3b3 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1367,6 +1367,19 @@ s16 ServerMap::getWaterLevel() return getMapgenParams()->water_level; } +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; +} + bool ServerMap::initBlockMake(v3s16 blockpos, BlockMakeData *data) { s16 csize = getMapgenParams()->chunksize; diff --git a/src/map.h b/src/map.h index aeb05c704..ea8dc76d1 100644 --- a/src/map.h +++ b/src/map.h @@ -379,6 +379,7 @@ public: /* Blocks are generated by using these and makeBlock(). */ + bool blockpos_over_mapgen_limit(v3s16 p); bool initBlockMake(v3s16 blockpos, BlockMakeData *data); void finishBlockMake(BlockMakeData *data, std::map *changed_blocks); diff --git a/src/mapblock.h b/src/mapblock.h index be2edc791..c48f337e0 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "modifiedstate.h" #include "util/numeric.h" // getContainerPos #include "settings.h" +#include "mapgen.h" class Map; class NodeMetadataList; @@ -689,19 +690,6 @@ inline bool blockpos_over_max_limit(v3s16 p) p.Z > max_limit_bp; } -inline bool blockpos_over_mapgen_limit(v3s16 p) -{ - const s16 mapgen_limit_bp = rangelim( - g_settings->getS16("map_generation_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; -} - /* Returns the position of the block where the node is located */ diff --git a/src/mapgen.cpp b/src/mapgen.cpp index a0b9990b7..6f3ea7cb0 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -97,11 +97,12 @@ STATIC_ASSERT( Mapgen::Mapgen() { - generating = false; - id = -1; - seed = 0; - water_level = 0; - flags = 0; + generating = false; + id = -1; + seed = 0; + water_level = 0; + mapgen_limit = 0; + flags = 0; vm = NULL; ndef = NULL; @@ -114,11 +115,12 @@ Mapgen::Mapgen() Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) : gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids) { - generating = false; - id = mapgenid; - water_level = params->water_level; - flags = params->flags; - csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); + generating = false; + id = mapgenid; + water_level = params->water_level; + mapgen_limit = params->mapgen_limit; + flags = params->flags; + csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE); /* We are losing half our entropy by doing this, but it is necessary to @@ -1005,6 +1007,7 @@ void MapgenParams::readParams(const Settings *settings) this->mgtype = Mapgen::getMapgenType(mg_name); settings->getS16NoEx("water_level", water_level); + settings->getS16NoEx("mapgen_limit", mapgen_limit); settings->getS16NoEx("chunksize", chunksize); settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen); @@ -1022,6 +1025,7 @@ void MapgenParams::writeParams(Settings *settings) const settings->set("mg_name", Mapgen::getMapgenName(mgtype)); settings->setU64("seed", seed); settings->setS16("water_level", water_level); + settings->setS16("mapgen_limit", mapgen_limit); settings->setS16("chunksize", chunksize); settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX); diff --git a/src/mapgen.h b/src/mapgen.h index 7aac1e6a0..c4e1652e8 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -124,6 +124,7 @@ struct MapgenParams { s16 chunksize; u64 seed; s16 water_level; + s16 mapgen_limit; u32 flags; BiomeParams *bparams; @@ -133,6 +134,7 @@ struct MapgenParams { chunksize(5), seed(0), water_level(1), + mapgen_limit(MAX_MAP_GENERATION_LIMIT), flags(MG_CAVES | MG_LIGHT | MG_DECORATIONS), bparams(NULL) { @@ -158,6 +160,7 @@ class Mapgen { public: s32 seed; int water_level; + int mapgen_limit; u32 flags; bool generating; int id; diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index ccf797eff..76a7a0582 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -70,9 +70,6 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeMa // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal this->m_bgen = (BiomeGenOriginal *)biomegen; - this->map_gen_limit = MYMIN(MAX_MAP_GENERATION_LIMIT, - g_settings->getU16("map_generation_limit")); - BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams; this->spflags = params->spflags; @@ -621,7 +618,7 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) const float massive_cave_threshold = 0.6f; // mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume. - float yblmin = -map_gen_limit + massive_cave_blend * 1.5f; + float yblmin = -mapgen_limit + massive_cave_blend * 1.5f; float yblmax = massive_cave_depth - massive_cave_blend * 1.5f; bool made_a_big_one = false; @@ -646,11 +643,11 @@ void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth) // lava_depth varies between one and ten as you approach // the bottom of the world. - s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / map_gen_limit); + s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / mapgen_limit); // This allows random lava spawns to be less common at the surface. s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth; // water_depth varies between ten and one on the way down. - s16 water_depth = ceil((map_gen_limit - abs(node_min.Y) + 1) * 10.f / map_gen_limit); + s16 water_depth = ceil((mapgen_limit - abs(node_min.Y) + 1) * 10.f / mapgen_limit); // This allows random water spawns to be more common at the surface. s16 water_chance = MYCUBE(water_features_lim) * water_depth; diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index 6dd7ebc47..4a7a11bcc 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -101,8 +101,6 @@ public: private: BiomeGenOriginal *m_bgen; - float map_gen_limit; - bool humid_rivers; bool use_altitude_chill; float humidity_adjust; -- cgit v1.2.3 From 021e667511658fdafcece72315f0f71bb26c6c68 Mon Sep 17 00:00:00 2001 From: Dániel Juhász Date: Thu, 13 Apr 2017 10:19:46 +0200 Subject: Add documentation for map block format 27 (#5576) --- doc/world_format.txt | 36 +++++++++++++++++++++++++++++------- src/mapblock.h | 7 ++++--- 2 files changed, 33 insertions(+), 10 deletions(-) (limited to 'src/mapblock.h') diff --git a/doc/world_format.txt b/doc/world_format.txt index a4fb3d8f2..976d14fd5 100644 --- a/doc/world_format.txt +++ b/doc/world_format.txt @@ -1,12 +1,13 @@ ============================= -Minetest World Format 22...25 +Minetest World Format 22...27 ============================= This applies to a world format carrying the block serialization version -22...25, used at least in +22...27, used at least in - 0.4.dev-20120322 ... 0.4.dev-20120606 (22...23) - 0.4.0 (23) - 24 was never released as stable and existed for ~2 days +- 27 was added in 0.4.15-dev The block serialization version does not fully specify every aspect of this format; if compliance with this format is to be checked, it needs to be @@ -262,15 +263,36 @@ u8 flags - 0x02: day_night_differs: Whether the lighting of the block is different on day and night. Only blocks that have this bit set are updated when day transforms to night. - - 0x04: lighting_expired: If true, lighting is invalid and should be - updated. If you can't calculate lighting in your generator properly, - you could try setting this 1 to everything and setting the uppermost - block in every sector as is_underground=0. I am quite sure it doesn't - work properly, though. + - 0x04: lighting_expired: Not used in version 27 and above. If true, + lighting is invalid and should be updated. If you can't calculate + lighting in your generator properly, you could try setting this 1 to + everything and setting the uppermost block in every sector as + is_underground=0. I am quite sure it doesn't work properly, though. - 0x08: generated: True if the block has been generated. If false, block is mostly filled with CONTENT_IGNORE and is likely to contain eg. parts of trees of neighboring blocks. +u16 lighting_complete +- Added in version 27. +- This contains 12 flags, each of them corresponds to a direction. +- Indicates if the light is correct at the sides of a map block. + Lighting may not be correct if the light changed, but a neighbor + block was not loaded at that time. + If these flags are false, Minetest will automatically recompute light + when both this block and its required neighbor are loaded. +- The bit order is: + nothing, nothing, nothing, nothing, + night X-, night Y-, night Z-, night Z+, night Y+, night X+, + day X-, day Y-, day Z-, day Z+, day Y+, day X+. + Where 'day' is for the day light bank, 'night' is for the night + light bank. + The 'nothing' bits should be always set, as they will be used + to indicate if direct sunlight spreading is finished. +- Example: if the block at (0, 0, 0) has + lighting_complete = 0b1111111111111110, + then Minetest will correct lighting in the day light bank when + the block at (1, 0, 0) is also loaded. + u8 content_width - Number of bytes in the content (param0) fields of nodes if map format version <= 23: diff --git a/src/mapblock.h b/src/mapblock.h index c48f337e0..7ff613fe8 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -633,9 +633,10 @@ private: /*! * Each bit indicates if light spreading was finished * in a direction. (Because the neighbor could also be unloaded.) - * Bits: day X+, day Y+, day Z+, day Z-, day Y-, day X-, - * night X+, night Y+, night Z+, night Z-, night Y-, night X-, - * nothing, nothing, nothing, nothing. + * Bits (most significant first): + * nothing, nothing, nothing, nothing, + * night X-, night Y-, night Z-, night Z+, night Y+, night X+, + * day X-, day Y-, day Z-, day Z+, day Y+, day X+. */ u16 m_lighting_complete; -- cgit v1.2.3 From 04cc9de8f2fbcb11f133c88f02fc11504b3ea6f3 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 15 Apr 2017 10:55:52 +0300 Subject: MeshUpdateQueue: Add a MapBlock cache that minimizes the amount of MapBlock copying done in the main thread Cache size is configurable by the meshgen_block_cache_size (default 20 MB). New profiler stats: - MeshUpdateQueue MapBlock cache hit % - MeshUpdateQueue MapBlock cache size kB Removes one type of stutter that was seen on the client when received MapBlocks were being handled. (the "MeshMakeData::fill" stutter) Kind of related to at least #5239 Originally preceded by these commits, now includes them: - Move the mesh generator thread into src/mesh_generator_thread.{cpp,h} - mesh_generator_thread.cpp: Update code style - MeshUpdateThread: Modify interface to house a different implementation: Actual functionality will be changed by next commits. - MeshMakeData: Add fillBlockData() interface (so that caller can fill in stuff from eg. a MapBlock cache) --- build/android/jni/Android.mk | 1 + builtin/settingtypes.txt | 5 + minetest.conf.example | 6 + src/CMakeLists.txt | 1 + src/client.cpp | 178 ++--------------------- src/client.h | 83 +---------- src/defaultsettings.cpp | 1 + src/mapblock.h | 5 + src/mapblock_mesh.cpp | 54 +++---- src/mapblock_mesh.h | 6 + src/mesh_generator_thread.cpp | 329 ++++++++++++++++++++++++++++++++++++++++++ src/mesh_generator_thread.h | 135 +++++++++++++++++ 12 files changed, 526 insertions(+), 278 deletions(-) create mode 100644 src/mesh_generator_thread.cpp create mode 100644 src/mesh_generator_thread.h (limited to 'src/mapblock.h') diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index fd104db06..2929eaba1 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -185,6 +185,7 @@ LOCAL_SRC_FILES := \ jni/src/mapnode.cpp \ jni/src/mapsector.cpp \ jni/src/mesh.cpp \ + jni/src/mesh_generator_thread.cpp \ jni/src/metadata.cpp \ jni/src/mg_biome.cpp \ jni/src/mg_decoration.cpp \ diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 2c74d6c44..5dc48c00e 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -551,6 +551,11 @@ enable_mesh_cache (Mesh cache) bool false # down the rate of mesh updates, thus reducing jitter on slower clients. mesh_generation_interval (Mapblock mesh generation delay) int 0 0 50 +# Size of the MapBlock cache of the mesh generator. Increasing this will +# increase the cache hit %, reducing the data being copied from the main +# thread, thus reducing jitter. +meshgen_block_cache_size (Mapblock mesh generator's MapBlock cache size MB) int 20 0 1000 + # Enables minimap. enable_minimap (Minimap) bool true diff --git a/minetest.conf.example b/minetest.conf.example index d147ebfac..9b50a775d 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -644,6 +644,12 @@ # type: int min: 0 max: 50 # mesh_generation_interval = 0 +# Size of the MapBlock cache of the mesh generator. Increasing this will +# increase the cache hit %, reducing the data being copied from the main +# thread, thus reducing jitter. +# type: int min: 0 max: 1000 +# meshgen_block_cache_size = 20 + # Enables minimap. # type: bool # enable_minimap = true diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index edb291545..37f72a44d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -522,6 +522,7 @@ set(client_SRCS main.cpp mapblock_mesh.cpp mesh.cpp + mesh_generator_thread.cpp minimap.cpp particles.cpp shader.cpp diff --git a/src/client.cpp b/src/client.cpp index 5ca51bd9c..7b962cd94 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -50,147 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc., extern gui::IGUIEnvironment* guienv; -/* - QueuedMeshUpdate -*/ - -QueuedMeshUpdate::QueuedMeshUpdate(): - p(-1337,-1337,-1337), - data(NULL), - ack_block_to_server(false) -{ -} - -QueuedMeshUpdate::~QueuedMeshUpdate() -{ - if(data) - delete data; -} - -/* - MeshUpdateQueue -*/ - -MeshUpdateQueue::MeshUpdateQueue() -{ -} - -MeshUpdateQueue::~MeshUpdateQueue() -{ - MutexAutoLock lock(m_mutex); - - for(std::vector::iterator - i = m_queue.begin(); - i != m_queue.end(); ++i) - { - QueuedMeshUpdate *q = *i; - delete q; - } -} - -/* - peer_id=0 adds with nobody to send to -*/ -void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent) -{ - DSTACK(FUNCTION_NAME); - - assert(data); // pre-condition - - MutexAutoLock lock(m_mutex); - - if(urgent) - m_urgents.insert(p); - - /* - Find if block is already in queue. - If it is, update the data and quit. - */ - for(std::vector::iterator - i = m_queue.begin(); - i != m_queue.end(); ++i) - { - QueuedMeshUpdate *q = *i; - if(q->p == p) - { - if(q->data) - delete q->data; - q->data = data; - if(ack_block_to_server) - q->ack_block_to_server = true; - return; - } - } - - /* - Add the block - */ - QueuedMeshUpdate *q = new QueuedMeshUpdate; - q->p = p; - q->data = data; - q->ack_block_to_server = ack_block_to_server; - m_queue.push_back(q); -} - -// Returned pointer must be deleted -// Returns NULL if queue is empty -QueuedMeshUpdate *MeshUpdateQueue::pop() -{ - MutexAutoLock lock(m_mutex); - - bool must_be_urgent = !m_urgents.empty(); - for(std::vector::iterator - i = m_queue.begin(); - i != m_queue.end(); ++i) - { - QueuedMeshUpdate *q = *i; - if(must_be_urgent && m_urgents.count(q->p) == 0) - continue; - m_queue.erase(i); - m_urgents.erase(q->p); - return q; - } - return NULL; -} - -/* - MeshUpdateThread -*/ - -MeshUpdateThread::MeshUpdateThread() : UpdateThread("Mesh") -{ - m_generation_interval = g_settings->getU16("mesh_generation_interval"); - m_generation_interval = rangelim(m_generation_interval, 0, 50); -} - -void MeshUpdateThread::enqueueUpdate(v3s16 p, MeshMakeData *data, - bool ack_block_to_server, bool urgent) -{ - m_queue_in.addBlock(p, data, ack_block_to_server, urgent); - deferUpdate(); -} - -void MeshUpdateThread::doUpdate() -{ - QueuedMeshUpdate *q; - while ((q = m_queue_in.pop())) { - if (m_generation_interval) - sleep_ms(m_generation_interval); - ScopeProfiler sp(g_profiler, "Client: Mesh making"); - - MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset); - - MeshUpdateResult r; - r.p = q->p; - r.mesh = mesh_new; - r.ack_block_to_server = q->ack_block_to_server; - - m_queue_out.push_back(r); - - delete q; - } -} - /* Client */ @@ -220,7 +79,7 @@ Client::Client( m_nodedef(nodedef), m_sound(sound), m_event(event), - m_mesh_update_thread(), + m_mesh_update_thread(this), m_env( new ClientMap(this, control, device->getSceneManager()->getRootSceneNode(), @@ -269,12 +128,6 @@ Client::Client( m_minimap = new Minimap(device, this); m_cache_save_interval = g_settings->getU16("server_map_save_interval"); - m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); - m_cache_enable_shaders = g_settings->getBool("enable_shaders"); - m_cache_use_tangent_vertices = m_cache_enable_shaders && ( - g_settings->getBool("enable_bumpmapping") || - g_settings->getBool("enable_parallax_occlusion")); - m_modding_enabled = g_settings->getBool("enable_client_modding"); m_script = new ClientScripting(this); m_env.setScript(m_script); @@ -1605,6 +1458,11 @@ int Client::getCrackLevel() return m_crack_level; } +v3s16 Client::getCrackPos() +{ + return m_crack_pos; +} + void Client::setCrack(int level, v3s16 pos) { int old_crack_level = m_crack_level; @@ -1670,28 +1528,14 @@ void Client::typeChatMessage(const std::wstring &message) void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) { + // Check if the block exists to begin with. In the case when a non-existing + // neighbor is automatically added, it may not. In that case we don't want + // to tell the mesh update thread about it. MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p); - if(b == NULL) + if (b == NULL) return; - /* - Create a task to update the mesh of the block - */ - - MeshMakeData *data = new MeshMakeData(this, m_cache_enable_shaders, - m_cache_use_tangent_vertices); - - { - //TimeTaker timer("data fill"); - // Release: ~0ms - // Debug: 1-6ms, avg=2ms - data->fill(b); - data->setCrack(m_crack_level, m_crack_pos); - data->setSmoothLighting(m_cache_smooth_lighting); - } - - // Add task to queue - m_mesh_update_thread.enqueueUpdate(p, data, ack_to_server, urgent); + m_mesh_update_thread.updateBlock(&m_env.getMap(), p, ack_to_server, urgent); } void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent) diff --git a/src/client.h b/src/client.h index e565acd93..e7fcb597d 100644 --- a/src/client.h +++ b/src/client.h @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "particles.h" #include "mapnode.h" #include "tileanimation.h" +#include "mesh_generator_thread.h" struct MeshMakeData; class MapBlockMesh; @@ -54,88 +55,12 @@ struct MinimapMapblock; class Camera; class NetworkPacket; -struct QueuedMeshUpdate -{ - v3s16 p; - MeshMakeData *data; - bool ack_block_to_server; - - QueuedMeshUpdate(); - ~QueuedMeshUpdate(); -}; - enum LocalClientState { LC_Created, LC_Init, LC_Ready }; -/* - A thread-safe queue of mesh update tasks -*/ -class MeshUpdateQueue -{ -public: - MeshUpdateQueue(); - - ~MeshUpdateQueue(); - - /* - peer_id=0 adds with nobody to send to - */ - void addBlock(v3s16 p, MeshMakeData *data, - bool ack_block_to_server, bool urgent); - - // Returned pointer must be deleted - // Returns NULL if queue is empty - QueuedMeshUpdate * pop(); - - u32 size() - { - MutexAutoLock lock(m_mutex); - return m_queue.size(); - } - -private: - std::vector m_queue; - std::set m_urgents; - Mutex m_mutex; -}; - -struct MeshUpdateResult -{ - v3s16 p; - MapBlockMesh *mesh; - bool ack_block_to_server; - - MeshUpdateResult(): - p(-1338,-1338,-1338), - mesh(NULL), - ack_block_to_server(false) - { - } -}; - -class MeshUpdateThread : public UpdateThread -{ -private: - MeshUpdateQueue m_queue_in; - int m_generation_interval; - -protected: - virtual void doUpdate(); - -public: - - MeshUpdateThread(); - - void enqueueUpdate(v3s16 p, MeshMakeData *data, - bool ack_block_to_server, bool urgent); - MutexedQueue m_queue_out; - - v3s16 m_camera_offset; -}; - enum ClientEventType { CE_NONE, @@ -471,6 +396,7 @@ public: float getAnimationTime(); int getCrackLevel(); + v3s16 getCrackPos(); void setCrack(int level, v3s16 pos); u16 getHP(); @@ -726,11 +652,6 @@ private: IntervalLimiter m_localdb_save_interval; u16 m_cache_save_interval; - // TODO: Add callback to update these when g_settings changes - bool m_cache_smooth_lighting; - bool m_cache_enable_shaders; - bool m_cache_use_tangent_vertices; - ClientScripting *m_script; bool m_modding_enabled; UNORDERED_MAP m_mod_storages; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index c6ced5931..573b5e2d8 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -39,6 +39,7 @@ void set_default_settings(Settings *settings) settings->setDefault("sound_volume", "0.8"); settings->setDefault("enable_mesh_cache", "false"); settings->setDefault("mesh_generation_interval", "0"); + settings->setDefault("meshgen_block_cache_size", "20"); settings->setDefault("enable_vbo", "true"); settings->setDefault("free_move", "false"); settings->setDefault("fast_move", "false"); diff --git a/src/mapblock.h b/src/mapblock.h index 7ff613fe8..8816dc817 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -154,6 +154,11 @@ public: raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_REALLOCATE); } + MapNode* getData() + { + return data; + } + //// //// Modification tracking methods //// diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index eddb061b4..933dfc32a 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -48,49 +48,43 @@ MeshMakeData::MeshMakeData(Client *client, bool use_shaders, m_use_tangent_vertices(use_tangent_vertices) {} -void MeshMakeData::fill(MapBlock *block) +void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) { - m_blockpos = block->getPos(); + m_blockpos = blockpos; v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE; - /* - Copy data - */ - - // Allocate this block + neighbors m_vmanip.clear(); VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE, blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1)); m_vmanip.addArea(voxel_area); +} - { - //TimeTaker timer("copy central block data"); - // 0ms +void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data) +{ + v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); + VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); - // Copy our data - block->copyTo(m_vmanip); - } - { - //TimeTaker timer("copy neighbor block data"); - // 0ms + v3s16 bp = m_blockpos + block_offset; + v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE; + m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size); +} + +void MeshMakeData::fill(MapBlock *block) +{ + fillBlockDataBegin(block->getPos()); - /* - Copy neighbors. This is lightning fast. - Copying only the borders would be *very* slow. - */ + fillBlockData(v3s16(0,0,0), block->getData()); - // Get map - Map *map = block->getParent(); + // Get map for reading neigbhor blocks + Map *map = block->getParent(); - for(u16 i=0; i<26; i++) - { - const v3s16 &dir = g_26dirs[i]; - v3s16 bp = m_blockpos + dir; - MapBlock *b = map->getBlockNoCreateNoEx(bp); - if(b) - b->copyTo(m_vmanip); - } + for (u16 i=0; i<26; i++) { + const v3s16 &dir = g_26dirs[i]; + v3s16 bp = m_blockpos + dir; + MapBlock *b = map->getBlockNoCreateNoEx(bp); + if(b) + fillBlockData(dir, b->getData()); } } diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 916703f3e..25c699e1c 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -52,6 +52,12 @@ struct MeshMakeData MeshMakeData(Client *client, bool use_shaders, bool use_tangent_vertices = false); + /* + Copy block data manually (to allow optimizations by the caller) + */ + void fillBlockDataBegin(const v3s16 &blockpos); + void fillBlockData(const v3s16 &block_offset, MapNode *data); + /* Copy central data directly from block, and other data from parent of block. diff --git a/src/mesh_generator_thread.cpp b/src/mesh_generator_thread.cpp new file mode 100644 index 000000000..126bf6327 --- /dev/null +++ b/src/mesh_generator_thread.cpp @@ -0,0 +1,329 @@ +/* +Minetest +Copyright (C) 2013, 2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "mesh_generator_thread.h" +#include "settings.h" +#include "profiler.h" +#include "client.h" +#include "mapblock.h" +#include "map.h" + +/* + CachedMapBlockData +*/ + +CachedMapBlockData::CachedMapBlockData(): + p(-1337,-1337,-1337), + data(NULL), + refcount_from_queue(0), + last_used_timestamp(time(0)) +{ +} + +CachedMapBlockData::~CachedMapBlockData() +{ + assert(refcount_from_queue == 0); + + if (data) + delete[] data; +} + +/* + QueuedMeshUpdate +*/ + +QueuedMeshUpdate::QueuedMeshUpdate(): + p(-1337,-1337,-1337), + ack_block_to_server(false), + urgent(false), + crack_level(-1), + crack_pos(0,0,0), + data(NULL) +{ +} + +QueuedMeshUpdate::~QueuedMeshUpdate() +{ + if (data) + delete data; +} + +/* + MeshUpdateQueue +*/ + +MeshUpdateQueue::MeshUpdateQueue(Client *client): + m_client(client) +{ + m_cache_enable_shaders = g_settings->getBool("enable_shaders"); + m_cache_use_tangent_vertices = m_cache_enable_shaders && ( + g_settings->getBool("enable_bumpmapping") || + g_settings->getBool("enable_parallax_occlusion")); + m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); + m_meshgen_block_cache_size = g_settings->getS32("meshgen_block_cache_size"); +} + +MeshUpdateQueue::~MeshUpdateQueue() +{ + MutexAutoLock lock(m_mutex); + + for (std::vector::iterator i = m_queue.begin(); + i != m_queue.end(); ++i) { + QueuedMeshUpdate *q = *i; + delete q; + } +} + +void MeshUpdateQueue::addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent) +{ + DSTACK(FUNCTION_NAME); + + MutexAutoLock lock(m_mutex); + + cleanupCache(); + + /* + Cache the block data (force-update the center block, don't update the + neighbors but get them if they aren't already cached) + */ + std::vector cached_blocks; + size_t cache_hit_counter = 0; + cached_blocks.reserve(3*3*3); + v3s16 dp; + for (dp.X = -1; dp.X <= 1; dp.X++) + for (dp.Y = -1; dp.Y <= 1; dp.Y++) + for (dp.Z = -1; dp.Z <= 1; dp.Z++) { + v3s16 p1 = p + dp; + CachedMapBlockData *cached_block; + if (dp == v3s16(0, 0, 0)) + cached_block = cacheBlock(map, p1, FORCE_UPDATE); + else + cached_block = cacheBlock(map, p1, SKIP_UPDATE_IF_ALREADY_CACHED, + &cache_hit_counter); + cached_blocks.push_back(cached_block); + } + g_profiler->avg("MeshUpdateQueue MapBlock cache hit %", + 100.0f * cache_hit_counter / cached_blocks.size()); + + /* + Mark the block as urgent if requested + */ + if (urgent) + m_urgents.insert(p); + + /* + Find if block is already in queue. + If it is, update the data and quit. + */ + for (std::vector::iterator i = m_queue.begin(); + i != m_queue.end(); ++i) { + QueuedMeshUpdate *q = *i; + if (q->p == p) { + // NOTE: We are not adding a new position to the queue, thus + // refcount_from_queue stays the same. + if(ack_block_to_server) + q->ack_block_to_server = true; + q->crack_level = m_client->getCrackLevel(); + q->crack_pos = m_client->getCrackPos(); + return; + } + } + + /* + Add the block + */ + QueuedMeshUpdate *q = new QueuedMeshUpdate; + q->p = p; + q->ack_block_to_server = ack_block_to_server; + q->crack_level = m_client->getCrackLevel(); + q->crack_pos = m_client->getCrackPos(); + m_queue.push_back(q); + + // This queue entry is a new reference to the cached blocks + for (size_t i=0; irefcount_from_queue++; + } +} + +// Returned pointer must be deleted +// Returns NULL if queue is empty +QueuedMeshUpdate *MeshUpdateQueue::pop() +{ + MutexAutoLock lock(m_mutex); + + bool must_be_urgent = !m_urgents.empty(); + for (std::vector::iterator i = m_queue.begin(); + i != m_queue.end(); ++i) { + QueuedMeshUpdate *q = *i; + if(must_be_urgent && m_urgents.count(q->p) == 0) + continue; + m_queue.erase(i); + m_urgents.erase(q->p); + fillDataFromMapBlockCache(q); + return q; + } + return NULL; +} + +CachedMapBlockData* MeshUpdateQueue::cacheBlock(Map *map, v3s16 p, UpdateMode mode, + size_t *cache_hit_counter) +{ + std::map::iterator it = + m_cache.find(p); + if (it != m_cache.end()) { + // Already in cache + CachedMapBlockData *cached_block = it->second; + if (mode == SKIP_UPDATE_IF_ALREADY_CACHED) { + if (cache_hit_counter) + (*cache_hit_counter)++; + return cached_block; + } + MapBlock *b = map->getBlockNoCreateNoEx(p); + if (b) { + if (cached_block->data == NULL) + cached_block->data = + new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE]; + memcpy(cached_block->data, b->getData(), + MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * sizeof(MapNode)); + } else { + delete[] cached_block->data; + cached_block->data = NULL; + } + return cached_block; + } else { + // Not yet in cache + CachedMapBlockData *cached_block = new CachedMapBlockData(); + m_cache[p] = cached_block; + MapBlock *b = map->getBlockNoCreateNoEx(p); + if (b) { + cached_block->data = + new MapNode[MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE]; + memcpy(cached_block->data, b->getData(), + MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * sizeof(MapNode)); + } + return cached_block; + } +} + +CachedMapBlockData* MeshUpdateQueue::getCachedBlock(const v3s16 &p) +{ + std::map::iterator it = m_cache.find(p); + if (it != m_cache.end()) { + return it->second; + } + return NULL; +} + +void MeshUpdateQueue::fillDataFromMapBlockCache(QueuedMeshUpdate *q) +{ + MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders, + m_cache_use_tangent_vertices); + q->data = data; + + data->fillBlockDataBegin(q->p); + + int t_now = time(0); + + // Collect data for 3*3*3 blocks from cache + v3s16 dp; + for (dp.X = -1; dp.X <= 1; dp.X++) + for (dp.Y = -1; dp.Y <= 1; dp.Y++) + for (dp.Z = -1; dp.Z <= 1; dp.Z++) { + v3s16 p = q->p + dp; + CachedMapBlockData *cached_block = getCachedBlock(p); + if (cached_block) { + cached_block->refcount_from_queue--; + cached_block->last_used_timestamp = t_now; + if (cached_block->data) + data->fillBlockData(dp, cached_block->data); + } + } + + data->setCrack(q->crack_level, q->crack_pos); + data->setSmoothLighting(m_cache_smooth_lighting); +} + +void MeshUpdateQueue::cleanupCache() +{ + const int mapblock_kB = MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE * + sizeof(MapNode) / 1000; + g_profiler->avg("MeshUpdateQueue MapBlock cache size kB", + mapblock_kB * m_cache.size()); + + // The cache size is kept roughly below cache_soft_max_size, not letting + // anything get older than cache_seconds_max or deleted before 2 seconds. + const int cache_seconds_max = 10; + const int cache_soft_max_size = m_meshgen_block_cache_size * 1000 / mapblock_kB; + int cache_seconds = MYMAX(2, cache_seconds_max - + m_cache.size() / (cache_soft_max_size / cache_seconds_max)); + + int t_now = time(0); + + for (std::map::iterator it = m_cache.begin(); + it != m_cache.end(); ) { + CachedMapBlockData *cached_block = it->second; + if (cached_block->refcount_from_queue == 0 && + cached_block->last_used_timestamp < t_now - cache_seconds) { + m_cache.erase(it++); + } else { + ++it; + } + } +} + +/* + MeshUpdateThread +*/ + +MeshUpdateThread::MeshUpdateThread(Client *client): + UpdateThread("Mesh"), + m_queue_in(client) +{ + m_generation_interval = g_settings->getU16("mesh_generation_interval"); + m_generation_interval = rangelim(m_generation_interval, 0, 50); +} + +void MeshUpdateThread::updateBlock(Map *map, v3s16 p, bool ack_block_to_server, + bool urgent) +{ + // Allow the MeshUpdateQueue to do whatever it wants + m_queue_in.addBlock(map, p, ack_block_to_server, urgent); + deferUpdate(); +} + +void MeshUpdateThread::doUpdate() +{ + QueuedMeshUpdate *q; + while ((q = m_queue_in.pop())) { + if (m_generation_interval) + sleep_ms(m_generation_interval); + ScopeProfiler sp(g_profiler, "Client: Mesh making"); + + MapBlockMesh *mesh_new = new MapBlockMesh(q->data, m_camera_offset); + + MeshUpdateResult r; + r.p = q->p; + r.mesh = mesh_new; + r.ack_block_to_server = q->ack_block_to_server; + + m_queue_out.push_back(r); + + delete q; + } +} diff --git a/src/mesh_generator_thread.h b/src/mesh_generator_thread.h new file mode 100644 index 000000000..e74861862 --- /dev/null +++ b/src/mesh_generator_thread.h @@ -0,0 +1,135 @@ +/* +Minetest +Copyright (C) 2013, 2017 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef MESH_GENERATOR_THREAD_HEADER +#define MESH_GENERATOR_THREAD_HEADER + +#include "mapblock_mesh.h" +#include "threading/mutex_auto_lock.h" +#include "util/thread.h" + +struct CachedMapBlockData +{ + v3s16 p; + MapNode *data; // A copy of the MapBlock's data member + int refcount_from_queue; + int last_used_timestamp; + + CachedMapBlockData(); + ~CachedMapBlockData(); +}; + +struct QueuedMeshUpdate +{ + v3s16 p; + bool ack_block_to_server; + bool urgent; + int crack_level; + v3s16 crack_pos; + MeshMakeData *data; // This is generated in MeshUpdateQueue::pop() + + QueuedMeshUpdate(); + ~QueuedMeshUpdate(); +}; + +/* + A thread-safe queue of mesh update tasks and a cache of MapBlock data +*/ +class MeshUpdateQueue +{ + enum UpdateMode { + FORCE_UPDATE, + SKIP_UPDATE_IF_ALREADY_CACHED, + }; +public: + MeshUpdateQueue(Client *client); + + ~MeshUpdateQueue(); + + // Caches the block at p and its neighbors (if needed) and queues a mesh + // update for the block at p + void addBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent); + + // Returned pointer must be deleted + // Returns NULL if queue is empty + QueuedMeshUpdate * pop(); + + u32 size() + { + MutexAutoLock lock(m_mutex); + return m_queue.size(); + } + +private: + Client *m_client; + std::vector m_queue; + std::set m_urgents; + std::map m_cache; + Mutex m_mutex; + + // TODO: Add callback to update these when g_settings changes + bool m_cache_enable_shaders; + bool m_cache_use_tangent_vertices; + bool m_cache_smooth_lighting; + int m_meshgen_block_cache_size; + + CachedMapBlockData* cacheBlock(Map *map, v3s16 p, UpdateMode mode, + size_t *cache_hit_counter=NULL); + CachedMapBlockData* getCachedBlock(const v3s16 &p); + void fillDataFromMapBlockCache(QueuedMeshUpdate *q); + void cleanupCache(); +}; + +struct MeshUpdateResult +{ + v3s16 p; + MapBlockMesh *mesh; + bool ack_block_to_server; + + MeshUpdateResult(): + p(-1338,-1338,-1338), + mesh(NULL), + ack_block_to_server(false) + { + } +}; + +class MeshUpdateThread : public UpdateThread +{ +public: + MeshUpdateThread(Client *client); + + // Caches the block at p and its neighbors (if needed) and queues a mesh + // update for the block at p + void updateBlock(Map *map, v3s16 p, bool ack_block_to_server, bool urgent); + + v3s16 m_camera_offset; + MutexedQueue m_queue_out; + +private: + MeshUpdateQueue m_queue_in; + + // TODO: Add callback to update these when g_settings changes + int m_generation_interval; + +protected: + virtual void doUpdate(); +}; + +#endif -- cgit v1.2.3