diff options
author | Perttu Ahola <celeron55@gmail.com> | 2011-02-01 03:06:02 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2011-02-01 03:06:02 +0200 |
commit | 6e196c2ce4285c0aea2a5c714e842d90c1b84b43 (patch) | |
tree | e1c25414910ed344ced69125ba4f8170dc27bc9f | |
parent | be851871cd43316d12fd9a5f2cc6dec98a1c9ce0 (diff) | |
download | minetest-6e196c2ce4285c0aea2a5c714e842d90c1b84b43.tar.gz minetest-6e196c2ce4285c0aea2a5c714e842d90c1b84b43.tar.bz2 minetest-6e196c2ce4285c0aea2a5c714e842d90c1b84b43.zip |
partly working chunk-based map generator (doesn't save properly, spawn is pretty random)
-rw-r--r-- | src/irrlichtwrapper.cpp | 10 | ||||
-rw-r--r-- | src/irrlichtwrapper.h | 6 | ||||
-rw-r--r-- | src/main.cpp | 41 | ||||
-rw-r--r-- | src/map.cpp | 1259 | ||||
-rw-r--r-- | src/map.h | 33 | ||||
-rw-r--r-- | src/mapblock.cpp | 11 | ||||
-rw-r--r-- | src/mapblock.h | 2 | ||||
-rw-r--r-- | src/mapnode.h | 8 | ||||
-rw-r--r-- | src/mapsector.cpp | 9 | ||||
-rw-r--r-- | src/server.cpp | 12 | ||||
-rw-r--r-- | src/utility.h | 20 | ||||
-rw-r--r-- | src/voxel.cpp | 19 | ||||
-rw-r--r-- | src/voxel.h | 28 |
13 files changed, 1351 insertions, 107 deletions
diff --git a/src/irrlichtwrapper.cpp b/src/irrlichtwrapper.cpp index e5cab98c6..a1d37f4bd 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<TextureSpec, video::ITexture*, u8, u8> result_queue; diff --git a/src/irrlichtwrapper.h b/src/irrlichtwrapper.h index 225ae3402..965d01208 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 dc4716fab..87deefee4 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 cc1a6d638..63fccc432 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 "<<generated_block_count <<" blocks"<<std::endl; - { + /*{ TimeTaker timer2("generateChunkRaw() lighting"); // Update lighting core::map<v3s16, MapBlock*> 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"<<std::endl; + return NULL; +} + +/* + Some helper functions +*/ + +s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d) +{ + v3s16 em = vmanip.m_area.getExtent(); + s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; + s16 y_nodes_min = vmanip.m_area.MinEdge.Y; + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); + s16 y; + for(y=y_nodes_max; y>=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<trunk_h; ii++) + { + if(vmanip.m_area.contains(p1)) + vmanip.m_data[vmanip.m_area.index(p1)] = treenode; + p1.Y++; + } + + // p1 is now the last piece of the trunk + p1.Y -= 1; + + VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2)); + SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]); + for(s32 i=0; i<leaves_a.getVolume(); i++) + leaves_d[i] = 0; + + // Force leaves at near the end of the trunk + { + s16 d = 1; + for(s16 z=-d; z<=d; z++) + for(s16 y=-d; y<=d; y++) + for(s16 x=-d; x<=d; x++) + { + leaves_d[leaves_a.index(v3s16(x,y,z))] = 1; + } + } + + // Add leaves randomly + for(u32 iii=0; iii<7; iii++) + { + s16 d = 1; + + v3s16 p( + myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), + myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), + myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) + ); + + for(s16 z=0; z<=d; z++) + for(s16 y=0; y<=d; y++) + for(s16 x=0; x<=d; x++) + { + leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1; + } + } + + // Blit leaves to vmanip + for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++) + for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++) + for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++) + { + v3s16 p(x,y,z); + p += p1; + if(vmanip.m_area.contains(p) == false) + continue; + u32 vi = vmanip.m_area.index(p); + if(vmanip.m_data[vi].d != CONTENT_AIR) + continue; + u32 i = leaves_a.index(x,y,z); + if(leaves_d[i] == 1) + vmanip.m_data[vi] = leavesnode; + } +} + +MapChunk* ServerMap::generateChunk(v2s16 chunkpos) +{ + TimeTaker timer("generateChunk()"); + + // The distance how far into the neighbors the generator is allowed to go + s16 max_spread_amount = m_chunksize * MAP_BLOCKSIZE / 2 + 2; + // Minimum amount of space left on sides for mud to fall in + s16 min_mud_fall_space = 2; + // Maximum diameter of stone obstacles in X and Z + s16 stone_obstacle_max_size = m_chunksize*MAP_BLOCKSIZE/2; + assert(stone_obstacle_max_size/2 <= max_spread_amount-min_mud_fall_space); + + s16 y_blocks_min = -4; + s16 y_blocks_max = 3; + s16 h_blocks = y_blocks_max - y_blocks_min + 1; + s16 y_nodes_min = y_blocks_min * MAP_BLOCKSIZE; + s16 y_nodes_max = y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1; + + v2s16 sectorpos_base = chunk_to_sector(chunkpos); + s16 sectorpos_base_size = m_chunksize; + v2s16 sectorpos_bigbase = chunk_to_sector(chunkpos - v2s16(1,1)); + s16 sectorpos_bigbase_size = m_chunksize * 3; + + v3s16 bigarea_blocks_min( + sectorpos_bigbase.X, + y_blocks_min, + sectorpos_bigbase.Y + ); + + v3s16 bigarea_blocks_max( + sectorpos_bigbase.X + sectorpos_bigbase_size - 1, + y_blocks_min + sectorpos_bigbase_size - 1, + sectorpos_bigbase.Y + sectorpos_bigbase_size - 1 + ); + + /* + Create the whole area of this and the neighboring chunks + */ + { + TimeTaker timer("generateChunk() create area"); + + for(s16 x=0; x<sectorpos_bigbase_size; x++) + for(s16 z=0; z<sectorpos_bigbase_size; z++) + { + v2s16 sectorpos = sectorpos_bigbase + v2s16(x,z); + ServerMapSector *sector = createSector(sectorpos); + assert(sector); + + for(s16 y=y_blocks_min; y<=y_blocks_max; y++) + { + v3s16 blockpos(sectorpos.X, y, sectorpos.Y); + MapBlock *block = createBlock(blockpos); + + // Lighting won't be calculated + //block->setLightingExpired(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; x<sectorpos_bigbase_size*MAP_BLOCKSIZE; x++) + for(s16 z=0; z<sectorpos_bigbase_size*MAP_BLOCKSIZE; z++) + { + // Node position + v2s16 p2d = sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z); + + /* + Skip of already generated + */ + { + v3s16 p(p2d.X, y_nodes_min, p2d.Y); + if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR) + continue; + } + + // Ground height at this point + float surface_y_f = 0.0; + /* + A hack to get the ground height from the sector. + Do this better. + */ + { + v2s16 sectorpos = getContainerPos(p2d, MAP_BLOCKSIZE); + v2s16 sector_relpos = p2d - sectorpos*MAP_BLOCKSIZE; + MapSector *sector = getSectorNoGenerate(sectorpos); + assert(sector); + float h = sector->getGroundHeight(sector_relpos); + if(h > GROUNDHEIGHT_VALID_MINVALUE) + surface_y_f = h; + else + dstream<<"WARNING: "<<__FUNCTION_NAME + <<": sector->getGroundHeight returned bad height"<<std::endl; + } + // Convert to integer + s16 surface_y = (s16)surface_y_f; + + /* + Fill ground with stone + */ + { + // Use fast index incrementing + v3s16 em = vmanip.m_area.getExtent(); + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y)); + for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++) + { + vmanip.m_data[i].d = CONTENT_STONE; + + vmanip.m_area.add_y(em, i, 1); + } + } + } + + /* + Add some random stone obstacles + */ + + for(u32 ri=0; ri<10; ri++) + { + // The size of these could actually be m_chunksize*MAP_BLOCKSIZE*2 + v3s16 ob_size( + myrand_range(5, stone_obstacle_max_size), + myrand_range(0, 15), + myrand_range(5, stone_obstacle_max_size) + ); + v2s16 ob_place( + myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1), + myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1) + ); + + for(s16 x=-ob_size.X/2; x<ob_size.X/2; x++) + for(s16 z=-ob_size.Z/2; z<ob_size.Z/2; z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + ob_place + v2s16(x,z); + + // Find stone ground level + // (ignore everything else than mud in already generated chunks) + // and mud amount over the stone level + s16 surface_y = 0; + s16 mud_amount = 0; + { + v3s16 em = vmanip.m_area.getExtent(); + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); + s16 y; + // Go to ground level + for(y=y_nodes_max; y>=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<sectorpos_base_size*MAP_BLOCKSIZE; x++) + for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + // Find ground level + s16 surface_y = find_ground_level(vmanip, p2d); + + /* + Add mud on ground + */ + { + s16 mudcount = 0; + 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)); + for(s16 y=y_start; y<=y_nodes_max; y++) + { + MapNode &n = vmanip.m_data[i]; + n.d = CONTENT_MUD; + mudcount++; + if(mudcount >= 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<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1; + x++) + for(s16 z=0-max_spread_amount+1; + z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount-1; + z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + v3s16 em = vmanip.m_area.getExtent(); + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); + s16 y; + // Go to ground level + for(y=y_nodes_max; y>=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<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount; + x++) + for(s16 z=0-max_spread_amount; + z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount; + z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + // Find ground level + s16 surface_y = find_ground_level(vmanip, p2d); + + // If ground level is over water level, skip. + if(surface_y > 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<count; i++) + { + s16 x = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1); + s16 z = myrand_range(0, sectorpos_base_size*MAP_BLOCKSIZE-1); + x += sectorpos_base.X*MAP_BLOCKSIZE; + z += sectorpos_base.Y*MAP_BLOCKSIZE; + s16 y = find_ground_level(vmanip, v2s16(x,z)); + // Don't make a tree under water level + if(y < WATER_LEVEL) + continue; + v3s16 p(x,y+1,z); + // Make a tree + make_tree(vmanip, p); + } + } + + /* + Grow grass + */ + + for(s16 x=0-4; x<sectorpos_base_size*MAP_BLOCKSIZE+4; x++) + for(s16 z=0-4; z<sectorpos_base_size*MAP_BLOCKSIZE+4; z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + /* + Find the lowest surface to which enough light ends up + to make grass grow. + + Basically just wait until not air and not leaves. + */ + s16 surface_y = 0; + { + v3s16 em = vmanip.m_area.getExtent(); + u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); + s16 y; + // Go to ground level + for(y=y_nodes_max; y>=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<v3s16, bool> light_sources; + + /*for(s16 x=0; x<sectorpos_base_size*MAP_BLOCKSIZE; x++) + for(s16 z=0; z<sectorpos_base_size*MAP_BLOCKSIZE; z++)*/ + for(s16 x=0-max_spread_amount; + x<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount; + x++) + for(s16 z=0-max_spread_amount; + z<sectorpos_base_size*MAP_BLOCKSIZE+max_spread_amount; + z++) + { + // Node position in 2d + v2s16 p2d = sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z); + + /* + Apply initial sunlight + */ + { + v3s16 em = vmanip.m_area.getExtent(); + s16 y_start = y_nodes_max; + u8 light = LIGHT_SUN; + 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); + } + } + } + + // 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<v3s16, MapBlock*> modified_blocks; + { + TimeTaker timer("generateChunk() blitBackAll"); + vmanip.blitBackAll(&modified_blocks); + } + /* + Update day/night difference cache of the MapBlocks + */ + { + for(core::map<v3s16, MapBlock*>::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"<<std::endl; - //TimeTaker attrtimer("emergeSector() attribute fetch"); + //dstream<<"generateSector(): Reading point attribute lists"<<std::endl; + //TimeTaker attrtimer("generateSector() attribute fetch"); // Get plant amount from attributes PointAttributeList *palist = m_padb.getList("plants_amount"); @@ -2345,8 +3303,9 @@ ServerMapSector * ServerMap::generateSector(v2s16 p2d) return sector; } +#endif -MapSector * ServerMap::emergeSector(v2s16 p2d) +ServerMapSector * ServerMap::createSector(v2s16 p2d) { DSTACK("%s: p2d=(%d,%d)", __FUNCTION_NAME, @@ -2355,25 +3314,117 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) /* Check if it exists already in memory */ - MapSector *sector = getSectorNoGenerateNoEx(p2d); + ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d); if(sector != NULL) return sector; /* - Try to load it from disk + Try to load it from disk (with blocks) */ if(loadSectorFull(p2d) == true) { - MapSector *sector = getSectorNoGenerateNoEx(p2d); + ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d); if(sector == NULL) { - dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl; + dstream<<"ServerMap::createSector(): loadSectorFull didn't make a sector"<<std::endl; throw InvalidPositionException(""); } return sector; } /* + If there is no master heightmap, throw. + */ + if(m_heightmap == NULL) + { + throw InvalidPositionException("createSector(): no heightmap"); + } + + /* + Do not create over-limit + */ + if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p2d.X > 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 ("<<p2d.X<<","<<p2d.Y<<")" + " heightmaps and objects"<<std::endl;*/ + + /* + Generate sector heightmap + */ + + v2s16 mhm_p = p2d * hm_split; + /*f32 corners[4] = { + m_heightmap->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; y<hm_split; y++) + for(s16 x=0; x<hm_split; x++) + { + v2s16 p_in_sector = v2s16(x,y); + v2s16 mhm_p = p2d * hm_split + p_in_sector; + f32 corners[4] = { + m_heightmap->getGroundHeight(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=("<<p_in_sector.X<<","<<p_in_sector.Y<<")" + <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")" + <<std::endl;*/ + + FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper, + mhm_p, hm_d); + 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<v3s16, u8> *objects = new core::map<v3s16, u8>; + sector->setObjects(objects); + + /* + Insert to container + */ + m_sectors.insert(p2d, sector); + + return sector; +} + +MapSector * ServerMap::emergeSector(v2s16 p2d) +{ + DSTACK("%s: p2d=(%d,%d)", + __FUNCTION_NAME, + p2d.X, p2d.Y); + + /* Check chunk status */ v2s16 chunkpos = sector_to_chunk(p2d); @@ -2383,7 +3434,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) chunk_exists = true; /* - If chunk is not generated, generate chunk + If chunk is not fully generated, generate chunk */ if(chunk_exists == false) { @@ -2394,11 +3445,25 @@ MapSector * ServerMap::emergeSector(v2s16 p2d) /* Return sector if it exists now */ - sector = getSectorNoGenerateNoEx(p2d); + MapSector *sector = getSectorNoGenerateNoEx(p2d); if(sector != NULL) return sector; /* + Try to load it from disk + */ + if(loadSectorFull(p2d) == true) + { + MapSector *sector = getSectorNoGenerateNoEx(p2d); + if(sector == NULL) + { + dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl; + throw InvalidPositionException(""); + } + return sector; + } + + /* generateChunk should have generated the sector */ assert(0); @@ -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"<<std::endl; + throw e; + }*/ + catch(std::exception &e) + { + dstream<<"createBlock: createSector() failed: " + <<e.what()<<std::endl; + throw e; + } + + /* + Try to get a block from the sector + */ + + MapBlock *block = sector->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(): " - <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" - <<std::endl;*/ - v2s16 p2d(p.X, p.Z); s16 block_y = p.Y; /* @@ -4434,7 +5538,6 @@ MapVoxelManipulator::~MapVoxelManipulator() <<std::endl;*/ } -#if 1 void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) { TimeTaker timer1("emerge", &emerge_time); @@ -4492,51 +5595,11 @@ void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) } } - m_loaded_blocks.insert(p, true); + m_loaded_blocks.insert(p, !block_data_inexistent); } //dstream<<"emerge done"<<std::endl; } -#endif - -#if 0 -/* - NOTE: This is slow -*/ -void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id) -{ - TimeTaker timer1("emerge", &emerge_time); - - v3s16 size = a.getExtent(); - - VoxelArea padded = a; - padded.pad(m_area.getExtent() / 4); - addArea(padded); - - for(s16 z=0; z<size.Z; z++) - for(s16 y=0; y<size.Y; y++) - for(s16 x=0; x<size.X; x++) - { - v3s16 p(x,y,z); - s32 i = m_area.index(a.MinEdge + p); - // Don't touch nodes that have already been loaded - if(!(m_flags[i] & VOXELFLAG_NOT_LOADED)) - continue; - try - { - TimeTaker timer1("emerge load", &emerge_load_time); - MapNode n = m_map->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<v3s16, MapBlock*> * modified_blocks) +{ + if(m_area.getExtent() == v3s16(0,0,0)) + return; + + /* + Copy data of all blocks + */ + for(core::map<v3s16, bool>::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 " + <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")" + <<std::endl; + continue; + } + + block->copyFrom(*this); + + if(modified_blocks) + modified_blocks->insert(p, block); } } @@ -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<v3s16, MapBlock*> &changed_blocks, core::map<v3s16, MapBlock*> &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<v3s16, bool> 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<v3s16, MapBlock*> * modified_blocks); protected: + bool m_create_area; }; #endif diff --git a/src/mapblock.cpp b/src/mapblock.cpp index a7bc730ce..3e20cb4cf 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 e39db35bd..02c072895 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 07153f934..4385f9acf 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 fa1bb68d0..1fd668793 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." <<std::endl; - assert(0); + + //assert(0); + MapSector *sector = n->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 80aa47671..28faaf440 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 a61de1c37..8839887f7 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 02635d3af..f60b641f1 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<size.Z; z++) + for(s16 y=0; y<size.Y; y++) + { + s32 i_dst = dst_area.index(dst_pos.X, dst_pos.Y+y, dst_pos.Z+z); + s32 i_local = m_area.index(from_pos.X, from_pos.Y+y, from_pos.Z+z); + memcpy(&dst[i_dst], &m_data[i_local], size.X*sizeof(MapNode)); + } +} + +/* + Algorithms + ----------------------------------------------------- +*/ void VoxelManipulator::clearFlag(u8 flags) { @@ -541,7 +556,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p) Lights neighbors of from_nodes, collects all them and then goes on recursively. - NOTE: This is faster in small areas but will overflow the + NOTE: This is faster on small areas but will overflow the stack on large areas. Thus it is not used. */ void VoxelManipulator::spreadLight(enum LightBank bank, diff --git a/src/voxel.h b/src/voxel.h index ff5d534cb..89333159c 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -262,7 +262,31 @@ public: { return index(p.X, p.Y, p.Z); } + + // Translate index in the X coordinate + void add_x(const v3s16 &extent, u32 &i, s16 a) + { + i += a; + } + // Translate index in the Y coordinate + void add_y(const v3s16 &extent, u32 &i, s16 a) + { + i += a * extent.X; + } + // Translate index in the Z coordinate + void add_z(const v3s16 &extent, u32 &i, s16 a) + { + i += a * extent.X*extent.Y; + } + // Translate index in space + void add_p(const v3s16 &extent, u32 &i, v3s16 a) + { + i += a.Z*extent.X*extent.Y + a.Y*extent.X + a.X; + } + /* + Print method for debugging + */ void print(std::ostream &o) const { v3s16 e = getExtent(); @@ -394,6 +418,10 @@ public: */ void copyFrom(MapNode *src, VoxelArea src_area, v3s16 from_pos, v3s16 to_pos, v3s16 size); + + // Copy data + void copyTo(MapNode *dst, VoxelArea dst_area, + v3s16 dst_pos, v3s16 from_pos, v3s16 size); /* Algorithms |