From 6079d762ce474aca8f45eef3741e99747e145037 Mon Sep 17 00:00:00 2001 From: DS <7613443+Desour@users.noreply.github.com> Date: Wed, 31 Dec 2025 15:19:45 +0100 Subject: [PATCH] Fix corner case when unknown node ids run out (#16790) * Fix `NodeDefManager::allocateId()` not terminating after overflow * MapBlock::correctBlockNodeIds(): Do not ignore errors Ignoring would lead to information loss, as the local ids were not translated, so they were replaced by different nodes. There is `ignore_world_load_errors` to ignore errors. * Do not advertise without warning for ignore_world_load_errors --- src/emerge.cpp | 3 ++- src/mapblock.cpp | 25 ++++--------------------- src/nodedef.cpp | 7 ++++++- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/src/emerge.cpp b/src/emerge.cpp index 825b50d5d1..ff8e3c95b3 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -788,7 +788,8 @@ void *EmergeThread::run() << "----" << std::endl << "\"" << e.what() << "\"" << std::endl << "See debug.txt." << std::endl - << "You can ignore this using [ignore_world_load_errors = true]." + << "This can be ignored using the `ignore_world_load_errors` setting. " + << "But it will also destroy stuff in the affected MapBlocks, do not use." << std::endl; m_server->setAsyncFatalError(err.str()); } diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 0af2338391..968eab548f 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -352,12 +352,6 @@ void MapBlock::correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, IGameDef *gamedef) { const NodeDefManager *nodedef = gamedef->ndef(); - // This means the block contains incorrect ids, and we contain - // the information to convert those to names. - // nodedef contains information to convert our names to globally - // correct ids. - std::unordered_set unnamed_contents; - std::unordered_set unallocatable_contents; // Used to cache local to global id lookup. IdIdMapping &mapping_cache = IdIdMapping::giveClearedThreadLocalInstance(); @@ -372,16 +366,16 @@ void MapBlock::correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, std::string name; if (!nimap->getName(local_id, name)) { - unnamed_contents.insert(local_id); - continue; + throw SerializationError("MapBlock::correctBlockNodeIds(): " + "Block contains id " + itos(local_id) + " with no name mapping"); } content_t global_id; if (!nodedef->getId(name, global_id)) { global_id = gamedef->allocateUnknownNodeId(name); if (global_id == CONTENT_IGNORE) { - unallocatable_contents.insert(name); - continue; + throw SerializationError("MapBlock::correctBlockNodeIds(): " + "Could not allocate global id for node name \"" + name + "\""); } } nodes[i].setContent(global_id); @@ -389,17 +383,6 @@ void MapBlock::correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, // Save previous node local_id & global_id result mapping_cache.set(local_id, global_id); } - - for (const content_t c: unnamed_contents) { - errorstream << "correctBlockNodeIds(): IGNORING ERROR: " - << "Block contains id " << c - << " with no name mapping" << std::endl; - } - for (const std::string &node_name: unallocatable_contents) { - errorstream << "correctBlockNodeIds(): IGNORING ERROR: " - << "Could not allocate global id for node name \"" - << node_name << "\"" << std::endl; - } } void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int compression_level) diff --git a/src/nodedef.cpp b/src/nodedef.cpp index bb9a2c3512..1484438865 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -794,7 +794,12 @@ content_t NodeDefManager::allocateId() } const ContentFeatures &f = m_content_features[id]; if (f.name.empty()) { - m_next_id = id + 1; + // use saturating add, to avoid overflow of m_next_id, which would + // make the loop not terminate (loop cond id >= 0 is always true) + if (id < CONTENT_MAX) + m_next_id = id + 1; + else + m_next_id = CONTENT_MAX; return id; } }