void MapgenV7::growGrass() { for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++) { // 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 = vm->m_area.getExtent(); u32 i = vm->m_area.index(x, node_max.Y, z); s16 y; // Go to ground level for (y = node_max.Y; y >= node_min.Y; y--) { MapNode &n = vm->m_data[i]; if (ndef->get(n).param_type != CPT_LIGHT || ndef->get(n).liquid_type != LIQUID_NONE) break; vm->m_area.add_y(em, i, -1); } surface_y = (y >= node_min.Y) ? y : node_min.Y; } u32 i = vm->m_area.index(x, surface_y, z); MapNode *n = &vm->m_data[i]; if (n->getContent() == c_dirt && surface_y >= water_level - 20) n->setContent(c_dirt_with_grass); } }
void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime) { // Get time difference u32 dtime_s = 0; u32 stamp = block->getTimestamp(); if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED) dtime_s = m_game_time - block->getTimestamp(); dtime_s += additional_dtime; // Set current time as timestamp (and let it set ChangedFlag) block->setTimestamp(m_game_time); //dstream<<"Block is "<<dtime_s<<" seconds old."<<std::endl; // Activate stored objects activateObjects(block); // Run node metadata bool changed = block->m_node_metadata.step((float)dtime_s); if(changed) { MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; event.p = block->getPos(); m_map->dispatchEvent(&event); block->setChangedFlag(); } // TODO: Do something // TODO: Implement usage of ActiveBlockModifier // Here's a quick demonstration v3s16 p0; for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++) for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++) for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++) { v3s16 p = p0 + block->getPosRelative(); MapNode n = block->getNodeNoEx(p0); #if 1 // Test something: // Convert all mud under proper day lighting to grass if(n.getContent() == CONTENT_MUD) { if(dtime_s > 300) { MapNode n_top = block->getNodeNoEx(p0+v3s16(0,1,0)); if(content_features(n_top).air_equivalent && n_top.getLight(LIGHTBANK_DAY) >= 13) { n.setContent(CONTENT_GRASS); m_map->addNodeWithEvent(p, n); } } } #endif } }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) { INodeDefManager *ndef = env->getGameDef()->ndef(); ServerMap *map = &env->getServerMap(); MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0)); content_t c_snow = ndef->getId("snow"); if(ndef->get(n_top).light_propagates && !ndef->get(n_top).isLiquid() && n_top.getLightBlend(env->getDayNightRatio(), ndef) >= 13) { if(c_snow != CONTENT_IGNORE && n_top.getContent() == c_snow) n.setContent(ndef->getId("dirt_with_snow")); else n.setContent(ndef->getId("mapgen_dirt_with_grass")); map->addNodeWithEvent(p, n); } }
virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n) { INodeDefManager *ndef = env->getGameDef()->ndef(); ServerMap *map = &env->getServerMap(); MapNode n_top = map->getNodeNoEx(p+v3s16(0,1,0)); if(!ndef->get(n_top).light_propagates || ndef->get(n_top).isLiquid()) { n.setContent(ndef->getId("dirt")); map->addNodeWithEvent(p, n); } }
// Correct ids in the block to match nodedef based on names. // Unknown ones are added to nodedef. // Will not update itself to match id-name pairs in nodedef. void correctBlockNodeIds(const NameIdMapping *nimap, MapBlock *block, IGameDef *gamedef) { INodeDefManager *nodedef = gamedef->ndef(); // This means the block contains incorrect ids, and we contain // the information to convert those to names. // nodedef contains information to convert our names to globally // correct ids. std::set<content_t> unnamed_contents; std::set<std::string> unallocatable_contents; for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) { v3s16 p(x0,y0,z0); MapNode n = block->getNode(p); content_t local_id = n.getContent(); std::string name; bool found = nimap->getName(local_id, name); if(!found){ unnamed_contents.insert(local_id); continue; } content_t global_id; found = nodedef->getId(name, global_id); if(!found){ global_id = gamedef->allocateUnknownNodeId(name); if(global_id == CONTENT_IGNORE){ unallocatable_contents.insert(name); continue; } } n.setContent(global_id); block->setNode(p, n); } for(std::set<content_t>::const_iterator i = unnamed_contents.begin(); i != unnamed_contents.end(); i++){ errorstream<<"correctBlockNodeIds(): IGNORING ERROR: " <<"Block contains id "<<(*i) <<" with no name mapping"<<std::endl; } for(std::set<std::string>::const_iterator i = unallocatable_contents.begin(); i != unallocatable_contents.end(); i++){ errorstream<<"correctBlockNodeIds(): IGNORING ERROR: " <<"Could not allocate global id for node name \"" <<(*i)<<"\""<<std::endl; } }
MapNode mapnode_translate_to_internal(MapNode n_from, u8 version) { MapNode result = n_from; if(version <= 19) { content_t c_from = n_from.getContent(); for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++) { if(trans_table_19[i][1] == c_from) { result.setContent(trans_table_19[i][0]); break; } } } return result; }
void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos) { // 340ms @cs=8 //TimeTaker timer1("flow mud"); // Iterate a few times for (s16 k = 0; k < 3; k++) { for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++) for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) { // Invert coordinates every 2nd iteration if (k % 2 == 0) { x = mudflow_maxpos - (x - mudflow_minpos); z = mudflow_maxpos - (z - mudflow_minpos); } // Node position in 2d v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z); v3s16 em = vm->m_area.getExtent(); u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y); s16 y = node_max.Y; while (y >= node_min.Y) { for (;; y--) { MapNode *n = NULL; // Find mud for (; y >= node_min.Y; y--) { n = &vm->m_data[i]; if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass || n->getContent() == c_gravel) break; vm->m_area.add_y(em, i, -1); } // Stop if out of area //if(vmanip.m_area.contains(i) == false) if (y < node_min.Y) break; if (n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) { // Make it exactly mud n->setContent(c_dirt); // Don't flow it if the stuff under it is not mud { u32 i2 = i; vm->m_area.add_y(em, i2, -1); // Cancel if out of area if (vm->m_area.contains(i2) == false) continue; MapNode *n2 = &vm->m_data[i2]; if (n2->getContent() != c_dirt && n2->getContent() != c_dirt_with_grass) continue; } } v3s16 dirs4[4] = { v3s16(0, 0, 1), // back v3s16(1, 0, 0), // right v3s16(0, 0, -1), // front v3s16(-1, 0, 0), // left }; // Check that upper is air or doesn't exist. // Cancel dropping if upper keeps it in place u32 i3 = i; vm->m_area.add_y(em, i3, 1); if (vm->m_area.contains(i3) == true && ndef->get(vm->m_data[i3]).walkable) continue; // Drop mud on side for(u32 di = 0; di < 4; di++) { v3s16 dirp = dirs4[di]; u32 i2 = i; // Move to side vm->m_area.add_p(em, i2, dirp); // Fail if out of area if (vm->m_area.contains(i2) == false) continue; // Check that side is air MapNode *n2 = &vm->m_data[i2]; if (ndef->get(*n2).walkable) continue; // Check that under side is air vm->m_area.add_y(em, i2, -1); if (vm->m_area.contains(i2) == false) continue; n2 = &vm->m_data[i2]; if (ndef->get(*n2).walkable) continue; // Loop further down until not air bool dropped_to_unknown = false; do { vm->m_area.add_y(em, i2, -1); n2 = &vm->m_data[i2]; // if out of known area if(vm->m_area.contains(i2) == false || n2->getContent() == CONTENT_IGNORE) { dropped_to_unknown = true; break; } } while (ndef->get(*n2).walkable == false); // Loop one up so that we're in air vm->m_area.add_y(em, i2, 1); n2 = &vm->m_data[i2]; bool old_is_water = (n->getContent() == c_water_source); // Move mud to new place if (!dropped_to_unknown) { *n2 = *n; // Set old place to be air (or water) if(old_is_water) *n = MapNode(c_water_source); else *n = MapNode(CONTENT_AIR); } // Done break; } } } } } }
void Run() { TC parent; MapBlock b(&parent, v3s16(1,1,1)); v3s16 relpos(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); UASSERT(b.getPosRelative() == relpos); UASSERT(b.getBox().MinEdge.X == MAP_BLOCKSIZE); UASSERT(b.getBox().MaxEdge.X == MAP_BLOCKSIZE*2-1); UASSERT(b.getBox().MinEdge.Y == MAP_BLOCKSIZE); UASSERT(b.getBox().MaxEdge.Y == MAP_BLOCKSIZE*2-1); UASSERT(b.getBox().MinEdge.Z == MAP_BLOCKSIZE); UASSERT(b.getBox().MaxEdge.Z == MAP_BLOCKSIZE*2-1); UASSERT(b.isValidPosition(v3s16(0,0,0)) == true); UASSERT(b.isValidPosition(v3s16(-1,0,0)) == false); UASSERT(b.isValidPosition(v3s16(-1,-142,-2341)) == false); UASSERT(b.isValidPosition(v3s16(-124,142,2341)) == false); UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true); UASSERT(b.isValidPosition(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE,MAP_BLOCKSIZE-1)) == false); /* TODO: this method should probably be removed if the block size isn't going to be set variable */ /*UASSERT(b.getSizeNodes() == v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE));*/ // Changed flag should be initially set UASSERT(b.getModified() == MOD_STATE_WRITE_NEEDED); b.resetModified(); UASSERT(b.getModified() == MOD_STATE_CLEAN); // All nodes should have been set to // .d=CONTENT_IGNORE and .getLight() = 0 for(u16 z=0; z<MAP_BLOCKSIZE; z++) for(u16 y=0; y<MAP_BLOCKSIZE; y++) for(u16 x=0; x<MAP_BLOCKSIZE; x++) { //UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_AIR); UASSERT(b.getNode(v3s16(x,y,z)).getContent() == CONTENT_IGNORE); UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(x,y,z)).getLight(LIGHTBANK_NIGHT) == 0); } { MapNode n(CONTENT_AIR); for(u16 z=0; z<MAP_BLOCKSIZE; z++) for(u16 y=0; y<MAP_BLOCKSIZE; y++) for(u16 x=0; x<MAP_BLOCKSIZE; x++) { b.setNode(v3s16(x,y,z), n); } } /* Parent fetch functions */ parent.position_valid = false; parent.node.setContent(5); MapNode n; // Positions in the block should still be valid UASSERT(b.isValidPositionParent(v3s16(0,0,0)) == true); UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1)) == true); n = b.getNodeParent(v3s16(0,MAP_BLOCKSIZE-1,0)); UASSERT(n.getContent() == CONTENT_AIR); // ...but outside the block they should be invalid UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == false); UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == false); UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == false); { bool exception_thrown = false; try{ // This should throw an exception MapNode n = b.getNodeParent(v3s16(0,0,-1)); } catch(InvalidPositionException &e) { exception_thrown = true; } UASSERT(exception_thrown); } parent.position_valid = true; // Now the positions outside should be valid UASSERT(b.isValidPositionParent(v3s16(-121,2341,0)) == true); UASSERT(b.isValidPositionParent(v3s16(-1,0,0)) == true); UASSERT(b.isValidPositionParent(v3s16(MAP_BLOCKSIZE-1,MAP_BLOCKSIZE-1,MAP_BLOCKSIZE)) == true); n = b.getNodeParent(v3s16(0,0,MAP_BLOCKSIZE)); UASSERT(n.getContent() == 5); /* Set a node */ v3s16 p(1,2,0); n.setContent(4); b.setNode(p, n); UASSERT(b.getNode(p).getContent() == 4); //TODO: Update to new system /*UASSERT(b.getNodeTile(p) == 4); UASSERT(b.getNodeTile(v3s16(-1,-1,0)) == 5);*/ /* propagateSunlight() */ // Set lighting of all nodes to 0 for(u16 z=0; z<MAP_BLOCKSIZE; z++){ for(u16 y=0; y<MAP_BLOCKSIZE; y++){ for(u16 x=0; x<MAP_BLOCKSIZE; x++){ MapNode n = b.getNode(v3s16(x,y,z)); n.setLight(LIGHTBANK_DAY, 0); n.setLight(LIGHTBANK_NIGHT, 0); b.setNode(v3s16(x,y,z), n); } } } { /* Check how the block handles being a lonely sky block */ parent.position_valid = true; b.setIsUnderground(false); parent.node.setContent(CONTENT_AIR); parent.node.setLight(LIGHTBANK_DAY, LIGHT_SUN); parent.node.setLight(LIGHTBANK_NIGHT, 0); core::map<v3s16, bool> light_sources; // The bottom block is invalid, because we have a shadowing node UASSERT(b.propagateSunlight(light_sources) == false); UASSERT(b.getNode(v3s16(1,4,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN); UASSERT(b.getNode(v3s16(1,3,0)).getLight(LIGHTBANK_DAY) == LIGHT_SUN); UASSERT(b.getNode(v3s16(1,2,0)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(1,1,0)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(1,0,0)).getLight(LIGHTBANK_DAY) == 0); UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == LIGHT_SUN); UASSERT(b.getFaceLight2(1000, p, v3s16(0,1,0)) == LIGHT_SUN); UASSERT(b.getFaceLight2(1000, p, v3s16(0,-1,0)) == 0); UASSERT(b.getFaceLight2(0, p, v3s16(0,-1,0)) == 0); // According to MapBlock::getFaceLight, // The face on the z+ side should have double-diminished light //UASSERT(b.getFaceLight(p, v3s16(0,0,1)) == diminish_light(diminish_light(LIGHT_MAX))); // The face on the z+ side should have diminished light UASSERT(b.getFaceLight2(1000, p, v3s16(0,0,1)) == diminish_light(LIGHT_MAX)); } /* Check how the block handles being in between blocks with some non-sunlight while being underground */ { // Make neighbours to exist and set some non-sunlight to them parent.position_valid = true; b.setIsUnderground(true); parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2); core::map<v3s16, bool> light_sources; // The block below should be valid because there shouldn't be // sunlight in there either UASSERT(b.propagateSunlight(light_sources, true) == true); // Should not touch nodes that are not affected (that is, all of them) //UASSERT(b.getNode(v3s16(1,2,3)).getLight() == LIGHT_SUN); // Should set light of non-sunlighted blocks to 0. UASSERT(b.getNode(v3s16(1,2,3)).getLight(LIGHTBANK_DAY) == 0); } /* Set up a situation where: - There is only air in this block - There is a valid non-sunlighted block at the bottom, and - Invalid blocks elsewhere. - the block is not underground. This should result in bottom block invalidity */ { b.setIsUnderground(false); // Clear block for(u16 z=0; z<MAP_BLOCKSIZE; z++){ for(u16 y=0; y<MAP_BLOCKSIZE; y++){ for(u16 x=0; x<MAP_BLOCKSIZE; x++){ MapNode n; n.setContent(CONTENT_AIR); n.setLight(LIGHTBANK_DAY, 0); b.setNode(v3s16(x,y,z), n); } } } // Make neighbours invalid parent.position_valid = false; // Add exceptions to the top of the bottom block for(u16 x=0; x<MAP_BLOCKSIZE; x++) for(u16 z=0; z<MAP_BLOCKSIZE; z++) { parent.validity_exceptions.push_back(v3s16(MAP_BLOCKSIZE+x, MAP_BLOCKSIZE-1, MAP_BLOCKSIZE+z)); } // Lighting value for the valid nodes parent.node.setLight(LIGHTBANK_DAY, LIGHT_MAX/2); core::map<v3s16, bool> light_sources; // Bottom block is not valid UASSERT(b.propagateSunlight(light_sources) == false); } }
void ServerEnvironment::step(float dtime) { DSTACK(__FUNCTION_NAME); //TimeTaker timer("ServerEnv step"); // Get some settings bool footprints = g_settings.getBool("footprints"); /* Increment game time */ { m_game_time_fraction_counter += dtime; u32 inc_i = (u32)m_game_time_fraction_counter; m_game_time += inc_i; m_game_time_fraction_counter -= (float)inc_i; } /* Handle players */ for(core::list<Player*>::Iterator i = m_players.begin(); i != m_players.end(); i++) { Player *player = *i; // Ignore disconnected players if(player->peer_id == 0) continue; v3f playerpos = player->getPosition(); // Move player->move(dtime, *m_map, 100*BS); /* Add footsteps to grass */ if(footprints) { // Get node that is at BS/4 under player v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); try{ MapNode n = m_map->getNode(bottompos); if(n.getContent() == CONTENT_GRASS) { n.setContent(CONTENT_GRASS_FOOTSTEPS); m_map->setNode(bottompos, n); } } catch(InvalidPositionException &e) { } } } /* Manage active block list */ if(m_active_blocks_management_interval.step(dtime, 2.0)) { /* Get player block positions */ core::list<v3s16> players_blockpos; for(core::list<Player*>::Iterator i = m_players.begin(); i != m_players.end(); i++) { Player *player = *i; // Ignore disconnected players if(player->peer_id == 0) continue; v3s16 blockpos = getNodeBlockPos( floatToInt(player->getPosition(), BS)); players_blockpos.push_back(blockpos); } /* Update list of active blocks, collecting changes */ const s16 active_block_range = 5; core::map<v3s16, bool> blocks_removed; core::map<v3s16, bool> blocks_added; m_active_blocks.update(players_blockpos, active_block_range, blocks_removed, blocks_added); /* Handle removed blocks */ // Convert active objects that are no more in active blocks to static deactivateFarObjects(false); for(core::map<v3s16, bool>::Iterator i = blocks_removed.getIterator(); i.atEnd()==false; i++) { v3s16 p = i.getNode()->getKey(); /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") became inactive"<<std::endl;*/ MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block==NULL) continue; // Set current time as timestamp (and let it set ChangedFlag) block->setTimestamp(m_game_time); } /* Handle added blocks */ for(core::map<v3s16, bool>::Iterator i = blocks_added.getIterator(); i.atEnd()==false; i++) { v3s16 p = i.getNode()->getKey(); /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") became active"<<std::endl;*/ MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block==NULL) continue; activateBlock(block); } } /* Mess around in active blocks */ if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0)) { float dtime = 1.0; for(core::map<v3s16, bool>::Iterator i = m_active_blocks.m_list.getIterator(); i.atEnd()==false; i++) { v3s16 p = i.getNode()->getKey(); /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") being handled"<<std::endl;*/ MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block==NULL) continue; // Reset block usage timer block->resetUsageTimer(); // Set current time as timestamp block->setTimestampNoChangedFlag(m_game_time); // Run node metadata bool changed = block->m_node_metadata.step(dtime); if(changed) { MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; event.p = p; m_map->dispatchEvent(&event); block->setChangedFlag(); } } } if(m_active_blocks_test_interval.step(dtime, 10.0)) { //float dtime = 10.0; for(core::map<v3s16, bool>::Iterator i = m_active_blocks.m_list.getIterator(); i.atEnd()==false; i++) { v3s16 p = i.getNode()->getKey(); /*dstream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z <<") being handled"<<std::endl;*/ MapBlock *block = m_map->getBlockNoCreateNoEx(p); if(block==NULL) continue; // Set current time as timestamp block->setTimestampNoChangedFlag(m_game_time); /* Do stuff! Note that map modifications should be done using the event- making map methods so that the server gets information about them. Reading can be done quickly directly from the block. Everything should bind to inside this single content searching loop to keep things fast. */ // TODO: Implement usage of ActiveBlockModifier // Find out how many objects the block contains u32 active_object_count = block->m_static_objects.m_active.size(); // Find out how many objects this and all the neighbors contain u32 active_object_count_wider = 0; for(s16 x=-1; x<=1; x++) for(s16 y=-1; y<=1; y++) for(s16 z=-1; z<=1; z++) { MapBlock *block = m_map->getBlockNoCreateNoEx(p+v3s16(x,y,z)); if(block==NULL) continue; active_object_count_wider += block->m_static_objects.m_active.size(); } v3s16 p0; for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++) for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++) for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++) { v3s16 p = p0 + block->getPosRelative(); MapNode n = block->getNodeNoEx(p0); /* Test something: Convert mud under proper lighting to grass */ if(n.getContent() == CONTENT_MUD) { if(myrand()%20 == 0) { MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0)); if(content_features(n_top).air_equivalent && n_top.getLightBlend(getDayNightRatio()) >= 13) { n.setContent(CONTENT_GRASS); m_map->addNodeWithEvent(p, n); } } } /* Convert grass into mud if under something else than air */ if(n.getContent() == CONTENT_GRASS) { //if(myrand()%20 == 0) { MapNode n_top = m_map->getNodeNoEx(p+v3s16(0,1,0)); if(content_features(n_top).air_equivalent == false) { n.setContent(CONTENT_MUD); m_map->addNodeWithEvent(p, n); } } } /* Rats spawn around regular trees */ if(n.getContent() == CONTENT_TREE || n.getContent() == CONTENT_JUNGLETREE) { if(myrand()%200 == 0 && active_object_count_wider == 0) { v3s16 p1 = p + v3s16(myrand_range(-2, 2), 0, myrand_range(-2, 2)); MapNode n1 = m_map->getNodeNoEx(p1); MapNode n1b = m_map->getNodeNoEx(p1+v3s16(0,-1,0)); if(n1b.getContent() == CONTENT_GRASS && n1.getContent() == CONTENT_AIR) { v3f pos = intToFloat(p1, BS); ServerActiveObject *obj = new RatSAO(this, 0, pos); addActiveObject(obj); } } } } } } /* Step active objects */ { //TimeTaker timer("Step active objects"); // This helps the objects to send data at the same time bool send_recommended = false; m_send_recommended_timer += dtime; if(m_send_recommended_timer > 0.15) { m_send_recommended_timer = 0; send_recommended = true; } for(core::map<u16, ServerActiveObject*>::Iterator i = m_active_objects.getIterator(); i.atEnd()==false; i++) { ServerActiveObject* obj = i.getNode()->getValue(); // Don't step if is to be removed or stored statically if(obj->m_removed || obj->m_pending_deactivation) continue; // Step object obj->step(dtime, send_recommended); // Read messages from object while(obj->m_messages_out.size() > 0) { m_active_object_messages.push_back( obj->m_messages_out.pop_front()); } } } /* Manage active objects */ if(m_object_management_interval.step(dtime, 0.5)) { /* Remove objects that satisfy (m_removed && m_known_by_count==0) */ removeRemovedObjects(); } if(g_settings.getBool("enable_experimental")) { /* TEST CODE */ #if 1 m_random_spawn_timer -= dtime; if(m_random_spawn_timer < 0) { //m_random_spawn_timer += myrand_range(2.0, 20.0); //m_random_spawn_timer += 2.0; m_random_spawn_timer += 200.0; /* Find some position */ /*v2s16 p2d(myrand_range(-5,5), myrand_range(-5,5)); s16 y = 1 + getServerMap().findGroundLevel(p2d); v3f pos(p2d.X*BS,y*BS,p2d.Y*BS);*/ Player *player = getRandomConnectedPlayer(); v3f pos(0,0,0); if(player) pos = player->getPosition(); pos += v3f( myrand_range(-3,3)*BS, 0, myrand_range(-3,3)*BS ); /* Create a ServerActiveObject */ //TestSAO *obj = new TestSAO(this, 0, pos); //ServerActiveObject *obj = new ItemSAO(this, 0, pos, "CraftItem Stick 1"); //ServerActiveObject *obj = new RatSAO(this, 0, pos); //ServerActiveObject *obj = new Oerkki1SAO(this, 0, pos); ServerActiveObject *obj = new FireflySAO(this, 0, pos); addActiveObject(obj); } #endif } // enable_experimental }
void ClientEnvironment::step(float dtime) { DSTACK(__FUNCTION_NAME); // Get some settings bool free_move = g_settings.getBool("free_move"); bool footprints = g_settings.getBool("footprints"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); assert(lplayer); // collision info queue core::list<CollisionInfo> player_collisions; /* Get the speed the player is going */ bool is_climbing = lplayer->is_climbing; f32 player_speed = 0.001; // just some small value player_speed = lplayer->getSpeed().getLength(); /* Maximum position increment */ //f32 position_max_increment = 0.05*BS; f32 position_max_increment = 0.1*BS; // Maximum time increment (for collision detection etc) // time = distance / speed f32 dtime_max_increment = position_max_increment / player_speed; // Maximum time increment is 10ms or lower if(dtime_max_increment > 0.01) dtime_max_increment = 0.01; // Don't allow overly huge dtime if(dtime > 0.5) dtime = 0.5; f32 dtime_downcount = dtime; /* Stuff that has a maximum time increment */ u32 loopcount = 0; do { loopcount++; f32 dtime_part; if(dtime_downcount > dtime_max_increment) { dtime_part = dtime_max_increment; dtime_downcount -= dtime_part; } else { dtime_part = dtime_downcount; /* Setting this to 0 (no -=dtime_part) disables an infinite loop when dtime_part is so small that dtime_downcount -= dtime_part does nothing */ dtime_downcount = 0; } /* Handle local player */ { v3f lplayerpos = lplayer->getPosition(); // Apply physics if(free_move == false && is_climbing == false) { // Gravity v3f speed = lplayer->getSpeed(); if(lplayer->swimming_up == false) speed.Y -= 9.81 * BS * dtime_part * 2; // Water resistance if(lplayer->in_water_stable || lplayer->in_water) { f32 max_down = 2.0*BS; if(speed.Y < -max_down) speed.Y = -max_down; f32 max = 2.5*BS; if(speed.getLength() > max) { speed = speed / speed.getLength() * max; } } lplayer->setSpeed(speed); } /* Move the lplayer. This also does collision detection. */ lplayer->move(dtime_part, *m_map, position_max_increment, &player_collisions); } } while(dtime_downcount > 0.001); //std::cout<<"Looped "<<loopcount<<" times."<<std::endl; for(core::list<CollisionInfo>::Iterator i = player_collisions.begin(); i != player_collisions.end(); i++) { CollisionInfo &info = *i; if(info.t == COLLISION_FALL) { //f32 tolerance = BS*10; // 2 without damage f32 tolerance = BS*12; // 3 without damage f32 factor = 1; if(info.speed > tolerance) { f32 damage_f = (info.speed - tolerance)/BS*factor; u16 damage = (u16)(damage_f+0.5); if(lplayer->hp > damage) lplayer->hp -= damage; else lplayer->hp = 0; ClientEnvEvent event; event.type = CEE_PLAYER_DAMAGE; event.player_damage.amount = damage; m_client_event_queue.push_back(event); } } } /* Stuff that can be done in an arbitarily large dtime */ for(core::list<Player*>::Iterator i = m_players.begin(); i != m_players.end(); i++) { Player *player = *i; v3f playerpos = player->getPosition(); /* Handle non-local players */ if(player->isLocal() == false) { // Move player->move(dtime, *m_map, 100*BS); // Update lighting on remote players on client u8 light = LIGHT_MAX; try{ // Get node at head v3s16 p = floatToInt(playerpos + v3f(0,BS+BS/2,0), BS); MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio()); } catch(InvalidPositionException &e) {} player->updateLight(light); } /* Add footsteps to grass */ if(footprints) { // Get node that is at BS/4 under player v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); try{ MapNode n = m_map->getNode(bottompos); if(n.getContent() == CONTENT_GRASS) { n.setContent(CONTENT_GRASS_FOOTSTEPS); m_map->setNode(bottompos, n); // Update mesh on client if(m_map->mapType() == MAPTYPE_CLIENT) { v3s16 p_blocks = getNodeBlockPos(bottompos); MapBlock *b = m_map->getBlockNoCreate(p_blocks); //b->updateMesh(getDayNightRatio()); b->setMeshExpired(true); } } } catch(InvalidPositionException &e) { } } } /* Step active objects and update lighting of them */ for(core::map<u16, ClientActiveObject*>::Iterator i = m_active_objects.getIterator(); i.atEnd()==false; i++) { ClientActiveObject* obj = i.getNode()->getValue(); // Step object obj->step(dtime, this); if(m_active_object_light_update_interval.step(dtime, 0.21)) { // Update lighting //u8 light = LIGHT_MAX; u8 light = 0; try{ // Get node at head v3s16 p = obj->getLightPosition(); MapNode n = m_map->getNode(p); light = n.getLightBlend(getDayNightRatio()); } catch(InvalidPositionException &e) {} obj->updateLight(light); } } }
void Run() { /* VoxelArea */ VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1)); assert(a.index(0,0,0) == 1*3*3 + 1*3 + 1); assert(a.index(-1,-1,-1) == 0); VoxelArea c(v3s16(-2,-2,-2), v3s16(2,2,2)); // An area that is 1 bigger in x+ and z- VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2)); core::list<VoxelArea> aa; d.diff(c, aa); // Correct results core::array<VoxelArea> results; results.push_back(VoxelArea(v3s16(-2,-2,-3),v3s16(3,2,-3))); results.push_back(VoxelArea(v3s16(3,-2,-2),v3s16(3,2,2))); assert(aa.size() == results.size()); infostream<<"Result of diff:"<<std::endl; for(core::list<VoxelArea>::Iterator i = aa.begin(); i != aa.end(); i++) { i->print(infostream); infostream<<std::endl; s32 j = results.linear_search(*i); assert(j != -1); results.erase(j, 1); } /* VoxelManipulator */ VoxelManipulator v; v.print(infostream); infostream<<"*** Setting (-1,0,-1)=2 ***"<<std::endl; v.setNodeNoRef(v3s16(-1,0,-1), MapNode(2)); v.print(infostream); assert(v.getNode(v3s16(-1,0,-1)).getContent() == 2); infostream<<"*** Reading from inexistent (0,0,-1) ***"<<std::endl; EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,0,-1))); v.print(infostream); infostream<<"*** Adding area ***"<<std::endl; v.addArea(a); v.print(infostream); assert(v.getNode(v3s16(-1,0,-1)).getContent() == 2); EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1))); #if 0 /* Water stuff */ v.clear(); const char *content = "#...###### " "#...##..## " "#........ .." "############" "#...###### " "#...##..## " "#........# " "############" ; v3s16 size(12, 4, 2); VoxelArea area(v3s16(0,0,0), size-v3s16(1,1,1)); const char *p = content; for(s16 z=0; z<size.Z; z++) for(s16 y=size.Y-1; y>=0; y--) for(s16 x=0; x<size.X; x++) { MapNode n; //n.pressure = size.Y - y; if(*p == '#') n.setContent(CONTENT_STONE); else if(*p == '.') n.setContent(CONTENT_WATER); else if(*p == ' ') n.setContent(CONTENT_AIR); else assert(0); v.setNode(v3s16(x,y,z), n); p++; } v.print(infostream, VOXELPRINT_WATERPRESSURE); core::map<v3s16, u8> active_nodes; v.updateAreaWaterPressure(area, active_nodes); v.print(infostream, VOXELPRINT_WATERPRESSURE); //s16 highest_y = -32768; /* NOTE: These are commented out because this behaviour is changed all the time */ //assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == -1); //assert(highest_y == 3); /*assert(v.getWaterPressure(v3s16(7, 1, 1), highest_y, 0) == 3); //assert(highest_y == 3);*/ active_nodes.clear(); active_nodes[v3s16(9,1,0)] = 1; //v.flowWater(active_nodes, 0, true, 1000); v.flowWater(active_nodes, 0, false, 1000); infostream<<"Final result of flowWater:"<<std::endl; v.print(infostream, VOXELPRINT_WATERPRESSURE); #endif //assert(0); }