- std::string path = savedir + "/env_meta.txt";
-
- // Open file and serialize
- std::ofstream os(path.c_str(), std::ios_base::binary);
- if(os.good() == false)
- {
- infostream<<"ServerEnvironment::saveMeta(): Failed to open "
- <<path<<std::endl;
- throw SerializationError("Couldn't save env meta");
- }
-
- Settings args;
- args.setU64("game_time", m_game_time);
- args.setU64("time_of_day", getTimeOfDay());
- args.writeLines(os);
- os<<"EnvArgsEnd\n";
-}
-
-void ServerEnvironment::loadMeta(const std::string &savedir)
-{
- std::string path = savedir + "/env_meta.txt";
-
- // Open file and deserialize
- std::ifstream is(path.c_str(), std::ios_base::binary);
- if(is.good() == false)
- {
- infostream<<"ServerEnvironment::loadMeta(): Failed to open "
- <<path<<std::endl;
- throw SerializationError("Couldn't load env meta");
- }
-
- Settings args;
-
- for(;;)
- {
- if(is.eof())
- throw SerializationError
- ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
- std::string line;
- std::getline(is, line);
- std::string trimmedline = trim(line);
- if(trimmedline == "EnvArgsEnd")
- break;
- args.parseConfigLine(line);
- }
-
- try{
- m_game_time = args.getU64("game_time");
- }catch(SettingNotFoundException &e){
- // Getting this is crucial, otherwise timestamps are useless
- throw SerializationError("Couldn't load env meta game_time");
- }
-
- try{
- m_time_of_day = args.getU64("time_of_day");
- }catch(SettingNotFoundException &e){
- // This is not as important
- m_time_of_day = 9000;
- }
-}
-
-struct ActiveABM
-{
- ActiveBlockModifier *abm;
- int chance;
- std::set<content_t> required_neighbors;
-};
-
-class ABMHandler
-{
-private:
- ServerEnvironment *m_env;
- std::map<content_t, std::list<ActiveABM> > m_aabms;
-public:
- ABMHandler(core::list<ABMWithState> &abms,
- float dtime_s, ServerEnvironment *env,
- bool use_timers):
- m_env(env)
- {
- if(dtime_s < 0.001)
- return;
- INodeDefManager *ndef = env->getGameDef()->ndef();
- for(core::list<ABMWithState>::Iterator
- i = abms.begin(); i != abms.end(); i++){
- ActiveBlockModifier *abm = i->abm;
- float trigger_interval = abm->getTriggerInterval();
- if(trigger_interval < 0.001)
- trigger_interval = 0.001;
- float actual_interval = dtime_s;
- if(use_timers){
- i->timer += dtime_s;
- if(i->timer < trigger_interval)
- continue;
- i->timer -= trigger_interval;
- actual_interval = trigger_interval;
- }
- ActiveABM aabm;
- aabm.abm = abm;
- float intervals = actual_interval / trigger_interval;
- float chance = abm->getTriggerChance();
- if(chance == 0)
- chance = 1;
- aabm.chance = 1.0 / pow((float)1.0/chance, (float)intervals);
- if(aabm.chance == 0)
- aabm.chance = 1;
- // Trigger neighbors
- std::set<std::string> required_neighbors_s
- = abm->getRequiredNeighbors();
- for(std::set<std::string>::iterator
- i = required_neighbors_s.begin();
- i != required_neighbors_s.end(); i++){
- content_t c = ndef->getId(*i);
- if(c == CONTENT_IGNORE)
- continue;
- aabm.required_neighbors.insert(c);
- }
- // Trigger contents
- std::set<std::string> contents_s = abm->getTriggerContents();
- for(std::set<std::string>::iterator
- i = contents_s.begin(); i != contents_s.end(); i++){
- content_t c = ndef->getId(*i);
- if(c == CONTENT_IGNORE)
- continue;
- std::map<content_t, std::list<ActiveABM> >::iterator j;
- j = m_aabms.find(c);
- if(j == m_aabms.end()){
- std::list<ActiveABM> aabmlist;
- m_aabms[c] = aabmlist;
- j = m_aabms.find(c);
- }
- j->second.push_back(aabm);
- }
- }
- }
- void apply(MapBlock *block)
- {
- if(m_aabms.empty())
- return;
-
- ServerMap *map = &m_env->getServerMap();
-
- 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++)
- {
- MapNode n = block->getNodeNoEx(p0);
- content_t c = n.getContent();
- v3s16 p = p0 + block->getPosRelative();
-
- std::map<content_t, std::list<ActiveABM> >::iterator j;
- j = m_aabms.find(c);
- if(j == m_aabms.end())
- continue;
-
- for(std::list<ActiveABM>::iterator
- i = j->second.begin(); i != j->second.end(); i++)
- {
- if(myrand() % i->chance != 0)
- continue;
-
- // Check neighbors
- if(!i->required_neighbors.empty())
- {
- v3s16 p1;
- for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
- for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
- for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
- {
- if(p1 == p)
- continue;
- MapNode n = map->getNodeNoEx(p1);
- content_t c = n.getContent();
- std::set<content_t>::const_iterator k;
- k = i->required_neighbors.find(c);
- if(k != i->required_neighbors.end()){
- goto neighbor_found;
- }
- }
- // No required neighbor found
- continue;
- }
-neighbor_found:
-
- // 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 *block2 = map->getBlockNoCreateNoEx(
- block->getPos() + v3s16(x,y,z));
- if(block2==NULL)
- continue;
- active_object_count_wider +=
- block2->m_static_objects.m_active.size()
- + block2->m_static_objects.m_stored.size();
- }
-
- // Call all the trigger variations
- i->abm->trigger(m_env, p, n);
- i->abm->trigger(m_env, p, n,
- active_object_count, active_object_count_wider);
- }
- }
- }
-};
-
-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;
-
- /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
- <<stamp<<", game time: "<<m_game_time<<std::endl;*/
-
- // Set current time as timestamp
- block->setTimestampNoChangedFlag(m_game_time);
-
- /*infostream<<"ServerEnvironment::activateBlock(): 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->raiseModified(MOD_STATE_WRITE_NEEDED,
- "node metadata modified in activateBlock");
- }
-
- /* Handle ActiveBlockModifiers */
- ABMHandler abmhandler(m_abms, dtime_s, this, false);
- abmhandler.apply(block);
-}
-
-void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
-{
- m_abms.push_back(ABMWithState(abm));
-}
-
-std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
-{
- std::set<u16> objects;
- for(core::map<u16, ServerActiveObject*>::Iterator
- i = m_active_objects.getIterator();
- i.atEnd()==false; i++)
- {
- ServerActiveObject* obj = i.getNode()->getValue();
- u16 id = i.getNode()->getKey();
- v3f objectpos = obj->getBasePosition();
- if(objectpos.getDistanceFrom(pos) > radius)
- continue;
- objects.insert(id);
- }
- return objects;
-}
-
-void ServerEnvironment::clearAllObjects()
-{
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Removing all active objects"<<std::endl;
- core::list<u16> objects_to_remove;
- for(core::map<u16, ServerActiveObject*>::Iterator
- i = m_active_objects.getIterator();
- i.atEnd()==false; i++)
- {
- ServerActiveObject* obj = i.getNode()->getValue();
- if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
- continue;
- u16 id = i.getNode()->getKey();
- v3f objectpos = obj->getBasePosition();
- // Delete static object if block is loaded
- if(obj->m_static_exists){
- MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
- if(block){
- block->m_static_objects.remove(id);
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "clearAllObjects");
- obj->m_static_exists = false;
- }
- }
- // If known by some client, don't delete immediately
- if(obj->m_known_by_count > 0){
- obj->m_pending_deactivation = true;
- obj->m_removed = true;
- continue;
- }
-
- // Tell the object about removal
- obj->removingFromEnvironment();
- // Deregister in scripting api
- scriptapi_rm_object_reference(m_lua, obj);
-
- // Delete active object
- if(obj->environmentDeletes())
- delete obj;
- // Id to be removed from m_active_objects
- objects_to_remove.push_back(id);
- }
- // Remove references from m_active_objects
- for(core::list<u16>::Iterator i = objects_to_remove.begin();
- i != objects_to_remove.end(); i++)
- {
- m_active_objects.remove(*i);
- }
-
- core::list<v3s16> loadable_blocks;
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Listing all loadable blocks"<<std::endl;
- m_map->listAllLoadableBlocks(loadable_blocks);
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Done listing all loadable blocks: "
- <<loadable_blocks.size()
- <<", now clearing"<<std::endl;
- u32 report_interval = loadable_blocks.size() / 10;
- u32 num_blocks_checked = 0;
- u32 num_blocks_cleared = 0;
- u32 num_objs_cleared = 0;
- for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
- i != loadable_blocks.end(); i++)
- {
- v3s16 p = *i;
- MapBlock *block = m_map->emergeBlock(p, false);
- if(!block){
- errorstream<<"ServerEnvironment::clearAllObjects(): "
- <<"Failed to emerge block "<<PP(p)<<std::endl;
- continue;
- }
- u32 num_stored = block->m_static_objects.m_stored.size();
- u32 num_active = block->m_static_objects.m_active.size();
- if(num_stored != 0 || num_active != 0){
- block->m_static_objects.m_stored.clear();
- block->m_static_objects.m_active.clear();
- block->raiseModified(MOD_STATE_WRITE_NEEDED,
- "clearAllObjects");
- num_objs_cleared += num_stored + num_active;
- num_blocks_cleared++;
- }
- num_blocks_checked++;
-
- if(num_blocks_checked % report_interval == 0){
- float percent = 100.0 * (float)num_blocks_checked /
- loadable_blocks.size();
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Cleared "<<num_objs_cleared<<" objects"
- <<" in "<<num_blocks_cleared<<" blocks ("
- <<percent<<"%)"<<std::endl;
- }
- }
- infostream<<"ServerEnvironment::clearAllObjects(): "
- <<"Finished: Cleared "<<num_objs_cleared<<" objects"
- <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
-}
-
-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
- */
- {
- ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
- 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() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
- {
- n.setContent(LEGN(m_gamedef->ndef(), "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))
- {
- ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
- /*
- 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 = g_settings->getS16("active_block_range");
- 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();
-
- /*infostream<<"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();
-
- /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
- <<") became active"<<std::endl;*/
-
- MapBlock *block = m_map->getBlockNoCreateNoEx(p);
- if(block==NULL){
- // Block needs to be fetched first
- m_emerger->queueBlockEmerge(p, false);
- m_active_blocks.m_list.remove(p);
- continue;
- }
-
- activateBlock(block);
- }
- }
-
- /*
- Mess around in active blocks
- */
- if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
- {
- ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
-
- 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();
-
- /*infostream<<"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);
- // If time has changed much from the one on disk,
- // set block to be saved when it is unloaded
- if(block->getTimestamp() > block->getDiskTimestamp() + 60)
- block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
- "Timestamp older than 60s (step)");
-
- // 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->raiseModified(MOD_STATE_WRITE_NEEDED,
- "node metadata modified in step");
- }
- }
- }
-
- const float abm_interval = 1.0;
- if(m_active_block_modifier_interval.step(dtime, abm_interval))
- {
- ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
- TimeTaker timer("modify in active blocks");
-
- // Initialize handling of ActiveBlockModifiers
- ABMHandler abmhandler(m_abms, abm_interval, this, true);
-
- for(core::map<v3s16, bool>::Iterator
- i = m_active_blocks.m_list.getIterator();
- i.atEnd()==false; i++)
- {
- v3s16 p = i.getNode()->getKey();
-
- /*infostream<<"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);
-
- /* Handle ActiveBlockModifiers */
- abmhandler.apply(block);
- }
-
- u32 time_ms = timer.stop(true);
- u32 max_time_ms = 200;
- if(time_ms > max_time_ms){
- infostream<<"WARNING: active block modifiers took "
- <<time_ms<<"ms (longer than "
- <<max_time_ms<<"ms)"<<std::endl;
- }
- }
-
- /*
- Step script environment (run global on_step())
- */
- scriptapi_environment_step(m_lua, dtime);
-
- /*
- Step active objects
- */
- {
- ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
- //TimeTaker timer("Step active objects");
-
- g_profiler->avg("SEnv: num of objects", m_active_objects.size());
-
- // 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 > getSendRecommendedInterval())
- {
- m_send_recommended_timer -= getSendRecommendedInterval();
- send_recommended = true;
- }
-
- for(core::map<u16, ServerActiveObject*>::Iterator
- i = m_active_objects.getIterator();
- i.atEnd()==false; i++)
- {
- ServerActiveObject* obj = i.getNode()->getValue();
- // Remove non-peaceful mobs on peaceful mode
- if(g_settings->getBool("only_peaceful_mobs")){
- if(!obj->isPeaceful())
- obj->m_removed = true;
- }
- // 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))
- {
- ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
- /*
- Remove objects that satisfy (m_removed && m_known_by_count==0)
- */
- removeRemovedObjects();
- }