diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index ddd7b5fe..95785797 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -247,7 +247,6 @@ void set_default_settings(Settings *settings) settings->setDefault("default_privs", "interact, shout"); settings->setDefault("player_transfer_distance", "0"); settings->setDefault("enable_pvp", "true"); - settings->setDefault("vertical_spawn_range", "16"); settings->setDefault("disallow_empty_password", "false"); settings->setDefault("disable_anticheat", "false"); settings->setDefault("enable_rollback_recording", "false"); diff --git a/src/emerge.cpp b/src/emerge.cpp index ccb4c170..93e8f2b3 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -334,6 +334,18 @@ v3s16 EmergeManager::getContainingChunk(v3s16 blockpos, s16 chunksize) } +int EmergeManager::getSpawnLevelAtPoint(v2s16 p) +{ + if (m_mapgens.size() == 0 || !m_mapgens[0]) { + errorstream << "EmergeManager: getSpawnLevelAtPoint() called" + " before mapgen init" << std::endl; + return 0; + } + + return m_mapgens[0]->getSpawnLevelAtPoint(p); +} + + int EmergeManager::getGroundLevelAtPoint(v2s16 p) { if (m_mapgens.size() == 0 || !m_mapgens[0]) { diff --git a/src/emerge.h b/src/emerge.h index 02bdf7e6..825ac1c0 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -136,6 +136,7 @@ public: // Mapgen helpers methods Biome *getBiomeAtPoint(v3s16 p); + int getSpawnLevelAtPoint(v2s16 p); int getGroundLevelAtPoint(v2s16 p); bool isBlockUnderground(v3s16 blockpos); diff --git a/src/mapgen.h b/src/mapgen.h index 9bb7d03b..abc3d2e8 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -181,6 +181,13 @@ public: virtual void makeChunk(BlockMakeData *data) {} virtual int getGroundLevelAtPoint(v2s16 p) { return 0; } + // getSpawnLevelAtPoint() is a function within each mapgen that returns a + // suitable y co-ordinate for player spawn ('suitable' usually meaning + // within 16 nodes of water_level). If a suitable spawn level cannot be + // found at the specified (X, Z) 'MAX_MAP_GENERATION_LIMIT' is returned to + // signify this and to cause Server::findSpawnPos() to try another (X, Z). + virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; } + private: DISABLE_CLASS_COPY(Mapgen); }; diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp index 3b7178dd..0d071411 100644 --- a/src/mapgen_flat.cpp +++ b/src/mapgen_flat.cpp @@ -192,18 +192,25 @@ void MapgenFlatParams::writeParams(Settings *settings) const ///////////////////////////////////////////////////////////////// -int MapgenFlat::getGroundLevelAtPoint(v2s16 p) +int MapgenFlat::getSpawnLevelAtPoint(v2s16 p) { + s16 level_at_point = ground_level; float n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed); + if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) { - s16 depress = (lake_threshold - n_terrain) * lake_steepness; - return ground_level - depress; + level_at_point = ground_level - + (lake_threshold - n_terrain) * lake_steepness; } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) { - s16 rise = (n_terrain - hill_threshold) * hill_steepness; - return ground_level + rise; - } else { - return ground_level; + level_at_point = ground_level + + (n_terrain - hill_threshold) * hill_steepness; } + + if (ground_level < water_level) // Ocean world, allow spawn in water + return MYMAX(level_at_point, water_level); + else if (level_at_point > water_level) + return level_at_point; // Spawn on land + else + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point } diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h index 0e15df78..282e4297 100644 --- a/src/mapgen_flat.h +++ b/src/mapgen_flat.h @@ -102,7 +102,7 @@ public: ~MapgenFlat(); virtual void makeChunk(BlockMakeData *data); - int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); s16 generateTerrain(); MgStoneType generateBiomes(float *heat_map, float *humidity_map); diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp index 9cb682a9..8a5c1e2b 100644 --- a/src/mapgen_fractal.cpp +++ b/src/mapgen_fractal.cpp @@ -209,17 +209,28 @@ void MapgenFractalParams::writeParams(Settings *settings) const ///////////////////////////////////////////////////////////////// -int MapgenFractal::getGroundLevelAtPoint(v2s16 p) +int MapgenFractal::getSpawnLevelAtPoint(v2s16 p) { - s16 search_start = 128; - s16 search_end = -128; - - for (s16 y = search_start; y >= search_end; y--) { - if (getFractalAtPoint(p.X, y, p.Y)) - return y; + bool solid_below = false; // Dry solid node is present below to spawn on + u8 air_count = 0; // Consecutive air nodes above the dry solid node + s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed); + // Seabed can rise above water_level or might be raised to create dry land + s16 search_start = MYMAX(seabed_level, water_level + 1); + if (seabed_level > water_level) + solid_below = true; + + for (s16 y = search_start; y <= search_start + 128; y++) { + if (getFractalAtPoint(p.X, y, p.Y)) { // Fractal node + solid_below = true; + air_count = 0; + } else if (solid_below) { // Air above solid node + air_count++; + if (air_count == 2) + return y - 2; + } } - return -MAX_MAP_GENERATION_LIMIT; + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point } diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h index 3d4f7ee8..ffb2acfb 100644 --- a/src/mapgen_fractal.h +++ b/src/mapgen_fractal.h @@ -114,7 +114,7 @@ public: ~MapgenFractal(); virtual void makeChunk(BlockMakeData *data); - int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); bool getFractalAtPoint(s16 x, s16 y, s16 z); s16 generateTerrain(); diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp index f8711526..ff985dd3 100644 --- a/src/mapgen_singlenode.cpp +++ b/src/mapgen_singlenode.cpp @@ -99,7 +99,7 @@ void MapgenSinglenode::makeChunk(BlockMakeData *data) } -int MapgenSinglenode::getGroundLevelAtPoint(v2s16 p) +int MapgenSinglenode::getSpawnLevelAtPoint(v2s16 p) { return 0; } diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h index f9c97b50..2c6496c0 100644 --- a/src/mapgen_singlenode.h +++ b/src/mapgen_singlenode.h @@ -41,7 +41,7 @@ public: ~MapgenSinglenode(); void makeChunk(BlockMakeData *data); - int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); }; struct MapgenFactorySinglenode : public MapgenFactory { diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 06e5f1ca..0bb3715a 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -171,7 +171,7 @@ void MapgenV5Params::writeParams(Settings *settings) const } -int MapgenV5::getGroundLevelAtPoint(v2s16 p) +int MapgenV5::getSpawnLevelAtPoint(v2s16 p) { //TimeTaker t("getGroundLevelAtPoint", NULL, PRECISION_MICRO); @@ -182,24 +182,25 @@ int MapgenV5::getGroundLevelAtPoint(v2s16 p) f *= 1.6; float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed); - s16 search_start = 128; // Only bother searching this range, actual - s16 search_end = -128; // ground level is rarely higher or lower. - - for (s16 y = search_start; y >= search_end; y--) { + for (s16 y = 128; y >= -128; y--) { float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed); - // If solid - if (n_ground * f > y - h) { + + if (n_ground * f > y - h) { // If solid // If either top 2 nodes of search are solid this is inside a - // mountain or floatland with no space for the player to spawn. - if (y >= search_start - 1) - return MAX_MAP_GENERATION_LIMIT; - else - return y; // Ground below at least 2 nodes of space + // mountain or floatland with possibly no space for the player to spawn. + if (y >= 127) { + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point + } else { // Ground below at least 2 nodes of empty space + if (y <= water_level || y > water_level + 16) + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point + else + return y; + } } } //printf("getGroundLevelAtPoint: %dus\n", t.stop()); - return -MAX_MAP_GENERATION_LIMIT; + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn position, no ground found } diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h index 4112bbfa..44a06907 100644 --- a/src/mapgen_v5.h +++ b/src/mapgen_v5.h @@ -90,7 +90,7 @@ public: ~MapgenV5(); virtual void makeChunk(BlockMakeData *data); - int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); void calculateNoise(); int generateBaseTerrain(); MgStoneType generateBiomes(float *heat_map, float *humidity_map); diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 57d0f59b..e24083cf 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -318,6 +318,17 @@ int MapgenV6::getGroundLevelAtPoint(v2s16 p) } +int MapgenV6::getSpawnLevelAtPoint(v2s16 p) +{ + s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT; + if (level_at_point <= water_level || + level_at_point > water_level + 16) + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point + else + return level_at_point; +} + + //////////////////////// Noise functions float MapgenV6::getMudAmount(v2s16 p) diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index 612e2322..a55fc6d5 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -129,6 +129,7 @@ public: void makeChunk(BlockMakeData *data); int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); float baseTerrainLevel(float terrain_base, float terrain_higher, float steepness, float height_select); diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index aec00b93..029f56a4 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -202,7 +202,7 @@ void MapgenV7Params::writeParams(Settings *settings) const /////////////////////////////////////// -int MapgenV7::getGroundLevelAtPoint(v2s16 p) +int MapgenV7::getSpawnLevelAtPoint(v2s16 p) { // Base terrain calculation s16 y = baseTerrainLevelAtPoint(p.X, p.Y); @@ -210,22 +210,24 @@ int MapgenV7::getGroundLevelAtPoint(v2s16 p) // Ridge/river terrain calculation float width = 0.2; float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2; - // actually computing the depth of the ridge is much more expensive; - // if inside a river, simply guess + // if inside a river this is an unsuitable spawn point if (fabs(uwatern) <= width) - return water_level - 10; + return MAX_MAP_GENERATION_LIMIT; // Mountain terrain calculation - int iters = 128; // don't even bother iterating more than 128 times.. + int iters = 128; while (iters--) { - //current point would have been air - if (!getMountainTerrainAtPoint(p.X, y, p.Y)) - return y; - + if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) { // Air, y is ground level + if (y <= water_level || y > water_level + 16) + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point + else + return y; + } y++; } - return y; + // Unsuitable spawn point, no ground surface found + return MAX_MAP_GENERATION_LIMIT; } diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index 9b483b04..82f89387 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -103,7 +103,7 @@ public: ~MapgenV7(); virtual void makeChunk(BlockMakeData *data); - int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); Biome *getBiomeAtPoint(v3s16 p); float baseTerrainLevelAtPoint(s16 x, s16 z); diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp index ceb2c774..2f96c339 100644 --- a/src/mapgen_valleys.cpp +++ b/src/mapgen_valleys.cpp @@ -56,8 +56,8 @@ with this program; if not, write to the Free Software Foundation, Inc., //Profiler *mapgen_profiler = &mapgen_prof; static FlagDesc flagdesc_mapgen_valleys[] = { - {"altitude_chill", MG_VALLEYS_ALT_CHILL}, - {"humid_rivers", MG_VALLEYS_HUMID_RIVERS}, + {"altitude_chill", MGVALLEYS_ALT_CHILL}, + {"humid_rivers", MGVALLEYS_HUMID_RIVERS}, {NULL, 0} }; @@ -86,8 +86,8 @@ MapgenValleys::MapgenValleys(int mapgenid, MapgenParams *params, EmergeManager * MapgenValleysParams *sp = (MapgenValleysParams *)params->sparams; this->spflags = sp->spflags; - this->humid_rivers = (spflags & MG_VALLEYS_HUMID_RIVERS); - this->use_altitude_chill = (spflags & MG_VALLEYS_ALT_CHILL); + this->humid_rivers = (spflags & MGVALLEYS_HUMID_RIVERS); + this->use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL); this->altitude_chill = sp->altitude_chill; this->humidity_adjust = params->np_biome_humidity.offset - 50.f; @@ -181,7 +181,7 @@ MapgenValleys::~MapgenValleys() MapgenValleysParams::MapgenValleysParams() { - spflags = MG_VALLEYS_HUMID_RIVERS | MG_VALLEYS_ALT_CHILL; + spflags = MGVALLEYS_HUMID_RIVERS | MGVALLEYS_ALT_CHILL; altitude_chill = 90; // The altitude at which temperature drops by 20C. large_cave_depth = -33; @@ -513,24 +513,19 @@ float MapgenValleys::adjustedTerrainLevelFromNoise(TerrainNoise *tn) } -int MapgenValleys::getGroundLevelAtPoint(v2s16 p) +int MapgenValleys::getSpawnLevelAtPoint(v2s16 p) { - // *********************************** - // This method (deliberately) does not - // return correct terrain values. - // *********************************** - - // Since MT doesn't normally deal with rivers, check - // to make sure this isn't a request for a location - // in a river. + // Check to make sure this isn't a request for a location in a river. float rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed); - - // If it's wet, return an unusable number. if (fabs(rivers) < river_size_factor) - return MAX_MAP_GENERATION_LIMIT; + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point - // Otherwise, return the real result. - return terrainLevelAtPoint(p.X, p.Y); + s16 level_at_point = terrainLevelAtPoint(p.X, p.Y); + if (level_at_point <= water_level || + level_at_point > water_level + 16) + return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point + else + return level_at_point; } diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h index ee4052d7..6b3eb9cf 100644 --- a/src/mapgen_valleys.h +++ b/src/mapgen_valleys.h @@ -30,9 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" -/////////////////// Mapgen Valleys flags -#define MG_VALLEYS_ALT_CHILL 0x01 -#define MG_VALLEYS_HUMID_RIVERS 0x02 +////////////// Mapgen Valleys flags +#define MGVALLEYS_ALT_CHILL 0x01 +#define MGVALLEYS_HUMID_RIVERS 0x02 // Feed only one variable into these. #define MYSQUARE(x) (x) * (x) @@ -96,7 +96,7 @@ public: ~MapgenValleys(); virtual void makeChunk(BlockMakeData *data); - int getGroundLevelAtPoint(v2s16 p); + int getSpawnLevelAtPoint(v2s16 p); s16 large_cave_depth; diff --git a/src/server.cpp b/src/server.cpp index 86096055..57253314 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3383,26 +3383,24 @@ v3f Server::findSpawnPos() return nodeposf * BS; } - s16 water_level = map.getWaterLevel(); - s16 vertical_spawn_range = g_settings->getS16("vertical_spawn_range"); bool is_good = false; // Try to find a good place a few times - for(s32 i = 0; i < 1000 && !is_good; i++) { + for(s32 i = 0; i < 4000 && !is_good; i++) { s32 range = 1 + i; // We're going to try to throw the player to this position v2s16 nodepos2d = v2s16( -range + (myrand() % (range * 2)), -range + (myrand() % (range * 2))); - // Get ground height at point - s16 groundheight = map.findGroundLevel(nodepos2d); - // Don't go underwater or to high places - if (groundheight <= water_level || - groundheight > water_level + vertical_spawn_range) + // 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; - v3s16 nodepos(nodepos2d.X, groundheight, nodepos2d.Y); + v3s16 nodepos(nodepos2d.X, spawn_level, nodepos2d.Y); s32 air_count = 0; for (s32 i = 0; i < 10; i++) {