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);
465 if (testplayer.getName() == playername) {
466 *player = testplayer;
470 path = players_path + playername + itos(i);
473 infostream << "Player file for player " << playername
474 << " not found" << std::endl;
483 void ServerEnvironment::saveMeta()
485 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
487 // Open file and serialize
488 std::ostringstream ss(std::ios_base::binary);
491 args.setU64("game_time", m_game_time);
492 args.setU64("time_of_day", getTimeOfDay());
496 if(!fs::safeWriteToFile(path, ss.str()))
498 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
500 throw SerializationError("Couldn't save env meta");
504 void ServerEnvironment::loadMeta()
506 std::string path = m_path_world + DIR_DELIM "env_meta.txt";
508 // Open file and deserialize
509 std::ifstream is(path.c_str(), std::ios_base::binary);
510 if(is.good() == false)
512 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
514 throw SerializationError("Couldn't load env meta");
522 throw SerializationError
523 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
525 std::getline(is, line);
526 std::string trimmedline = trim(line);
527 if(trimmedline == "EnvArgsEnd")
529 args.parseConfigLine(line);
533 m_game_time = args.getU64("game_time");
534 }catch(SettingNotFoundException &e){
535 // Getting this is crucial, otherwise timestamps are useless
536 throw SerializationError("Couldn't load env meta game_time");
540 m_time_of_day = args.getU64("time_of_day");
541 }catch(SettingNotFoundException &e){
542 // This is not as important
543 m_time_of_day = 9000;
549 ActiveBlockModifier *abm;
551 std::set<content_t> required_neighbors;
557 ServerEnvironment *m_env;
558 std::map<content_t, std::list<ActiveABM> > m_aabms;
560 ABMHandler(std::list<ABMWithState> &abms,
561 float dtime_s, ServerEnvironment *env,
567 INodeDefManager *ndef = env->getGameDef()->ndef();
568 for(std::list<ABMWithState>::iterator
569 i = abms.begin(); i != abms.end(); ++i){
570 ActiveBlockModifier *abm = i->abm;
571 float trigger_interval = abm->getTriggerInterval();
572 if(trigger_interval < 0.001)
573 trigger_interval = 0.001;
574 float actual_interval = dtime_s;
577 if(i->timer < trigger_interval)
579 i->timer -= trigger_interval;
580 actual_interval = trigger_interval;
582 float intervals = actual_interval / trigger_interval;
585 float chance = abm->getTriggerChance();
590 aabm.chance = chance / intervals;
594 std::set<std::string> required_neighbors_s
595 = abm->getRequiredNeighbors();
596 for(std::set<std::string>::iterator
597 i = required_neighbors_s.begin();
598 i != required_neighbors_s.end(); i++)
600 ndef->getIds(*i, aabm.required_neighbors);
603 std::set<std::string> contents_s = abm->getTriggerContents();
604 for(std::set<std::string>::iterator
605 i = contents_s.begin(); i != contents_s.end(); i++)
607 std::set<content_t> ids;
608 ndef->getIds(*i, ids);
609 for(std::set<content_t>::const_iterator k = ids.begin();
613 std::map<content_t, std::list<ActiveABM> >::iterator j;
615 if(j == m_aabms.end()){
616 std::list<ActiveABM> aabmlist;
617 m_aabms[c] = aabmlist;
620 j->second.push_back(aabm);
625 // Find out how many objects the given block and its neighbours contain.
626 // Returns the number of objects in the block, and also in 'wider' the
627 // number of objects in the block and all its neighbours. The latter
628 // may an estimate if any neighbours are unloaded.
629 u32 countObjects(MapBlock *block, ServerMap * map, u32 &wider)
632 u32 wider_unknown_count = 0;
633 for(s16 x=-1; x<=1; x++)
634 for(s16 y=-1; y<=1; y++)
635 for(s16 z=-1; z<=1; z++)
637 MapBlock *block2 = map->getBlockNoCreateNoEx(
638 block->getPos() + v3s16(x,y,z));
640 wider_unknown_count++;
643 wider += block2->m_static_objects.m_active.size()
644 + block2->m_static_objects.m_stored.size();
647 u32 active_object_count = block->m_static_objects.m_active.size();
648 u32 wider_known_count = 3*3*3 - wider_unknown_count;
649 wider += wider_unknown_count * wider / wider_known_count;
650 return active_object_count;
653 void apply(MapBlock *block)
658 ServerMap *map = &m_env->getServerMap();
660 u32 active_object_count_wider;
661 u32 active_object_count = this->countObjects(block, map, active_object_count_wider);
662 m_env->m_added_objects = 0;
665 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
666 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
667 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
669 MapNode n = block->getNodeNoEx(p0);
670 content_t c = n.getContent();
671 v3s16 p = p0 + block->getPosRelative();
673 std::map<content_t, std::list<ActiveABM> >::iterator j;
675 if(j == m_aabms.end())
678 for(std::list<ActiveABM>::iterator
679 i = j->second.begin(); i != j->second.end(); i++)
681 if(myrand() % i->chance != 0)
685 if(!i->required_neighbors.empty())
688 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
689 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
690 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
694 MapNode n = map->getNodeNoEx(p1);
695 content_t c = n.getContent();
696 std::set<content_t>::const_iterator k;
697 k = i->required_neighbors.find(c);
698 if(k != i->required_neighbors.end()){
702 // No required neighbor found
707 // Call all the trigger variations
708 i->abm->trigger(m_env, p, n);
709 i->abm->trigger(m_env, p, n,
710 active_object_count, active_object_count_wider);
712 // Count surrounding objects again if the abms added any
713 if(m_env->m_added_objects > 0) {
714 active_object_count = countObjects(block, map, active_object_count_wider);
715 m_env->m_added_objects = 0;
722 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
724 // Reset usage timer immediately, otherwise a block that becomes active
725 // again at around the same time as it would normally be unloaded will
726 // get unloaded incorrectly. (I think this still leaves a small possibility
727 // of a race condition between this and server::AsyncRunStep, which only
728 // some kind of synchronisation will fix, but it at least reduces the window
729 // of opportunity for it to break from seconds to nanoseconds)
730 block->resetUsageTimer();
732 // Get time difference
734 u32 stamp = block->getTimestamp();
735 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
736 dtime_s = m_game_time - block->getTimestamp();
737 dtime_s += additional_dtime;
739 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
740 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
742 // Set current time as timestamp
743 block->setTimestampNoChangedFlag(m_game_time);
745 /*infostream<<"ServerEnvironment::activateBlock(): block is "
746 <<dtime_s<<" seconds old."<<std::endl;*/
748 // Activate stored objects
749 activateObjects(block, dtime_s);
752 std::map<v3s16, NodeTimer> elapsed_timers =
753 block->m_node_timers.step((float)dtime_s);
754 if(!elapsed_timers.empty()){
756 for(std::map<v3s16, NodeTimer>::iterator
757 i = elapsed_timers.begin();
758 i != elapsed_timers.end(); i++){
759 n = block->getNodeNoEx(i->first);
760 v3s16 p = i->first + block->getPosRelative();
761 if(m_script->node_on_timer(p,n,i->second.elapsed))
762 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
766 /* Handle ActiveBlockModifiers */
767 ABMHandler abmhandler(m_abms, dtime_s, this, false);
768 abmhandler.apply(block);
771 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
773 m_abms.push_back(ABMWithState(abm));
776 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
778 INodeDefManager *ndef = m_gamedef->ndef();
779 MapNode n_old = m_map->getNodeNoEx(p);
781 if(ndef->get(n_old).has_on_destruct)
782 m_script->node_on_destruct(p, n_old);
784 bool succeeded = m_map->addNodeWithEvent(p, n);
787 // Call post-destructor
788 if(ndef->get(n_old).has_after_destruct)
789 m_script->node_after_destruct(p, n_old);
791 if(ndef->get(n).has_on_construct)
792 m_script->node_on_construct(p, n);
796 bool ServerEnvironment::removeNode(v3s16 p)
798 INodeDefManager *ndef = m_gamedef->ndef();
799 MapNode n_old = m_map->getNodeNoEx(p);
801 if(ndef->get(n_old).has_on_destruct)
802 m_script->node_on_destruct(p, n_old);
804 // This is slightly optimized compared to addNodeWithEvent(air)
805 bool succeeded = m_map->removeNodeWithEvent(p);
808 // Call post-destructor
809 if(ndef->get(n_old).has_after_destruct)
810 m_script->node_after_destruct(p, n_old);
811 // Air doesn't require constructor
815 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
817 return m_map->addNodeWithEvent(p, n, false);
820 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
822 std::set<u16> objects;
823 for(std::map<u16, ServerActiveObject*>::iterator
824 i = m_active_objects.begin();
825 i != m_active_objects.end(); ++i)
827 ServerActiveObject* obj = i->second;
829 v3f objectpos = obj->getBasePosition();
830 if(objectpos.getDistanceFrom(pos) > radius)
837 void ServerEnvironment::clearAllObjects()
839 infostream<<"ServerEnvironment::clearAllObjects(): "
840 <<"Removing all active objects"<<std::endl;
841 std::list<u16> objects_to_remove;
842 for(std::map<u16, ServerActiveObject*>::iterator
843 i = m_active_objects.begin();
844 i != m_active_objects.end(); ++i)
846 ServerActiveObject* obj = i->second;
847 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
850 // Delete static object if block is loaded
851 if(obj->m_static_exists){
852 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
854 block->m_static_objects.remove(id);
855 block->raiseModified(MOD_STATE_WRITE_NEEDED,
857 obj->m_static_exists = false;
860 // If known by some client, don't delete immediately
861 if(obj->m_known_by_count > 0){
862 obj->m_pending_deactivation = true;
863 obj->m_removed = true;
867 // Tell the object about removal
868 obj->removingFromEnvironment();
869 // Deregister in scripting api
870 m_script->removeObjectReference(obj);
872 // Delete active object
873 if(obj->environmentDeletes())
875 // Id to be removed from m_active_objects
876 objects_to_remove.push_back(id);
878 // Remove references from m_active_objects
879 for(std::list<u16>::iterator i = objects_to_remove.begin();
880 i != objects_to_remove.end(); ++i)
882 m_active_objects.erase(*i);
885 // Get list of loaded blocks
886 std::list<v3s16> loaded_blocks;
887 infostream<<"ServerEnvironment::clearAllObjects(): "
888 <<"Listing all loaded blocks"<<std::endl;
889 m_map->listAllLoadedBlocks(loaded_blocks);
890 infostream<<"ServerEnvironment::clearAllObjects(): "
891 <<"Done listing all loaded blocks: "
892 <<loaded_blocks.size()<<std::endl;
894 // Get list of loadable blocks
895 std::list<v3s16> loadable_blocks;
896 infostream<<"ServerEnvironment::clearAllObjects(): "
897 <<"Listing all loadable blocks"<<std::endl;
898 m_map->listAllLoadableBlocks(loadable_blocks);
899 infostream<<"ServerEnvironment::clearAllObjects(): "
900 <<"Done listing all loadable blocks: "
901 <<loadable_blocks.size()
902 <<", now clearing"<<std::endl;
904 // Grab a reference on each loaded block to avoid unloading it
905 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
906 i != loaded_blocks.end(); ++i)
909 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
914 // Remove objects in all loadable blocks
915 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
916 unload_interval = MYMAX(unload_interval, 1);
917 u32 report_interval = loadable_blocks.size() / 10;
918 u32 num_blocks_checked = 0;
919 u32 num_blocks_cleared = 0;
920 u32 num_objs_cleared = 0;
921 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
922 i != loadable_blocks.end(); ++i)
925 MapBlock *block = m_map->emergeBlock(p, false);
927 errorstream<<"ServerEnvironment::clearAllObjects(): "
928 <<"Failed to emerge block "<<PP(p)<<std::endl;
931 u32 num_stored = block->m_static_objects.m_stored.size();
932 u32 num_active = block->m_static_objects.m_active.size();
933 if(num_stored != 0 || num_active != 0){
934 block->m_static_objects.m_stored.clear();
935 block->m_static_objects.m_active.clear();
936 block->raiseModified(MOD_STATE_WRITE_NEEDED,
938 num_objs_cleared += num_stored + num_active;
939 num_blocks_cleared++;
941 num_blocks_checked++;
943 if(report_interval != 0 &&
944 num_blocks_checked % report_interval == 0){
945 float percent = 100.0 * (float)num_blocks_checked /
946 loadable_blocks.size();
947 infostream<<"ServerEnvironment::clearAllObjects(): "
948 <<"Cleared "<<num_objs_cleared<<" objects"
949 <<" in "<<num_blocks_cleared<<" blocks ("
950 <<percent<<"%)"<<std::endl;
952 if(num_blocks_checked % unload_interval == 0){
953 m_map->unloadUnreferencedBlocks();
956 m_map->unloadUnreferencedBlocks();
958 // Drop references that were added above
959 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
960 i != loaded_blocks.end(); ++i)
963 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
968 infostream<<"ServerEnvironment::clearAllObjects(): "
969 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
970 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
973 void ServerEnvironment::step(float dtime)
975 DSTACK(__FUNCTION_NAME);
977 //TimeTaker timer("ServerEnv step");
979 /* Step time of day */
980 stepTimeOfDay(dtime);
983 // NOTE: This is kind of funny on a singleplayer game, but doesn't
984 // really matter that much.
985 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
991 m_game_time_fraction_counter += dtime;
992 u32 inc_i = (u32)m_game_time_fraction_counter;
993 m_game_time += inc_i;
994 m_game_time_fraction_counter -= (float)inc_i;
1001 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1002 for(std::list<Player*>::iterator i = m_players.begin();
1003 i != m_players.end(); ++i)
1005 Player *player = *i;
1007 // Ignore disconnected players
1008 if(player->peer_id == 0)
1012 player->move(dtime, this, 100*BS);
1017 Manage active block list
1019 if(m_active_blocks_management_interval.step(dtime, 2.0))
1021 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1023 Get player block positions
1025 std::list<v3s16> players_blockpos;
1026 for(std::list<Player*>::iterator
1027 i = m_players.begin();
1028 i != m_players.end(); ++i)
1030 Player *player = *i;
1031 // Ignore disconnected players
1032 if(player->peer_id == 0)
1034 v3s16 blockpos = getNodeBlockPos(
1035 floatToInt(player->getPosition(), BS));
1036 players_blockpos.push_back(blockpos);
1040 Update list of active blocks, collecting changes
1042 const s16 active_block_range = g_settings->getS16("active_block_range");
1043 std::set<v3s16> blocks_removed;
1044 std::set<v3s16> blocks_added;
1045 m_active_blocks.update(players_blockpos, active_block_range,
1046 blocks_removed, blocks_added);
1049 Handle removed blocks
1052 // Convert active objects that are no more in active blocks to static
1053 deactivateFarObjects(false);
1055 for(std::set<v3s16>::iterator
1056 i = blocks_removed.begin();
1057 i != blocks_removed.end(); ++i)
1061 /* infostream<<"Server: Block " << PP(p)
1062 << " became inactive"<<std::endl; */
1064 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1068 // Set current time as timestamp (and let it set ChangedFlag)
1069 block->setTimestamp(m_game_time);
1076 for(std::set<v3s16>::iterator
1077 i = blocks_added.begin();
1078 i != blocks_added.end(); ++i)
1082 MapBlock *block = m_map->getBlockOrEmerge(p);
1084 m_active_blocks.m_list.erase(p);
1088 activateBlock(block);
1089 /* infostream<<"Server: Block " << PP(p)
1090 << " became active"<<std::endl; */
1095 Mess around in active blocks
1097 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1099 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1103 for(std::set<v3s16>::iterator
1104 i = m_active_blocks.m_list.begin();
1105 i != m_active_blocks.m_list.end(); ++i)
1109 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1110 <<") being handled"<<std::endl;*/
1112 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1116 // Reset block usage timer
1117 block->resetUsageTimer();
1119 // Set current time as timestamp
1120 block->setTimestampNoChangedFlag(m_game_time);
1121 // If time has changed much from the one on disk,
1122 // set block to be saved when it is unloaded
1123 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1124 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1125 "Timestamp older than 60s (step)");
1128 std::map<v3s16, NodeTimer> elapsed_timers =
1129 block->m_node_timers.step((float)dtime);
1130 if(!elapsed_timers.empty()){
1132 for(std::map<v3s16, NodeTimer>::iterator
1133 i = elapsed_timers.begin();
1134 i != elapsed_timers.end(); i++){
1135 n = block->getNodeNoEx(i->first);
1136 p = i->first + block->getPosRelative();
1137 if(m_script->node_on_timer(p,n,i->second.elapsed))
1138 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1144 const float abm_interval = 1.0;
1145 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1147 if(m_active_block_interval_overload_skip > 0){
1148 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1149 m_active_block_interval_overload_skip--;
1152 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1153 TimeTaker timer("modify in active blocks");
1155 // Initialize handling of ActiveBlockModifiers
1156 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1158 for(std::set<v3s16>::iterator
1159 i = m_active_blocks.m_list.begin();
1160 i != m_active_blocks.m_list.end(); ++i)
1164 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1165 <<") being handled"<<std::endl;*/
1167 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1171 // Set current time as timestamp
1172 block->setTimestampNoChangedFlag(m_game_time);
1174 /* Handle ActiveBlockModifiers */
1175 abmhandler.apply(block);
1178 u32 time_ms = timer.stop(true);
1179 u32 max_time_ms = 200;
1180 if(time_ms > max_time_ms){
1181 infostream<<"WARNING: active block modifiers took "
1182 <<time_ms<<"ms (longer than "
1183 <<max_time_ms<<"ms)"<<std::endl;
1184 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1189 Step script environment (run global on_step())
1191 m_script->environment_Step(dtime);
1197 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1198 //TimeTaker timer("Step active objects");
1200 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1202 // This helps the objects to send data at the same time
1203 bool send_recommended = false;
1204 m_send_recommended_timer += dtime;
1205 if(m_send_recommended_timer > getSendRecommendedInterval())
1207 m_send_recommended_timer -= getSendRecommendedInterval();
1208 send_recommended = true;
1211 for(std::map<u16, ServerActiveObject*>::iterator
1212 i = m_active_objects.begin();
1213 i != m_active_objects.end(); ++i)
1215 ServerActiveObject* obj = i->second;
1216 // Remove non-peaceful mobs on peaceful mode
1217 if(g_settings->getBool("only_peaceful_mobs")){
1218 if(!obj->isPeaceful())
1219 obj->m_removed = true;
1221 // Don't step if is to be removed or stored statically
1222 if(obj->m_removed || obj->m_pending_deactivation)
1225 obj->step(dtime, send_recommended);
1226 // Read messages from object
1227 while(!obj->m_messages_out.empty())
1229 m_active_object_messages.push_back(
1230 obj->m_messages_out.pop_front());
1236 Manage active objects
1238 if(m_object_management_interval.step(dtime, 0.5))
1240 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1242 Remove objects that satisfy (m_removed && m_known_by_count==0)
1244 removeRemovedObjects();
1248 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1250 std::map<u16, ServerActiveObject*>::iterator n;
1251 n = m_active_objects.find(id);
1252 if(n == m_active_objects.end())
1257 bool isFreeServerActiveObjectId(u16 id,
1258 std::map<u16, ServerActiveObject*> &objects)
1263 return objects.find(id) == objects.end();
1266 u16 getFreeServerActiveObjectId(
1267 std::map<u16, ServerActiveObject*> &objects)
1269 //try to reuse id's as late as possible
1270 static u16 last_used_id = 0;
1271 u16 startid = last_used_id;
1275 if(isFreeServerActiveObjectId(last_used_id, objects))
1276 return last_used_id;
1278 if(last_used_id == startid)
1283 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1287 u16 id = addActiveObjectRaw(object, true, 0);
1292 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1296 v3f objectpos = obj->getBasePosition();
1298 // The block in which the object resides in
1299 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1302 Update the static data
1305 // Create new static object
1306 std::string staticdata = obj->getStaticData();
1307 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1308 // Add to the block where the object is located in
1309 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1310 // Get or generate the block
1311 MapBlock *block = m_map->emergeBlock(blockpos);
1313 bool succeeded = false;
1317 block->m_static_objects.insert(0, s_obj);
1318 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1319 "addActiveObjectAsStatic");
1323 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1324 <<"Could not find or generate "
1325 <<"a block for storing static object"<<std::endl;
1329 if(obj->environmentDeletes())
1337 Finds out what new objects have been added to
1338 inside a radius around a position
1340 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1341 std::set<u16> ¤t_objects,
1342 std::set<u16> &added_objects)
1344 v3f pos_f = intToFloat(pos, BS);
1345 f32 radius_f = radius * BS;
1347 Go through the object list,
1348 - discard m_removed objects,
1349 - discard objects that are too far away,
1350 - discard objects that are found in current_objects.
1351 - add remaining objects to added_objects
1353 for(std::map<u16, ServerActiveObject*>::iterator
1354 i = m_active_objects.begin();
1355 i != m_active_objects.end(); ++i)
1359 ServerActiveObject *object = i->second;
1362 // Discard if removed or deactivating
1363 if(object->m_removed || object->m_pending_deactivation)
1365 if(object->unlimitedTransferDistance() == false){
1366 // Discard if too far
1367 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1368 if(distance_f > radius_f)
1371 // Discard if already on current_objects
1372 std::set<u16>::iterator n;
1373 n = current_objects.find(id);
1374 if(n != current_objects.end())
1376 // Add to added_objects
1377 added_objects.insert(id);
1382 Finds out what objects have been removed from
1383 inside a radius around a position
1385 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1386 std::set<u16> ¤t_objects,
1387 std::set<u16> &removed_objects)
1389 v3f pos_f = intToFloat(pos, BS);
1390 f32 radius_f = radius * BS;
1392 Go through current_objects; object is removed if:
1393 - object is not found in m_active_objects (this is actually an
1394 error condition; objects should be set m_removed=true and removed
1395 only after all clients have been informed about removal), or
1396 - object has m_removed=true, or
1397 - object is too far away
1399 for(std::set<u16>::iterator
1400 i = current_objects.begin();
1401 i != current_objects.end(); ++i)
1404 ServerActiveObject *object = getActiveObject(id);
1407 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1408 <<" object in current_objects is NULL"<<std::endl;
1409 removed_objects.insert(id);
1413 if(object->m_removed || object->m_pending_deactivation)
1415 removed_objects.insert(id);
1419 // If transfer distance is unlimited, don't remove
1420 if(object->unlimitedTransferDistance())
1423 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1425 if(distance_f >= radius_f)
1427 removed_objects.insert(id);
1435 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1437 if(m_active_object_messages.empty())
1438 return ActiveObjectMessage(0);
1440 ActiveObjectMessage message = m_active_object_messages.front();
1441 m_active_object_messages.pop_front();
1446 ************ Private methods *************
1449 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1450 bool set_changed, u32 dtime_s)
1453 if(object->getId() == 0){
1454 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1457 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1458 <<"no free ids available"<<std::endl;
1459 if(object->environmentDeletes())
1463 object->setId(new_id);
1466 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1467 <<"supplied with id "<<object->getId()<<std::endl;
1469 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1471 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1472 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1473 if(object->environmentDeletes())
1477 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1478 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1480 m_active_objects[object->getId()] = object;
1482 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"Added id="<<object->getId()<<"; there are now "
1484 <<m_active_objects.size()<<" active objects."
1487 // Register reference in scripting api (must be done before post-init)
1488 m_script->addObjectReference(object);
1489 // Post-initialize object
1490 object->addedToEnvironment(dtime_s);
1492 // Add static data to block
1493 if(object->isStaticAllowed())
1495 // Add static object to active static list of the block
1496 v3f objectpos = object->getBasePosition();
1497 std::string staticdata = object->getStaticData();
1498 StaticObject s_obj(object->getType(), objectpos, staticdata);
1499 // Add to the block where the object is located in
1500 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1501 MapBlock *block = m_map->emergeBlock(blockpos);
1503 block->m_static_objects.m_active[object->getId()] = s_obj;
1504 object->m_static_exists = true;
1505 object->m_static_block = blockpos;
1508 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1509 "addActiveObjectRaw");
1511 v3s16 p = floatToInt(objectpos, BS);
1512 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1513 <<"could not emerge block for storing id="<<object->getId()
1514 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1518 return object->getId();
1522 Remove objects that satisfy (m_removed && m_known_by_count==0)
1524 void ServerEnvironment::removeRemovedObjects()
1526 std::list<u16> objects_to_remove;
1527 for(std::map<u16, ServerActiveObject*>::iterator
1528 i = m_active_objects.begin();
1529 i != m_active_objects.end(); ++i)
1532 ServerActiveObject* obj = i->second;
1533 // This shouldn't happen but check it
1536 infostream<<"NULL object found in ServerEnvironment"
1537 <<" while finding removed objects. id="<<id<<std::endl;
1538 // Id to be removed from m_active_objects
1539 objects_to_remove.push_back(id);
1544 We will delete objects that are marked as removed or thatare
1545 waiting for deletion after deactivation
1547 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1551 Delete static data from block if is marked as removed
1553 if(obj->m_static_exists && obj->m_removed)
1555 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1557 block->m_static_objects.remove(id);
1558 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1559 "removeRemovedObjects/remove");
1560 obj->m_static_exists = false;
1562 infostream<<"Failed to emerge block from which an object to "
1563 <<"be removed was loaded from. id="<<id<<std::endl;
1567 // If m_known_by_count > 0, don't actually remove. On some future
1568 // invocation this will be 0, which is when removal will continue.
1569 if(obj->m_known_by_count > 0)
1573 Move static data from active to stored if not marked as removed
1575 if(obj->m_static_exists && !obj->m_removed){
1576 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1578 std::map<u16, StaticObject>::iterator i =
1579 block->m_static_objects.m_active.find(id);
1580 if(i != block->m_static_objects.m_active.end()){
1581 block->m_static_objects.m_stored.push_back(i->second);
1582 block->m_static_objects.m_active.erase(id);
1583 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1584 "removeRemovedObjects/deactivate");
1587 infostream<<"Failed to emerge block from which an object to "
1588 <<"be deactivated was loaded from. id="<<id<<std::endl;
1592 // Tell the object about removal
1593 obj->removingFromEnvironment();
1594 // Deregister in scripting api
1595 m_script->removeObjectReference(obj);
1598 if(obj->environmentDeletes())
1600 // Id to be removed from m_active_objects
1601 objects_to_remove.push_back(id);
1603 // Remove references from m_active_objects
1604 for(std::list<u16>::iterator i = objects_to_remove.begin();
1605 i != objects_to_remove.end(); ++i)
1607 m_active_objects.erase(*i);
1611 static void print_hexdump(std::ostream &o, const std::string &data)
1613 const int linelength = 16;
1614 for(int l=0; ; l++){
1615 int i0 = linelength * l;
1616 bool at_end = false;
1617 int thislinelength = linelength;
1618 if(i0 + thislinelength > (int)data.size()){
1619 thislinelength = data.size() - i0;
1622 for(int di=0; di<linelength; di++){
1625 if(di<thislinelength)
1626 snprintf(buf, 4, "%.2x ", data[i]);
1628 snprintf(buf, 4, " ");
1632 for(int di=0; di<thislinelength; di++){
1646 Convert stored objects from blocks near the players to active.
1648 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1652 // Ignore if no stored objects (to not set changed flag)
1653 if(block->m_static_objects.m_stored.size() == 0)
1655 verbosestream<<"ServerEnvironment::activateObjects(): "
1656 <<"activating objects of block "<<PP(block->getPos())
1657 <<" ("<<block->m_static_objects.m_stored.size()
1658 <<" objects)"<<std::endl;
1659 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1661 errorstream<<"suspiciously large amount of objects detected: "
1662 <<block->m_static_objects.m_stored.size()<<" in "
1663 <<PP(block->getPos())
1664 <<"; removing all of them."<<std::endl;
1665 // Clear stored list
1666 block->m_static_objects.m_stored.clear();
1667 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1668 "stored list cleared in activateObjects due to "
1669 "large amount of objects");
1673 // Activate stored objects
1674 std::list<StaticObject> new_stored;
1675 for(std::list<StaticObject>::iterator
1676 i = block->m_static_objects.m_stored.begin();
1677 i != block->m_static_objects.m_stored.end(); ++i)
1679 /*infostream<<"Server: Creating an active object from "
1680 <<"static data"<<std::endl;*/
1681 StaticObject &s_obj = *i;
1682 // Create an active object from the data
1683 ServerActiveObject *obj = ServerActiveObject::create
1684 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1685 // If couldn't create object, store static data back.
1688 errorstream<<"ServerEnvironment::activateObjects(): "
1689 <<"failed to create active object from static object "
1690 <<"in block "<<PP(s_obj.pos/BS)
1691 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1692 print_hexdump(verbosestream, s_obj.data);
1694 new_stored.push_back(s_obj);
1697 verbosestream<<"ServerEnvironment::activateObjects(): "
1698 <<"activated static object pos="<<PP(s_obj.pos/BS)
1699 <<" type="<<(int)s_obj.type<<std::endl;
1700 // This will also add the object to the active static list
1701 addActiveObjectRaw(obj, false, dtime_s);
1703 // Clear stored list
1704 block->m_static_objects.m_stored.clear();
1705 // Add leftover failed stuff to stored list
1706 for(std::list<StaticObject>::iterator
1707 i = new_stored.begin();
1708 i != new_stored.end(); ++i)
1710 StaticObject &s_obj = *i;
1711 block->m_static_objects.m_stored.push_back(s_obj);
1714 // Turn the active counterparts of activated objects not pending for
1716 for(std::map<u16, StaticObject>::iterator
1717 i = block->m_static_objects.m_active.begin();
1718 i != block->m_static_objects.m_active.end(); ++i)
1721 ServerActiveObject *object = getActiveObject(id);
1723 object->m_pending_deactivation = false;
1727 Note: Block hasn't really been modified here.
1728 The objects have just been activated and moved from the stored
1729 static list to the active static list.
1730 As such, the block is essentially the same.
1731 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1732 Otherwise there would be a huge amount of unnecessary I/O.
1737 Convert objects that are not standing inside active blocks to static.
1739 If m_known_by_count != 0, active object is not deleted, but static
1740 data is still updated.
1742 If force_delete is set, active object is deleted nevertheless. It
1743 shall only be set so in the destructor of the environment.
1745 If block wasn't generated (not in memory or on disk),
1747 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1749 std::list<u16> objects_to_remove;
1750 for(std::map<u16, ServerActiveObject*>::iterator
1751 i = m_active_objects.begin();
1752 i != m_active_objects.end(); ++i)
1754 ServerActiveObject* obj = i->second;
1757 // Do not deactivate if static data creation not allowed
1758 if(!force_delete && !obj->isStaticAllowed())
1761 // If pending deactivation, let removeRemovedObjects() do it
1762 if(!force_delete && obj->m_pending_deactivation)
1766 v3f objectpos = obj->getBasePosition();
1768 // The block in which the object resides in
1769 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1771 // If object's static data is stored in a deactivated block and object
1772 // is actually located in an active block, re-save to the block in
1773 // which the object is actually located in.
1775 obj->m_static_exists &&
1776 !m_active_blocks.contains(obj->m_static_block) &&
1777 m_active_blocks.contains(blockpos_o))
1779 v3s16 old_static_block = obj->m_static_block;
1781 // Save to block where object is located
1782 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1784 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1785 <<"Could not save object id="<<id
1786 <<" to it's current block "<<PP(blockpos_o)
1790 std::string staticdata_new = obj->getStaticData();
1791 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1792 block->m_static_objects.insert(id, s_obj);
1793 obj->m_static_block = blockpos_o;
1794 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1795 "deactivateFarObjects: Static data moved in");
1797 // Delete from block where object was located
1798 block = m_map->emergeBlock(old_static_block, false);
1800 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1801 <<"Could not delete object id="<<id
1802 <<" from it's previous block "<<PP(old_static_block)
1806 block->m_static_objects.remove(id);
1807 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1808 "deactivateFarObjects: Static data moved out");
1812 // If block is active, don't remove
1813 if(!force_delete && m_active_blocks.contains(blockpos_o))
1816 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1817 <<"deactivating object id="<<id<<" on inactive block "
1818 <<PP(blockpos_o)<<std::endl;
1820 // If known by some client, don't immediately delete.
1821 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1824 Update the static data
1827 if(obj->isStaticAllowed())
1829 // Create new static object
1830 std::string staticdata_new = obj->getStaticData();
1831 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1833 bool stays_in_same_block = false;
1834 bool data_changed = true;
1836 if(obj->m_static_exists){
1837 if(obj->m_static_block == blockpos_o)
1838 stays_in_same_block = true;
1840 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1842 std::map<u16, StaticObject>::iterator n =
1843 block->m_static_objects.m_active.find(id);
1844 if(n != block->m_static_objects.m_active.end()){
1845 StaticObject static_old = n->second;
1847 float save_movem = obj->getMinimumSavedMovement();
1849 if(static_old.data == staticdata_new &&
1850 (static_old.pos - objectpos).getLength() < save_movem)
1851 data_changed = false;
1853 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1854 <<"id="<<id<<" m_static_exists=true but "
1855 <<"static data doesn't actually exist in "
1856 <<PP(obj->m_static_block)<<std::endl;
1860 bool shall_be_written = (!stays_in_same_block || data_changed);
1862 // Delete old static object
1863 if(obj->m_static_exists)
1865 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1868 block->m_static_objects.remove(id);
1869 obj->m_static_exists = false;
1870 // Only mark block as modified if data changed considerably
1871 if(shall_be_written)
1872 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1873 "deactivateFarObjects: Static data "
1874 "changed considerably");
1878 // Add to the block where the object is located in
1879 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1880 // Get or generate the block
1881 MapBlock *block = NULL;
1883 block = m_map->emergeBlock(blockpos);
1884 } catch(InvalidPositionException &e){
1885 // Handled via NULL pointer
1886 // NOTE: emergeBlock's failure is usually determined by it
1887 // actually returning NULL
1892 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1893 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1894 <<" statically but block "<<PP(blockpos)
1895 <<" already contains "
1896 <<block->m_static_objects.m_stored.size()
1898 <<" Forcing delete."<<std::endl;
1899 force_delete = true;
1901 // If static counterpart already exists in target block,
1903 // This shouldn't happen because the object is removed from
1904 // the previous block before this according to
1905 // obj->m_static_block, but happens rarely for some unknown
1906 // reason. Unsuccessful attempts have been made to find
1908 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1909 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1911 block->m_static_objects.remove(id);
1913 // Store static data
1914 u16 store_id = pending_delete ? id : 0;
1915 block->m_static_objects.insert(store_id, s_obj);
1917 // Only mark block as modified if data changed considerably
1918 if(shall_be_written)
1919 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1920 "deactivateFarObjects: Static data "
1921 "changed considerably");
1923 obj->m_static_exists = true;
1924 obj->m_static_block = block->getPos();
1929 v3s16 p = floatToInt(objectpos, BS);
1930 errorstream<<"ServerEnv: Could not find or generate "
1931 <<"a block for storing id="<<obj->getId()
1932 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1939 If known by some client, set pending deactivation.
1940 Otherwise delete it immediately.
1943 if(pending_delete && !force_delete)
1945 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1946 <<"object id="<<id<<" is known by clients"
1947 <<"; not deleting yet"<<std::endl;
1949 obj->m_pending_deactivation = true;
1953 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1954 <<"object id="<<id<<" is not known by clients"
1955 <<"; deleting"<<std::endl;
1957 // Tell the object about removal
1958 obj->removingFromEnvironment();
1959 // Deregister in scripting api
1960 m_script->removeObjectReference(obj);
1962 // Delete active object
1963 if(obj->environmentDeletes())
1965 // Id to be removed from m_active_objects
1966 objects_to_remove.push_back(id);
1969 // Remove references from m_active_objects
1970 for(std::list<u16>::iterator i = objects_to_remove.begin();
1971 i != objects_to_remove.end(); ++i)
1973 m_active_objects.erase(*i);
1980 #include "clientsimpleobject.h"
1986 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1987 ITextureSource *texturesource, IGameDef *gamedef,
1988 IrrlichtDevice *irr):
1991 m_texturesource(texturesource),
1996 memset(m_attachements, zero, sizeof(m_attachements));
1999 ClientEnvironment::~ClientEnvironment()
2001 // delete active objects
2002 for(std::map<u16, ClientActiveObject*>::iterator
2003 i = m_active_objects.begin();
2004 i != m_active_objects.end(); ++i)
2009 for(std::list<ClientSimpleObject*>::iterator
2010 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2019 Map & ClientEnvironment::getMap()
2024 ClientMap & ClientEnvironment::getClientMap()
2029 void ClientEnvironment::addPlayer(Player *player)
2031 DSTACK(__FUNCTION_NAME);
2033 It is a failure if player is local and there already is a local
2036 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2038 Environment::addPlayer(player);
2041 LocalPlayer * ClientEnvironment::getLocalPlayer()
2043 for(std::list<Player*>::iterator i = m_players.begin();
2044 i != m_players.end(); ++i)
2046 Player *player = *i;
2047 if(player->isLocal())
2048 return (LocalPlayer*)player;
2053 void ClientEnvironment::step(float dtime)
2055 DSTACK(__FUNCTION_NAME);
2057 /* Step time of day */
2058 stepTimeOfDay(dtime);
2060 // Get some settings
2061 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2062 bool free_move = fly_allowed && g_settings->getBool("free_move");
2065 LocalPlayer *lplayer = getLocalPlayer();
2067 // collision info queue
2068 std::list<CollisionInfo> player_collisions;
2071 Get the speed the player is going
2073 bool is_climbing = lplayer->is_climbing;
2075 f32 player_speed = lplayer->getSpeed().getLength();
2078 Maximum position increment
2080 //f32 position_max_increment = 0.05*BS;
2081 f32 position_max_increment = 0.1*BS;
2083 // Maximum time increment (for collision detection etc)
2084 // time = distance / speed
2085 f32 dtime_max_increment = 1;
2086 if(player_speed > 0.001)
2087 dtime_max_increment = position_max_increment / player_speed;
2089 // Maximum time increment is 10ms or lower
2090 if(dtime_max_increment > 0.01)
2091 dtime_max_increment = 0.01;
2093 // Don't allow overly huge dtime
2097 f32 dtime_downcount = dtime;
2100 Stuff that has a maximum time increment
2109 if(dtime_downcount > dtime_max_increment)
2111 dtime_part = dtime_max_increment;
2112 dtime_downcount -= dtime_part;
2116 dtime_part = dtime_downcount;
2118 Setting this to 0 (no -=dtime_part) disables an infinite loop
2119 when dtime_part is so small that dtime_downcount -= dtime_part
2122 dtime_downcount = 0;
2131 if(free_move == false && is_climbing == false)
2134 v3f speed = lplayer->getSpeed();
2135 if(lplayer->in_liquid == false)
2136 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2138 // Liquid floating / sinking
2139 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2140 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2142 // Liquid resistance
2143 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2145 // How much the node's viscosity blocks movement, ranges between 0 and 1
2146 // Should match the scale at which viscosity increase affects other liquid attributes
2147 const f32 viscosity_factor = 0.3;
2149 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2150 f32 dl = d_wanted.getLength();
2151 if(dl > lplayer->movement_liquid_fluidity_smooth)
2152 dl = lplayer->movement_liquid_fluidity_smooth;
2153 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2155 v3f d = d_wanted.normalize() * dl;
2159 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2160 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2161 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= 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.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= 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;
2168 lplayer->setSpeed(speed);
2173 This also does collision detection.
2175 lplayer->move(dtime_part, this, position_max_increment,
2176 &player_collisions);
2179 while(dtime_downcount > 0.001);
2181 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2183 for(std::list<CollisionInfo>::iterator
2184 i = player_collisions.begin();
2185 i != player_collisions.end(); ++i)
2187 CollisionInfo &info = *i;
2188 v3f speed_diff = info.new_speed - info.old_speed;;
2189 // Handle only fall damage
2190 // (because otherwise walking against something in fast_move kills you)
2191 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2193 // Get rid of other components
2196 f32 pre_factor = 1; // 1 hp per node/s
2197 f32 tolerance = BS*14; // 5 without damage
2198 f32 post_factor = 1; // 1 hp per node/s
2199 if(info.type == COLLISION_NODE)
2201 const ContentFeatures &f = m_gamedef->ndef()->
2202 get(m_map->getNodeNoEx(info.node_p));
2203 // Determine fall damage multiplier
2204 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2205 pre_factor = 1.0 + (float)addp/100.0;
2207 float speed = pre_factor * speed_diff.getLength();
2208 if(speed > tolerance)
2210 f32 damage_f = (speed - tolerance)/BS * post_factor;
2211 u16 damage = (u16)(damage_f+0.5);
2213 damageLocalPlayer(damage, true);
2214 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2215 m_gamedef->event()->put(e);
2221 A quick draft of lava damage
2223 if(m_lava_hurt_interval.step(dtime, 1.0))
2225 v3f pf = lplayer->getPosition();
2227 // Feet, middle and head
2228 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2229 MapNode n1 = m_map->getNodeNoEx(p1);
2230 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2231 MapNode n2 = m_map->getNodeNoEx(p2);
2232 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2233 MapNode n3 = m_map->getNodeNoEx(p3);
2235 u32 damage_per_second = 0;
2236 damage_per_second = MYMAX(damage_per_second,
2237 m_gamedef->ndef()->get(n1).damage_per_second);
2238 damage_per_second = MYMAX(damage_per_second,
2239 m_gamedef->ndef()->get(n2).damage_per_second);
2240 damage_per_second = MYMAX(damage_per_second,
2241 m_gamedef->ndef()->get(n3).damage_per_second);
2243 if(damage_per_second != 0)
2245 damageLocalPlayer(damage_per_second, true);
2252 if(m_drowning_interval.step(dtime, 2.0))
2254 v3f pf = lplayer->getPosition();
2257 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2258 MapNode n = m_map->getNodeNoEx(p);
2259 ContentFeatures c = m_gamedef->ndef()->get(n);
2260 u8 drowning_damage = c.drowning;
2261 if(drowning_damage > 0 && lplayer->hp > 0){
2262 u16 breath = lplayer->getBreath();
2269 lplayer->setBreath(breath);
2270 updateLocalPlayerBreath(breath);
2273 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2274 damageLocalPlayer(drowning_damage, true);
2277 if(m_breathing_interval.step(dtime, 0.5))
2279 v3f pf = lplayer->getPosition();
2282 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2283 MapNode n = m_map->getNodeNoEx(p);
2284 ContentFeatures c = m_gamedef->ndef()->get(n);
2286 lplayer->setBreath(11);
2288 else if(c.drowning == 0){
2289 u16 breath = lplayer->getBreath();
2292 lplayer->setBreath(breath);
2293 updateLocalPlayerBreath(breath);
2299 Stuff that can be done in an arbitarily large dtime
2301 for(std::list<Player*>::iterator i = m_players.begin();
2302 i != m_players.end(); ++i)
2304 Player *player = *i;
2307 Handle non-local players
2309 if(player->isLocal() == false)
2312 player->move(dtime, this, 100*BS);
2316 // Update lighting on all players on client
2320 v3s16 p = player->getLightPosition();
2321 MapNode n = m_map->getNode(p);
2322 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2324 catch(InvalidPositionException &e){
2325 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2327 player->light = light;
2331 Step active objects and update lighting of them
2334 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2335 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2336 for(std::map<u16, ClientActiveObject*>::iterator
2337 i = m_active_objects.begin();
2338 i != m_active_objects.end(); ++i)
2340 ClientActiveObject* obj = i->second;
2342 obj->step(dtime, this);
2350 v3s16 p = obj->getLightPosition();
2351 MapNode n = m_map->getNode(p);
2352 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2354 catch(InvalidPositionException &e){
2355 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2357 obj->updateLight(light);
2362 Step and handle simple objects
2364 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2365 for(std::list<ClientSimpleObject*>::iterator
2366 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2368 ClientSimpleObject *simple = *i;
2369 std::list<ClientSimpleObject*>::iterator cur = i;
2371 simple->step(dtime);
2372 if(simple->m_to_be_removed){
2374 m_simple_objects.erase(cur);
2379 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2381 m_simple_objects.push_back(simple);
2384 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2386 std::map<u16, ClientActiveObject*>::iterator n;
2387 n = m_active_objects.find(id);
2388 if(n == m_active_objects.end())
2393 bool isFreeClientActiveObjectId(u16 id,
2394 std::map<u16, ClientActiveObject*> &objects)
2399 return objects.find(id) == objects.end();
2402 u16 getFreeClientActiveObjectId(
2403 std::map<u16, ClientActiveObject*> &objects)
2405 //try to reuse id's as late as possible
2406 static u16 last_used_id = 0;
2407 u16 startid = last_used_id;
2411 if(isFreeClientActiveObjectId(last_used_id, objects))
2412 return last_used_id;
2414 if(last_used_id == startid)
2419 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2422 if(object->getId() == 0)
2424 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2427 infostream<<"ClientEnvironment::addActiveObject(): "
2428 <<"no free ids available"<<std::endl;
2432 object->setId(new_id);
2434 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2436 infostream<<"ClientEnvironment::addActiveObject(): "
2437 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2441 infostream<<"ClientEnvironment::addActiveObject(): "
2442 <<"added (id="<<object->getId()<<")"<<std::endl;
2443 m_active_objects[object->getId()] = object;
2444 object->addToScene(m_smgr, m_texturesource, m_irr);
2445 { // Update lighting immediately
2449 v3s16 p = object->getLightPosition();
2450 MapNode n = m_map->getNode(p);
2451 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2453 catch(InvalidPositionException &e){
2454 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2456 object->updateLight(light);
2458 return object->getId();
2461 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2462 const std::string &init_data)
2464 ClientActiveObject* obj =
2465 ClientActiveObject::create(type, m_gamedef, this);
2468 infostream<<"ClientEnvironment::addActiveObject(): "
2469 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2478 obj->initialize(init_data);
2480 catch(SerializationError &e)
2482 errorstream<<"ClientEnvironment::addActiveObject():"
2483 <<" id="<<id<<" type="<<type
2484 <<": SerializationError in initialize(): "
2486 <<": init_data="<<serializeJsonString(init_data)
2490 addActiveObject(obj);
2493 void ClientEnvironment::removeActiveObject(u16 id)
2495 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2496 <<"id="<<id<<std::endl;
2497 ClientActiveObject* obj = getActiveObject(id);
2500 infostream<<"ClientEnvironment::removeActiveObject(): "
2501 <<"id="<<id<<" not found"<<std::endl;
2504 obj->removeFromScene(true);
2506 m_active_objects.erase(id);
2509 void ClientEnvironment::processActiveObjectMessage(u16 id,
2510 const std::string &data)
2512 ClientActiveObject* obj = getActiveObject(id);
2515 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2516 <<" got message for id="<<id<<", which doesn't exist."
2522 obj->processMessage(data);
2524 catch(SerializationError &e)
2526 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2527 <<" id="<<id<<" type="<<obj->getType()
2528 <<" SerializationError in processMessage(),"
2529 <<" message="<<serializeJsonString(data)
2535 Callbacks for activeobjects
2538 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2540 LocalPlayer *lplayer = getLocalPlayer();
2544 if(lplayer->hp > damage)
2545 lplayer->hp -= damage;
2550 ClientEnvEvent event;
2551 event.type = CEE_PLAYER_DAMAGE;
2552 event.player_damage.amount = damage;
2553 event.player_damage.send_to_server = handle_hp;
2554 m_client_event_queue.push_back(event);
2557 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2559 ClientEnvEvent event;
2560 event.type = CEE_PLAYER_BREATH;
2561 event.player_breath.amount = breath;
2562 m_client_event_queue.push_back(event);
2566 Client likes to call these
2569 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2570 std::vector<DistanceSortedActiveObject> &dest)
2572 for(std::map<u16, ClientActiveObject*>::iterator
2573 i = m_active_objects.begin();
2574 i != m_active_objects.end(); ++i)
2576 ClientActiveObject* obj = i->second;
2578 f32 d = (obj->getPosition() - origin).getLength();
2583 DistanceSortedActiveObject dso(obj, d);
2585 dest.push_back(dso);
2589 ClientEnvEvent ClientEnvironment::getClientEvent()
2591 ClientEnvEvent event;
2592 if(m_client_event_queue.empty())
2593 event.type = CEE_NONE;
2595 event = m_client_event_queue.front();
2596 m_client_event_queue.pop_front();
2601 #endif // #ifndef SERVER