starting to separate "material" to "content" and "tile"

This commit is contained in:
Perttu Ahola 2010-12-12 14:33:13 +02:00
parent db49f37692
commit 47a593b519
10 changed files with 340 additions and 388 deletions

@ -53,3 +53,6 @@
#max_simultaneous_block_sends_per_client = 1 #max_simultaneous_block_sends_per_client = 1
#max_simultaneous_block_sends_server_total = 4 #max_simultaneous_block_sends_server_total = 4
#max_block_send_distance = 8
#max_block_generate_distance = 5

@ -179,200 +179,19 @@ SUGG: MovingObject::move and Player::move are basically the same.
TODO: Transfer sign texts as metadata of block and not as data of TODO: Transfer sign texts as metadata of block and not as data of
object object
SUGG: Implement a "Fast check queue" (a queue with a map for checking
if something is already in it)
- TODO: Use it in active block queue in water flowing
TODO: Proper looking torches.
- Signs could be done in the same way?
Doing now: Doing now:
====================================================================== ======================================================================
Water dynamics pseudo-code (block = MapNode): TODO: A system for showing some nodes in some other way than cubes
SUGG: Create separate flag table in VoxelManipulator to allow fast - Needed for torches
clearing of "modified" flags - Also for signs, stairs, etc
neighborCausedPressure(pos):
pressure = 0
dirs = {down, left, right, back, front, up}
for d in dirs:
pos2 = pos + d
p = block_at(pos2).pressure
if d.Y == 1 and p > min:
p -= 1
if d.Y == -1 and p < max:
p += 1
if p > pressure:
pressure = p
return pressure
# This should somehow update all changed pressure values
# in an unknown body of water
updateWaterPressure(pos):
TODO
FIXME: This goes in an indefinite loop when there is an underwater
chamber like this:
#111######
#222##22##
#33333333x<- block removed from here
##########
#111######
#222##22##
#3333333x1
##########
#111######
#222##22##
#333333x11
##########
#111######
#222##2x##
#333333333
##########
#111######
#222##x2##
#333333333
##########
Now, consider moving to the last block not allowed.
Consider it a 3D case with a depth of 2. We're now at this situation.
Note the additional blocking ## in the second depth plane.
z=1 z=2
#111###### #111######
#222##x2## #222##22##
#333333333 #33333##33
########## ##########
#111###### #111######
#222##22## #222##x2##
#333333333 #33333##33
########## ##########
#111###### #111######
#222##22## #222##2x##
#333333333 #33333##33
########## ##########
Now there is nowhere to go, without going to an already visited block,
but the pressure calculated in here from neighboring blocks is >= 2,
so it is not the final ending.
We will back up to a state where there is somewhere to go to.
It is this state:
#111###### #111######
#222##22## #222##22##
#333333x33 #33333##33
########## ##########
Then just go on, avoiding already visited blocks:
#111###### #111######
#222##22## #222##22##
#33333x333 #33333##33
########## ##########
#111###### #111######
#222##22## #222##22##
#3333x3333 #33333##33
########## ##########
#111###### #111######
#222##22## #222##22##
#333x33333 #33333##33
########## ##########
#111###### #111######
#222##22## #222##22##
#33x333333 #33333##33
########## ##########
#111###### #111######
#22x##22## #222##22##
#333333333 #33333##33
########## ##########
#11x###### #111######
#222##22## #222##22##
#333333333 #33333##33
########## ##########
"Blob". the air bubble finally got out of the water.
Then return recursively to a state where there is air next to water,
clear the visit flags and feed the neighbor of the water recursively
to the algorithm.
#11 ###### #111######
#222##22## #222##22##
#333333333x #33333##33
########## ##########
#11 ###### #111######
#222##22## #222##22##
#33333333x3 #33333##33
########## ##########
...and so on.
# removed_pos: a position that has been changed from something to air
flowWater(removed_pos):
dirs = {top, left, right, back, front, bottom}
selected_dir = None
for d in dirs:
b2 = removed_pos + d
# Ignore positions that don't have water
if block_at(b2) != water:
continue
# Ignore positions that have already been checked
if block_at(b2).checked:
continue
# If block is at top, select it always.
if d.Y == 1:
selected_dir = d
break
# If block is at bottom, select it if it has enough pressure.
# >= 3 needed for stability (and sanity)
if d.Y == -1:
if block_at(b2).pressure >= 3:
selected_dir = d
break
continue
# Else block is at some side. select it if it has enough pressure.
if block_at(b2).pressure >= 2:
selected_dir = d
break
# If there is nothing to do anymore, return.
if selected_dir == None
return
b2 = removed_pos + selected_dir
# Move block
set_block(removed_pos, block_at(b2))
set_block(b2, air_block)
# Update pressure
updateWaterPressure(removed_pos)
# Flow water to the newly created empty position
flowWater(b2)
# Check empty positions around and try flowing water to them
for d in dirs:
b3 = removed_pos + d
# Ignore positions that are not air
if block_at(b3) is not air:
continue
flowWater(b3)
====================================================================== ======================================================================
@ -448,7 +267,8 @@ const char *g_material_filenames[MATERIALS_COUNT] =
"../data/leaves.png", "../data/leaves.png",
"../data/grass_footsteps.png", "../data/grass_footsteps.png",
"../data/mese.png", "../data/mese.png",
"../data/mud.png" "../data/mud.png",
"../data/water.png", // ocean
}; };
video::SMaterial g_materials[MATERIALS_COUNT]; video::SMaterial g_materials[MATERIALS_COUNT];
@ -499,6 +319,8 @@ void set_default_settings()
g_settings.set("name", ""); g_settings.set("name", "");
g_settings.set("random_input", "false"); g_settings.set("random_input", "false");
g_settings.set("client_delete_unused_sectors_timeout", "1200"); g_settings.set("client_delete_unused_sectors_timeout", "1200");
g_settings.set("max_block_send_distance", "8");
g_settings.set("max_block_generate_distance", "5");
// Server stuff // Server stuff
g_settings.set("creative_mode", "false"); g_settings.set("creative_mode", "false");
@ -1489,13 +1311,12 @@ int main(int argc, char *argv[])
g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false); g_materials[i].setFlag(video::EMF_BILINEAR_FILTER, false);
//g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false); //g_materials[i].setFlag(video::EMF_ANISOTROPIC_FILTER, false);
//g_materials[i].setFlag(video::EMF_FOG_ENABLE, true); //g_materials[i].setFlag(video::EMF_FOG_ENABLE, true);
if(i == MATERIAL_WATER)
{
g_materials[i].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
//g_materials[i].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
}
} }
g_materials[MATERIAL_WATER].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
//g_materials[MATERIAL_WATER].MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
g_materials[MATERIAL_OCEAN].MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
/*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png")); /*g_mesh_materials[0].setTexture(0, driver->getTexture("../data/water.png"));
g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png")); g_mesh_materials[1].setTexture(0, driver->getTexture("../data/grass.png"));
g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png")); g_mesh_materials[2].setTexture(0, driver->getTexture("../data/stone.png"));

@ -1819,7 +1819,8 @@ MapBlock * ServerMap::emergeBlock(
// If under water level, it's water // If under water level, it's water
if(real_y < WATER_LEVEL) if(real_y < WATER_LEVEL)
{ {
n.d = MATERIAL_WATER; //n.d = MATERIAL_WATER;
n.d = MATERIAL_OCEAN;
n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1)); n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
} }
// else air // else air
@ -2731,34 +2732,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver,
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
#if 0
/*
Draw master heightmap mesh
*/
{
JMutexAutoLock lock(mesh_mutex);
if(mesh != NULL)
{
u32 c = mesh->getMeshBufferCount();
for(u32 i=0; i<c; i++)
{
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
const video::SMaterial& material = buf->getMaterial();
video::IMaterialRenderer* rnd =
driver->getMaterialRenderer(material.MaterialType);
bool transparent = (rnd && rnd->isTransparent());
// Render transparent on transparent pass and likewise.
if(transparent == is_transparent_pass)
{
driver->setMaterial(buf->getMaterial());
driver->drawMeshBuffer(buf);
}
}
}
}
#endif
/* /*
Get time for measuring timeout. Get time for measuring timeout.
@ -3162,7 +3135,8 @@ MapVoxelManipulator::~MapVoxelManipulator()
<<std::endl; <<std::endl;
} }
void MapVoxelManipulator::emerge(VoxelArea a) #if 1
void MapVoxelManipulator::emerge(VoxelArea a, s32 caller_id)
{ {
TimeTaker timer1("emerge", g_device, &emerge_time); TimeTaker timer1("emerge", g_device, &emerge_time);
@ -3190,8 +3164,11 @@ void MapVoxelManipulator::emerge(VoxelArea a)
{ {
TimeTaker timer1("emerge load", g_device, &emerge_load_time); TimeTaker timer1("emerge load", g_device, &emerge_load_time);
dstream<<"Loading block ("<<p.X<<","<<p.Y<<","<<p.Z<<")" /*dstream<<"Loading block (caller_id="<<caller_id<<")"
<<std::endl; <<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<" wanted area: ";
a.print(dstream);
dstream<<std::endl;*/
MapBlock *block = m_map->getBlockNoCreate(p); MapBlock *block = m_map->getBlockNoCreate(p);
if(block->isDummy()) if(block->isDummy())
@ -3221,6 +3198,43 @@ void MapVoxelManipulator::emerge(VoxelArea a)
//dstream<<"emerge done"<<std::endl; //dstream<<"emerge done"<<std::endl;
} }
#endif
#if 0
void MapVoxelManipulator::emerge(VoxelArea a)
{
TimeTaker timer1("emerge", g_device, &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", g_device, &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
/* /*
TODO: Add an option to only update eg. water and air nodes. TODO: Add an option to only update eg. water and air nodes.
@ -3230,6 +3244,9 @@ void MapVoxelManipulator::emerge(VoxelArea a)
void MapVoxelManipulator::blitBack void MapVoxelManipulator::blitBack
(core::map<v3s16, MapBlock*> & modified_blocks) (core::map<v3s16, MapBlock*> & modified_blocks)
{ {
if(m_area.getExtent() == v3s16(0,0,0))
return;
TimeTaker timer1("blitBack", g_device); TimeTaker timer1("blitBack", g_device);
/* /*

@ -603,15 +603,18 @@ public:
m_loaded_blocks.clear(); m_loaded_blocks.clear();
} }
virtual void emerge(VoxelArea a); virtual void emerge(VoxelArea a, s32 caller_id=-1);
void blitBack(core::map<v3s16, MapBlock*> & modified_blocks); void blitBack(core::map<v3s16, MapBlock*> & modified_blocks);
private: private:
Map *m_map; Map *m_map;
// bool is dummy value /*
// SUGG: How 'bout an another VoxelManipulator for storing the NOTE: This might be used or not
// information about which block is loaded? bool is dummy value
SUGG: How 'bout an another VoxelManipulator for storing the
information about which block is loaded?
*/
core::map<v3s16, bool> m_loaded_blocks; core::map<v3s16, bool> m_loaded_blocks;
}; };

@ -111,7 +111,7 @@ FastFace * MapBlock::makeFastFace(u8 material, u8 light, v3f p,
u8 alpha = 255; u8 alpha = 255;
if(material == MATERIAL_WATER) if(material == MATERIAL_WATER || material == MATERIAL_OCEAN)
{ {
alpha = 128; alpha = 128;
} }
@ -173,13 +173,14 @@ u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
/* /*
Gets node material from any place relative to block. Gets node material from any place relative to block.
Returns MATERIAL_AIR if doesn't exist. Returns MATERIAL_IGNORE if doesn't exist or should not be drawn.
*/ */
u8 MapBlock::getNodeMaterial(v3s16 p) u8 MapBlock::getNodeMaterial(v3s16 p)
{ {
try{ try{
MapNode n = getNodeParent(p); MapNode n = getNodeParent(p);
return n.d;
return content_cube_material(n.d);
} }
catch(InvalidPositionException &e) catch(InvalidPositionException &e)
{ {
@ -470,46 +471,6 @@ void MapBlock::updateMesh()
collector.fillMesh(mesh_new); collector.fillMesh(mesh_new);
#if 0
scene::IMeshBuffer *buf = NULL;
core::list<FastFace*>::Iterator i = fastfaces_new->begin();
// MATERIAL_AIR shouldn't be used by any face
u8 material_in_use = MATERIAL_AIR;
for(; i != fastfaces_new->end(); i++)
{
FastFace *f = *i;
if(f->material != material_in_use || buf == NULL)
{
// Try to get a meshbuffer associated with the material
buf = mesh_new->getMeshBuffer(g_materials[f->material]);
// If not found, create one
if(buf == NULL)
{
// This is a "Standard MeshBuffer",
// it's a typedeffed CMeshBuffer<video::S3DVertex>
buf = new scene::SMeshBuffer();
// Set material
((scene::SMeshBuffer*)buf)->Material = g_materials[f->material];
// Use VBO
//buf->setHardwareMappingHint(scene::EHM_STATIC);
// Add to mesh
mesh_new->addMeshBuffer(buf);
// Mesh grabbed it
buf->drop();
}
material_in_use = f->material;
}
u16 new_indices[] = {0,1,2,2,3,0};
//buf->append(f->vertices, 4, indices, 6);
}
#endif
// Use VBO for mesh (this just would set this for ever buffer) // Use VBO for mesh (this just would set this for ever buffer)
//mesh_new->setHardwareMappingHint(scene::EHM_STATIC); //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
@ -518,7 +479,10 @@ void MapBlock::updateMesh()
<<" materials (meshbuffers)"<<std::endl;*/ <<" materials (meshbuffers)"<<std::endl;*/
} }
// TODO: Get rid of the FastFace stage /*
Clear temporary FastFaces
*/
core::list<FastFace*>::Iterator i; core::list<FastFace*>::Iterator i;
i = fastfaces_new->begin(); i = fastfaces_new->begin();
for(; i != fastfaces_new->end(); i++) for(; i != fastfaces_new->end(); i++)
@ -528,6 +492,18 @@ void MapBlock::updateMesh()
fastfaces_new->clear(); fastfaces_new->clear();
delete fastfaces_new; delete fastfaces_new;
/*
Add special graphics:
- torches
*/
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
for(s16 y=0; y<MAP_BLOCKSIZE; y++)
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
{
v3s16 p(x,y,z);
}
/* /*
Replace the mesh Replace the mesh
*/ */

@ -56,6 +56,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
GRAVEL GRAVEL
- Dynamics of gravel: if there is a drop of more than two - Dynamics of gravel: if there is a drop of more than two
blocks on any side, it will drop in there. Is this doable? blocks on any side, it will drop in there. Is this doable?
TODO: These should be named to "content" or something like that
*/ */
enum Material enum Material
@ -78,6 +80,8 @@ enum Material
MATERIAL_MUD, MATERIAL_MUD,
MATERIAL_OCEAN,
// This is set to the number of the actual values in this enum // This is set to the number of the actual values in this enum
USEFUL_MATERIAL_COUNT USEFUL_MATERIAL_COUNT
}; };
@ -88,7 +92,7 @@ enum Material
*/ */
inline bool light_propagates_material(u8 m) inline bool light_propagates_material(u8 m)
{ {
return (m == MATERIAL_AIR || m == MATERIAL_LIGHT || m == MATERIAL_WATER); return (m == MATERIAL_AIR || m == MATERIAL_LIGHT || m == MATERIAL_WATER || m == MATERIAL_OCEAN);
} }
/* /*
@ -110,7 +114,7 @@ inline u8 material_solidness(u8 m)
{ {
if(m == MATERIAL_AIR) if(m == MATERIAL_AIR)
return 0; return 0;
if(m == MATERIAL_WATER) if(m == MATERIAL_WATER || m == MATERIAL_OCEAN)
return 1; return 1;
return 2; return 2;
} }
@ -118,29 +122,54 @@ inline u8 material_solidness(u8 m)
// Objects collide with walkable materials // Objects collide with walkable materials
inline bool material_walkable(u8 m) inline bool material_walkable(u8 m)
{ {
return (m != MATERIAL_AIR && m != MATERIAL_WATER); return (m != MATERIAL_AIR && m != MATERIAL_WATER && m != MATERIAL_OCEAN && m != MATERIAL_LIGHT);
} }
// A liquid resists fast movement // A liquid resists fast movement
inline bool material_liquid(u8 m) inline bool material_liquid(u8 m)
{ {
return (m == MATERIAL_WATER); return (m == MATERIAL_WATER || m == MATERIAL_OCEAN);
} }
// Pointable materials can be pointed to in the map // Pointable materials can be pointed to in the map
inline bool material_pointable(u8 m) inline bool material_pointable(u8 m)
{ {
return (m != MATERIAL_AIR && m != MATERIAL_WATER); return (m != MATERIAL_AIR && m != MATERIAL_WATER && m != MATERIAL_OCEAN);
} }
inline bool material_diggable(u8 m) inline bool material_diggable(u8 m)
{ {
return (m != MATERIAL_AIR && m != MATERIAL_WATER); return (m != MATERIAL_AIR && m != MATERIAL_WATER && m != MATERIAL_OCEAN);
} }
inline bool material_buildable_to(u8 m) inline bool material_buildable_to(u8 m)
{ {
return (m == MATERIAL_AIR || m == MATERIAL_WATER); return (m == MATERIAL_AIR || m == MATERIAL_WATER || m == MATERIAL_OCEAN);
}
/*
As of now, input is a "material" and the output is a "material"
*/
inline u8 content_cube_material(u8 c)
{
if(c == MATERIAL_IGNORE || c == MATERIAL_LIGHT)
return MATERIAL_AIR;
return c;
}
/*
Returns true for materials that form the base ground that
follows the main heightmap
*/
inline bool is_ground_material(u8 m)
{
return(
m == MATERIAL_STONE ||
m == MATERIAL_GRASS ||
m == MATERIAL_GRASS_FOOTSTEPS ||
m == MATERIAL_MESE ||
m == MATERIAL_MUD
);
} }
/* /*
@ -168,21 +197,6 @@ inline u8 face_materials(u8 m1, u8 m2)
return 2; return 2;
} }
/*
Returns true for materials that form the base ground that
follows the main heightmap
*/
inline bool is_ground_material(u8 m)
{
return(
m == MATERIAL_STONE ||
m == MATERIAL_GRASS ||
m == MATERIAL_GRASS_FOOTSTEPS ||
m == MATERIAL_MESE ||
m == MATERIAL_MUD
);
}
struct MapNode struct MapNode
{ {
//TODO: block type to differ from material //TODO: block type to differ from material
@ -214,7 +228,9 @@ struct MapNode
bool operator==(const MapNode &other) bool operator==(const MapNode &other)
{ {
return (d == other.d && param == other.param); return (d == other.d
&& param == other.param
&& pressure == other.pressure);
} }
bool light_propagates() bool light_propagates()

@ -196,6 +196,28 @@ void * EmergeThread::Thread()
{ {
MapBlock *block = i.getNode()->getValue(); MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block); modified_blocks.insert(block->getPos(), block);
/*
Update water pressure.
This also adds suitable nodes to active_nodes.
*/
MapVoxelManipulator v(&map);
VoxelArea area(block->getPosRelative(),
block->getPosRelative() + v3s16(1,1,1)*(MAP_BLOCKSIZE-1));
try
{
v.updateAreaWaterPressure(area, m_server->m_flow_active_nodes);
}
catch(ProcessingLimitException &e)
{
dstream<<"Processing limit reached (1)"<<std::endl;
}
v.blitBack(modified_blocks);
} }
/*dstream<<"lighting "<<lighting_invalidated_blocks.size() /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
@ -244,12 +266,6 @@ void * EmergeThread::Thread()
// Remove block from sent history // Remove block from sent history
client->SetBlocksNotSent(modified_blocks); client->SetBlocksNotSent(modified_blocks);
} }
/*if(q->peer_ids.find(client->peer_id) != NULL)
{
// Decrement emerge queue count of client
client->BlockEmerged();
}*/
} }
} }
@ -348,14 +364,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
//bool has_incomplete_blocks = false; //bool has_incomplete_blocks = false;
/* s16 d_max = g_settings.getS16("max_block_send_distance");
TODO: Get this from somewhere s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
*/
//s16 d_max = 7;
s16 d_max = 8;
//TODO: Get this from somewhere (probably a bigger value)
s16 d_max_gen = 5;
//dstream<<"Starting from "<<d_start<<std::endl; //dstream<<"Starting from "<<d_start<<std::endl;
@ -998,7 +1008,7 @@ void Server::AsyncRunStep()
{ {
static float counter = 0.0; static float counter = 0.0;
counter += dtime; counter += dtime;
if(counter >= 1.0) if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
{ {
counter = 0.0; counter = 0.0;
@ -1011,16 +1021,7 @@ void Server::AsyncRunStep()
MapVoxelManipulator v(&m_env.getMap()); MapVoxelManipulator v(&m_env.getMap());
/*try{ v.flowWater(m_flow_active_nodes, 0, false, 50);
v.flowWater(m_flow_active_nodes, 0, false, 20);
//v.flowWater(p_under, 0, true, 100);
}
catch(ProcessingLimitException &e)
{
dstream<<"Processing limit reached"<<std::endl;
}*/
v.flowWater(m_flow_active_nodes, 0, false, 20);
v.blitBack(modified_blocks); v.blitBack(modified_blocks);
@ -1059,7 +1060,7 @@ void Server::AsyncRunStep()
} }
} }
} } // interval counter
} }
// Periodically print some info // Periodically print some info
@ -1547,7 +1548,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
catch(ProcessingLimitException &e) catch(ProcessingLimitException &e)
{ {
dstream<<"Processing limit reached"<<std::endl; dstream<<"Processing limit reached (1)"<<std::endl;
} }
v.blitBack(modified_blocks); v.blitBack(modified_blocks);
@ -1624,6 +1625,28 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
*/ */
core::map<v3s16, MapBlock*> modified_blocks; core::map<v3s16, MapBlock*> modified_blocks;
m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks); m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
/*
Update water
*/
// Update water pressure around modification
// This also adds it to m_flow_active_nodes if appropriate
MapVoxelManipulator v(&m_env.getMap());
VoxelArea area(p_over-v3s16(1,1,1), p_over+v3s16(1,1,1));
try
{
v.updateAreaWaterPressure(area, m_flow_active_nodes);
}
catch(ProcessingLimitException &e)
{
dstream<<"Processing limit reached (1)"<<std::endl;
}
v.blitBack(modified_blocks);
} }
/* /*
Handle block object items Handle block object items
@ -2019,6 +2042,10 @@ void Server::peerAdded(con::Peer *peer)
assert(USEFUL_MATERIAL_COUNT <= PLAYER_INVENTORY_SIZE); assert(USEFUL_MATERIAL_COUNT <= PLAYER_INVENTORY_SIZE);
for(u16 i=0; i<USEFUL_MATERIAL_COUNT; i++) for(u16 i=0; i<USEFUL_MATERIAL_COUNT; i++)
{ {
// Skip some materials
if(i == MATERIAL_OCEAN)
continue;
InventoryItem *item = new MaterialItem(i, 1); InventoryItem *item = new MaterialItem(i, 1);
player->inventory.addItem(item); player->inventory.addItem(item);
} }

@ -264,11 +264,13 @@ struct TestVoxelManipulator
s16 highest_y = -32768; s16 highest_y = -32768;
assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == -1); assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == -1);
assert(highest_y == 3); assert(highest_y == 3);
/*assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == 3);
//assert(highest_y == 3);*/
active_nodes.clear(); active_nodes.clear();
active_nodes[v3s16(9,1,0)] = 1; active_nodes[v3s16(9,1,0)] = 1;
//v.flowWater(active_nodes, 0, false); //v.flowWater(active_nodes, 0, false);
v.flowWater(active_nodes, 0, true); v.flowWater(active_nodes, 0, true, 1000);
dstream<<"Final result of flowWater:"<<std::endl; dstream<<"Final result of flowWater:"<<std::endl;
v.print(dstream, VOXELPRINT_WATERPRESSURE); v.print(dstream, VOXELPRINT_WATERPRESSURE);

@ -335,23 +335,26 @@ int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
if(p.Y > highest_y) if(p.Y > highest_y)
highest_y = p.Y; highest_y = p.Y;
recur_count++; /*if(recur_count > 1000)
if(recur_count > 30)
throw ProcessingLimitException throw ProcessingLimitException
("getWaterPressure recur_count limit reached"); ("getWaterPressure recur_count limit reached");*/
if(recur_count > 10000)
return -1;
recur_count++;
v3s16 dirs[6] = { v3s16 dirs[6] = {
v3s16(0,1,0), // top v3s16(0,1,0), // top
v3s16(-1,0,0), // left
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,0,1), // back v3s16(0,0,1), // back
v3s16(0,0,-1), // front
v3s16(1,0,0), // right
v3s16(-1,0,0), // left
v3s16(0,-1,0), // bottom v3s16(0,-1,0), // bottom
}; };
// Load neighboring nodes // Load neighboring nodes
// TODO: A bigger area would be better emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 1);
emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)));
s32 i; s32 i;
for(i=0; i<6; i++) for(i=0; i<6; i++)
@ -368,13 +371,13 @@ int VoxelManipulator::getWaterPressure(v3s16 p, s16 &highest_y, int recur_count)
int pr; int pr;
// If at surface // If at ocean surface
/*if(n.pressure == 1) if(n.pressure == 1 && n.d == MATERIAL_OCEAN)
{ {
pr = 1; pr = 1;
} }
// Otherwise recurse more // Otherwise recurse more
else*/ else
{ {
pr = getWaterPressure(p2, highest_y, recur_count); pr = getWaterPressure(p2, highest_y, recur_count);
if(pr == -1) if(pr == -1)
@ -410,10 +413,21 @@ void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
core::map<v3s16, u8> &active_nodes, core::map<v3s16, u8> &active_nodes,
int recur_count) int recur_count)
{ {
//if(recur_count > 10000)
/*throw ProcessingLimitException
("spreadWaterPressure recur_count limit reached");*/
if(recur_count > 10)
return;
recur_count++; recur_count++;
if(recur_count > 10000)
throw ProcessingLimitException /*dstream<<"spreadWaterPressure: p=("
("spreadWaterPressure recur_count limit reached"); <<p.X<<","<<p.Y<<","<<p.Z<<")"
<<", oldpr="<<(int)m_data[m_area.index(p)].pressure
<<", pr="<<pr
<<", recur_count="<<recur_count
<<", request_area=";
request_area.print(dstream);
dstream<<std::endl;*/
m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3; m_flags[m_area.index(p)] |= VOXELFLAG_CHECKED3;
m_data[m_area.index(p)].pressure = pr; m_data[m_area.index(p)].pressure = pr;
@ -428,7 +442,7 @@ void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
}; };
// Load neighboring nodes // Load neighboring nodes
emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1))); emerge(VoxelArea(p - v3s16(1,1,1), p + v3s16(1,1,1)), 2);
s32 i; s32 i;
for(i=0; i<6; i++) for(i=0; i<6; i++)
@ -455,6 +469,7 @@ void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
// If block is at top // If block is at top
if(i == 0) if(i == 0)
{ {
//if(pr >= PRESERVE_WATER_VOLUME ? 3 : 2)
if(pr >= 3) if(pr >= 3)
pressure_causes_flow = true; pressure_causes_flow = true;
} }
@ -466,6 +481,7 @@ void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
// If block is at side // If block is at side
else else
{ {
//if(pr >= PRESERVE_WATER_VOLUME ? 2 : 1)
if(pr >= 2) if(pr >= 2)
pressure_causes_flow = true; pressure_causes_flow = true;
} }
@ -497,7 +513,10 @@ void VoxelManipulator::spreadWaterPressure(v3s16 p, int pr,
} }
// Ignore if correct pressure is already set and is not on // Ignore if correct pressure is already set and is not on
// request_area // request_area.
// Thus, request_area can be used for updating as much
// pressure info in some area as possible to possibly
// make some calls to getWaterPressure unnecessary.
if(n.pressure == pr2 && request_area.contains(p2) == false) if(n.pressure == pr2 && request_area.contains(p2) == false)
continue; continue;
@ -512,7 +531,7 @@ void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
TimeTaker timer("updateAreaWaterPressure", g_device, TimeTaker timer("updateAreaWaterPressure", g_device,
&updateareawaterpressure_time); &updateareawaterpressure_time);
emerge(a); emerge(a, 3);
bool checked2_clear = false; bool checked2_clear = false;
@ -596,20 +615,21 @@ void VoxelManipulator::updateAreaWaterPressure(VoxelArea a,
bool VoxelManipulator::flowWater(v3s16 removed_pos, bool VoxelManipulator::flowWater(v3s16 removed_pos,
core::map<v3s16, u8> &active_nodes, core::map<v3s16, u8> &active_nodes,
int recursion_depth, bool debugprint, int recursion_depth, bool debugprint,
int *counter, int counterlimit) u32 stoptime)
{ {
v3s16 dirs[6] = { v3s16 dirs[6] = {
v3s16(0,1,0), // top v3s16(0,1,0), // top
v3s16(-1,0,0), // left
v3s16(1,0,0), // right
v3s16(0,0,-1), // front v3s16(0,0,-1), // front
v3s16(0,0,1), // back v3s16(0,0,1), // back
v3s16(-1,0,0), // left
v3s16(1,0,0), // right
v3s16(0,-1,0), // bottom v3s16(0,-1,0), // bottom
}; };
recursion_depth++; recursion_depth++;
v3s16 p; v3s16 p;
bool from_ocean = false;
// Randomize horizontal order // Randomize horizontal order
static s32 cs = 0; static s32 cs = 0;
@ -625,7 +645,7 @@ bool VoxelManipulator::flowWater(v3s16 removed_pos,
TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time); TimeTaker timer1("flowWater pre", g_device, &flowwater_pre_time);
// Load neighboring nodes // Load neighboring nodes
emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1))); emerge(VoxelArea(removed_pos - v3s16(1,1,1), removed_pos + v3s16(1,1,1)), 4);
// Ignore incorrect removed_pos // Ignore incorrect removed_pos
{ {
@ -660,11 +680,13 @@ bool VoxelManipulator::flowWater(v3s16 removed_pos,
// If block is at bottom, select it if it has enough pressure // If block is at bottom, select it if it has enough pressure
if(i == 5) if(i == 5)
{ {
//if(n.pressure >= PRESERVE_WATER_VOLUME ? 3 : 2)
if(n.pressure >= 3) if(n.pressure >= 3)
break; break;
continue; continue;
} }
// Else block is at some side. Select it if it has enough pressure // Else block is at some side. Select it if it has enough pressure
//if(n.pressure >= PRESERVE_WATER_VOLUME ? 2 : 1)
if(n.pressure >= 2) if(n.pressure >= 2)
{ {
break; break;
@ -675,22 +697,47 @@ bool VoxelManipulator::flowWater(v3s16 removed_pos,
if(i==6) if(i==6)
return false; return false;
// Switch nodes at p and removed_pos /*
Move water and bubble
*/
u8 m = m_data[m_area.index(p)].d; u8 m = m_data[m_area.index(p)].d;
u8 f = m_flags[m_area.index(p)]; u8 f = m_flags[m_area.index(p)];
if(m == MATERIAL_OCEAN)
from_ocean = true;
// Move air bubble if not taking water from ocean
if(from_ocean == false)
{
m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d; m_data[m_area.index(p)].d = m_data[m_area.index(removed_pos)].d;
m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)]; m_flags[m_area.index(p)] = m_flags[m_area.index(removed_pos)];
}
m_data[m_area.index(removed_pos)].d = m; m_data[m_area.index(removed_pos)].d = m;
m_flags[m_area.index(removed_pos)] = f; m_flags[m_area.index(removed_pos)] = f;
// Mark removed_pos checked // Mark removed_pos checked
m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED; m_flags[m_area.index(removed_pos)] |= VOXELFLAG_CHECKED;
// If block was dropped from surface, increase pressure // If block was dropped from surface, increase pressure
if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1) if(i == 0 && m_data[m_area.index(removed_pos)].pressure == 1)
{ {
m_data[m_area.index(removed_pos)].pressure = 2; m_data[m_area.index(removed_pos)].pressure = 2;
} }
/*
NOTE: This does not work as-is
if(m == MATERIAL_OCEAN)
{
// If block was raised to surface, increase pressure of
// source node
if(i == 5 && m_data[m_area.index(p)].pressure == 1)
{
m_data[m_area.index(p)].pressure = 2;
}
}*/
/*if(debugprint) /*if(debugprint)
{ {
dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl; dstream<<"VoxelManipulator::flowWater(): Moved bubble:"<<std::endl;
@ -721,11 +768,29 @@ bool VoxelManipulator::flowWater(v3s16 removed_pos,
}//timer1 }//timer1
//if(PRESERVE_WATER_VOLUME)
if(from_ocean == false)
{
// Flow water to the newly created empty position // Flow water to the newly created empty position
/*flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit);*/
flowWater(p, active_nodes, recursion_depth, flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit); debugprint, stoptime);
}
if(stoptime != 0 && g_device != NULL)
{
u32 timenow = g_device->getTimer()->getRealTime();
if(timenow >= stoptime ||
(stoptime < 0x80000000 && timenow > 0x80000000))
{
dstream<<"flowWater: stoptime reached"<<std::endl;
throw ProcessingLimitException("flowWater stoptime reached");
}
}
find_again: find_again:
// Try flowing water to empty positions around removed_pos. // Try flowing water to empty positions around removed_pos.
// They are checked in reverse order compared to the previous loop. // They are checked in reverse order compared to the previous loop.
for(s32 i=5; i>=0; i--) for(s32 i=5; i>=0; i--)
@ -745,7 +810,9 @@ find_again:
// Flow water to node // Flow water to node
bool moved = bool moved =
flowWater(p, active_nodes, recursion_depth, flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit); debugprint, stoptime);
/*flowWater(p, active_nodes, recursion_depth,
debugprint, counter, counterlimit);*/
if(moved) if(moved)
{ {
@ -754,27 +821,13 @@ find_again:
} }
} }
if(counter != NULL)
{
(*counter)++;
if((*counter) % 10 == 0)
dstream<<"flowWater(): moved "<<(*counter)<<" nodes"
<<std::endl;
if(counterlimit != -1 && (*counter) > counterlimit)
{
dstream<<"Counter limit reached; returning"<<std::endl;
throw ProcessingLimitException("flowWater counterlimit reached");
}
}
return true; return true;
} }
void VoxelManipulator::flowWater( void VoxelManipulator::flowWater(
core::map<v3s16, u8> &active_nodes, core::map<v3s16, u8> &active_nodes,
int recursion_depth, bool debugprint, int recursion_depth, bool debugprint,
int counterlimit) u32 timelimit)
{ {
addarea_time = 0; addarea_time = 0;
emerge_time = 0; emerge_time = 0;
@ -783,25 +836,53 @@ void VoxelManipulator::flowWater(
updateareawaterpressure_time = 0; updateareawaterpressure_time = 0;
flowwater_pre_time = 0; flowwater_pre_time = 0;
if(active_nodes.size() == 0)
{
dstream<<"flowWater: no active nodes"<<std::endl;
return;
}
TimeTaker timer1("flowWater (active_nodes)", g_device); TimeTaker timer1("flowWater (active_nodes)", g_device);
dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl; dstream<<"active_nodes.size() = "<<active_nodes.size()<<std::endl;
int counter = 0; //int counter = 0;
u32 stoptime = 0;
if(g_device != NULL)
{
stoptime = g_device->getTimer()->getRealTime() + timelimit;
}
// Count of handled active nodes
u32 handled_count = 0;
try try
{ {
/*
Take random one at first
This is randomized only at the first time so that all
subsequent nodes will be taken at roughly the same position
*/
s32 k = 0;
if(active_nodes.size() != 0)
k = (s32)rand() % (s32)active_nodes.size();
// Flow water to active nodes // Flow water to active nodes
for(;;) for(;;)
//for(s32 h=0; h<1; h++)
{ {
// Clear check flags
clearFlag(VOXELFLAG_CHECKED);
if(active_nodes.size() == 0) if(active_nodes.size() == 0)
break; break;
dstream<<"Selecting a new active_node"<<std::endl; handled_count++;
// Clear check flags
clearFlag(VOXELFLAG_CHECKED);
//dstream<<"Selecting a new active_node"<<std::endl;
#if 0 #if 0
// Take first one // Take first one
@ -810,9 +891,7 @@ void VoxelManipulator::flowWater(
#endif #endif
#if 1 #if 1
// Take random one
s32 k = (s32)rand() % (s32)active_nodes.size();
//s32 k = 0;
core::map<v3s16, u8>::Iterator core::map<v3s16, u8>::Iterator
i = active_nodes.getIterator().getNode(); i = active_nodes.getIterator().getNode();
for(s32 j=0; j<k; j++) for(s32 j=0; j<k; j++)
@ -820,12 +899,17 @@ void VoxelManipulator::flowWater(
i++; i++;
} }
core::map<v3s16, u8>::Node *n = i.getNode(); core::map<v3s16, u8>::Node *n = i.getNode();
// Decrement index if less than 0.
// This keeps us in existing indices always.
if(k > 0)
k--;
#endif #endif
v3s16 p = n->getKey(); v3s16 p = n->getKey();
active_nodes.remove(p); active_nodes.remove(p);
flowWater(p, active_nodes, recursion_depth, flowWater(p, active_nodes, recursion_depth,
debugprint, &counter, counterlimit); debugprint, stoptime);
} }
} }
@ -836,9 +920,12 @@ void VoxelManipulator::flowWater(
v3s16 e = m_area.getExtent(); v3s16 e = m_area.getExtent();
s32 v = m_area.getVolume(); s32 v = m_area.getVolume();
dstream<<"flowWater (active): moved "<<counter<<" nodes, " //dstream<<"flowWater (active): moved "<<counter<<" nodes, "
dstream<<"flowWater (active): "
<<"area ended up as " <<"area ended up as "
<<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v <<e.X<<"x"<<e.Y<<"x"<<e.Z<<" = "<<v
<<", handled a_node count: "<<handled_count
<<", active_nodes.size() = "<<active_nodes.size()
<<std::endl; <<std::endl;
dstream<<"addarea_time: "<<addarea_time dstream<<"addarea_time: "<<addarea_time

@ -428,8 +428,8 @@ public:
bool flowWater(v3s16 removed_pos, bool flowWater(v3s16 removed_pos,
core::map<v3s16, u8> &active_nodes, core::map<v3s16, u8> &active_nodes,
int recursion_depth=0, int recursion_depth=0,
bool debugprint=false, int *counter=NULL, bool debugprint=false,
int counterlimit=-1 u32 stoptime=0
); );
/* /*
@ -446,7 +446,7 @@ public:
void flowWater(core::map<v3s16, u8> &active_nodes, void flowWater(core::map<v3s16, u8> &active_nodes,
int recursion_depth=0, int recursion_depth=0,
bool debugprint=false, bool debugprint=false,
int counterlimit=-1 u32 timelimit=50
); );
/* /*
@ -460,7 +460,7 @@ public:
If not found from source, add with VOXELFLAG_INEXISTENT If not found from source, add with VOXELFLAG_INEXISTENT
*/ */
virtual void emerge(VoxelArea a) virtual void emerge(VoxelArea a, s32 caller_id=-1)
{ {
//dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl; //dstream<<"emerge p=("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;
addArea(a); addArea(a);