3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "environment.h"
23 #include "collision.h"
24 #include "content_mapnode.h"
26 #include "serverobject.h"
27 #include "content_sao.h"
31 #include "scripting_game.h"
33 #include "nodemetadata.h"
34 #include "main.h" // For g_settings, g_profiler
37 #include "clientmap.h"
38 #include "localplayer.h"
41 #include "daynightratio.h"
44 #include "util/serialize.h"
45 #include "jthread/jmutexautolock.h"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 Environment::Environment():
51 m_time_of_day_f(9000./24000),
52 m_time_of_day_speed(0),
54 m_enable_day_night_ratio_override(false),
55 m_day_night_ratio_override(0.0f)
59 Environment::~Environment()
62 for(std::list<Player*>::iterator i = m_players.begin();
63 i != m_players.end(); ++i)
69 void Environment::addPlayer(Player *player)
71 DSTACK(__FUNCTION_NAME);
73 Check that peer_ids are unique.
74 Also check that names are unique.
75 Exception: there can be multiple players with peer_id=0
77 // If peer id is non-zero, it has to be unique.
78 if(player->peer_id != 0)
79 assert(getPlayer(player->peer_id) == NULL);
80 // Name has to be unique.
81 assert(getPlayer(player->getName()) == NULL);
83 m_players.push_back(player);
86 void Environment::removePlayer(u16 peer_id)
88 DSTACK(__FUNCTION_NAME);
90 for(std::list<Player*>::iterator i = m_players.begin();
91 i != m_players.end();)
94 if(player->peer_id == peer_id) {
96 i = m_players.erase(i);
103 void Environment::removePlayer(const char *name)
105 for (std::list<Player*>::iterator it = m_players.begin();
106 it != m_players.end(); ++it) {
107 if (strcmp((*it)->getName(), name) == 0) {
115 Player * Environment::getPlayer(u16 peer_id)
117 for(std::list<Player*>::iterator i = m_players.begin();
118 i != m_players.end(); ++i)
121 if(player->peer_id == peer_id)
127 Player * Environment::getPlayer(const char *name)
129 for(std::list<Player*>::iterator i = m_players.begin();
130 i != m_players.end(); ++i)
133 if(strcmp(player->getName(), name) == 0)
139 Player * Environment::getRandomConnectedPlayer()
141 std::list<Player*> connected_players = getPlayers(true);
142 u32 chosen_one = myrand() % connected_players.size();
144 for(std::list<Player*>::iterator
145 i = connected_players.begin();
146 i != connected_players.end(); ++i)
158 Player * Environment::getNearestConnectedPlayer(v3f pos)
160 std::list<Player*> connected_players = getPlayers(true);
162 Player *nearest_player = NULL;
163 for(std::list<Player*>::iterator
164 i = connected_players.begin();
165 i != connected_players.end(); ++i)
168 f32 d = player->getPosition().getDistanceFrom(pos);
169 if(d < nearest_d || nearest_player == NULL)
172 nearest_player = player;
175 return nearest_player;
178 std::list<Player*> Environment::getPlayers()
183 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
185 std::list<Player*> newlist;
186 for(std::list<Player*>::iterator
187 i = m_players.begin();
188 i != m_players.end(); ++i)
192 if(ignore_disconnected)
194 // Ignore disconnected players
195 if(player->peer_id == 0)
199 newlist.push_back(player);
204 u32 Environment::getDayNightRatio()
206 if(m_enable_day_night_ratio_override)
207 return m_day_night_ratio_override;
208 bool smooth = g_settings->getBool("enable_shaders");
209 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
212 void Environment::setTimeOfDaySpeed(float speed)
214 JMutexAutoLock(this->m_lock);
215 m_time_of_day_speed = speed;
218 float Environment::getTimeOfDaySpeed()
220 JMutexAutoLock(this->m_lock);
221 float retval = m_time_of_day_speed;
225 void Environment::stepTimeOfDay(float dtime)
229 JMutexAutoLock(this->m_lock);
230 day_speed = m_time_of_day_speed;
233 m_time_counter += dtime;
234 f32 speed = day_speed * 24000./(24.*3600);
235 u32 units = (u32)(m_time_counter*speed);
239 if(m_time_of_day + units >= 24000)
241 m_time_of_day = (m_time_of_day + units) % 24000;
243 m_time_of_day_f = (float)m_time_of_day / 24000.0;
246 m_time_counter -= (f32)units / speed;
249 m_time_of_day_f += day_speed/24/3600*dtime;
250 if(m_time_of_day_f > 1.0)
251 m_time_of_day_f -= 1.0;
252 if(m_time_of_day_f < 0.0)
253 m_time_of_day_f += 1.0;
261 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
265 // Initialize timer to random value to spread processing
266 float itv = abm->getTriggerInterval();
267 itv = MYMAX(0.001, itv); // No less than 1ms
268 int minval = MYMAX(-0.51*itv, -60); // Clamp to
269 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
270 timer = myrand_range(minval, maxval);
277 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
280 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
281 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
282 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
289 void ActiveBlockList::update(std::list<v3s16> &active_positions,
291 std::set<v3s16> &blocks_removed,
292 std::set<v3s16> &blocks_added)
297 std::set<v3s16> newlist = m_forceloaded_list;
298 for(std::list<v3s16>::iterator i = active_positions.begin();
299 i != active_positions.end(); ++i)
301 fillRadiusBlock(*i, radius, newlist);
305 Find out which blocks on the old list are not on the new list
307 // Go through old list
308 for(std::set<v3s16>::iterator i = m_list.begin();
309 i != m_list.end(); ++i)
312 // If not on new list, it's been removed
313 if(newlist.find(p) == newlist.end())
314 blocks_removed.insert(p);
318 Find out which blocks on the new list are not on the old list
320 // Go through new list
321 for(std::set<v3s16>::iterator i = newlist.begin();
322 i != newlist.end(); ++i)
325 // If not on old list, it's been added
326 if(m_list.find(p) == m_list.end())
327 blocks_added.insert(p);
334 for(std::set<v3s16>::iterator i = newlist.begin();
335 i != newlist.end(); ++i)
346 ServerEnvironment::ServerEnvironment(ServerMap *map,
347 GameScripting *scriptIface, IGameDef *gamedef,
348 const std::string &path_world) :
350 m_script(scriptIface),
352 m_path_world(path_world),
353 m_send_recommended_timer(0),
354 m_active_block_interval_overload_skip(0),
356 m_game_time_fraction_counter(0),
357 m_recommended_send_interval(0.1),
358 m_max_lag_estimate(0.1)
362 ServerEnvironment::~ServerEnvironment()
364 // Clear active block list.
365 // This makes the next one delete all active objects.
366 m_active_blocks.clear();
368 // Convert all objects to static and delete the active objects
369 deactivateFarObjects(true);
374 // Delete ActiveBlockModifiers
375 for(std::list<ABMWithState>::iterator
376 i = m_abms.begin(); i != m_abms.end(); ++i){
381 Map & ServerEnvironment::getMap()
386 ServerMap & ServerEnvironment::getServerMap()
391 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
393 float distance = pos1.getDistanceFrom(pos2);
395 //calculate normalized direction vector
396 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
397 (pos2.Y - pos1.Y)/distance,
398 (pos2.Z - pos1.Z)/distance);
400 //find out if there's a node on path between pos1 and pos2
401 for (float i = 1; i < distance; i += stepsize) {
402 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
403 normalized_vector.Y * i,
404 normalized_vector.Z * i) +pos1,BS);
406 MapNode n = getMap().getNodeNoEx(pos);
408 if(n.param0 != CONTENT_AIR) {
418 void ServerEnvironment::saveLoadedPlayers()
420 std::string players_path = m_path_world + DIR_DELIM "players";
421 fs::CreateDir(players_path);
423 for (std::list<Player*>::iterator it = m_players.begin();
424 it != m_players.end();
426 RemotePlayer *player = static_cast<RemotePlayer*>(*it);
427 if (player->checkModified()) {
428 player->save(players_path);
433 void ServerEnvironment::savePlayer(const std::string &playername)
435 std::string players_path = m_path_world + DIR_DELIM "players";
436 fs::CreateDir(players_path);
438 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
440 player->save(players_path);
444 Player *ServerEnvironment::loadPlayer(const std::string &playername)
446 std::string players_path = m_path_world + DIR_DELIM "players" DIR_DELIM;
448 RemotePlayer *player = static_cast<RemotePlayer*>(getPlayer(playername.c_str()));
449 bool newplayer = false;
452 player = new RemotePlayer(m_gamedef);
456 RemotePlayer testplayer(m_gamedef);
457 std::string path = players_path + playername;
458 for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
459 // Open file and deserialize
460 std::ifstream is(path.c_str(), std::ios_base::binary);
464 testplayer.deSerialize(is, path);
466 if (testplayer.getName() == playername) {
467 *player = testplayer;
471 path = players_path + playername + itos(i);
474 infostream << "Player file for player " << playername
475 << " not found" << std::endl;
484 void ServerEnvironment::saveMeta()
486 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
488 // Open file and serialize
489 std::ostringstream ss(std::ios_base::binary);
492 args.setU64("game_time", m_game_time);
493 args.setU64("time_of_day", getTimeOfDay());
497 if(!fs::safeWriteToFile(path, ss.str()))
499 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
501 throw SerializationError("Couldn't save env meta");
505 void ServerEnvironment::loadMeta()
507 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
509 // Open file and deserialize
510 std::ifstream is(path.c_str(), std::ios_base::binary);
511 if(is.good() == false)
513 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
515 throw SerializationError("Couldn't load env meta");
523 throw SerializationError
524 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
526 std::getline(is, line);
527 std::string trimmedline = trim(line);
528 if(trimmedline == "EnvArgsEnd")
530 args.parseConfigLine(line);
534 m_game_time = args.getU64("game_time");
535 }catch(SettingNotFoundException &e){
536 // Getting this is crucial, otherwise timestamps are useless
537 throw SerializationError("Couldn't load env meta game_time");
541 m_time_of_day = args.getU64("time_of_day");
542 }catch(SettingNotFoundException &e){
543 // This is not as important
544 m_time_of_day = 9000;
550 ActiveBlockModifier *abm;
552 std::set<content_t> required_neighbors;
558 ServerEnvironment *m_env;
559 std::map<content_t, std::list<ActiveABM> > m_aabms;
561 ABMHandler(std::list<ABMWithState> &abms,
562 float dtime_s, ServerEnvironment *env,
568 INodeDefManager *ndef = env->getGameDef()->ndef();
569 for(std::list<ABMWithState>::iterator
570 i = abms.begin(); i != abms.end(); ++i){
571 ActiveBlockModifier *abm = i->abm;
572 float trigger_interval = abm->getTriggerInterval();
573 if(trigger_interval < 0.001)
574 trigger_interval = 0.001;
575 float actual_interval = dtime_s;
578 if(i->timer < trigger_interval)
580 i->timer -= trigger_interval;
581 actual_interval = trigger_interval;
583 float intervals = actual_interval / trigger_interval;
586 float chance = abm->getTriggerChance();
591 aabm.chance = chance / intervals;
595 std::set<std::string> required_neighbors_s
596 = abm->getRequiredNeighbors();
597 for(std::set<std::string>::iterator
598 i = required_neighbors_s.begin();
599 i != required_neighbors_s.end(); i++)
601 ndef->getIds(*i, aabm.required_neighbors);
604 std::set<std::string> contents_s = abm->getTriggerContents();
605 for(std::set<std::string>::iterator
606 i = contents_s.begin(); i != contents_s.end(); i++)
608 std::set<content_t> ids;
609 ndef->getIds(*i, ids);
610 for(std::set<content_t>::const_iterator k = ids.begin();
614 std::map<content_t, std::list<ActiveABM> >::iterator j;
616 if(j == m_aabms.end()){
617 std::list<ActiveABM> aabmlist;
618 m_aabms[c] = aabmlist;
621 j->second.push_back(aabm);
626 // Find out how many objects the given block and its neighbours contain.
627 // Returns the number of objects in the block, and also in 'wider' the
628 // number of objects in the block and all its neighbours. The latter
629 // may an estimate if any neighbours are unloaded.
630 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
633 u32 wider_unknown_count = 0;
634 for(s16 x=-1; x<=1; x++)
635 for(s16 y=-1; y<=1; y++)
636 for(s16 z=-1; z<=1; z++)
638 MapBlock *block2 = map->getBlockNoCreateNoEx(
639 block->getPos() + v3s16(x,y,z));
641 wider_unknown_count++;
644 wider += block2->m_static_objects.m_active.size()
645 + block2->m_static_objects.m_stored.size();
648 u32 active_object_count = block->m_static_objects.m_active.size();
649 u32 wider_known_count = 3*3*3 - wider_unknown_count;
650 wider += wider_unknown_count * wider / wider_known_count;
651 return active_object_count;
654 void apply(MapBlock *block)
659 ServerMap *map = &m_env->getServerMap();
661 u32 active_object_count_wider;
662 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
663 m_env->m_added_objects = 0;
666 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
667 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
668 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
670 MapNode n = block->getNodeNoEx(p0);
671 content_t c = n.getContent();
672 v3s16 p = p0 + block->getPosRelative();
674 std::map<content_t, std::list<ActiveABM> >::iterator j;
676 if(j == m_aabms.end())
679 for(std::list<ActiveABM>::iterator
680 i = j->second.begin(); i != j->second.end(); i++)
682 if(myrand() % i->chance != 0)
686 if(!i->required_neighbors.empty())
689 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
690 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
691 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
695 MapNode n = map->getNodeNoEx(p1);
696 content_t c = n.getContent();
697 std::set<content_t>::const_iterator k;
698 k = i->required_neighbors.find(c);
699 if(k != i->required_neighbors.end()){
703 // No required neighbor found
708 // Call all the trigger variations
709 i->abm->trigger(m_env, p, n);
710 i->abm->trigger(m_env, p, n,
711 active_object_count, active_object_count_wider);
713 // Count surrounding objects again if the abms added any
714 if(m_env->m_added_objects > 0) {
715 active_object_count = countObjects(block, map, active_object_count_wider);
716 m_env->m_added_objects = 0;
723 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
725 // Reset usage timer immediately, otherwise a block that becomes active
726 // again at around the same time as it would normally be unloaded will
727 // get unloaded incorrectly. (I think this still leaves a small possibility
728 // of a race condition between this and server::AsyncRunStep, which only
729 // some kind of synchronisation will fix, but it at least reduces the window
730 // of opportunity for it to break from seconds to nanoseconds)
731 block->resetUsageTimer();
733 // Get time difference
735 u32 stamp = block->getTimestamp();
736 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
737 dtime_s = m_game_time - block->getTimestamp();
738 dtime_s += additional_dtime;
740 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
741 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
743 // Set current time as timestamp
744 block->setTimestampNoChangedFlag(m_game_time);
746 /*infostream<<"ServerEnvironment::activateBlock(): block is "
747 <<dtime_s<<" seconds old."<<std::endl;*/
749 // Activate stored objects
750 activateObjects(block, dtime_s);
753 std::map<v3s16, NodeTimer> elapsed_timers =
754 block->m_node_timers.step((float)dtime_s);
755 if(!elapsed_timers.empty()){
757 for(std::map<v3s16, NodeTimer>::iterator
758 i = elapsed_timers.begin();
759 i != elapsed_timers.end(); i++){
760 n = block->getNodeNoEx(i->first);
761 v3s16 p = i->first + block->getPosRelative();
762 if(m_script->node_on_timer(p,n,i->second.elapsed))
763 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
767 /* Handle ActiveBlockModifiers */
768 ABMHandler abmhandler(m_abms, dtime_s, this, false);
769 abmhandler.apply(block);
772 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
774 m_abms.push_back(ABMWithState(abm));
777 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
779 INodeDefManager *ndef = m_gamedef->ndef();
780 MapNode n_old = m_map->getNodeNoEx(p);
782 if(ndef->get(n_old).has_on_destruct)
783 m_script->node_on_destruct(p, n_old);
785 bool succeeded = m_map->addNodeWithEvent(p, n);
788 // Call post-destructor
789 if(ndef->get(n_old).has_after_destruct)
790 m_script->node_after_destruct(p, n_old);
792 if(ndef->get(n).has_on_construct)
793 m_script->node_on_construct(p, n);
797 bool ServerEnvironment::removeNode(v3s16 p)
799 INodeDefManager *ndef = m_gamedef->ndef();
800 MapNode n_old = m_map->getNodeNoEx(p);
802 if(ndef->get(n_old).has_on_destruct)
803 m_script->node_on_destruct(p, n_old);
805 // This is slightly optimized compared to addNodeWithEvent(air)
806 bool succeeded = m_map->removeNodeWithEvent(p);
809 // Call post-destructor
810 if(ndef->get(n_old).has_after_destruct)
811 m_script->node_after_destruct(p, n_old);
812 // Air doesn't require constructor
816 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
818 return m_map->addNodeWithEvent(p, n, false);
821 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
823 std::set<u16> objects;
824 for(std::map<u16, ServerActiveObject*>::iterator
825 i = m_active_objects.begin();
826 i != m_active_objects.end(); ++i)
828 ServerActiveObject* obj = i->second;
830 v3f objectpos = obj->getBasePosition();
831 if(objectpos.getDistanceFrom(pos) > radius)
838 void ServerEnvironment::clearAllObjects()
840 infostream<<"ServerEnvironment::clearAllObjects(): "
841 <<"Removing all active objects"<<std::endl;
842 std::list<u16> objects_to_remove;
843 for(std::map<u16, ServerActiveObject*>::iterator
844 i = m_active_objects.begin();
845 i != m_active_objects.end(); ++i)
847 ServerActiveObject* obj = i->second;
848 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
851 // Delete static object if block is loaded
852 if(obj->m_static_exists){
853 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
855 block->m_static_objects.remove(id);
856 block->raiseModified(MOD_STATE_WRITE_NEEDED,
858 obj->m_static_exists = false;
861 // If known by some client, don't delete immediately
862 if(obj->m_known_by_count > 0){
863 obj->m_pending_deactivation = true;
864 obj->m_removed = true;
868 // Tell the object about removal
869 obj->removingFromEnvironment();
870 // Deregister in scripting api
871 m_script->removeObjectReference(obj);
873 // Delete active object
874 if(obj->environmentDeletes())
876 // Id to be removed from m_active_objects
877 objects_to_remove.push_back(id);
879 // Remove references from m_active_objects
880 for(std::list<u16>::iterator i = objects_to_remove.begin();
881 i != objects_to_remove.end(); ++i)
883 m_active_objects.erase(*i);
886 // Get list of loaded blocks
887 std::list<v3s16> loaded_blocks;
888 infostream<<"ServerEnvironment::clearAllObjects(): "
889 <<"Listing all loaded blocks"<<std::endl;
890 m_map->listAllLoadedBlocks(loaded_blocks);
891 infostream<<"ServerEnvironment::clearAllObjects(): "
892 <<"Done listing all loaded blocks: "
893 <<loaded_blocks.size()<<std::endl;
895 // Get list of loadable blocks
896 std::list<v3s16> loadable_blocks;
897 infostream<<"ServerEnvironment::clearAllObjects(): "
898 <<"Listing all loadable blocks"<<std::endl;
899 m_map->listAllLoadableBlocks(loadable_blocks);
900 infostream<<"ServerEnvironment::clearAllObjects(): "
901 <<"Done listing all loadable blocks: "
902 <<loadable_blocks.size()
903 <<", now clearing"<<std::endl;
905 // Grab a reference on each loaded block to avoid unloading it
906 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
907 i != loaded_blocks.end(); ++i)
910 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
915 // Remove objects in all loadable blocks
916 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
917 unload_interval = MYMAX(unload_interval, 1);
918 u32 report_interval = loadable_blocks.size() / 10;
919 u32 num_blocks_checked = 0;
920 u32 num_blocks_cleared = 0;
921 u32 num_objs_cleared = 0;
922 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
923 i != loadable_blocks.end(); ++i)
926 MapBlock *block = m_map->emergeBlock(p, false);
928 errorstream<<"ServerEnvironment::clearAllObjects(): "
929 <<"Failed to emerge block "<<PP(p)<<std::endl;
932 u32 num_stored = block->m_static_objects.m_stored.size();
933 u32 num_active = block->m_static_objects.m_active.size();
934 if(num_stored != 0 || num_active != 0){
935 block->m_static_objects.m_stored.clear();
936 block->m_static_objects.m_active.clear();
937 block->raiseModified(MOD_STATE_WRITE_NEEDED,
939 num_objs_cleared += num_stored + num_active;
940 num_blocks_cleared++;
942 num_blocks_checked++;
944 if(report_interval != 0 &&
945 num_blocks_checked % report_interval == 0){
946 float percent = 100.0 * (float)num_blocks_checked /
947 loadable_blocks.size();
948 infostream<<"ServerEnvironment::clearAllObjects(): "
949 <<"Cleared "<<num_objs_cleared<<" objects"
950 <<" in "<<num_blocks_cleared<<" blocks ("
951 <<percent<<"%)"<<std::endl;
953 if(num_blocks_checked % unload_interval == 0){
954 m_map->unloadUnreferencedBlocks();
957 m_map->unloadUnreferencedBlocks();
959 // Drop references that were added above
960 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
961 i != loaded_blocks.end(); ++i)
964 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
969 infostream<<"ServerEnvironment::clearAllObjects(): "
970 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
971 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
974 void ServerEnvironment::step(float dtime)
976 DSTACK(__FUNCTION_NAME);
978 //TimeTaker timer("ServerEnv step");
980 /* Step time of day */
981 stepTimeOfDay(dtime);
984 // NOTE: This is kind of funny on a singleplayer game, but doesn't
985 // really matter that much.
986 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
992 m_game_time_fraction_counter += dtime;
993 u32 inc_i = (u32)m_game_time_fraction_counter;
994 m_game_time += inc_i;
995 m_game_time_fraction_counter -= (float)inc_i;
1002 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1003 for(std::list<Player*>::iterator i = m_players.begin();
1004 i != m_players.end(); ++i)
1006 Player *player = *i;
1008 // Ignore disconnected players
1009 if(player->peer_id == 0)
1013 player->move(dtime, this, 100*BS);
1018 Manage active block list
1020 if(m_active_blocks_management_interval.step(dtime, 2.0))
1022 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1024 Get player block positions
1026 std::list<v3s16> players_blockpos;
1027 for(std::list<Player*>::iterator
1028 i = m_players.begin();
1029 i != m_players.end(); ++i)
1031 Player *player = *i;
1032 // Ignore disconnected players
1033 if(player->peer_id == 0)
1035 v3s16 blockpos = getNodeBlockPos(
1036 floatToInt(player->getPosition(), BS));
1037 players_blockpos.push_back(blockpos);
1041 Update list of active blocks, collecting changes
1043 const s16 active_block_range = g_settings->getS16("active_block_range");
1044 std::set<v3s16> blocks_removed;
1045 std::set<v3s16> blocks_added;
1046 m_active_blocks.update(players_blockpos, active_block_range,
1047 blocks_removed, blocks_added);
1050 Handle removed blocks
1053 // Convert active objects that are no more in active blocks to static
1054 deactivateFarObjects(false);
1056 for(std::set<v3s16>::iterator
1057 i = blocks_removed.begin();
1058 i != blocks_removed.end(); ++i)
1062 /* infostream<<"Server: Block " << PP(p)
1063 << " became inactive"<<std::endl; */
1065 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1069 // Set current time as timestamp (and let it set ChangedFlag)
1070 block->setTimestamp(m_game_time);
1077 for(std::set<v3s16>::iterator
1078 i = blocks_added.begin();
1079 i != blocks_added.end(); ++i)
1083 MapBlock *block = m_map->getBlockOrEmerge(p);
1085 m_active_blocks.m_list.erase(p);
1089 activateBlock(block);
1090 /* infostream<<"Server: Block " << PP(p)
1091 << " became active"<<std::endl; */
1096 Mess around in active blocks
1098 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1100 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1104 for(std::set<v3s16>::iterator
1105 i = m_active_blocks.m_list.begin();
1106 i != m_active_blocks.m_list.end(); ++i)
1110 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1111 <<") being handled"<<std::endl;*/
1113 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1117 // Reset block usage timer
1118 block->resetUsageTimer();
1120 // Set current time as timestamp
1121 block->setTimestampNoChangedFlag(m_game_time);
1122 // If time has changed much from the one on disk,
1123 // set block to be saved when it is unloaded
1124 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1125 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1126 "Timestamp older than 60s (step)");
1129 std::map<v3s16, NodeTimer> elapsed_timers =
1130 block->m_node_timers.step((float)dtime);
1131 if(!elapsed_timers.empty()){
1133 for(std::map<v3s16, NodeTimer>::iterator
1134 i = elapsed_timers.begin();
1135 i != elapsed_timers.end(); i++){
1136 n = block->getNodeNoEx(i->first);
1137 p = i->first + block->getPosRelative();
1138 if(m_script->node_on_timer(p,n,i->second.elapsed))
1139 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1145 const float abm_interval = 1.0;
1146 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1148 if(m_active_block_interval_overload_skip > 0){
1149 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1150 m_active_block_interval_overload_skip--;
1153 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1154 TimeTaker timer("modify in active blocks");
1156 // Initialize handling of ActiveBlockModifiers
1157 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1159 for(std::set<v3s16>::iterator
1160 i = m_active_blocks.m_list.begin();
1161 i != m_active_blocks.m_list.end(); ++i)
1165 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1166 <<") being handled"<<std::endl;*/
1168 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1172 // Set current time as timestamp
1173 block->setTimestampNoChangedFlag(m_game_time);
1175 /* Handle ActiveBlockModifiers */
1176 abmhandler.apply(block);
1179 u32 time_ms = timer.stop(true);
1180 u32 max_time_ms = 200;
1181 if(time_ms > max_time_ms){
1182 infostream<<"WARNING: active block modifiers took "
1183 <<time_ms<<"ms (longer than "
1184 <<max_time_ms<<"ms)"<<std::endl;
1185 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1190 Step script environment (run global on_step())
1192 m_script->environment_Step(dtime);
1198 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1199 //TimeTaker timer("Step active objects");
1201 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1203 // This helps the objects to send data at the same time
1204 bool send_recommended = false;
1205 m_send_recommended_timer += dtime;
1206 if(m_send_recommended_timer > getSendRecommendedInterval())
1208 m_send_recommended_timer -= getSendRecommendedInterval();
1209 send_recommended = true;
1212 for(std::map<u16, ServerActiveObject*>::iterator
1213 i = m_active_objects.begin();
1214 i != m_active_objects.end(); ++i)
1216 ServerActiveObject* obj = i->second;
1217 // Remove non-peaceful mobs on peaceful mode
1218 if(g_settings->getBool("only_peaceful_mobs")){
1219 if(!obj->isPeaceful())
1220 obj->m_removed = true;
1222 // Don't step if is to be removed or stored statically
1223 if(obj->m_removed || obj->m_pending_deactivation)
1226 obj->step(dtime, send_recommended);
1227 // Read messages from object
1228 while(!obj->m_messages_out.empty())
1230 m_active_object_messages.push_back(
1231 obj->m_messages_out.pop_front());
1237 Manage active objects
1239 if(m_object_management_interval.step(dtime, 0.5))
1241 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1243 Remove objects that satisfy (m_removed && m_known_by_count==0)
1245 removeRemovedObjects();
1249 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1251 std::map<u16, ServerActiveObject*>::iterator n;
1252 n = m_active_objects.find(id);
1253 if(n == m_active_objects.end())
1258 bool isFreeServerActiveObjectId(u16 id,
1259 std::map<u16, ServerActiveObject*> &objects)
1264 return objects.find(id) == objects.end();
1267 u16 getFreeServerActiveObjectId(
1268 std::map<u16, ServerActiveObject*> &objects)
1270 //try to reuse id's as late as possible
1271 static u16 last_used_id = 0;
1272 u16 startid = last_used_id;
1276 if(isFreeServerActiveObjectId(last_used_id, objects))
1277 return last_used_id;
1279 if(last_used_id == startid)
1284 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1288 u16 id = addActiveObjectRaw(object, true, 0);
1293 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1297 v3f objectpos = obj->getBasePosition();
1299 // The block in which the object resides in
1300 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1303 Update the static data
1306 // Create new static object
1307 std::string staticdata = obj->getStaticData();
1308 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1309 // Add to the block where the object is located in
1310 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1311 // Get or generate the block
1312 MapBlock *block = m_map->emergeBlock(blockpos);
1314 bool succeeded = false;
1318 block->m_static_objects.insert(0, s_obj);
1319 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1320 "addActiveObjectAsStatic");
1324 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1325 <<"Could not find or generate "
1326 <<"a block for storing static object"<<std::endl;
1330 if(obj->environmentDeletes())
1338 Finds out what new objects have been added to
1339 inside a radius around a position
1341 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1342 std::set<u16> ¤t_objects,
1343 std::set<u16> &added_objects)
1345 v3f pos_f = intToFloat(pos, BS);
1346 f32 radius_f = radius * BS;
1348 Go through the object list,
1349 - discard m_removed objects,
1350 - discard objects that are too far away,
1351 - discard objects that are found in current_objects.
1352 - add remaining objects to added_objects
1354 for(std::map<u16, ServerActiveObject*>::iterator
1355 i = m_active_objects.begin();
1356 i != m_active_objects.end(); ++i)
1360 ServerActiveObject *object = i->second;
1363 // Discard if removed or deactivating
1364 if(object->m_removed || object->m_pending_deactivation)
1366 if(object->unlimitedTransferDistance() == false){
1367 // Discard if too far
1368 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1369 if(distance_f > radius_f)
1372 // Discard if already on current_objects
1373 std::set<u16>::iterator n;
1374 n = current_objects.find(id);
1375 if(n != current_objects.end())
1377 // Add to added_objects
1378 added_objects.insert(id);
1383 Finds out what objects have been removed from
1384 inside a radius around a position
1386 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1387 std::set<u16> ¤t_objects,
1388 std::set<u16> &removed_objects)
1390 v3f pos_f = intToFloat(pos, BS);
1391 f32 radius_f = radius * BS;
1393 Go through current_objects; object is removed if:
1394 - object is not found in m_active_objects (this is actually an
1395 error condition; objects should be set m_removed=true and removed
1396 only after all clients have been informed about removal), or
1397 - object has m_removed=true, or
1398 - object is too far away
1400 for(std::set<u16>::iterator
1401 i = current_objects.begin();
1402 i != current_objects.end(); ++i)
1405 ServerActiveObject *object = getActiveObject(id);
1408 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1409 <<" object in current_objects is NULL"<<std::endl;
1410 removed_objects.insert(id);
1414 if(object->m_removed || object->m_pending_deactivation)
1416 removed_objects.insert(id);
1420 // If transfer distance is unlimited, don't remove
1421 if(object->unlimitedTransferDistance())
1424 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1426 if(distance_f >= radius_f)
1428 removed_objects.insert(id);
1436 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1438 if(m_active_object_messages.empty())
1439 return ActiveObjectMessage(0);
1441 ActiveObjectMessage message = m_active_object_messages.front();
1442 m_active_object_messages.pop_front();
1447 ************ Private methods *************
1450 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1451 bool set_changed, u32 dtime_s)
1454 if(object->getId() == 0){
1455 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1458 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1459 <<"no free ids available"<<std::endl;
1460 if(object->environmentDeletes())
1464 object->setId(new_id);
1467 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1468 <<"supplied with id "<<object->getId()<<std::endl;
1470 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1472 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1473 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1474 if(object->environmentDeletes())
1478 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1479 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1481 m_active_objects[object->getId()] = object;
1483 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"Added id="<<object->getId()<<"; there are now "
1485 <<m_active_objects.size()<<" active objects."
1488 // Register reference in scripting api (must be done before post-init)
1489 m_script->addObjectReference(object);
1490 // Post-initialize object
1491 object->addedToEnvironment(dtime_s);
1493 // Add static data to block
1494 if(object->isStaticAllowed())
1496 // Add static object to active static list of the block
1497 v3f objectpos = object->getBasePosition();
1498 std::string staticdata = object->getStaticData();
1499 StaticObject s_obj(object->getType(), objectpos, staticdata);
1500 // Add to the block where the object is located in
1501 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1502 MapBlock *block = m_map->emergeBlock(blockpos);
1504 block->m_static_objects.m_active[object->getId()] = s_obj;
1505 object->m_static_exists = true;
1506 object->m_static_block = blockpos;
1509 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1510 "addActiveObjectRaw");
1512 v3s16 p = floatToInt(objectpos, BS);
1513 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1514 <<"could not emerge block for storing id="<<object->getId()
1515 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1519 return object->getId();
1523 Remove objects that satisfy (m_removed && m_known_by_count==0)
1525 void ServerEnvironment::removeRemovedObjects()
1527 std::list<u16> objects_to_remove;
1528 for(std::map<u16, ServerActiveObject*>::iterator
1529 i = m_active_objects.begin();
1530 i != m_active_objects.end(); ++i)
1533 ServerActiveObject* obj = i->second;
1534 // This shouldn't happen but check it
1537 infostream<<"NULL object found in ServerEnvironment"
1538 <<" while finding removed objects. id="<<id<<std::endl;
1539 // Id to be removed from m_active_objects
1540 objects_to_remove.push_back(id);
1545 We will delete objects that are marked as removed or thatare
1546 waiting for deletion after deactivation
1548 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1552 Delete static data from block if is marked as removed
1554 if(obj->m_static_exists && obj->m_removed)
1556 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1558 block->m_static_objects.remove(id);
1559 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1560 "removeRemovedObjects/remove");
1561 obj->m_static_exists = false;
1563 infostream<<"Failed to emerge block from which an object to "
1564 <<"be removed was loaded from. id="<<id<<std::endl;
1568 // If m_known_by_count > 0, don't actually remove. On some future
1569 // invocation this will be 0, which is when removal will continue.
1570 if(obj->m_known_by_count > 0)
1574 Move static data from active to stored if not marked as removed
1576 if(obj->m_static_exists && !obj->m_removed){
1577 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1579 std::map<u16, StaticObject>::iterator i =
1580 block->m_static_objects.m_active.find(id);
1581 if(i != block->m_static_objects.m_active.end()){
1582 block->m_static_objects.m_stored.push_back(i->second);
1583 block->m_static_objects.m_active.erase(id);
1584 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1585 "removeRemovedObjects/deactivate");
1588 infostream<<"Failed to emerge block from which an object to "
1589 <<"be deactivated was loaded from. id="<<id<<std::endl;
1593 // Tell the object about removal
1594 obj->removingFromEnvironment();
1595 // Deregister in scripting api
1596 m_script->removeObjectReference(obj);
1599 if(obj->environmentDeletes())
1601 // Id to be removed from m_active_objects
1602 objects_to_remove.push_back(id);
1604 // Remove references from m_active_objects
1605 for(std::list<u16>::iterator i = objects_to_remove.begin();
1606 i != objects_to_remove.end(); ++i)
1608 m_active_objects.erase(*i);
1612 static void print_hexdump(std::ostream &o, const std::string &data)
1614 const int linelength = 16;
1615 for(int l=0; ; l++){
1616 int i0 = linelength * l;
1617 bool at_end = false;
1618 int thislinelength = linelength;
1619 if(i0 + thislinelength > (int)data.size()){
1620 thislinelength = data.size() - i0;
1623 for(int di=0; di<linelength; di++){
1626 if(di<thislinelength)
1627 snprintf(buf, 4, "%.2x ", data[i]);
1629 snprintf(buf, 4, " ");
1633 for(int di=0; di<thislinelength; di++){
1647 Convert stored objects from blocks near the players to active.
1649 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1653 // Ignore if no stored objects (to not set changed flag)
1654 if(block->m_static_objects.m_stored.size() == 0)
1656 verbosestream<<"ServerEnvironment::activateObjects(): "
1657 <<"activating objects of block "<<PP(block->getPos())
1658 <<" ("<<block->m_static_objects.m_stored.size()
1659 <<" objects)"<<std::endl;
1660 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1662 errorstream<<"suspiciously large amount of objects detected: "
1663 <<block->m_static_objects.m_stored.size()<<" in "
1664 <<PP(block->getPos())
1665 <<"; removing all of them."<<std::endl;
1666 // Clear stored list
1667 block->m_static_objects.m_stored.clear();
1668 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1669 "stored list cleared in activateObjects due to "
1670 "large amount of objects");
1674 // Activate stored objects
1675 std::list<StaticObject> new_stored;
1676 for(std::list<StaticObject>::iterator
1677 i = block->m_static_objects.m_stored.begin();
1678 i != block->m_static_objects.m_stored.end(); ++i)
1680 /*infostream<<"Server: Creating an active object from "
1681 <<"static data"<<std::endl;*/
1682 StaticObject &s_obj = *i;
1683 // Create an active object from the data
1684 ServerActiveObject *obj = ServerActiveObject::create
1685 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1686 // If couldn't create object, store static data back.
1689 errorstream<<"ServerEnvironment::activateObjects(): "
1690 <<"failed to create active object from static object "
1691 <<"in block "<<PP(s_obj.pos/BS)
1692 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1693 print_hexdump(verbosestream, s_obj.data);
1695 new_stored.push_back(s_obj);
1698 verbosestream<<"ServerEnvironment::activateObjects(): "
1699 <<"activated static object pos="<<PP(s_obj.pos/BS)
1700 <<" type="<<(int)s_obj.type<<std::endl;
1701 // This will also add the object to the active static list
1702 addActiveObjectRaw(obj, false, dtime_s);
1704 // Clear stored list
1705 block->m_static_objects.m_stored.clear();
1706 // Add leftover failed stuff to stored list
1707 for(std::list<StaticObject>::iterator
1708 i = new_stored.begin();
1709 i != new_stored.end(); ++i)
1711 StaticObject &s_obj = *i;
1712 block->m_static_objects.m_stored.push_back(s_obj);
1715 // Turn the active counterparts of activated objects not pending for
1717 for(std::map<u16, StaticObject>::iterator
1718 i = block->m_static_objects.m_active.begin();
1719 i != block->m_static_objects.m_active.end(); ++i)
1722 ServerActiveObject *object = getActiveObject(id);
1724 object->m_pending_deactivation = false;
1728 Note: Block hasn't really been modified here.
1729 The objects have just been activated and moved from the stored
1730 static list to the active static list.
1731 As such, the block is essentially the same.
1732 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1733 Otherwise there would be a huge amount of unnecessary I/O.
1738 Convert objects that are not standing inside active blocks to static.
1740 If m_known_by_count != 0, active object is not deleted, but static
1741 data is still updated.
1743 If force_delete is set, active object is deleted nevertheless. It
1744 shall only be set so in the destructor of the environment.
1746 If block wasn't generated (not in memory or on disk),
1748 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1750 std::list<u16> objects_to_remove;
1751 for(std::map<u16, ServerActiveObject*>::iterator
1752 i = m_active_objects.begin();
1753 i != m_active_objects.end(); ++i)
1755 ServerActiveObject* obj = i->second;
1758 // Do not deactivate if static data creation not allowed
1759 if(!force_delete && !obj->isStaticAllowed())
1762 // If pending deactivation, let removeRemovedObjects() do it
1763 if(!force_delete && obj->m_pending_deactivation)
1767 v3f objectpos = obj->getBasePosition();
1769 // The block in which the object resides in
1770 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1772 // If object's static data is stored in a deactivated block and object
1773 // is actually located in an active block, re-save to the block in
1774 // which the object is actually located in.
1776 obj->m_static_exists &&
1777 !m_active_blocks.contains(obj->m_static_block) &&
1778 m_active_blocks.contains(blockpos_o))
1780 v3s16 old_static_block = obj->m_static_block;
1782 // Save to block where object is located
1783 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1785 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1786 <<"Could not save object id="<<id
1787 <<" to it's current block "<<PP(blockpos_o)
1791 std::string staticdata_new = obj->getStaticData();
1792 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1793 block->m_static_objects.insert(id, s_obj);
1794 obj->m_static_block = blockpos_o;
1795 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1796 "deactivateFarObjects: Static data moved in");
1798 // Delete from block where object was located
1799 block = m_map->emergeBlock(old_static_block, false);
1801 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1802 <<"Could not delete object id="<<id
1803 <<" from it's previous block "<<PP(old_static_block)
1807 block->m_static_objects.remove(id);
1808 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1809 "deactivateFarObjects: Static data moved out");
1813 // If block is active, don't remove
1814 if(!force_delete && m_active_blocks.contains(blockpos_o))
1817 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1818 <<"deactivating object id="<<id<<" on inactive block "
1819 <<PP(blockpos_o)<<std::endl;
1821 // If known by some client, don't immediately delete.
1822 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1825 Update the static data
1828 if(obj->isStaticAllowed())
1830 // Create new static object
1831 std::string staticdata_new = obj->getStaticData();
1832 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1834 bool stays_in_same_block = false;
1835 bool data_changed = true;
1837 if(obj->m_static_exists){
1838 if(obj->m_static_block == blockpos_o)
1839 stays_in_same_block = true;
1841 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1843 std::map<u16, StaticObject>::iterator n =
1844 block->m_static_objects.m_active.find(id);
1845 if(n != block->m_static_objects.m_active.end()){
1846 StaticObject static_old = n->second;
1848 float save_movem = obj->getMinimumSavedMovement();
1850 if(static_old.data == staticdata_new &&
1851 (static_old.pos - objectpos).getLength() < save_movem)
1852 data_changed = false;
1854 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1855 <<"id="<<id<<" m_static_exists=true but "
1856 <<"static data doesn't actually exist in "
1857 <<PP(obj->m_static_block)<<std::endl;
1861 bool shall_be_written = (!stays_in_same_block || data_changed);
1863 // Delete old static object
1864 if(obj->m_static_exists)
1866 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1869 block->m_static_objects.remove(id);
1870 obj->m_static_exists = false;
1871 // Only mark block as modified if data changed considerably
1872 if(shall_be_written)
1873 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1874 "deactivateFarObjects: Static data "
1875 "changed considerably");
1879 // Add to the block where the object is located in
1880 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1881 // Get or generate the block
1882 MapBlock *block = NULL;
1884 block = m_map->emergeBlock(blockpos);
1885 } catch(InvalidPositionException &e){
1886 // Handled via NULL pointer
1887 // NOTE: emergeBlock's failure is usually determined by it
1888 // actually returning NULL
1893 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1894 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1895 <<" statically but block "<<PP(blockpos)
1896 <<" already contains "
1897 <<block->m_static_objects.m_stored.size()
1899 <<" Forcing delete."<<std::endl;
1900 force_delete = true;
1902 // If static counterpart already exists in target block,
1904 // This shouldn't happen because the object is removed from
1905 // the previous block before this according to
1906 // obj->m_static_block, but happens rarely for some unknown
1907 // reason. Unsuccessful attempts have been made to find
1909 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1910 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1912 block->m_static_objects.remove(id);
1914 // Store static data
1915 u16 store_id = pending_delete ? id : 0;
1916 block->m_static_objects.insert(store_id, s_obj);
1918 // Only mark block as modified if data changed considerably
1919 if(shall_be_written)
1920 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1921 "deactivateFarObjects: Static data "
1922 "changed considerably");
1924 obj->m_static_exists = true;
1925 obj->m_static_block = block->getPos();
1930 v3s16 p = floatToInt(objectpos, BS);
1931 errorstream<<"ServerEnv: Could not find or generate "
1932 <<"a block for storing id="<<obj->getId()
1933 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1940 If known by some client, set pending deactivation.
1941 Otherwise delete it immediately.
1944 if(pending_delete && !force_delete)
1946 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1947 <<"object id="<<id<<" is known by clients"
1948 <<"; not deleting yet"<<std::endl;
1950 obj->m_pending_deactivation = true;
1954 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1955 <<"object id="<<id<<" is not known by clients"
1956 <<"; deleting"<<std::endl;
1958 // Tell the object about removal
1959 obj->removingFromEnvironment();
1960 // Deregister in scripting api
1961 m_script->removeObjectReference(obj);
1963 // Delete active object
1964 if(obj->environmentDeletes())
1966 // Id to be removed from m_active_objects
1967 objects_to_remove.push_back(id);
1970 // Remove references from m_active_objects
1971 for(std::list<u16>::iterator i = objects_to_remove.begin();
1972 i != objects_to_remove.end(); ++i)
1974 m_active_objects.erase(*i);
1981 #include "clientsimpleobject.h"
1987 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1988 ITextureSource *texturesource, IGameDef *gamedef,
1989 IrrlichtDevice *irr):
1992 m_texturesource(texturesource),
1997 memset(m_attachements, zero, sizeof(m_attachements));
2000 ClientEnvironment::~ClientEnvironment()
2002 // delete active objects
2003 for(std::map<u16, ClientActiveObject*>::iterator
2004 i = m_active_objects.begin();
2005 i != m_active_objects.end(); ++i)
2010 for(std::list<ClientSimpleObject*>::iterator
2011 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2020 Map & ClientEnvironment::getMap()
2025 ClientMap & ClientEnvironment::getClientMap()
2030 void ClientEnvironment::addPlayer(Player *player)
2032 DSTACK(__FUNCTION_NAME);
2034 It is a failure if player is local and there already is a local
2037 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2039 Environment::addPlayer(player);
2042 LocalPlayer * ClientEnvironment::getLocalPlayer()
2044 for(std::list<Player*>::iterator i = m_players.begin();
2045 i != m_players.end(); ++i)
2047 Player *player = *i;
2048 if(player->isLocal())
2049 return (LocalPlayer*)player;
2054 void ClientEnvironment::step(float dtime)
2056 DSTACK(__FUNCTION_NAME);
2058 /* Step time of day */
2059 stepTimeOfDay(dtime);
2061 // Get some settings
2062 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2063 bool free_move = fly_allowed && g_settings->getBool("free_move");
2066 LocalPlayer *lplayer = getLocalPlayer();
2068 // collision info queue
2069 std::list<CollisionInfo> player_collisions;
2072 Get the speed the player is going
2074 bool is_climbing = lplayer->is_climbing;
2076 f32 player_speed = lplayer->getSpeed().getLength();
2079 Maximum position increment
2081 //f32 position_max_increment = 0.05*BS;
2082 f32 position_max_increment = 0.1*BS;
2084 // Maximum time increment (for collision detection etc)
2085 // time = distance / speed
2086 f32 dtime_max_increment = 1;
2087 if(player_speed > 0.001)
2088 dtime_max_increment = position_max_increment / player_speed;
2090 // Maximum time increment is 10ms or lower
2091 if(dtime_max_increment > 0.01)
2092 dtime_max_increment = 0.01;
2094 // Don't allow overly huge dtime
2098 f32 dtime_downcount = dtime;
2101 Stuff that has a maximum time increment
2110 if(dtime_downcount > dtime_max_increment)
2112 dtime_part = dtime_max_increment;
2113 dtime_downcount -= dtime_part;
2117 dtime_part = dtime_downcount;
2119 Setting this to 0 (no -=dtime_part) disables an infinite loop
2120 when dtime_part is so small that dtime_downcount -= dtime_part
2123 dtime_downcount = 0;
2132 if(free_move == false && is_climbing == false)
2135 v3f speed = lplayer->getSpeed();
2136 if(lplayer->in_liquid == false)
2137 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2139 // Liquid floating / sinking
2140 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2141 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2143 // Liquid resistance
2144 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2146 // How much the node's viscosity blocks movement, ranges between 0 and 1
2147 // Should match the scale at which viscosity increase affects other liquid attributes
2148 const f32 viscosity_factor = 0.3;
2150 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2151 f32 dl = d_wanted.getLength();
2152 if(dl > lplayer->movement_liquid_fluidity_smooth)
2153 dl = lplayer->movement_liquid_fluidity_smooth;
2154 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2156 v3f d = d_wanted.normalize() * dl;
2160 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2161 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2162 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2163 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2164 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2165 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2169 lplayer->setSpeed(speed);
2174 This also does collision detection.
2176 lplayer->move(dtime_part, this, position_max_increment,
2177 &player_collisions);
2180 while(dtime_downcount > 0.001);
2182 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2184 for(std::list<CollisionInfo>::iterator
2185 i = player_collisions.begin();
2186 i != player_collisions.end(); ++i)
2188 CollisionInfo &info = *i;
2189 v3f speed_diff = info.new_speed - info.old_speed;;
2190 // Handle only fall damage
2191 // (because otherwise walking against something in fast_move kills you)
2192 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2194 // Get rid of other components
2197 f32 pre_factor = 1; // 1 hp per node/s
2198 f32 tolerance = BS*14; // 5 without damage
2199 f32 post_factor = 1; // 1 hp per node/s
2200 if(info.type == COLLISION_NODE)
2202 const ContentFeatures &f = m_gamedef->ndef()->
2203 get(m_map->getNodeNoEx(info.node_p));
2204 // Determine fall damage multiplier
2205 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2206 pre_factor = 1.0 + (float)addp/100.0;
2208 float speed = pre_factor * speed_diff.getLength();
2209 if(speed > tolerance)
2211 f32 damage_f = (speed - tolerance)/BS * post_factor;
2212 u16 damage = (u16)(damage_f+0.5);
2214 damageLocalPlayer(damage, true);
2215 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2216 m_gamedef->event()->put(e);
2222 A quick draft of lava damage
2224 if(m_lava_hurt_interval.step(dtime, 1.0))
2226 v3f pf = lplayer->getPosition();
2228 // Feet, middle and head
2229 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2230 MapNode n1 = m_map->getNodeNoEx(p1);
2231 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2232 MapNode n2 = m_map->getNodeNoEx(p2);
2233 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2234 MapNode n3 = m_map->getNodeNoEx(p3);
2236 u32 damage_per_second = 0;
2237 damage_per_second = MYMAX(damage_per_second,
2238 m_gamedef->ndef()->get(n1).damage_per_second);
2239 damage_per_second = MYMAX(damage_per_second,
2240 m_gamedef->ndef()->get(n2).damage_per_second);
2241 damage_per_second = MYMAX(damage_per_second,
2242 m_gamedef->ndef()->get(n3).damage_per_second);
2244 if(damage_per_second != 0)
2246 damageLocalPlayer(damage_per_second, true);
2253 if(m_drowning_interval.step(dtime, 2.0))
2255 v3f pf = lplayer->getPosition();
2258 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2259 MapNode n = m_map->getNodeNoEx(p);
2260 ContentFeatures c = m_gamedef->ndef()->get(n);
2261 u8 drowning_damage = c.drowning;
2262 if(drowning_damage > 0 && lplayer->hp > 0){
2263 u16 breath = lplayer->getBreath();
2270 lplayer->setBreath(breath);
2271 updateLocalPlayerBreath(breath);
2274 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2275 damageLocalPlayer(drowning_damage, true);
2278 if(m_breathing_interval.step(dtime, 0.5))
2280 v3f pf = lplayer->getPosition();
2283 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2284 MapNode n = m_map->getNodeNoEx(p);
2285 ContentFeatures c = m_gamedef->ndef()->get(n);
2287 lplayer->setBreath(11);
2289 else if(c.drowning == 0){
2290 u16 breath = lplayer->getBreath();
2293 lplayer->setBreath(breath);
2294 updateLocalPlayerBreath(breath);
2300 Stuff that can be done in an arbitarily large dtime
2302 for(std::list<Player*>::iterator i = m_players.begin();
2303 i != m_players.end(); ++i)
2305 Player *player = *i;
2308 Handle non-local players
2310 if(player->isLocal() == false)
2313 player->move(dtime, this, 100*BS);
2317 // Update lighting on all players on client
2321 v3s16 p = player->getLightPosition();
2322 MapNode n = m_map->getNode(p);
2323 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2325 catch(InvalidPositionException &e){
2326 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2328 player->light = light;
2332 Step active objects and update lighting of them
2335 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2336 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2337 for(std::map<u16, ClientActiveObject*>::iterator
2338 i = m_active_objects.begin();
2339 i != m_active_objects.end(); ++i)
2341 ClientActiveObject* obj = i->second;
2343 obj->step(dtime, this);
2351 v3s16 p = obj->getLightPosition();
2352 MapNode n = m_map->getNode(p);
2353 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2355 catch(InvalidPositionException &e){
2356 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2358 obj->updateLight(light);
2363 Step and handle simple objects
2365 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2366 for(std::list<ClientSimpleObject*>::iterator
2367 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2369 ClientSimpleObject *simple = *i;
2370 std::list<ClientSimpleObject*>::iterator cur = i;
2372 simple->step(dtime);
2373 if(simple->m_to_be_removed){
2375 m_simple_objects.erase(cur);
2380 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2382 m_simple_objects.push_back(simple);
2385 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2387 std::map<u16, ClientActiveObject*>::iterator n;
2388 n = m_active_objects.find(id);
2389 if(n == m_active_objects.end())
2394 bool isFreeClientActiveObjectId(u16 id,
2395 std::map<u16, ClientActiveObject*> &objects)
2400 return objects.find(id) == objects.end();
2403 u16 getFreeClientActiveObjectId(
2404 std::map<u16, ClientActiveObject*> &objects)
2406 //try to reuse id's as late as possible
2407 static u16 last_used_id = 0;
2408 u16 startid = last_used_id;
2412 if(isFreeClientActiveObjectId(last_used_id, objects))
2413 return last_used_id;
2415 if(last_used_id == startid)
2420 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2423 if(object->getId() == 0)
2425 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2428 infostream<<"ClientEnvironment::addActiveObject(): "
2429 <<"no free ids available"<<std::endl;
2433 object->setId(new_id);
2435 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2437 infostream<<"ClientEnvironment::addActiveObject(): "
2438 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2442 infostream<<"ClientEnvironment::addActiveObject(): "
2443 <<"added (id="<<object->getId()<<")"<<std::endl;
2444 m_active_objects[object->getId()] = object;
2445 object->addToScene(m_smgr, m_texturesource, m_irr);
2446 { // Update lighting immediately
2450 v3s16 p = object->getLightPosition();
2451 MapNode n = m_map->getNode(p);
2452 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2454 catch(InvalidPositionException &e){
2455 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2457 object->updateLight(light);
2459 return object->getId();
2462 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2463 const std::string &init_data)
2465 ClientActiveObject* obj =
2466 ClientActiveObject::create(type, m_gamedef, this);
2469 infostream<<"ClientEnvironment::addActiveObject(): "
2470 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2479 obj->initialize(init_data);
2481 catch(SerializationError &e)
2483 errorstream<<"ClientEnvironment::addActiveObject():"
2484 <<" id="<<id<<" type="<<type
2485 <<": SerializationError in initialize(): "
2487 <<": init_data="<<serializeJsonString(init_data)
2491 addActiveObject(obj);
2494 void ClientEnvironment::removeActiveObject(u16 id)
2496 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2497 <<"id="<<id<<std::endl;
2498 ClientActiveObject* obj = getActiveObject(id);
2501 infostream<<"ClientEnvironment::removeActiveObject(): "
2502 <<"id="<<id<<" not found"<<std::endl;
2505 obj->removeFromScene(true);
2507 m_active_objects.erase(id);
2510 void ClientEnvironment::processActiveObjectMessage(u16 id,
2511 const std::string &data)
2513 ClientActiveObject* obj = getActiveObject(id);
2516 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2517 <<" got message for id="<<id<<", which doesn't exist."
2523 obj->processMessage(data);
2525 catch(SerializationError &e)
2527 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2528 <<" id="<<id<<" type="<<obj->getType()
2529 <<" SerializationError in processMessage(),"
2530 <<" message="<<serializeJsonString(data)
2536 Callbacks for activeobjects
2539 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2541 LocalPlayer *lplayer = getLocalPlayer();
2545 if(lplayer->hp > damage)
2546 lplayer->hp -= damage;
2551 ClientEnvEvent event;
2552 event.type = CEE_PLAYER_DAMAGE;
2553 event.player_damage.amount = damage;
2554 event.player_damage.send_to_server = handle_hp;
2555 m_client_event_queue.push_back(event);
2558 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2560 ClientEnvEvent event;
2561 event.type = CEE_PLAYER_BREATH;
2562 event.player_breath.amount = breath;
2563 m_client_event_queue.push_back(event);
2567 Client likes to call these
2570 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2571 std::vector<DistanceSortedActiveObject> &dest)
2573 for(std::map<u16, ClientActiveObject*>::iterator
2574 i = m_active_objects.begin();
2575 i != m_active_objects.end(); ++i)
2577 ClientActiveObject* obj = i->second;
2579 f32 d = (obj->getPosition() - origin).getLength();
2584 DistanceSortedActiveObject dso(obj, d);
2586 dest.push_back(dso);
2590 ClientEnvEvent ClientEnvironment::getClientEvent()
2592 ClientEnvEvent event;
2593 if(m_client_event_queue.empty())
2594 event.type = CEE_NONE;
2596 event = m_client_event_queue.front();
2597 m_client_event_queue.pop_front();
2602 #endif // #ifndef SERVER