From bc66bb2d409f13554bdcec7386766af82a343cad Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Sat, 2 Apr 2011 20:55:22 +0300 Subject: [PATCH] Mapgen is better now. Not a lot, but a bit! --- data/water.png | Bin 548 -> 512 bytes src/main.cpp | 8 +- src/map.cpp | 1050 ++++++++++++++++++++++++++++++++++++++++++--- src/map.h | 106 ++--- src/mapblock.cpp | 4 + src/mapblock.h | 29 +- src/mapchunk.h | 66 --- src/materials.cpp | 4 +- src/server.cpp | 17 +- 9 files changed, 1074 insertions(+), 210 deletions(-) delete mode 100644 src/mapchunk.h diff --git a/data/water.png b/data/water.png index 20f74edfe9536e2bf10b5d681d714ea0dfc8e130..9657dbed7dd74ee9545422c54d3f2b942a145e6e 100644 GIT binary patch delta 467 zcmV;^0WAKc1b_sPBn$&{LP=Bz2ngHZPvDV27k>exNklK8?!C8KRHqJAQzl+R05MD-{Z2$7 z#($c#v}*>y;^JvrLjX{383StCa$#X+w}jAZtvSyz`G6jCj?rrc;A50!VPY1yh9KTE zldKDfh)BZu1k+YZ%LPEQ8?y^3sToY~t?QUO!Wuk?4JoXiTC(uG;6(ONNAE9@Q%Grz@x8sMDFzW{{z13EG}8ReNX@Z002ov JPDHLkV1kRr+o=Em delta 503 zcmVZ;bV*G` z2ipS$201nX;HW)L_t(2&ux;)j@vL0M5~)kQqtJK0E2u={w#l;9FkZYx2_yy zcybt?o2ccLynp-vAP`j}2LJ$0SJS-QB?tfx)?;TMPB#?=pwaH{AGMsr2k3R%+1ZZm z`cFuj7ZExC{2?FXo_pPP0F9ey zNb|x=qZIZ5KrFiee6e801lRnt}WufB)U`_`REPv$XE$6q#@1G>o zyo8kT)w9cM%$el+=a;t*;8iF9?+!rinf94s)IySwe7}=?U5iLPb^w+$L;+Y`*Goa8 zT~J3Y^zq^qr}_W%=5*~CQpAF)wsM3RA^EDVJRYyHJS~M7)4b3}+mATSmZyI1qg|tN zkw^}b*Mb08oMr&+JW#wleWc|UQg#;hp)uXxzm>9;?GdN>GsDF}L^#-vO++q6BGSe| t5~A%qIQYro%P~KTvXJHlK-nMO{U4*AHje|xF<<}y002ovPDHLkV1mG0>;nJ* diff --git a/src/main.cpp b/src/main.cpp index ec406086..fb1b2d8f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -194,7 +194,7 @@ TODO: Copy the text of the last picked sign to inventory in creative TODO: Check what goes wrong with caching map to disk (Kray) - Nothing? -FIXME: Server went into some infinite PeerNotFoundException loop +FIXME: Server sometimes goes into some infinite PeerNotFoundException loop * Fix the problem with the server constantly saving one or a few blocks? List the first saved block, maybe it explains. @@ -259,7 +259,7 @@ FEATURE: Erosion simulation at map generation time - Simulate rock falling from cliffs when water has removed enough solid rock from the bottom -Mapgen v2: +Mapgen v2 (not doing): * only_from_disk might not work anymore - check and fix it. * Make the generator to run in background and not blocking block placement and transfer @@ -280,7 +280,7 @@ Mapgen v4 (not doing): * Make chunks to be tiled vertically too * MAKE IT FASTER -Mapgen v3: +Mapgen v3 (not doing): * Generate trees better - Add a "trees_added" flag to sector, or something * How 'bout making turbulence controlled so that for a given 2d position @@ -292,7 +292,7 @@ Mapgen v3: Mapgen v4: * This will be the final way. -* Generate blocks in the same way as chunks, by copying a voxelmanipulator +* Generate blocks in the same way as chunks, by copying a VoxelManipulator from the map that is one block larger in all directions. Misc. stuff: diff --git a/src/map.cpp b/src/map.cpp index aabe8406..651bece4 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1710,13 +1710,6 @@ ServerMap::ServerMap(std::string savedir): Map(dout_server), m_seed(0) { - - //m_chunksize = 64; - //m_chunksize = 16; // Too slow - //m_chunksize = 8; // Takes a few seconds - m_chunksize = 4; // Too small? - //m_chunksize = 2; - // TODO: Save to and load from a file m_seed = (((u64)(myrand()%0xffff)<<0) + ((u64)(myrand()%0xffff)<<16) @@ -1751,12 +1744,9 @@ ServerMap::ServerMap(std::string savedir): } else { - // Load map metadata (seed, chunksize) + // Load map metadata (seed) loadMapMeta(); - // Load chunk metadata - loadChunkMeta(); - /*// Load sector (0,0) and throw and exception on fail if(loadSectorFull(v2s16(0,0)) == false) throw LoadError("Failed to load sector (0,0)");*/ @@ -1819,16 +1809,6 @@ ServerMap::~ServerMap() dstream<::Iterator i = m_chunks.getIterator(); - for(; i.atEnd() == false; i++) - { - MapChunk *chunk = i.getNode()->getValue(); - delete chunk; - } } /* @@ -1883,6 +1863,7 @@ void make_tree(VoxelManipulator &vmanip, v3s16 p0) { MapNode treenode(CONTENT_TREE); MapNode leavesnode(CONTENT_LEAVES); + leavesnode.setLight(LIGHTBANK_DAY, LIGHT_MAX-1); vmanip.emerge(VoxelArea(p0-v3s16(2,0,2),p0+v3s16(2,7+2,2))); @@ -1962,18 +1943,18 @@ double tree_amount_2d(u64 seed, v2s16 p) double noise = noise2d_perlin( 0.5+(float)p.X/250, 0.5+(float)p.Y/250, seed+2, 5, 0.7); - double zeroval = -0.4; + double zeroval = -0.5; if(noise < zeroval) return 0; else - return 0.025 * (noise-zeroval) / (1.0-zeroval); + return 0.03 * (noise-zeroval) / (1.0-zeroval); } #define AVERAGE_MUD_AMOUNT 4.0 double get_mud_amount(u64 seed, v2f p) { - return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin( + return ((float)AVERAGE_MUD_AMOUNT + 2.5 * noise2d_perlin( 0.5+p.X/200, 0.5+p.Y/200, seed+1, 5, 0.65)); } @@ -2121,8 +2102,8 @@ double base_rock_level_2d(u64 seed, v2f p) { // Mountains double m4 = 1.0 - 3.0 * noise2d_perlin_abs( - 0.324+(float)p.X/2000., 0.423+(float)p.Y/2000., - (seed>>32)+65012102, 9, 0.57); + 0.324+(float)p.X/1000., 0.423+(float)p.Y/1000., + (seed>>32)+65012102, 8, 0.57); m4 *= 120; if(m4 > h) h = m4; @@ -2365,7 +2346,7 @@ bool is_carved(u64 seed, v3f p) #endif double f = 10.0; - double y_div = 1.5; + double y_div = 1.0; double v4 = contour(f*noise3d_perlin( 0.5+p.X/200, @@ -4082,6 +4063,7 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, } #endif +#if 0 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos, core::map &changed_blocks, bool force) @@ -4813,6 +4795,7 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos1, MapChunk *chunk = getChunk(chunkpos1); return chunk; } +#endif ServerMapSector * ServerMap::createSector(v2s16 p2d) { @@ -4873,7 +4856,8 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, DSTACK("%s: p2d=(%d,%d)", __FUNCTION_NAME, p2d.X, p2d.Y); - + +#if 0 /* Check chunk status */ @@ -4892,7 +4876,8 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, // Generate chunk and neighbors generateChunk(chunkpos, changed_blocks); } - +#endif + /* Return sector if it exists now */ @@ -4923,7 +4908,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d, < &changed_blocks, + bool force) +{ + DSTACK(__FUNCTION_NAME); + + /* + Don't generate if already fully generated + */ + if(force == false) + { + MapBlock *block = getBlockNoCreateNoEx(blockpos0); + if(block != NULL && block->isFullyGenerated()) + { + dstream<<"generateBlockRaw(): Block " + <<"("< blocks_created; + + { + //TimeTaker timer("generateBlockRaw() create area"); + + for(s16 x=-1; x<=1; x++) + for(s16 z=-1; z<=1; z++) + { + v2s16 sectorpos = sectorpos0 + v2s16(x,z); + ServerMapSector *sector = createSector(sectorpos); + assert(sector); + + for(s16 y=blockpos0.Y-1; y<=blockpos0.Y+1; y++) + { + v3s16 blockpos(sectorpos.X, y, sectorpos.Y); + + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block && block->isDummy() == false) + continue; + + block = createBlock(blockpos); + block->setFullyGenerated(false); + + blocks_created.push_back(blockpos); + + // Lighting won't be calculated + block->setLightingExpired(true); + // Lighting will be calculated + //block->setLightingExpired(false); + + /* + Block gets sunlight if this is true. + + This should be set to true when the top side of a block + is completely exposed to the sky. + + This doesn't matter if the initial lighting is done + here. + */ + //block->setIsUnderground(y != y_blocks_max); + block->setIsUnderground(false); + } + } + } + + /* + Now we have a big empty area of (16x16x16)x27. + + Make a ManualMapVoxelManipulator that contains the whole area. + */ + + ManualMapVoxelManipulator vmanip(this); + // Add the area we just generated + { + //TimeTaker timer("generateBlockRaw() initialEmerge"); + vmanip.initialEmerge(blockpos0-v3s16(1,1,1), blockpos0+v3s16(1,1,1)); + } + + // Clear all flags + vmanip.clearFlag(0xff); + + // Block type of blockpos0 + BlockType center_block_type = BT_SURFACE; + + /* + Generate general ground level to newly created blocks. + Only stone is used and it is converted to other stuff later on. + */ + { + // 22ms @cs=8 + //dstream<<"Generating base ground..."<::Iterator i = blocks_created.begin(); + i != blocks_created.end(); i++) + { + v3s16 blockpos = *i; + v2s16 sectorpos(blockpos.X, blockpos.Z); + + /* + Approximate whether this block is a surface block, an air + block or a ground block. + + This shall never mark a surface block as non-surface. + */ + + BlockType block_type = BT_SURFACE; + v3s16 p_nodes = blockpos * MAP_BLOCKSIZE; + s32 lowest_ground_y = 32767; + s32 highest_ground_y = -32768; + u8 water_material = CONTENT_WATERSOURCE; + + { + /* + Estimate surface at different positions of the block, to + try to accomodate the effect of turbulence. + */ + v3f checklist[] = { + v3f(0,0,0), + v3f(0,1,0), + v3f(0,1,1), + v3f(0,0,1), + v3f(1,0,0), + v3f(1,1,0), + v3f(1,1,1), + v3f(1,0,1), + v3f(0.5,0.5,0.5), + }; + v3f p_nodes_f = intToFloat(p_nodes, 1); + float surface_y_max = -1000000; + float surface_y_min = 1000000; + for(u32 i=0; i surface_y_max) + surface_y_max = surface_y_f; + if(surface_y_f < surface_y_min) + surface_y_min = surface_y_f; + } + + float block_low_y_f = p_nodes_f.Y; + float block_high_y_f = p_nodes_f.Y + MAP_BLOCKSIZE; + + /*dstream<<"surface_y_max="<= surface_y_max + d_up + && block_low_y_f > WATER_LEVEL + d_up) + { + //dstream<<"BT_SKY"<setIsUnderground(true); + } + + /* + If the block has ground, generate ground precisely. + */ + + if(block_type == BT_SURFACE || block_type == BT_GROUND) + { + for(s16 z0=0; z0 0.001); + + float surface_y_f = 0; + s16 surface_y = 0; + + float noturb_surface_y_f = base_rock_level_2d(m_seed, real_p2d_f); + s16 noturb_surface_y = noturb_surface_y_f; + + // Get some statistics of surface height + if(noturb_surface_y < lowest_ground_y) + lowest_ground_y = noturb_surface_y; + if(noturb_surface_y > highest_ground_y) + highest_ground_y = noturb_surface_y; + + // Use fast index incrementing + v3s16 em = vmanip.m_area.getExtent(); + u32 i = vmanip.m_area.index(v3s16( + real_p2d.X, + blockpos.Y*MAP_BLOCKSIZE, + real_p2d.Y)); + for(s16 y0=0; y0= TURBULENCE_BOTTOM_CUTOFF_Y); + + bool is_cavern = false; + + if(is_carved(m_seed, real_pos_f)) + { + is_ground = false; + if(real_y < noturb_surface_y) + is_cavern = true; + } + else + { + if(turb_for_node) + { + double depth_guess; + is_ground = is_base_ground(m_seed, + real_pos_f, &depth_guess); + + // Estimate the surface height + surface_y_f = (float)real_y + depth_guess; + surface_y = real_y + depth_guess; + + // Save some statistics of surface height + if(surface_y < lowest_ground_y) + lowest_ground_y = surface_y; + if(surface_y > highest_ground_y) + highest_ground_y = surface_y; + } + else + { + surface_y = noturb_surface_y; + } + + is_ground = (real_y <= surface_y); + } + + // If node is not ground, it's air or water + if(is_ground == false) + { + // If under water level, it's water + if(real_y < WATER_LEVEL && !is_cavern) + { + n.d = water_material; + u8 dist = 16; + if(real_y >= surface_y) + dist = WATER_LEVEL-real_y+1; + n.setLight(LIGHTBANK_DAY, + diminish_light(LIGHT_SUN, dist)); + /* + Add to transforming liquid queue (in case it'd + start flowing) + */ + m_transforming_liquid.push_back(real_pos); + } + // else air + else + { + n.d = CONTENT_AIR; + + /* + Guess lighting + */ + if(real_y > surface_y + 4) + n.setLight(LIGHTBANK_DAY, LIGHT_SUN); + } + } + // Else it's ground + else + { + if(is_underground_mud(m_seed, real_pos_f)) + n.d = CONTENT_MUD; + else + n.d = CONTENT_STONE; + } + + vmanip.m_data[i] = n; + vmanip.m_area.add_y(em, i, 1); + } + } + }// BT_SURFACE + else // BT_SKY or anything else + { + MapNode n_fill; + if(block_type == BT_GROUND) + { + //n_fill.d = CONTENT_STONE; + } + else if(block_type == BT_SKY) + { + n_fill.d = CONTENT_AIR; + n_fill.setLight(LIGHTBANK_DAY, LIGHT_SUN); + } + else // fallback + { + n_fill.d = CONTENT_MESE; + } + + for(s16 x=0; x=min; y--) + { + if(vmanip.m_data[i].d == CONTENT_STONE) + { + found = true; + break; + } + vmanip.m_area.add_y(em, i, -1); + } + if(found == false) + continue; + // Set mud + s16 mud_amount = get_mud_amount(m_seed, real_p2d_f); + for(s16 j=0; j= WATER_LEVEL) + vmanip.m_data[i].d = CONTENT_GRASS; + else + vmanip.m_data[i].d = CONTENT_MUD; + vmanip.m_area.add_y(em, i, -1); + } + } + } +#endif + } + + /* + Convert mud to sand + */ + if(center_block_type == BT_SURFACE) + { + for(s16 x=0; x= WATER_LEVEL + 2) + continue; + + { + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = surface_y; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + u32 not_sand_counter = 0; + for(s16 y=y_start; y>=y_nodes_min; y--) + { + MapNode *n = &vmanip.m_data[i]; + if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS) + { + n->d = CONTENT_SAND; + } + else + { + not_sand_counter++; + if(not_sand_counter > 3) + break; + } + + vmanip.m_area.add_y(em, i, -1); + } + } + } + } + + /* + Add some minerals + */ + + if(center_block_type == BT_SURFACE || center_block_type == BT_GROUND) + { + s16 underground_level = 1 - blockpos0.Y; + + /* + Add meseblocks + */ + for(s16 i=0; i= (blockpos0.Y+1)*MAP_BLOCKSIZE) + continue; + v3s16 p(x,y,z); + /* + Trees grow only on mud and grass + */ + { + u32 i = vmanip.m_area.index(v3s16(p)); + MapNode *n = &vmanip.m_data[i]; + if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) + continue; + } + p.Y++; + // Make a tree + make_tree(vmanip, p); + } + } + } + +#if 0 + /* + Initial lighting (sunlight) + TODO: Do the lighting this way, with the VoxelManipulator + */ + + core::map light_sources; + + { + // 750ms @cs=8, can't optimize more + TimeTaker timer1("initial lighting"); + + /* + Go through the edges and apply sunlight to them, not caring + about neighbors + */ + + // Four edges + for(s16 i=0; i<4; i++) + // Edge length + for(s16 j=lighting_min_d; + j<=lighting_max_d; + j++) + { + s16 x; + s16 z; + // +-X + if(i == 0 || i == 1) + { + x = (i==0) ? lighting_min_d : lighting_max_d; + if(i == 0) + z = lighting_min_d; + else + z = lighting_max_d; + } + // +-Z + else + { + z = (i==0) ? lighting_min_d : lighting_max_d; + if(i == 0) + x = lighting_min_d; + else + x = lighting_max_d; + } + + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + // Loop from top to down + { + u8 light = LIGHT_SUN; + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = y_nodes_max; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + for(s16 y=y_start; y>=y_nodes_min; y--) + { + MapNode *n = &vmanip.m_data[i]; + if(light_propagates_content(n->d) == false) + { + light = 0; + } + else if(light != LIGHT_SUN + || sunlight_propagates_content(n->d) == false) + { + if(light > 0) + light--; + } + + n->setLight(LIGHTBANK_DAY, light); + n->setLight(LIGHTBANK_NIGHT, 0); + + if(light != 0) + { + // Insert light source + light_sources.insert(v3s16(p2d.X, y, p2d.Y), true); + } + + // Increment index by y + vmanip.m_area.add_y(em, i, -1); + } + } + } + + /* + This has to be 1 smaller than the actual area, because + neighboring nodes are checked. + */ + for(s16 x=lighting_min_d+1; + x<=lighting_max_d-1; + x++) + for(s16 z=lighting_min_d+1; + z<=lighting_max_d-1; + z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + /* + Apply initial sunlight + */ + { + u8 light = LIGHT_SUN; + bool add_to_sources = false; + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = y_nodes_max; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + for(s16 y=y_start; y>=y_nodes_min; y--) + { + MapNode *n = &vmanip.m_data[i]; + + if(light_propagates_content(n->d) == false) + { + light = 0; + } + else if(light != LIGHT_SUN + || sunlight_propagates_content(n->d) == false) + { + if(light > 0) + light--; + } + + // This doesn't take much time + if(add_to_sources == false) + { + /* + Check sides. If side is not air or water, start + adding to light_sources. + */ + v3s16 dirs4[4] = { + v3s16(0,0,1), // back + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(-1,0,0), // left + }; + for(u32 di=0; di<4; di++) + { + v3s16 dirp = dirs4[di]; + u32 i2 = i; + vmanip.m_area.add_p(em, i2, dirp); + MapNode *n2 = &vmanip.m_data[i2]; + if( + n2->d != CONTENT_AIR + && n2->d != CONTENT_WATERSOURCE + && n2->d != CONTENT_WATER + ){ + add_to_sources = true; + break; + } + } + } + + n->setLight(LIGHTBANK_DAY, light); + n->setLight(LIGHTBANK_NIGHT, 0); + + // This doesn't take much time + if(light != 0 && add_to_sources) + { + // Insert light source + light_sources.insert(v3s16(p2d.X, y, p2d.Y), true); + } + + // Increment index by y + vmanip.m_area.add_y(em, i, -1); + } + } + } + + }//timer1 + + // Spread light around + { + TimeTaker timer("generateBlockRaw() spreadLight"); + vmanip.spreadLight(LIGHTBANK_DAY, light_sources); + } +#endif + + /* + Blit generated stuff to map + */ + { + vmanip.blitBackAll(&changed_blocks); + } + +#if 0 + /* + Update day/night difference cache of the MapBlocks + */ + { + for(core::map::Iterator i = changed_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + block->updateDayNightDiff(); + } + } +#endif + + /*for(core::map::Iterator + i = changed_blocks*/ + + // Done! + MapBlock *block = getBlockNoCreate(blockpos0); + block->setFullyGenerated(true); + + /* + TODO: Calculate lighting with the VoxelManipulator, not this way + */ + // emergeBlock reads this + block->setLightingExpired(true); + // Also set the above one, trees often will be there + { + MapBlock *block = getBlockNoCreate(blockpos0+v3s16(0,1,0)); + if(block) + block->setLightingExpired(true); + } + + return block; +} + + +/*MapBlock* ServerMap::generateBlock(v3s16 blockpos1, + core::map &changed_blocks)*/ +MapBlock * ServerMap::generateBlock( + v3s16 blockpos1, + MapBlock *original_dummy, + ServerMapSector *sector, + core::map &changed_blocks, + core::map &lighting_invalidated_blocks +) +{ + dstream<<"generateBlock(): Generating block " + <<"("<isFullyGenerated()) + continue; + generateBlockRaw(blockpos0, changed_blocks); + } + + assert(blockNonVolatile(blockpos1)); + + MapBlock *block = getBlockNoCreate(blockpos1); + + return block; +} + +#if 0 /* NOTE: This is not used for main map generation, only for blocks that are very high or low. @@ -5887,6 +6822,7 @@ continue_generating: return block; } +#endif MapBlock * ServerMap::createBlock(v3s16 p) { @@ -6019,6 +6955,7 @@ MapBlock * ServerMap::emergeBlock( bool does_not_exist = false; bool lighting_expired = false; + bool half_generated = false; MapBlock *block = sector->getBlockNoCreateNoEx(block_y); if(block == NULL) @@ -6033,6 +6970,10 @@ MapBlock * ServerMap::emergeBlock( { lighting_expired = true; } + else if(block->isFullyGenerated() == false) + { + half_generated = true; + } else { // Valid block @@ -6040,24 +6981,27 @@ MapBlock * ServerMap::emergeBlock( return block; } - /* - If block was not found on disk and not going to generate a - new one, make sure there is a dummy block in place. - */ - if(only_from_disk && (does_not_exist || lighting_expired)) + if(half_generated == false) { - //dstream<<"emergeBlock(): Was not on disk but not generating"<insertBlock(block); + if(block == NULL) + { + // Create dummy block + block = new MapBlock(this, p, true); + + // Add block to sector + sector->insertBlock(block); + } + // Done. + return block; } - // Done. - return block; } //dstream<<"Not found on disk, generating."< list = fs::GetDirListing(m_savedir+"/sectors/"); @@ -6363,7 +7307,8 @@ void ServerMap::saveMapMeta() DSTACK(__FUNCTION_NAME); dstream<<"INFO: ServerMap::saveMapMeta(): " - <<"seed="<::Node *n; - n = m_chunks.find(chunkpos); - if(n == NULL) - return NULL; - return n->getValue(); - } - - /* - True if the chunk and its neighbors are fully generated. - It means the chunk will not be touched in the future by the - generator. If false, generateChunk will make it true. - */ - bool chunkNonVolatile(v2s16 chunkpos) - { - /*for(s16 x=-1; x<=1; x++) - for(s16 y=-1; y<=1; y++)*/ - s16 x=0; - s16 y=0; + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + for(s16 z=-1; z<=1; z++) { - v2s16 chunkpos0 = chunkpos + v2s16(x,y); - MapChunk *chunk = getChunk(chunkpos); - if(chunk == NULL) + v3s16 blockpos0 = blockpos + v3s16(x,y,z); + MapBlock *block = getBlockNoCreateNoEx(blockpos); + if(block == NULL) return false; - if(chunk->getGenLevel() != GENERATED_FULLY) + if(block->isFullyGenerated() == false) return false; } return true; } - /* - Generate a chunk. - - All chunks touching this one can be altered also. - */ - MapChunk* generateChunkRaw(v2s16 chunkpos, - core::map &changed_blocks, - bool force=false); - - /* - Generate a chunk and its neighbors so that it won't be touched - anymore. - */ - MapChunk* generateChunk(v2s16 chunkpos, - core::map &changed_blocks); - /* Generate a sector. - - This is mainly called by generateChunkRaw. */ //ServerMapSector * generateSector(v2s16 p); @@ -437,6 +384,27 @@ public: return emergeSector(p, changed_blocks); } + /*MapBlock * generateBlock( + v3s16 p, + MapBlock *original_dummy, + ServerMapSector *sector, + core::map &changed_blocks, + core::map &lighting_invalidated_blocks + );*/ + + /* + Generate a block. + + All blocks touching this one can be altered also. + */ + MapBlock* generateBlockRaw(v3s16 blockpos, + core::map &changed_blocks, + bool force=false); + + /* + Generate a block and its neighbors so that it won't be touched + anymore. + */ MapBlock * generateBlock( v3s16 p, MapBlock *original_dummy, @@ -444,6 +412,8 @@ public: core::map &changed_blocks, core::map &lighting_invalidated_blocks ); + /*MapBlock* generateBlock(v3s16 blockpos, + core::map &changed_blocks);*/ /* Get a block from somewhere. @@ -516,9 +486,6 @@ public: void saveMapMeta(); void loadMapMeta(); - void saveChunkMeta(); - void loadChunkMeta(); - // The sector mutex should be locked when calling most of these // This only saves sector-specific data such as the heightmap @@ -551,11 +518,6 @@ private: std::string m_savedir; bool m_map_saving_enabled; - - // Chunk size in MapSectors - s16 m_chunksize; - // Chunks - core::map m_chunks; }; /* diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 9594b296..94c6ad90 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -36,6 +36,7 @@ MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy): is_underground(false), m_lighting_expired(true), m_day_night_differs(false), + m_not_fully_generated(false), m_objects(this) { data = NULL; @@ -1762,6 +1763,8 @@ void MapBlock::serialize(std::ostream &os, u8 version) flags |= 0x02; if(m_lighting_expired) flags |= 0x04; + if(m_not_fully_generated) + flags |= 0x08; os.write((char*)&flags, 1); u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; @@ -1884,6 +1887,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version) is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; m_lighting_expired = (flags & 0x04) ? true : false; + m_not_fully_generated = (flags & 0x08) ? true : false; // Uncompress data std::ostringstream os(std::ios_base::binary); diff --git a/src/mapblock.h b/src/mapblock.h index 02c07289..97129bd3 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -244,6 +244,17 @@ public: return m_lighting_expired; } + bool isFullyGenerated() + { + return !m_not_fully_generated; + } + + void setFullyGenerated(bool b) + { + setChangedFlag(); + m_not_fully_generated = !b; + } + bool isValid() { if(m_lighting_expired) @@ -655,12 +666,28 @@ private: // Whether day and night lighting differs bool m_day_night_differs; + /* + Whether everything that is mainly located on this block has + been added to the world. + + While this is false, a block can still be changed a bit when + stuff is added to the neighboring blocks that extends to this + one. + + When this is false on every one of a 3x3x3 chunk of blocks, the + central one will not be changed by the map generator in the + future. + + TODO: Save in file + */ + bool m_not_fully_generated; + MapBlockObjectList m_objects; // Object spawning stuff float m_spawn_timer; -#ifndef SERVER +#ifndef SERVER // Only on client /* Set to true if the mesh has been ordered to be updated sometime in the background. diff --git a/src/mapchunk.h b/src/mapchunk.h deleted file mode 100644 index 1819fa13..00000000 --- a/src/mapchunk.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 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 General Public License for more details. - -You should have received a copy of the GNU 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 MAPCHUNK_HEADER -#define MAPCHUNK_HEADER - -/* - MapChunk contains map-generation-time metadata for an area of - some MapSectors. (something like 16x16) -*/ - -// These should fit in 8 bits, as they are saved as such. -enum{ - GENERATED_FULLY = 0, - GENERATED_PARTLY = 10, - GENERATED_NOT = 20 -}; - -class MapChunk -{ -public: - MapChunk(): - m_generation_level(GENERATED_NOT) - { - } - - /* - Generation level. Possible values: - GENERATED_FULLY = 0 = fully generated - GENERATED_PARTLY = partly generated - GENERATED_NOT = not generated - */ - u16 getGenLevel(){ return m_generation_level; } - void setGenLevel(u16 lev){ m_generation_level = lev; } - - void serialize(std::ostream &os, u8 version) - { - os.write((char*)&m_generation_level, 1); - } - void deSerialize(std::istream &is, u8 version) - { - is.read((char*)&m_generation_level, 1); - } - -private: - u8 m_generation_level; -}; - -#endif - diff --git a/src/materials.cpp b/src/materials.cpp index f56b024b..0558a5e3 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -13,9 +13,9 @@ void setStoneLikeDiggingProperties(u8 material, float toughness) DiggingProperties(true, 15.0*toughness, 0)); g_material_properties[material].setDiggingProperties("WPick", - DiggingProperties(true, 1.5*toughness, 65535./30.*toughness)); + DiggingProperties(true, 1.3*toughness, 65535./30.*toughness)); g_material_properties[material].setDiggingProperties("STPick", - DiggingProperties(true, 0.7*toughness, 65535./100.*toughness)); + DiggingProperties(true, 0.65*toughness, 65535./100.*toughness)); /*g_material_properties[material].setDiggingProperties("MesePick", DiggingProperties(true, 0.0*toughness, 65535./20.*toughness));*/ diff --git a/src/server.cpp b/src/server.cpp index 61f35417..abdbd975 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -544,11 +544,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, block_is_invalid = true; } - v2s16 p2d(p.X, p.Z); - ServerMap *map = (ServerMap*)(&server->m_env.getMap()); - v2s16 chunkpos = map->sector_to_chunk(p2d); - if(map->chunkNonVolatile(chunkpos) == false) + if(block->isFullyGenerated() == false) + { block_is_invalid = true; + } } /* @@ -3420,16 +3419,6 @@ Player *Server::emergePlayer(const char *name, const char *password, nodepos = v2s16(-range + (myrand()%(range*2)), -range + (myrand()%(range*2))); v2s16 sectorpos = getNodeSectorPos(nodepos); - /* - Ignore position if it is near a chunk edge. - Otherwise it would cause excessive loading time at - initial generation - */ - { - if(m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(1,1)) - != m_env.getServerMap().sector_to_chunk(sectorpos+v2s16(-1,-1))) - continue; - } // Get sector (NOTE: Don't get because it's slow) //m_env.getMap().emergeSector(sectorpos); // Get ground height at point (fallbacks to heightmap function)