From 6e196c2ce4285c0aea2a5c714e842d90c1b84b43 Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Tue, 1 Feb 2011 03:06:02 +0200 Subject: [PATCH] partly working chunk-based map generator (doesn't save properly, spawn is pretty random) --- src/irrlichtwrapper.cpp | 10 + src/irrlichtwrapper.h | 6 + src/main.cpp | 41 +- src/map.cpp | 1297 +++++++++++++++++++++++++++++++++++---- src/map.h | 33 +- src/mapblock.cpp | 11 + src/mapblock.h | 2 + src/mapnode.h | 8 + src/mapsector.cpp | 9 +- src/server.cpp | 12 +- src/utility.h | 20 + src/voxel.cpp | 19 +- src/voxel.h | 28 + 13 files changed, 1370 insertions(+), 126 deletions(-) diff --git a/src/irrlichtwrapper.cpp b/src/irrlichtwrapper.cpp index e5cab98c..a1d37f4b 100644 --- a/src/irrlichtwrapper.cpp +++ b/src/irrlichtwrapper.cpp @@ -5,6 +5,7 @@ IrrlichtWrapper::IrrlichtWrapper(IrrlichtDevice *device) { + m_running = true; m_main_thread = get_current_thread_id(); m_device_mutex.Init(); m_device = device; @@ -35,6 +36,11 @@ void IrrlichtWrapper::Run() } } +void IrrlichtWrapper::Shutdown(bool shutdown) +{ + m_running = !shutdown; +} + textureid_t IrrlichtWrapper::getTextureId(const std::string &name) { u32 id = m_namecache.getId(name); @@ -73,6 +79,10 @@ video::ITexture* IrrlichtWrapper::getTexture(const TextureSpec &spec) } else { + // If irrlicht has shut down, just return NULL + if(m_running == false) + return NULL; + // We're gonna ask the result to be put into here ResultQueue result_queue; diff --git a/src/irrlichtwrapper.h b/src/irrlichtwrapper.h index 225ae340..965d0120 100644 --- a/src/irrlichtwrapper.h +++ b/src/irrlichtwrapper.h @@ -132,11 +132,15 @@ public: /* These are called from the main thread */ + IrrlichtWrapper(IrrlichtDevice *device); // Run queued tasks void Run(); + // Shutdown wrapper; this disables queued texture fetching + void Shutdown(bool shutdown); + /* These are called from other threads */ @@ -181,6 +185,8 @@ private: /* Members */ + + bool m_running; // The id of the thread that can (and has to) use irrlicht directly threadid_t m_main_thread; diff --git a/src/main.cpp b/src/main.cpp index dc4716fa..87deefee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -288,7 +288,7 @@ FEATURE: Map generator version 2 where some minerals are found - Create a system that allows a huge amount of different "map generator modules/filters" - + FEATURE: The map could be generated procedually: - This would need the map to be generated in larger pieces - How large? How do they connect to each other? @@ -296,7 +296,10 @@ FEATURE: The map could be generated procedually: - Lighting would not have to be necessarily calculated until the blocks are actually needed - it would be quite fast - Something like 64*64*16 MapBlocks? - - TODO: Separate lighting and block generation + - No, MapSectors. And as much as it is efficient to do, + 64x64 might be too much. + - FIXME: This is currently halfway done and the generator is + fairly broken * Make the stone level with a heightmap * Carve out stuff in the stone * Dump dirt all around, and simulate it falling off steep @@ -311,16 +314,25 @@ FEATURE: The map could be generated procedually: parameter field is free for this. - Simulate rock falling from cliffs when water has removed enough solid rock from the bottom -TODO: Lazy lighting updates: - - Set updateLighting to ignore MapBlocks with expired lighting, - except the blocks specified to it - - When a MapBlock is generated, lighting expires in all blocks - touching it (26 blocks + self) - - When a lighting-wise valid MapBlock is needed and lighting of it - has expired, what to do? Doing now: ---------- +# maybe done +* not done + +* Remove all kinds of systems that are made redundant by the new map + generator + - Sector heightmaps? At least they should be made redundant. + - Sector objects +* Do something about AttributeDatabase/List being too slow +* Save chunk metadata on disk +* Change water side textures so that buggy water doesn't look bad +* Make server find the spawning place from the real map data, not from + the heightmap +* only_from_disk doesn't work that well anymore +* Make the generator to run in background and not blocking block + placement and transfer +* Fix the strange mineral occurences ====================================================================== @@ -1886,6 +1898,9 @@ int main(int argc, char *argv[]) */ { + // This is set to true at the end of the scope + g_irrlicht->Shutdown(false); + /* Draw "Loading" screen */ @@ -3017,6 +3032,14 @@ int main(int argc, char *argv[]) delete quick_inventory; + /* + Disable texture fetches and other stuff that is queued + to be processed by the main loop. + + This has to be done before client goes out of scope. + */ + g_irrlicht->Shutdown(true); + } // client and server are deleted at this point } //try diff --git a/src/map.cpp b/src/map.cpp index cc1a6d63..63fccc43 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -133,6 +133,18 @@ MapBlock * Map::getBlockNoCreateNoEx(v3s16 p3d) } } +/*MapBlock * Map::getBlockCreate(v3s16 p3d) +{ + v2s16 p2d(p3d.X, p3d.Z); + MapSector * sector = getSectorCreate(p2d); + assert(sector); + MapBlock *block = sector->getBlockNoCreate(p3d.Y); + if(block) + return block; + block = sector->createBlankBlock(p3d.Y); + return block; +}*/ + f32 Map::getGroundHeight(v2s16 p, bool generate) { try{ @@ -1724,7 +1736,8 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): //m_chunksize = 64; //m_chunksize = 16; //m_chunksize = 8; - m_chunksize = 2; + m_chunksize = 4; + //m_chunksize = 2; /* Experimental and debug stuff @@ -1739,6 +1752,7 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): PointAttributeList *list_plants_amount = m_padb.getList("plants_amount"); PointAttributeList *list_caves_amount = m_padb.getList("caves_amount"); +#if 0 /* NOTE: BEWARE: Too big amount of these will make map generation slow. Especially those that are read by every block emerge. @@ -1749,13 +1763,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): 15000 points: 40ms */ - for(u32 i=0; i<5000; i++) + for(u32 i=0; i<500; i++) { /*u32 lim = MAP_GENERATION_LIMIT; if(i < 400) lim = 2000;*/ - u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000; + u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500; v3s16 p( -lim + myrand()%(lim*2), @@ -1788,13 +1802,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): list_plants_amount->addPoint(p, Attribute(plants_amount)); } - for(u32 i=0; i<1000; i++) + for(u32 i=0; i<500; i++) { /*u32 lim = MAP_GENERATION_LIMIT; if(i < 400) lim = 2000;*/ - u32 lim = 500 + MAP_GENERATION_LIMIT * i / 1000; + u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500; v3s16 p( -lim + myrand()%(lim*2), @@ -1819,13 +1833,13 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): list_caves_amount->addPoint(p, Attribute(caves_amount)); } - for(u32 i=0; i<5000; i++) + for(u32 i=0; i<500; i++) { /*u32 lim = MAP_GENERATION_LIMIT; if(i < 400) lim = 2000;*/ - u32 lim = 1000 + MAP_GENERATION_LIMIT * i / 5000; + u32 lim = 500 + MAP_GENERATION_LIMIT * i / 500; v3s16 p( -lim + (myrand()%(lim*2)), @@ -1896,10 +1910,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp): list_randmax->addPoint(p, Attribute(randmax)); list_randfactor->addPoint(p, Attribute(randfactor)); } - - /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(5)); - list_randmax->addPoint(v3s16(0,0,0), Attribute(20)); - list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.6));*/ +#endif + + // Add only one entry + list_baseheight->addPoint(v3s16(0,0,0), Attribute(0)); + list_randmax->addPoint(v3s16(0,0,0), Attribute(30)); + list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.45)); // Easy spawn point /*list_baseheight->addPoint(v3s16(0,0,0), Attribute(0)); @@ -2010,6 +2026,7 @@ ServerMap::~ServerMap() } } +#if 0 MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos) { // Return if chunk already exists @@ -2080,12 +2097,12 @@ MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos) dstream<<"generateChunkRaw generated "< lighting_modified_blocks; updateLighting(lighting_invalidated_blocks, lighting_modified_blocks); - } + }*/ // Add chunk meta information chunk = new MapChunk(); @@ -2114,7 +2131,948 @@ MapChunk* ServerMap::generateChunk(v2s16 chunkpos) // Return it return chunk; } +#endif +MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos) +{ + dstream<<"WARNING: No-op "<<__FUNCTION_NAME<<" called"<=y_nodes_min; y--) + { + MapNode &n = vmanip.m_data[i]; + if(content_walkable(n.d)) + break; + + vmanip.m_area.add_y(em, i, -1); + } + if(y >= y_nodes_min) + return y; + else + return y_nodes_min; +} + +void make_tree(VoxelManipulator &vmanip, v3s16 p0) +{ + MapNode treenode(CONTENT_TREE); + MapNode leavesnode(CONTENT_LEAVES); + + s16 trunk_h = myrand_range(2, 6); + v3s16 p1 = p0; + for(s16 ii=0; ii leaves_d(new u8[leaves_a.getVolume()]); + for(s32 i=0; isetLightingExpired(true); + // Lighting will be calculated + block->setLightingExpired(false); + + /* + TODO: Do this better. + 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. + */ + block->setIsUnderground(y != y_blocks_max); + } + } + } + + /* + Now we have a big empty area. + + Make a ManualMapVoxelManipulator that contains this and the + neighboring chunks + */ + + ManualMapVoxelManipulator vmanip(this); + // Add the area we just generated + { + TimeTaker timer("generateChunk() initialEmerge"); + vmanip.initialEmerge(bigarea_blocks_min, bigarea_blocks_max); + } + + TimeTaker timer_generate("generateChunk() generate"); + + /* + Generate general ground level to full area + */ + + for(s16 x=0; xgetGroundHeight(sector_relpos); + if(h > GROUNDHEIGHT_VALID_MINVALUE) + surface_y_f = h; + else + dstream<<"WARNING: "<<__FUNCTION_NAME + <<": sector->getGroundHeight returned bad height"<=y_nodes_min; y--) + { + MapNode &n = vmanip.m_data[i]; + /*if(content_walkable(n.d) + && n.d != CONTENT_MUD + && n.d != CONTENT_GRASS) + break;*/ + if(n.d == CONTENT_STONE) + break; + + if(n.d == CONTENT_MUD || n.d == CONTENT_GRASS) + mud_amount++; + + vmanip.m_area.add_y(em, i, -1); + } + if(y >= y_nodes_min) + surface_y = y; + else + surface_y = y_nodes_min; + } + + + /* + Add stone on ground + */ + { + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = surface_y+1; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); + s16 y; + // Add stone + s16 count = 0; + for(y=y_start; y<=y_nodes_max; y++) + { + MapNode &n = vmanip.m_data[i]; + n.d = CONTENT_STONE; + count++; + if(count >= ob_size.Y) + break; + + vmanip.m_area.add_y(em, i, 1); + } + // Add mud + count = 0; + for(; y<=y_nodes_max; y++) + { + MapNode &n = vmanip.m_data[i]; + n.d = CONTENT_MUD; + count++; + if(count >= mud_amount) + break; + + vmanip.m_area.add_y(em, i, 1); + } + } + + } + } + + /* + Make dungeons + */ + for(u32 jj=0; jj<2; jj++) + { + s16 max_tunnel_diameter = 8; + + // Allowed route area size in nodes + v3s16 ar( + sectorpos_base_size*MAP_BLOCKSIZE, + h_blocks*MAP_BLOCKSIZE, + sectorpos_base_size*MAP_BLOCKSIZE + ); + + // Area starting point in nodes + v3s16 of( + sectorpos_base.X*MAP_BLOCKSIZE, + y_blocks_min*MAP_BLOCKSIZE, + sectorpos_base.Y*MAP_BLOCKSIZE + ); + + // Allow a bit more + //(this should be more than the maximum radius of the tunnel) + s16 more = max_spread_amount - max_tunnel_diameter/2 - 1; + ar += v3s16(1,0,1) * more * 2; + of -= v3s16(1,0,1) * more; + + // Randomize starting position + v3f orp( + (float)(myrand()%ar.X)+0.5, + (float)(myrand()%ar.Y)+0.5, + (float)(myrand()%ar.Z)+0.5 + ); + + MapNode airnode(CONTENT_AIR); + + /* + Generate some tunnel starting from orp + */ + + for(u16 j=0; j<10; j++) + { + /*v3f rp( + (float)(myrand()%ar.X)+0.5, + (float)(myrand()%ar.Y)+0.5, + (float)(myrand()%ar.Z)+0.5 + ); + v3f vec = rp - orp;*/ + + v3s16 maxlen(60, 10, 60); + v3f vec( + (float)(myrand()%(maxlen.X*2))-(float)maxlen.X, + (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y, + (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z + ); + v3f rp = orp + vec; + if(rp.X < 0) + rp.X = 0; + else if(rp.X >= ar.X) + rp.X = ar.X; + if(rp.Y < 0) + rp.Y = 0; + else if(rp.Y >= ar.Y) + rp.Y = ar.Y; + if(rp.Z < 0) + rp.Z = 0; + else if(rp.Z >= ar.Z) + rp.Z = ar.Z; + vec = rp - orp; + + // Randomize size + s16 min_d = 0; + s16 max_d = max_tunnel_diameter; + s16 rs = myrand_range(min_d, max_d); + + for(float f=0; f<1.0; f+=1.0/vec.getLength()) + { + v3f fp = orp + vec * f; + v3s16 cp(fp.X, fp.Y, fp.Z); + s16 d0 = -rs/2; + s16 d1 = d0 + rs - 1; + for(s16 z0=d0; z0<=d1; z0++) + { + s16 si = rs - abs(z0); + for(s16 x0=-si; x0<=si-1; x0++) + { + s16 si2 = rs - abs(x0); + for(s16 y0=-si2+1; y0<=si2-1; y0++) + { + s16 z = cp.Z + z0; + s16 y = cp.Y + y0; + s16 x = cp.X + x0; + v3s16 p(x,y,z); + /*if(isInArea(p, ar) == false) + continue;*/ + // Check only height + if(y < 0 || y >= ar.Y) + continue; + p += of; + + assert(vmanip.m_area.contains(p)); + + // Just set it to air, it will be changed to + // water afterwards + u32 i = vmanip.m_area.index(p); + vmanip.m_data[i] = airnode; + } + } + } + } + + orp = rp; + } + + } + + /* + Make ore veins + */ + for(u32 jj=0; jj<1000; jj++) + { + s16 max_vein_diameter = 3; + + // Allowed route area size in nodes + v3s16 ar( + sectorpos_base_size*MAP_BLOCKSIZE, + h_blocks*MAP_BLOCKSIZE, + sectorpos_base_size*MAP_BLOCKSIZE + ); + + // Area starting point in nodes + v3s16 of( + sectorpos_base.X*MAP_BLOCKSIZE, + y_blocks_min*MAP_BLOCKSIZE, + sectorpos_base.Y*MAP_BLOCKSIZE + ); + + // Allow a bit more + //(this should be more than the maximum radius of the tunnel) + s16 more = max_spread_amount - max_vein_diameter/2 - 1; + ar += v3s16(1,0,1) * more * 2; + of -= v3s16(1,0,1) * more; + + // Randomize starting position + v3f orp( + (float)(myrand()%ar.X)+0.5, + (float)(myrand()%ar.Y)+0.5, + (float)(myrand()%ar.Z)+0.5 + ); + + // Randomize mineral + u8 mineral = myrand_range(1, MINERAL_COUNT-1); + + /* + Generate some vein starting from orp + */ + + for(u16 j=0; j<2; j++) + { + /*v3f rp( + (float)(myrand()%ar.X)+0.5, + (float)(myrand()%ar.Y)+0.5, + (float)(myrand()%ar.Z)+0.5 + ); + v3f vec = rp - orp;*/ + + v3s16 maxlen(10, 10, 10); + v3f vec( + (float)(myrand()%(maxlen.X*2))-(float)maxlen.X, + (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y, + (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z + ); + v3f rp = orp + vec; + if(rp.X < 0) + rp.X = 0; + else if(rp.X >= ar.X) + rp.X = ar.X; + if(rp.Y < 0) + rp.Y = 0; + else if(rp.Y >= ar.Y) + rp.Y = ar.Y; + if(rp.Z < 0) + rp.Z = 0; + else if(rp.Z >= ar.Z) + rp.Z = ar.Z; + vec = rp - orp; + + // Randomize size + s16 min_d = 0; + s16 max_d = max_vein_diameter; + s16 rs = myrand_range(min_d, max_d); + + for(float f=0; f<1.0; f+=1.0/vec.getLength()) + { + v3f fp = orp + vec * f; + v3s16 cp(fp.X, fp.Y, fp.Z); + s16 d0 = -rs/2; + s16 d1 = d0 + rs - 1; + for(s16 z0=d0; z0<=d1; z0++) + { + s16 si = rs - abs(z0); + for(s16 x0=-si; x0<=si-1; x0++) + { + s16 si2 = rs - abs(x0); + for(s16 y0=-si2+1; y0<=si2-1; y0++) + { + // Don't put mineral to every place + if(myrand()%5 != 0) + continue; + + s16 z = cp.Z + z0; + s16 y = cp.Y + y0; + s16 x = cp.X + x0; + v3s16 p(x,y,z); + /*if(isInArea(p, ar) == false) + continue;*/ + // Check only height + if(y < 0 || y >= ar.Y) + continue; + p += of; + + assert(vmanip.m_area.contains(p)); + + // Just set it to air, it will be changed to + // water afterwards + u32 i = vmanip.m_area.index(p); + MapNode *n = &vmanip.m_data[i]; + if(n->d == CONTENT_STONE) + n->param = mineral; + } + } + } + } + + orp = rp; + } + + } + + /* + Add mud to the central chunk + */ + + for(s16 x=0; x= 3) + break; + + vmanip.m_area.add_y(em, i, 1); + } + } + + } + + /* + Flow mud away from steep edges + */ + + // Iterate a few times + for(s16 k=0; k<4; k++) + { + + for(s16 x=0-max_spread_amount+1; + x=y_nodes_min; y--) + { + MapNode &n = vmanip.m_data[i]; + //if(n.d != CONTENT_AIR) + if(content_walkable(n.d)) + break; + + vmanip.m_area.add_y(em, i, -1); + } + + // If not mud, do nothing to it + MapNode *n = &vmanip.m_data[i]; + if(n->d != CONTENT_MUD) + continue; + + v3s16 dirs4[4] = { + v3s16(0,0,1), // back + v3s16(1,0,0), // right + v3s16(0,0,-1), // front + v3s16(-1,0,0), // left + }; + + // Drop mud on side + + for(u32 di=0; di<4; di++) + { + v3s16 dirp = dirs4[di]; + u32 i2 = i; + // Check that side is air + vmanip.m_area.add_p(em, i2, dirp); + MapNode *n2 = &vmanip.m_data[i2]; + if(content_walkable(n2->d)) + continue; + // Check that under side is air + vmanip.m_area.add_y(em, i2, -1); + n2 = &vmanip.m_data[i2]; + if(content_walkable(n2->d)) + continue; + // Loop further down until not air + do{ + vmanip.m_area.add_y(em, i2, -1); + n2 = &vmanip.m_data[i2]; + }while(content_walkable(n2->d) == false); + // Loop one up so that we're in air + vmanip.m_area.add_y(em, i2, 1); + n2 = &vmanip.m_data[i2]; + + // Move mud to new place + *n2 = *n; + // Set old place to be air + *n = MapNode(CONTENT_AIR); + + #if 0 + // Switch mud and other and change mud source to air + //MapNode tempnode = *n2; + *n2 = *n; + //*n = tempnode; + // Force old mud position to be air + n->d = CONTENT_AIR; + #endif + + // Done + break; + } + } + + } + + /* + Add water to the central chunk (and a bit more) + */ + + for(s16 x=0-max_spread_amount; + x WATER_LEVEL) + continue; + + /* + Add water on ground + */ + { + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = WATER_LEVEL; + u8 light = LIGHT_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]; + + // Fill gaps inside water, too + if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE + && n->d != CONTENT_WATER) + break; + + n->d = CONTENT_WATERSOURCE; + n->setLight(LIGHTBANK_DAY, light); + + /* + Add to transforming liquid queue (in case it'd + start flowing) + */ + v3s16 p = v3s16(p2d.X, y, p2d.Y); + m_transforming_liquid.push_back(p); + + // Next one + vmanip.m_area.add_y(em, i, -1); + if(light > 0) + light--; + } + } + + } + + /* + Plant some trees + */ + { + u32 tree_max = 100; + + u32 count = myrand_range(0, tree_max); + for(u32 i=0; i=y_nodes_min; y--) + { + MapNode &n = vmanip.m_data[i]; + if(n.d != CONTENT_AIR + && n.d != CONTENT_LEAVES) + break; + vmanip.m_area.add_y(em, i, -1); + } + if(y >= y_nodes_min) + surface_y = y; + else + surface_y = y_nodes_min; + } + + u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y); + MapNode *n = &vmanip.m_data[i]; + if(n->d == CONTENT_MUD) + n->d = CONTENT_GRASS; + } + + /* + Handle lighting + */ + + core::map light_sources; + + /*for(s16 x=0; x=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); + } + } + } + + // Spread light around + { + TimeTaker timer("generateChunk() spreadLight"); + vmanip.spreadLight(LIGHTBANK_DAY, light_sources); + } + + /* + Generation ended + */ + + timer_generate.stop(); + + /* + Blit generated stuff to map + */ + core::map modified_blocks; + { + TimeTaker timer("generateChunk() blitBackAll"); + vmanip.blitBackAll(&modified_blocks); + } + /* + Update day/night difference cache of the MapBlocks + */ + { + for(core::map::Iterator i = modified_blocks.getIterator(); + i.atEnd() == false; i++) + { + MapBlock *block = i.getNode()->getValue(); + block->updateDayNightDiff(); + } + } + + + /* + Create chunks and set them volatile + */ + + for(s16 x=-1; x<=1; x++) + for(s16 y=-1; y<=1; y++) + { + v2s16 chunkpos0 = chunkpos + v2s16(x,y); + // Add chunk meta information + MapChunk *chunk = getChunk(chunkpos); + if(chunk == NULL) + { + chunk = new MapChunk(); + m_chunks.insert(chunkpos0, chunk); + } + chunk->setIsVolatile(true); + } + + /* + Set central chunk non-volatile and return it + */ + MapChunk *chunk = getChunk(chunkpos); + assert(chunk); + // Set non-volatile + chunk->setIsVolatile(false); + // Return it + return chunk; +} + +#if 0 ServerMapSector * ServerMap::generateSector(v2s16 p2d) { DSTACK("%s: p2d=(%d,%d)", @@ -2131,7 +3089,7 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d) */ if(m_heightmap == NULL) { - throw InvalidPositionException("emergeSector(): no heightmap"); + throw InvalidPositionException("generateSector(): no heightmap"); } /* @@ -2141,7 +3099,7 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d) || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - throw InvalidPositionException("emergeSector(): pos. over limit"); + throw InvalidPositionException("generateSector(): pos. over limit"); /* Generate sector and heightmaps @@ -2209,8 +3167,8 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d) #if 0 { - //dstream<<"emergeSector(): Reading point attribute lists"< MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) + throw InvalidPositionException("createSector(): pos. over limit"); + + /* + Generate blank sector + */ + + // Number of heightmaps in sector in each direction + u16 hm_split = SECTOR_HEIGHTMAP_SPLIT; + + // Heightmap side width + s16 hm_d = MAP_BLOCKSIZE / hm_split; + + sector = new ServerMapSector(this, p2d, hm_split); + + // Sector position on map in nodes + v2s16 nodepos2d = p2d * MAP_BLOCKSIZE; + + /*dstream<<"Generating sector ("<getGroundHeight(mhm_p+v2s16(0,0)*hm_split), + m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split), + m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split), + m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split), + };*/ + + // Loop through sub-heightmaps + for(s16 y=0; ygetGroundHeight(mhm_p+v2s16(0,0)), + m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)), + m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)), + m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)), + }; + + /*dstream<<"p_in_sector=("<setHeightmap(p_in_sector, hm); + + //hm->generateContinued(1.0, 0.5, corners); + hm->generateContinued(0.5, 0.5, corners); + + //hm->print(); + } + + // Add dummy objects + core::map *objects = new core::map; + sector->setObjects(objects); + + /* + Insert to container + */ + m_sectors.insert(p2d, sector); + + return sector; +} MapSector * ServerMap::emergeSector(v2s16 p2d) { @@ -2353,7 +3425,25 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) p2d.X, p2d.Y); /* - Check if it exists already in memory + Check chunk status + */ + v2s16 chunkpos = sector_to_chunk(p2d); + bool chunk_exists = false; + MapChunk *chunk = getChunk(chunkpos); + if(chunk && chunk->getIsVolatile() == false) + chunk_exists = true; + + /* + If chunk is not fully generated, generate chunk + */ + if(chunk_exists == false) + { + // Generate chunk and neighbors + generateChunk(chunkpos); + } + + /* + Return sector if it exists now */ MapSector *sector = getSectorNoGenerateNoEx(p2d); if(sector != NULL) @@ -2373,31 +3463,6 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) return sector; } - /* - Check chunk status - */ - v2s16 chunkpos = sector_to_chunk(p2d); - bool chunk_exists = false; - MapChunk *chunk = getChunk(chunkpos); - if(chunk && chunk->getIsVolatile() == false) - chunk_exists = true; - - /* - If chunk is not generated, generate chunk - */ - if(chunk_exists == false) - { - // Generate chunk and neighbors - generateChunk(chunkpos); - } - - /* - Return sector if it exists now - */ - sector = getSectorNoGenerateNoEx(p2d); - if(sector != NULL) - return sector; - /* generateChunk should have generated the sector */ @@ -3288,6 +4353,49 @@ continue_generating: return block; } +MapBlock * ServerMap::createBlock(v3s16 p) +{ + DSTACK("%s: p=(%d,%d,%d)", + __FUNCTION_NAME, p.X, p.Y, p.Z); + + v2s16 p2d(p.X, p.Z); + s16 block_y = p.Y; + /* + This will create or load a sector if not found in memory. + If block exists on disk, it will be loaded. + + NOTE: On old save formats, this will be slow, as it generates + lighting on blocks for them. + */ + ServerMapSector *sector; + try{ + sector = (ServerMapSector*)createSector(p2d); + assert(sector->getId() == MAPSECTOR_SERVER); + } + /*catch(InvalidPositionException &e) + { + dstream<<"createBlock: createSector() failed"<getBlockNoCreateNoEx(block_y); + if(block) + return block; + // Create blank + block = sector->createBlankBlock(block_y); + return block; +} + MapBlock * ServerMap::emergeBlock( v3s16 p, bool only_from_disk, @@ -3299,10 +4407,6 @@ MapBlock * ServerMap::emergeBlock( __FUNCTION_NAME, p.X, p.Y, p.Z, only_from_disk); - /*dstream<<"emergeBlock(): " - <<"("<getNode(a.MinEdge + p); - m_data[i] = n; - m_flags[i] = 0; - } - catch(InvalidPositionException &e) - { - m_flags[i] = VOXELFLAG_INEXISTENT; - } - } -} -#endif - /* SUGG: Add an option to only update eg. water and air nodes. @@ -4620,23 +5683,8 @@ ManualMapVoxelManipulator::~ManualMapVoxelManipulator() void ManualMapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) { - // Just create the area to avoid segfaults + // Just create the area so that it can be pointed to VoxelManipulator::emerge(a, caller_id); - - /* - Just create the area to avoid segfaults - */ - /*addArea(a); - for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) - for(s32 y=a.MinEdge.Y; y<=a.MaxEdge.Y; y++) - for(s32 x=a.MinEdge.X; x<=a.MaxEdge.X; x++) - { - s32 i = m_area.index(x,y,z); - // Don't touch nodes that have already been loaded - if(!(m_flags[i] & VOXELFLAG_NOT_LOADED)) - continue; - m_flags[i] = VOXELFLAG_INEXISTENT; - }*/ } void ManualMapVoxelManipulator::initialEmerge( @@ -4681,6 +5729,9 @@ void ManualMapVoxelManipulator::initialEmerge( if(block_data_inexistent) { + /* + Mark area inexistent + */ VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); // Fill with VOXELFLAG_INEXISTENT for(s32 z=a.MinEdge.Z; z<=a.MaxEdge.Z; z++) @@ -4691,7 +5742,41 @@ void ManualMapVoxelManipulator::initialEmerge( } } - m_loaded_blocks.insert(p, true); + m_loaded_blocks.insert(p, !block_data_inexistent); + } +} + +void ManualMapVoxelManipulator::blitBackAll( + core::map * modified_blocks) +{ + if(m_area.getExtent() == v3s16(0,0,0)) + return; + + /* + Copy data of all blocks + */ + for(core::map::Iterator + i = m_loaded_blocks.getIterator(); + i.atEnd() == false; i++) + { + bool existed = i.getNode()->getValue(); + if(existed == false) + continue; + v3s16 p = i.getNode()->getKey(); + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + if(block == NULL) + { + dstream<<"WARNING: "<<__FUNCTION_NAME + <<": got NULL block " + <<"("<copyFrom(*this); + + if(modified_blocks) + modified_blocks->insert(p, block); } } diff --git a/src/map.h b/src/map.h index bc6984c8..757f06ab 100644 --- a/src/map.h +++ b/src/map.h @@ -93,6 +93,8 @@ public: MapSector * getSectorNoGenerateNoEx(v2s16 p2d); // On failure throws InvalidPositionException MapSector * getSectorNoGenerate(v2s16 p2d); + // Gets an existing sector or creates an empty one + //MapSector * getSectorCreate(v2s16 p2d); /* This is overloaded by ClientMap and ServerMap to allow @@ -104,6 +106,8 @@ public: MapBlock * getBlockNoCreate(v3s16 p); // Returns NULL if not found MapBlock * getBlockNoCreateNoEx(v3s16 p); + // Gets an existing block or creates an empty one + //MapBlock * getBlockCreate(v3s16 p); // Returns InvalidPositionException if not found f32 getGroundHeight(v2s16 p, bool generate=false); @@ -382,12 +386,20 @@ public: This is mainly called by generateChunkRaw. */ - ServerMapSector * generateSector(v2s16 p); + //ServerMapSector * generateSector(v2s16 p); + + /* + Get a sector from somewhere. + - Check memory + - Check disk (loads blocks also) + - Create blank one + */ + ServerMapSector * createSector(v2s16 p); /* Get a sector from somewhere. - Check memory - - Check disk + - Check disk (loads blocks also) - Generate chunk */ MapSector * emergeSector(v2s16 p); @@ -399,6 +411,13 @@ public: core::map &changed_blocks, core::map &lighting_invalidated_blocks ); + + /* + Get a block from somewhere. + - Memory + - Create blank + */ + MapBlock * createBlock(v3s16 p); MapBlock * emergeBlock( v3s16 p, @@ -636,10 +655,8 @@ public: protected: Map *m_map; /* - NOTE: This might be used or not - bool is dummy value - SUGG: How 'bout an another VoxelManipulator for storing the - information about which block is loaded? + key = blockpos + value = block existed when loaded */ core::map m_loaded_blocks; }; @@ -653,8 +670,12 @@ public: virtual void emerge(VoxelArea a, s32 caller_id=-1); void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max); + + // This is much faster with big chunks of generated data + void blitBackAll(core::map * modified_blocks); protected: + bool m_create_area; }; #endif diff --git a/src/mapblock.cpp b/src/mapblock.cpp index a7bc730c..3e20cb4c 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -1309,10 +1309,21 @@ void MapBlock::copyTo(VoxelManipulator &dst) v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); + // Copy from data to VoxelManipulator dst.copyFrom(data, data_area, v3s16(0,0,0), getPosRelative(), data_size); } +void MapBlock::copyFrom(VoxelManipulator &dst) +{ + v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); + VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); + + // Copy from VoxelManipulator to data + dst.copyTo(data, data_area, v3s16(0,0,0), + getPosRelative(), data_size); +} + void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio) { /* diff --git a/src/mapblock.h b/src/mapblock.h index e39db35b..02c07289 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -426,6 +426,8 @@ public: // Copies data to VoxelManipulator to getPosRelative() void copyTo(VoxelManipulator &dst); + // Copies data from VoxelManipulator getPosRelative() + void copyFrom(VoxelManipulator &dst); /* MapBlockObject stuff diff --git a/src/mapnode.h b/src/mapnode.h index 07153f93..4385f9ac 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -438,6 +438,14 @@ struct MapNode param2 = a_param2; } + /*MapNode & operator=(const MapNode &other) + { + d = other.d; + param = other.param; + param2 = other.param2; + return *this; + }*/ + bool operator==(const MapNode &other) { return (d == other.d diff --git a/src/mapsector.cpp b/src/mapsector.cpp index fa1bb68d..1fd66879 100644 --- a/src/mapsector.cpp +++ b/src/mapsector.cpp @@ -570,10 +570,15 @@ ServerMapSector* ServerMapSector::deSerialize( if(n != NULL) { - dstream<<"deSerializing existent sectors not supported " + dstream<<"WARNING: deSerializing existent sectors not supported " "at the moment, because code hasn't been tested." <getValue(); + assert(sector->getId() == MAPSECTOR_SERVER); + return (ServerMapSector*)sector; + // NOTE: At least hm_split mismatch would have to be checked //sector = n->getValue(); diff --git a/src/server.cpp b/src/server.cpp index 80aa4767..28faaf44 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -606,6 +606,15 @@ 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); + MapChunk *chunk = map->getChunk(chunkpos); + if(chunk == NULL) + block_is_invalid = true; + else if(chunk->getIsVolatile() == true) + block_is_invalid = true; } /* @@ -3258,7 +3267,8 @@ Player *Server::emergePlayer(const char *name, const char *password, player->setPosition(intToFloat(v3s16( nodepos.X, - groundheight + 1, + //groundheight + 1, + groundheight + 15, nodepos.Y ))); diff --git a/src/utility.h b/src/utility.h index a61de1c3..8839887f 100644 --- a/src/utility.h +++ b/src/utility.h @@ -207,6 +207,10 @@ public: { return ptr == t; } + T & operator[](unsigned int i) + { + return ptr[i]; + } private: void drop() { @@ -572,6 +576,15 @@ inline bool isInArea(v2s16 p, s16 d) ); } +inline bool isInArea(v3s16 p, v3s16 d) +{ + return ( + p.X >= 0 && p.X < d.X && + p.Y >= 0 && p.Y < d.Y && + p.Z >= 0 && p.Z < d.Z + ); +} + inline s16 rangelim(s16 i, s16 min, s16 max) { if(i < min) @@ -1459,6 +1472,13 @@ int myrand(void); void mysrand(unsigned seed); #define MYRAND_MAX 32767 +inline int myrand_range(int min, int max) +{ + if(min >= max) + return max; + return (myrand()%(max-min+1))+min; +} + /* Some kind of a thing that stores attributes related to coordinate points diff --git a/src/voxel.cpp b/src/voxel.cpp index 02635d3a..f60b641f 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -41,7 +41,6 @@ VoxelManipulator::VoxelManipulator(): m_data(NULL), m_flags(NULL) { - m_disable_water_climb = false; } VoxelManipulator::~VoxelManipulator() @@ -221,6 +220,22 @@ void VoxelManipulator::copyFrom(MapNode *src, VoxelArea src_area, } } +void VoxelManipulator::copyTo(MapNode *dst, VoxelArea dst_area, + v3s16 dst_pos, v3s16 from_pos, v3s16 size) +{ + for(s16 z=0; z