aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorParamat <paramat@users.noreply.github.com>2019-08-07 22:07:51 +0100
committerGitHub <noreply@github.com>2019-08-07 22:07:51 +0100
commit37923920a07f06d201662d8f1f4e5821efcc7b09 (patch)
treec90559dd092fea68a9e75ccdb81437564c34e627
parent0c533dc4363747c8ccb4a0f519516cc8ae73d3fa (diff)
downloadminetest-37923920a07f06d201662d8f1f4e5821efcc7b09.tar.gz
minetest-37923920a07f06d201662d8f1f4e5821efcc7b09.tar.bz2
minetest-37923920a07f06d201662d8f1f4e5821efcc7b09.zip
Avoid crash caused by, and improve, 'findSpawnPos()' (#8728)
Avoid an unsuitable spawn position (which if outside mapgen limits can cause a crash) if the main 0-3999 loop reaches its end. Fallback to a spawn at 0,0,0. Check the mapgen-returned 'spawn_level' value for being outside limits. When 'air_count' reaches 2, move back down 1 to spawn in the lower empty node. If the spawn position is disallowed by 'objectpos_over_limit()', 'break' from loop instead of 'continue' because positions above are probably also over limit. Reset 'air_count' to 0 if an obstruction is found, to make 'air_count' consecutive empty nodes. Allow spawn in 'airlike' drawtype nodes such as mod-added vacuum, alien atmospheres, fog etc. Add clarifying comments and improve codestyle.
-rw-r--r--src/server.cpp43
1 files changed, 31 insertions, 12 deletions
diff --git a/src/server.cpp b/src/server.cpp
index b3a6b4909..7e6208711 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -3512,52 +3512,71 @@ v3f Server::findSpawnPos()
{
ServerMap &map = m_env->getServerMap();
v3f nodeposf;
- if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) {
+ if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf))
return nodeposf * BS;
- }
bool is_good = false;
// Limit spawn range to mapgen edges (determined by 'mapgen_limit')
s32 range_max = map.getMapgenParams()->getSpawnRangeMax();
// Try to find a good place a few times
- for(s32 i = 0; i < 4000 && !is_good; i++) {
+ for (s32 i = 0; i < 4000 && !is_good; i++) {
s32 range = MYMIN(1 + i, range_max);
// We're going to try to throw the player to this position
v2s16 nodepos2d = v2s16(
-range + (myrand() % (range * 2)),
-range + (myrand() % (range * 2)));
-
// Get spawn level at point
s16 spawn_level = m_emerge->getSpawnLevelAtPoint(nodepos2d);
- // Continue if MAX_MAP_GENERATION_LIMIT was returned by
- // the mapgen to signify an unsuitable spawn position
- if (spawn_level == MAX_MAP_GENERATION_LIMIT)
+ // Continue if MAX_MAP_GENERATION_LIMIT was returned by the mapgen to
+ // signify an unsuitable spawn position, or if outside limits.
+ if (spawn_level >= MAX_MAP_GENERATION_LIMIT ||
+ spawn_level <= -MAX_MAP_GENERATION_LIMIT)
continue;
v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y);
-
+ // Consecutive empty nodes
s32 air_count = 0;
- for (s32 i = 0; i < 10; i++) {
+
+ // Search upwards from 'spawn level' for 2 consecutive empty nodes, to
+ // avoid obstructions in already-generated mapblocks.
+ // In ungenerated mapblocks consisting of 'ignore' nodes, there will be
+ // no obstructions, but mapgen decorations are generated after spawn so
+ // the player may end up inside one.
+ for (s32 i = 0; i < 8; i++) {
v3s16 blockpos = getNodeBlockPos(nodepos);
map.emergeBlock(blockpos, true);
content_t c = map.getNodeNoEx(nodepos).getContent();
- if (c == CONTENT_AIR || c == CONTENT_IGNORE) {
+
+ // In generated mapblocks allow spawn in all 'airlike' drawtype nodes.
+ // In ungenerated mapblocks allow spawn in 'ignore' nodes.
+ if (m_nodedef->get(c).drawtype == NDT_AIRLIKE || c == CONTENT_IGNORE) {
air_count++;
if (air_count >= 2) {
+ // Spawn in lower empty node
+ nodepos.Y--;
nodeposf = intToFloat(nodepos, BS);
// Don't spawn the player outside map boundaries
if (objectpos_over_limit(nodeposf))
- continue;
+ // Exit this loop, positions above are probably over limit
+ break;
+
+ // Good position found, cause an exit from main loop
is_good = true;
break;
}
+ } else {
+ air_count = 0;
}
nodepos.Y++;
}
}
- return nodeposf;
+ if (is_good)
+ return nodeposf;
+
+ // No suitable spawn point found, return fallback 0,0,0
+ return v3f(0.0f, 0.0f, 0.0f);
}
void Server::requestShutdown(const std::string &msg, bool reconnect, float delay)