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"
46 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
48 Environment::Environment():
50 m_time_of_day_f(9000./24000),
51 m_time_of_day_speed(0),
53 m_enable_day_night_ratio_override(false),
54 m_day_night_ratio_override(0.0f)
58 Environment::~Environment()
61 for(std::list<Player*>::iterator i = m_players.begin();
62 i != m_players.end(); ++i)
68 void Environment::addPlayer(Player *player)
70 DSTACK(__FUNCTION_NAME);
72 Check that peer_ids are unique.
73 Also check that names are unique.
74 Exception: there can be multiple players with peer_id=0
76 // If peer id is non-zero, it has to be unique.
77 if(player->peer_id != 0)
78 assert(getPlayer(player->peer_id) == NULL);
79 // Name has to be unique.
80 assert(getPlayer(player->getName()) == NULL);
82 m_players.push_back(player);
85 void Environment::removePlayer(u16 peer_id)
87 DSTACK(__FUNCTION_NAME);
89 for(std::list<Player*>::iterator i = m_players.begin();
90 i != m_players.end(); ++i)
93 if(player->peer_id != peer_id)
98 // See if there is an another one
99 // (shouldn't be, but just to be sure)
104 Player * Environment::getPlayer(u16 peer_id)
106 for(std::list<Player*>::iterator i = m_players.begin();
107 i != m_players.end(); ++i)
110 if(player->peer_id == peer_id)
116 Player * Environment::getPlayer(const char *name)
118 for(std::list<Player*>::iterator i = m_players.begin();
119 i != m_players.end(); ++i)
122 if(strcmp(player->getName(), name) == 0)
128 Player * Environment::getRandomConnectedPlayer()
130 std::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(std::list<Player*>::iterator
134 i = connected_players.begin();
135 i != connected_players.end(); ++i)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 std::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(std::list<Player*>::iterator
153 i = connected_players.begin();
154 i != connected_players.end(); ++i)
157 f32 d = player->getPosition().getDistanceFrom(pos);
158 if(d < nearest_d || nearest_player == NULL)
161 nearest_player = player;
164 return nearest_player;
167 std::list<Player*> Environment::getPlayers()
172 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 std::list<Player*> newlist;
175 for(std::list<Player*>::iterator
176 i = m_players.begin();
177 i != m_players.end(); ++i)
181 if(ignore_disconnected)
183 // Ignore disconnected players
184 if(player->peer_id == 0)
188 newlist.push_back(player);
193 u32 Environment::getDayNightRatio()
195 if(m_enable_day_night_ratio_override)
196 return m_day_night_ratio_override;
197 bool smooth = g_settings->getBool("enable_shaders");
198 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
201 void Environment::stepTimeOfDay(float dtime)
203 m_time_counter += dtime;
204 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
205 u32 units = (u32)(m_time_counter*speed);
206 m_time_counter -= (f32)units / speed;
210 if(m_time_of_day + units >= 24000)
212 m_time_of_day = (m_time_of_day + units) % 24000;
214 m_time_of_day_f = (float)m_time_of_day / 24000.0;
217 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
218 if(m_time_of_day_f > 1.0)
219 m_time_of_day_f -= 1.0;
220 if(m_time_of_day_f < 0.0)
221 m_time_of_day_f += 1.0;
229 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
233 // Initialize timer to random value to spread processing
234 float itv = abm->getTriggerInterval();
235 itv = MYMAX(0.001, itv); // No less than 1ms
236 int minval = MYMAX(-0.51*itv, -60); // Clamp to
237 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
238 timer = myrand_range(minval, maxval);
245 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
248 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
249 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
250 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
257 void ActiveBlockList::update(std::list<v3s16> &active_positions,
259 std::set<v3s16> &blocks_removed,
260 std::set<v3s16> &blocks_added)
265 std::set<v3s16> newlist = m_forceloaded_list;
266 for(std::list<v3s16>::iterator i = active_positions.begin();
267 i != active_positions.end(); ++i)
269 fillRadiusBlock(*i, radius, newlist);
273 Find out which blocks on the old list are not on the new list
275 // Go through old list
276 for(std::set<v3s16>::iterator i = m_list.begin();
277 i != m_list.end(); ++i)
280 // If not on new list, it's been removed
281 if(newlist.find(p) == newlist.end())
282 blocks_removed.insert(p);
286 Find out which blocks on the new list are not on the old list
288 // Go through new list
289 for(std::set<v3s16>::iterator i = newlist.begin();
290 i != newlist.end(); ++i)
293 // If not on old list, it's been added
294 if(m_list.find(p) == m_list.end())
295 blocks_added.insert(p);
302 for(std::set<v3s16>::iterator i = newlist.begin();
303 i != newlist.end(); ++i)
314 ServerEnvironment::ServerEnvironment(ServerMap *map,
315 GameScripting *scriptIface,
316 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
318 m_script(scriptIface),
321 m_random_spawn_timer(3),
322 m_send_recommended_timer(0),
323 m_active_block_interval_overload_skip(0),
325 m_game_time_fraction_counter(0),
326 m_recommended_send_interval(0.1),
327 m_max_lag_estimate(0.1)
329 m_use_weather = g_settings->getBool("weather");
332 ServerEnvironment::~ServerEnvironment()
334 // Clear active block list.
335 // This makes the next one delete all active objects.
336 m_active_blocks.clear();
338 // Convert all objects to static and delete the active objects
339 deactivateFarObjects(true);
344 // Delete ActiveBlockModifiers
345 for(std::list<ABMWithState>::iterator
346 i = m_abms.begin(); i != m_abms.end(); ++i){
351 Map & ServerEnvironment::getMap()
356 ServerMap & ServerEnvironment::getServerMap()
361 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize, v3s16 *p)
363 float distance = pos1.getDistanceFrom(pos2);
365 //calculate normalized direction vector
366 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
367 (pos2.Y - pos1.Y)/distance,
368 (pos2.Z - pos1.Z)/distance);
370 //find out if there's a node on path between pos1 and pos2
371 for (float i = 1; i < distance; i += stepsize) {
372 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
373 normalized_vector.Y * i,
374 normalized_vector.Z * i) +pos1,BS);
376 MapNode n = getMap().getNodeNoEx(pos);
378 if(n.param0 != CONTENT_AIR) {
388 void ServerEnvironment::serializePlayers(const std::string &savedir)
390 std::string players_path = savedir + "/players";
391 fs::CreateDir(players_path);
393 std::set<Player*> saved_players;
395 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
396 for(u32 i=0; i<player_files.size(); i++)
398 if(player_files[i].dir || player_files[i].name[0] == '.')
401 // Full path to this file
402 std::string path = players_path + "/" + player_files[i].name;
404 //infostream<<"Checking player file "<<path<<std::endl;
406 // Load player to see what is its name
407 RemotePlayer testplayer(m_gamedef);
409 // Open file and deserialize
410 std::ifstream is(path.c_str(), std::ios_base::binary);
411 if(is.good() == false)
413 infostream<<"Failed to read "<<path<<std::endl;
416 testplayer.deSerialize(is, player_files[i].name);
419 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
421 // Search for the player
422 std::string playername = testplayer.getName();
423 Player *player = getPlayer(playername.c_str());
426 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
430 //infostream<<"Found matching player, overwriting."<<std::endl;
432 // OK, found. Save player there.
433 if(player->checkModified())
435 // Open file and serialize
436 std::ostringstream ss(std::ios_base::binary);
437 player->serialize(ss);
438 if(!fs::safeWriteToFile(path, ss.str()))
440 infostream<<"Failed to write "<<path<<std::endl;
443 saved_players.insert(player);
445 saved_players.insert(player);
449 for(std::list<Player*>::iterator i = m_players.begin();
450 i != m_players.end(); ++i)
453 if(saved_players.find(player) != saved_players.end())
455 /*infostream<<"Player "<<player->getName()
456 <<" was already saved."<<std::endl;*/
459 std::string playername = player->getName();
460 // Don't save unnamed player
463 //infostream<<"Not saving unnamed player."<<std::endl;
469 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
470 playername = "player";
471 std::string path = players_path + "/" + playername;
473 for(u32 i=0; i<1000; i++)
475 if(fs::PathExists(path) == false)
480 path = players_path + "/" + playername + itos(i);
484 infostream<<"Didn't find free file for player"<<std::endl;
489 /*infostream<<"Saving player "<<player->getName()<<" to "
491 // Open file and serialize
492 std::ostringstream ss(std::ios_base::binary);
493 player->serialize(ss);
494 if(!fs::safeWriteToFile(path, ss.str()))
496 infostream<<"Failed to write "<<path<<std::endl;
499 saved_players.insert(player);
503 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
506 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
508 std::string players_path = savedir + "/players";
510 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
511 for(u32 i=0; i<player_files.size(); i++)
513 if(player_files[i].dir)
516 // Full path to this file
517 std::string path = players_path + "/" + player_files[i].name;
519 //infostream<<"Checking player file "<<path<<std::endl;
521 // Load player to see what is its name
522 RemotePlayer testplayer(m_gamedef);
524 // Open file and deserialize
525 std::ifstream is(path.c_str(), std::ios_base::binary);
526 if(is.good() == false)
528 infostream<<"Failed to read "<<path<<std::endl;
531 testplayer.deSerialize(is, player_files[i].name);
534 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
536 infostream<<"Not loading player with invalid name: "
537 <<testplayer.getName()<<std::endl;
540 /*infostream<<"Loaded test player with name "<<testplayer.getName()
543 // Search for the player
544 std::string playername = testplayer.getName();
545 Player *player = getPlayer(playername.c_str());
546 bool newplayer = false;
549 //infostream<<"Is a new player"<<std::endl;
550 player = new RemotePlayer(m_gamedef);
556 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
558 // Open file and deserialize
559 std::ifstream is(path.c_str(), std::ios_base::binary);
560 if(is.good() == false)
562 infostream<<"Failed to read "<<path<<std::endl;
565 player->deSerialize(is, player_files[i].name);
575 void ServerEnvironment::saveMeta(const std::string &savedir)
577 std::string path = savedir + "/env_meta.txt";
579 // Open file and serialize
580 std::ostringstream ss(std::ios_base::binary);
583 args.setU64("game_time", m_game_time);
584 args.setU64("time_of_day", getTimeOfDay());
588 if(!fs::safeWriteToFile(path, ss.str()))
590 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
592 throw SerializationError("Couldn't save env meta");
596 void ServerEnvironment::loadMeta(const std::string &savedir)
598 std::string path = savedir + "/env_meta.txt";
600 // Open file and deserialize
601 std::ifstream is(path.c_str(), std::ios_base::binary);
602 if(is.good() == false)
604 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
606 throw SerializationError("Couldn't load env meta");
614 throw SerializationError
615 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
617 std::getline(is, line);
618 std::string trimmedline = trim(line);
619 if(trimmedline == "EnvArgsEnd")
621 args.parseConfigLine(line);
625 m_game_time = args.getU64("game_time");
626 }catch(SettingNotFoundException &e){
627 // Getting this is crucial, otherwise timestamps are useless
628 throw SerializationError("Couldn't load env meta game_time");
632 m_time_of_day = args.getU64("time_of_day");
633 }catch(SettingNotFoundException &e){
634 // This is not as important
635 m_time_of_day = 9000;
641 ActiveBlockModifier *abm;
643 std::set<content_t> required_neighbors;
649 ServerEnvironment *m_env;
650 std::map<content_t, std::list<ActiveABM> > m_aabms;
652 ABMHandler(std::list<ABMWithState> &abms,
653 float dtime_s, ServerEnvironment *env,
659 INodeDefManager *ndef = env->getGameDef()->ndef();
660 for(std::list<ABMWithState>::iterator
661 i = abms.begin(); i != abms.end(); ++i){
662 ActiveBlockModifier *abm = i->abm;
663 float trigger_interval = abm->getTriggerInterval();
664 if(trigger_interval < 0.001)
665 trigger_interval = 0.001;
666 float actual_interval = dtime_s;
669 if(i->timer < trigger_interval)
671 i->timer -= trigger_interval;
672 actual_interval = trigger_interval;
674 float intervals = actual_interval / trigger_interval;
677 float chance = abm->getTriggerChance();
682 aabm.chance = chance / intervals;
686 std::set<std::string> required_neighbors_s
687 = abm->getRequiredNeighbors();
688 for(std::set<std::string>::iterator
689 i = required_neighbors_s.begin();
690 i != required_neighbors_s.end(); i++)
692 ndef->getIds(*i, aabm.required_neighbors);
695 std::set<std::string> contents_s = abm->getTriggerContents();
696 for(std::set<std::string>::iterator
697 i = contents_s.begin(); i != contents_s.end(); i++)
699 std::set<content_t> ids;
700 ndef->getIds(*i, ids);
701 for(std::set<content_t>::const_iterator k = ids.begin();
705 std::map<content_t, std::list<ActiveABM> >::iterator j;
707 if(j == m_aabms.end()){
708 std::list<ActiveABM> aabmlist;
709 m_aabms[c] = aabmlist;
712 j->second.push_back(aabm);
717 void apply(MapBlock *block)
722 ServerMap *map = &m_env->getServerMap();
725 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
726 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
727 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
729 MapNode n = block->getNodeNoEx(p0);
730 content_t c = n.getContent();
731 v3s16 p = p0 + block->getPosRelative();
733 std::map<content_t, std::list<ActiveABM> >::iterator j;
735 if(j == m_aabms.end())
738 for(std::list<ActiveABM>::iterator
739 i = j->second.begin(); i != j->second.end(); i++)
741 if(myrand() % i->chance != 0)
745 if(!i->required_neighbors.empty())
748 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
749 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
750 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
754 MapNode n = map->getNodeNoEx(p1);
755 content_t c = n.getContent();
756 std::set<content_t>::const_iterator k;
757 k = i->required_neighbors.find(c);
758 if(k != i->required_neighbors.end()){
762 // No required neighbor found
767 // Find out how many objects the block contains
768 u32 active_object_count = block->m_static_objects.m_active.size();
769 // Find out how many objects this and all the neighbors contain
770 u32 active_object_count_wider = 0;
771 u32 wider_unknown_count = 0;
772 for(s16 x=-1; x<=1; x++)
773 for(s16 y=-1; y<=1; y++)
774 for(s16 z=-1; z<=1; z++)
776 MapBlock *block2 = map->getBlockNoCreateNoEx(
777 block->getPos() + v3s16(x,y,z));
779 wider_unknown_count = 0;
782 active_object_count_wider +=
783 block2->m_static_objects.m_active.size()
784 + block2->m_static_objects.m_stored.size();
787 u32 wider_known_count = 3*3*3 - wider_unknown_count;
788 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
790 // Call all the trigger variations
791 i->abm->trigger(m_env, p, n);
792 i->abm->trigger(m_env, p, n,
793 active_object_count, active_object_count_wider);
799 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
801 // Get time difference
803 u32 stamp = block->getTimestamp();
804 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
805 dtime_s = m_game_time - block->getTimestamp();
806 dtime_s += additional_dtime;
808 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
809 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
811 // Set current time as timestamp
812 block->setTimestampNoChangedFlag(m_game_time);
814 /*infostream<<"ServerEnvironment::activateBlock(): block is "
815 <<dtime_s<<" seconds old."<<std::endl;*/
817 // Activate stored objects
818 activateObjects(block, dtime_s);
821 std::map<v3s16, NodeTimer> elapsed_timers =
822 block->m_node_timers.step((float)dtime_s);
823 if(!elapsed_timers.empty()){
825 for(std::map<v3s16, NodeTimer>::iterator
826 i = elapsed_timers.begin();
827 i != elapsed_timers.end(); i++){
828 n = block->getNodeNoEx(i->first);
829 v3s16 p = i->first + block->getPosRelative();
830 if(m_script->node_on_timer(p,n,i->second.elapsed))
831 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
835 /* Handle ActiveBlockModifiers */
836 ABMHandler abmhandler(m_abms, dtime_s, this, false);
837 abmhandler.apply(block);
840 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
842 m_abms.push_back(ABMWithState(abm));
845 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
847 INodeDefManager *ndef = m_gamedef->ndef();
848 MapNode n_old = m_map->getNodeNoEx(p);
850 if(ndef->get(n_old).has_on_destruct)
851 m_script->node_on_destruct(p, n_old);
853 bool succeeded = m_map->addNodeWithEvent(p, n);
856 // Call post-destructor
857 if(ndef->get(n_old).has_after_destruct)
858 m_script->node_after_destruct(p, n_old);
860 if(ndef->get(n).has_on_construct)
861 m_script->node_on_construct(p, n);
865 bool ServerEnvironment::removeNode(v3s16 p)
867 INodeDefManager *ndef = m_gamedef->ndef();
868 MapNode n_old = m_map->getNodeNoEx(p);
870 if(ndef->get(n_old).has_on_destruct)
871 m_script->node_on_destruct(p, n_old);
873 // This is slightly optimized compared to addNodeWithEvent(air)
874 bool succeeded = m_map->removeNodeWithEvent(p);
877 // Call post-destructor
878 if(ndef->get(n_old).has_after_destruct)
879 m_script->node_after_destruct(p, n_old);
880 // Air doesn't require constructor
884 bool ServerEnvironment::swapNode(v3s16 p, const MapNode &n)
886 return m_map->addNodeWithEvent(p, n, false);
889 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
891 std::set<u16> objects;
892 for(std::map<u16, ServerActiveObject*>::iterator
893 i = m_active_objects.begin();
894 i != m_active_objects.end(); ++i)
896 ServerActiveObject* obj = i->second;
898 v3f objectpos = obj->getBasePosition();
899 if(objectpos.getDistanceFrom(pos) > radius)
906 void ServerEnvironment::clearAllObjects()
908 infostream<<"ServerEnvironment::clearAllObjects(): "
909 <<"Removing all active objects"<<std::endl;
910 std::list<u16> objects_to_remove;
911 for(std::map<u16, ServerActiveObject*>::iterator
912 i = m_active_objects.begin();
913 i != m_active_objects.end(); ++i)
915 ServerActiveObject* obj = i->second;
916 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
919 // Delete static object if block is loaded
920 if(obj->m_static_exists){
921 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
923 block->m_static_objects.remove(id);
924 block->raiseModified(MOD_STATE_WRITE_NEEDED,
926 obj->m_static_exists = false;
929 // If known by some client, don't delete immediately
930 if(obj->m_known_by_count > 0){
931 obj->m_pending_deactivation = true;
932 obj->m_removed = true;
936 // Tell the object about removal
937 obj->removingFromEnvironment();
938 // Deregister in scripting api
939 m_script->removeObjectReference(obj);
941 // Delete active object
942 if(obj->environmentDeletes())
944 // Id to be removed from m_active_objects
945 objects_to_remove.push_back(id);
947 // Remove references from m_active_objects
948 for(std::list<u16>::iterator i = objects_to_remove.begin();
949 i != objects_to_remove.end(); ++i)
951 m_active_objects.erase(*i);
954 // Get list of loaded blocks
955 std::list<v3s16> loaded_blocks;
956 infostream<<"ServerEnvironment::clearAllObjects(): "
957 <<"Listing all loaded blocks"<<std::endl;
958 m_map->listAllLoadedBlocks(loaded_blocks);
959 infostream<<"ServerEnvironment::clearAllObjects(): "
960 <<"Done listing all loaded blocks: "
961 <<loaded_blocks.size()<<std::endl;
963 // Get list of loadable blocks
964 std::list<v3s16> loadable_blocks;
965 infostream<<"ServerEnvironment::clearAllObjects(): "
966 <<"Listing all loadable blocks"<<std::endl;
967 m_map->listAllLoadableBlocks(loadable_blocks);
968 infostream<<"ServerEnvironment::clearAllObjects(): "
969 <<"Done listing all loadable blocks: "
970 <<loadable_blocks.size()
971 <<", now clearing"<<std::endl;
973 // Grab a reference on each loaded block to avoid unloading it
974 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
975 i != loaded_blocks.end(); ++i)
978 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
983 // Remove objects in all loadable blocks
984 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
985 unload_interval = MYMAX(unload_interval, 1);
986 u32 report_interval = loadable_blocks.size() / 10;
987 u32 num_blocks_checked = 0;
988 u32 num_blocks_cleared = 0;
989 u32 num_objs_cleared = 0;
990 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
991 i != loadable_blocks.end(); ++i)
994 MapBlock *block = m_map->emergeBlock(p, false);
996 errorstream<<"ServerEnvironment::clearAllObjects(): "
997 <<"Failed to emerge block "<<PP(p)<<std::endl;
1000 u32 num_stored = block->m_static_objects.m_stored.size();
1001 u32 num_active = block->m_static_objects.m_active.size();
1002 if(num_stored != 0 || num_active != 0){
1003 block->m_static_objects.m_stored.clear();
1004 block->m_static_objects.m_active.clear();
1005 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1007 num_objs_cleared += num_stored + num_active;
1008 num_blocks_cleared++;
1010 num_blocks_checked++;
1012 if(report_interval != 0 &&
1013 num_blocks_checked % report_interval == 0){
1014 float percent = 100.0 * (float)num_blocks_checked /
1015 loadable_blocks.size();
1016 infostream<<"ServerEnvironment::clearAllObjects(): "
1017 <<"Cleared "<<num_objs_cleared<<" objects"
1018 <<" in "<<num_blocks_cleared<<" blocks ("
1019 <<percent<<"%)"<<std::endl;
1021 if(num_blocks_checked % unload_interval == 0){
1022 m_map->unloadUnreferencedBlocks();
1025 m_map->unloadUnreferencedBlocks();
1027 // Drop references that were added above
1028 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1029 i != loaded_blocks.end(); ++i)
1032 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1037 infostream<<"ServerEnvironment::clearAllObjects(): "
1038 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1039 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1042 void ServerEnvironment::step(float dtime)
1044 DSTACK(__FUNCTION_NAME);
1046 //TimeTaker timer("ServerEnv step");
1048 /* Step time of day */
1049 stepTimeOfDay(dtime);
1052 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1053 // really matter that much.
1054 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1060 m_game_time_fraction_counter += dtime;
1061 u32 inc_i = (u32)m_game_time_fraction_counter;
1062 m_game_time += inc_i;
1063 m_game_time_fraction_counter -= (float)inc_i;
1070 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1071 for(std::list<Player*>::iterator i = m_players.begin();
1072 i != m_players.end(); ++i)
1074 Player *player = *i;
1076 // Ignore disconnected players
1077 if(player->peer_id == 0)
1081 player->move(dtime, *m_map, 100*BS);
1086 Manage active block list
1088 if(m_active_blocks_management_interval.step(dtime, 2.0))
1090 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1092 Get player block positions
1094 std::list<v3s16> players_blockpos;
1095 for(std::list<Player*>::iterator
1096 i = m_players.begin();
1097 i != m_players.end(); ++i)
1099 Player *player = *i;
1100 // Ignore disconnected players
1101 if(player->peer_id == 0)
1103 v3s16 blockpos = getNodeBlockPos(
1104 floatToInt(player->getPosition(), BS));
1105 players_blockpos.push_back(blockpos);
1109 Update list of active blocks, collecting changes
1111 const s16 active_block_range = g_settings->getS16("active_block_range");
1112 std::set<v3s16> blocks_removed;
1113 std::set<v3s16> blocks_added;
1114 m_active_blocks.update(players_blockpos, active_block_range,
1115 blocks_removed, blocks_added);
1118 Handle removed blocks
1121 // Convert active objects that are no more in active blocks to static
1122 deactivateFarObjects(false);
1124 for(std::set<v3s16>::iterator
1125 i = blocks_removed.begin();
1126 i != blocks_removed.end(); ++i)
1130 /* infostream<<"Server: Block " << PP(p)
1131 << " became inactive"<<std::endl; */
1133 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1137 // Set current time as timestamp (and let it set ChangedFlag)
1138 block->setTimestamp(m_game_time);
1145 for(std::set<v3s16>::iterator
1146 i = blocks_added.begin();
1147 i != blocks_added.end(); ++i)
1151 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1153 // Block needs to be fetched first
1154 m_emerger->enqueueBlockEmerge(
1155 PEER_ID_INEXISTENT, p, false);
1156 m_active_blocks.m_list.erase(p);
1160 activateBlock(block);
1161 /* infostream<<"Server: Block " << PP(p)
1162 << " became active"<<std::endl; */
1167 Mess around in active blocks
1169 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1171 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1175 for(std::set<v3s16>::iterator
1176 i = m_active_blocks.m_list.begin();
1177 i != m_active_blocks.m_list.end(); ++i)
1181 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1182 <<") being handled"<<std::endl;*/
1184 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1188 // Reset block usage timer
1189 block->resetUsageTimer();
1191 // Set current time as timestamp
1192 block->setTimestampNoChangedFlag(m_game_time);
1193 // If time has changed much from the one on disk,
1194 // set block to be saved when it is unloaded
1195 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1196 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1197 "Timestamp older than 60s (step)");
1200 std::map<v3s16, NodeTimer> elapsed_timers =
1201 block->m_node_timers.step((float)dtime);
1202 if(!elapsed_timers.empty()){
1204 for(std::map<v3s16, NodeTimer>::iterator
1205 i = elapsed_timers.begin();
1206 i != elapsed_timers.end(); i++){
1207 n = block->getNodeNoEx(i->first);
1208 p = i->first + block->getPosRelative();
1209 if(m_script->node_on_timer(p,n,i->second.elapsed))
1210 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1216 const float abm_interval = 1.0;
1217 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1219 if(m_active_block_interval_overload_skip > 0){
1220 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1221 m_active_block_interval_overload_skip--;
1224 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1225 TimeTaker timer("modify in active blocks");
1227 // Initialize handling of ActiveBlockModifiers
1228 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1230 for(std::set<v3s16>::iterator
1231 i = m_active_blocks.m_list.begin();
1232 i != m_active_blocks.m_list.end(); ++i)
1236 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1237 <<") being handled"<<std::endl;*/
1239 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1243 // Set current time as timestamp
1244 block->setTimestampNoChangedFlag(m_game_time);
1246 /* Handle ActiveBlockModifiers */
1247 abmhandler.apply(block);
1250 u32 time_ms = timer.stop(true);
1251 u32 max_time_ms = 200;
1252 if(time_ms > max_time_ms){
1253 infostream<<"WARNING: active block modifiers took "
1254 <<time_ms<<"ms (longer than "
1255 <<max_time_ms<<"ms)"<<std::endl;
1256 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1261 Step script environment (run global on_step())
1263 m_script->environment_Step(dtime);
1269 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1270 //TimeTaker timer("Step active objects");
1272 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1274 // This helps the objects to send data at the same time
1275 bool send_recommended = false;
1276 m_send_recommended_timer += dtime;
1277 if(m_send_recommended_timer > getSendRecommendedInterval())
1279 m_send_recommended_timer -= getSendRecommendedInterval();
1280 send_recommended = true;
1283 for(std::map<u16, ServerActiveObject*>::iterator
1284 i = m_active_objects.begin();
1285 i != m_active_objects.end(); ++i)
1287 ServerActiveObject* obj = i->second;
1288 // Remove non-peaceful mobs on peaceful mode
1289 if(g_settings->getBool("only_peaceful_mobs")){
1290 if(!obj->isPeaceful())
1291 obj->m_removed = true;
1293 // Don't step if is to be removed or stored statically
1294 if(obj->m_removed || obj->m_pending_deactivation)
1297 obj->step(dtime, send_recommended);
1298 // Read messages from object
1299 while(!obj->m_messages_out.empty())
1301 m_active_object_messages.push_back(
1302 obj->m_messages_out.pop_front());
1308 Manage active objects
1310 if(m_object_management_interval.step(dtime, 0.5))
1312 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1314 Remove objects that satisfy (m_removed && m_known_by_count==0)
1316 removeRemovedObjects();
1320 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1322 std::map<u16, ServerActiveObject*>::iterator n;
1323 n = m_active_objects.find(id);
1324 if(n == m_active_objects.end())
1329 bool isFreeServerActiveObjectId(u16 id,
1330 std::map<u16, ServerActiveObject*> &objects)
1335 return objects.find(id) == objects.end();
1338 u16 getFreeServerActiveObjectId(
1339 std::map<u16, ServerActiveObject*> &objects)
1341 //try to reuse id's as late as possible
1342 static u16 last_used_id = 0;
1343 u16 startid = last_used_id;
1347 if(isFreeServerActiveObjectId(last_used_id, objects))
1348 return last_used_id;
1350 if(last_used_id == startid)
1355 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1358 u16 id = addActiveObjectRaw(object, true, 0);
1363 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1367 v3f objectpos = obj->getBasePosition();
1369 // The block in which the object resides in
1370 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1373 Update the static data
1376 // Create new static object
1377 std::string staticdata = obj->getStaticData();
1378 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1379 // Add to the block where the object is located in
1380 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1381 // Get or generate the block
1382 MapBlock *block = m_map->emergeBlock(blockpos);
1384 bool succeeded = false;
1388 block->m_static_objects.insert(0, s_obj);
1389 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1390 "addActiveObjectAsStatic");
1394 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1395 <<"Could not find or generate "
1396 <<"a block for storing static object"<<std::endl;
1400 if(obj->environmentDeletes())
1408 Finds out what new objects have been added to
1409 inside a radius around a position
1411 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1412 std::set<u16> ¤t_objects,
1413 std::set<u16> &added_objects)
1415 v3f pos_f = intToFloat(pos, BS);
1416 f32 radius_f = radius * BS;
1418 Go through the object list,
1419 - discard m_removed objects,
1420 - discard objects that are too far away,
1421 - discard objects that are found in current_objects.
1422 - add remaining objects to added_objects
1424 for(std::map<u16, ServerActiveObject*>::iterator
1425 i = m_active_objects.begin();
1426 i != m_active_objects.end(); ++i)
1430 ServerActiveObject *object = i->second;
1433 // Discard if removed or deactivating
1434 if(object->m_removed || object->m_pending_deactivation)
1436 if(object->unlimitedTransferDistance() == false){
1437 // Discard if too far
1438 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1439 if(distance_f > radius_f)
1442 // Discard if already on current_objects
1443 std::set<u16>::iterator n;
1444 n = current_objects.find(id);
1445 if(n != current_objects.end())
1447 // Add to added_objects
1448 added_objects.insert(id);
1453 Finds out what objects have been removed from
1454 inside a radius around a position
1456 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1457 std::set<u16> ¤t_objects,
1458 std::set<u16> &removed_objects)
1460 v3f pos_f = intToFloat(pos, BS);
1461 f32 radius_f = radius * BS;
1463 Go through current_objects; object is removed if:
1464 - object is not found in m_active_objects (this is actually an
1465 error condition; objects should be set m_removed=true and removed
1466 only after all clients have been informed about removal), or
1467 - object has m_removed=true, or
1468 - object is too far away
1470 for(std::set<u16>::iterator
1471 i = current_objects.begin();
1472 i != current_objects.end(); ++i)
1475 ServerActiveObject *object = getActiveObject(id);
1478 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1479 <<" object in current_objects is NULL"<<std::endl;
1480 removed_objects.insert(id);
1484 if(object->m_removed || object->m_pending_deactivation)
1486 removed_objects.insert(id);
1490 // If transfer distance is unlimited, don't remove
1491 if(object->unlimitedTransferDistance())
1494 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1496 if(distance_f >= radius_f)
1498 removed_objects.insert(id);
1506 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1508 if(m_active_object_messages.empty())
1509 return ActiveObjectMessage(0);
1511 ActiveObjectMessage message = m_active_object_messages.front();
1512 m_active_object_messages.pop_front();
1517 ************ Private methods *************
1520 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1521 bool set_changed, u32 dtime_s)
1524 if(object->getId() == 0){
1525 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1528 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1529 <<"no free ids available"<<std::endl;
1530 if(object->environmentDeletes())
1534 object->setId(new_id);
1537 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1538 <<"supplied with id "<<object->getId()<<std::endl;
1540 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1542 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1543 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1544 if(object->environmentDeletes())
1548 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1549 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1551 m_active_objects[object->getId()] = object;
1553 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1554 <<"Added id="<<object->getId()<<"; there are now "
1555 <<m_active_objects.size()<<" active objects."
1558 // Register reference in scripting api (must be done before post-init)
1559 m_script->addObjectReference(object);
1560 // Post-initialize object
1561 object->addedToEnvironment(dtime_s);
1563 // Add static data to block
1564 if(object->isStaticAllowed())
1566 // Add static object to active static list of the block
1567 v3f objectpos = object->getBasePosition();
1568 std::string staticdata = object->getStaticData();
1569 StaticObject s_obj(object->getType(), objectpos, staticdata);
1570 // Add to the block where the object is located in
1571 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1572 MapBlock *block = m_map->emergeBlock(blockpos);
1574 block->m_static_objects.m_active[object->getId()] = s_obj;
1575 object->m_static_exists = true;
1576 object->m_static_block = blockpos;
1579 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1580 "addActiveObjectRaw");
1582 v3s16 p = floatToInt(objectpos, BS);
1583 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1584 <<"could not emerge block for storing id="<<object->getId()
1585 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1589 return object->getId();
1593 Remove objects that satisfy (m_removed && m_known_by_count==0)
1595 void ServerEnvironment::removeRemovedObjects()
1597 std::list<u16> objects_to_remove;
1598 for(std::map<u16, ServerActiveObject*>::iterator
1599 i = m_active_objects.begin();
1600 i != m_active_objects.end(); ++i)
1603 ServerActiveObject* obj = i->second;
1604 // This shouldn't happen but check it
1607 infostream<<"NULL object found in ServerEnvironment"
1608 <<" while finding removed objects. id="<<id<<std::endl;
1609 // Id to be removed from m_active_objects
1610 objects_to_remove.push_back(id);
1615 We will delete objects that are marked as removed or thatare
1616 waiting for deletion after deactivation
1618 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1622 Delete static data from block if is marked as removed
1624 if(obj->m_static_exists && obj->m_removed)
1626 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1628 block->m_static_objects.remove(id);
1629 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1630 "removeRemovedObjects/remove");
1631 obj->m_static_exists = false;
1633 infostream<<"Failed to emerge block from which an object to "
1634 <<"be removed was loaded from. id="<<id<<std::endl;
1638 // If m_known_by_count > 0, don't actually remove. On some future
1639 // invocation this will be 0, which is when removal will continue.
1640 if(obj->m_known_by_count > 0)
1644 Move static data from active to stored if not marked as removed
1646 if(obj->m_static_exists && !obj->m_removed){
1647 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1649 std::map<u16, StaticObject>::iterator i =
1650 block->m_static_objects.m_active.find(id);
1651 if(i != block->m_static_objects.m_active.end()){
1652 block->m_static_objects.m_stored.push_back(i->second);
1653 block->m_static_objects.m_active.erase(id);
1654 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1655 "removeRemovedObjects/deactivate");
1658 infostream<<"Failed to emerge block from which an object to "
1659 <<"be deactivated was loaded from. id="<<id<<std::endl;
1663 // Tell the object about removal
1664 obj->removingFromEnvironment();
1665 // Deregister in scripting api
1666 m_script->removeObjectReference(obj);
1669 if(obj->environmentDeletes())
1671 // Id to be removed from m_active_objects
1672 objects_to_remove.push_back(id);
1674 // Remove references from m_active_objects
1675 for(std::list<u16>::iterator i = objects_to_remove.begin();
1676 i != objects_to_remove.end(); ++i)
1678 m_active_objects.erase(*i);
1682 static void print_hexdump(std::ostream &o, const std::string &data)
1684 const int linelength = 16;
1685 for(int l=0; ; l++){
1686 int i0 = linelength * l;
1687 bool at_end = false;
1688 int thislinelength = linelength;
1689 if(i0 + thislinelength > (int)data.size()){
1690 thislinelength = data.size() - i0;
1693 for(int di=0; di<linelength; di++){
1696 if(di<thislinelength)
1697 snprintf(buf, 4, "%.2x ", data[i]);
1699 snprintf(buf, 4, " ");
1703 for(int di=0; di<thislinelength; di++){
1717 Convert stored objects from blocks near the players to active.
1719 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1723 // Ignore if no stored objects (to not set changed flag)
1724 if(block->m_static_objects.m_stored.size() == 0)
1726 verbosestream<<"ServerEnvironment::activateObjects(): "
1727 <<"activating objects of block "<<PP(block->getPos())
1728 <<" ("<<block->m_static_objects.m_stored.size()
1729 <<" objects)"<<std::endl;
1730 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1732 errorstream<<"suspiciously large amount of objects detected: "
1733 <<block->m_static_objects.m_stored.size()<<" in "
1734 <<PP(block->getPos())
1735 <<"; removing all of them."<<std::endl;
1736 // Clear stored list
1737 block->m_static_objects.m_stored.clear();
1738 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1739 "stored list cleared in activateObjects due to "
1740 "large amount of objects");
1744 // Activate stored objects
1745 std::list<StaticObject> new_stored;
1746 for(std::list<StaticObject>::iterator
1747 i = block->m_static_objects.m_stored.begin();
1748 i != block->m_static_objects.m_stored.end(); ++i)
1750 /*infostream<<"Server: Creating an active object from "
1751 <<"static data"<<std::endl;*/
1752 StaticObject &s_obj = *i;
1753 // Create an active object from the data
1754 ServerActiveObject *obj = ServerActiveObject::create
1755 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1756 // If couldn't create object, store static data back.
1759 errorstream<<"ServerEnvironment::activateObjects(): "
1760 <<"failed to create active object from static object "
1761 <<"in block "<<PP(s_obj.pos/BS)
1762 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1763 print_hexdump(verbosestream, s_obj.data);
1765 new_stored.push_back(s_obj);
1768 verbosestream<<"ServerEnvironment::activateObjects(): "
1769 <<"activated static object pos="<<PP(s_obj.pos/BS)
1770 <<" type="<<(int)s_obj.type<<std::endl;
1771 // This will also add the object to the active static list
1772 addActiveObjectRaw(obj, false, dtime_s);
1774 // Clear stored list
1775 block->m_static_objects.m_stored.clear();
1776 // Add leftover failed stuff to stored list
1777 for(std::list<StaticObject>::iterator
1778 i = new_stored.begin();
1779 i != new_stored.end(); ++i)
1781 StaticObject &s_obj = *i;
1782 block->m_static_objects.m_stored.push_back(s_obj);
1785 // Turn the active counterparts of activated objects not pending for
1787 for(std::map<u16, StaticObject>::iterator
1788 i = block->m_static_objects.m_active.begin();
1789 i != block->m_static_objects.m_active.end(); ++i)
1792 ServerActiveObject *object = getActiveObject(id);
1794 object->m_pending_deactivation = false;
1798 Note: Block hasn't really been modified here.
1799 The objects have just been activated and moved from the stored
1800 static list to the active static list.
1801 As such, the block is essentially the same.
1802 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1803 Otherwise there would be a huge amount of unnecessary I/O.
1808 Convert objects that are not standing inside active blocks to static.
1810 If m_known_by_count != 0, active object is not deleted, but static
1811 data is still updated.
1813 If force_delete is set, active object is deleted nevertheless. It
1814 shall only be set so in the destructor of the environment.
1816 If block wasn't generated (not in memory or on disk),
1818 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1820 std::list<u16> objects_to_remove;
1821 for(std::map<u16, ServerActiveObject*>::iterator
1822 i = m_active_objects.begin();
1823 i != m_active_objects.end(); ++i)
1825 ServerActiveObject* obj = i->second;
1828 // Do not deactivate if static data creation not allowed
1829 if(!force_delete && !obj->isStaticAllowed())
1832 // If pending deactivation, let removeRemovedObjects() do it
1833 if(!force_delete && obj->m_pending_deactivation)
1837 v3f objectpos = obj->getBasePosition();
1839 // The block in which the object resides in
1840 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1842 // If object's static data is stored in a deactivated block and object
1843 // is actually located in an active block, re-save to the block in
1844 // which the object is actually located in.
1846 obj->m_static_exists &&
1847 !m_active_blocks.contains(obj->m_static_block) &&
1848 m_active_blocks.contains(blockpos_o))
1850 v3s16 old_static_block = obj->m_static_block;
1852 // Save to block where object is located
1853 MapBlock *block = m_map->emergeBlock(blockpos_o, false);
1855 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1856 <<"Could not save object id="<<id
1857 <<" to it's current block "<<PP(blockpos_o)
1861 std::string staticdata_new = obj->getStaticData();
1862 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1863 block->m_static_objects.insert(id, s_obj);
1864 obj->m_static_block = blockpos_o;
1865 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1866 "deactivateFarObjects: Static data moved in");
1868 // Delete from block where object was located
1869 block = m_map->emergeBlock(old_static_block, false);
1871 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1872 <<"Could not delete object id="<<id
1873 <<" from it's previous block "<<PP(old_static_block)
1877 block->m_static_objects.remove(id);
1878 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1879 "deactivateFarObjects: Static data moved out");
1883 // If block is active, don't remove
1884 if(!force_delete && m_active_blocks.contains(blockpos_o))
1887 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1888 <<"deactivating object id="<<id<<" on inactive block "
1889 <<PP(blockpos_o)<<std::endl;
1891 // If known by some client, don't immediately delete.
1892 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1895 Update the static data
1898 if(obj->isStaticAllowed())
1900 // Create new static object
1901 std::string staticdata_new = obj->getStaticData();
1902 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1904 bool stays_in_same_block = false;
1905 bool data_changed = true;
1907 if(obj->m_static_exists){
1908 if(obj->m_static_block == blockpos_o)
1909 stays_in_same_block = true;
1911 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1913 std::map<u16, StaticObject>::iterator n =
1914 block->m_static_objects.m_active.find(id);
1915 if(n != block->m_static_objects.m_active.end()){
1916 StaticObject static_old = n->second;
1918 float save_movem = obj->getMinimumSavedMovement();
1920 if(static_old.data == staticdata_new &&
1921 (static_old.pos - objectpos).getLength() < save_movem)
1922 data_changed = false;
1924 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1925 <<"id="<<id<<" m_static_exists=true but "
1926 <<"static data doesn't actually exist in "
1927 <<PP(obj->m_static_block)<<std::endl;
1931 bool shall_be_written = (!stays_in_same_block || data_changed);
1933 // Delete old static object
1934 if(obj->m_static_exists)
1936 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1939 block->m_static_objects.remove(id);
1940 obj->m_static_exists = false;
1941 // Only mark block as modified if data changed considerably
1942 if(shall_be_written)
1943 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1944 "deactivateFarObjects: Static data "
1945 "changed considerably");
1949 // Add to the block where the object is located in
1950 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1951 // Get or generate the block
1952 MapBlock *block = NULL;
1954 block = m_map->emergeBlock(blockpos);
1955 } catch(InvalidPositionException &e){
1956 // Handled via NULL pointer
1957 // NOTE: emergeBlock's failure is usually determined by it
1958 // actually returning NULL
1963 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1964 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1965 <<" statically but block "<<PP(blockpos)
1966 <<" already contains "
1967 <<block->m_static_objects.m_stored.size()
1969 <<" Forcing delete."<<std::endl;
1970 force_delete = true;
1972 // If static counterpart already exists in target block,
1974 // This shouldn't happen because the object is removed from
1975 // the previous block before this according to
1976 // obj->m_static_block, but happens rarely for some unknown
1977 // reason. Unsuccessful attempts have been made to find
1979 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1980 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1982 block->m_static_objects.remove(id);
1984 // Store static data
1985 u16 store_id = pending_delete ? id : 0;
1986 block->m_static_objects.insert(store_id, s_obj);
1988 // Only mark block as modified if data changed considerably
1989 if(shall_be_written)
1990 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1991 "deactivateFarObjects: Static data "
1992 "changed considerably");
1994 obj->m_static_exists = true;
1995 obj->m_static_block = block->getPos();
2000 v3s16 p = floatToInt(objectpos, BS);
2001 errorstream<<"ServerEnv: Could not find or generate "
2002 <<"a block for storing id="<<obj->getId()
2003 <<" statically (pos="<<PP(p)<<")"<<std::endl;
2010 If known by some client, set pending deactivation.
2011 Otherwise delete it immediately.
2014 if(pending_delete && !force_delete)
2016 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2017 <<"object id="<<id<<" is known by clients"
2018 <<"; not deleting yet"<<std::endl;
2020 obj->m_pending_deactivation = true;
2024 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
2025 <<"object id="<<id<<" is not known by clients"
2026 <<"; deleting"<<std::endl;
2028 // Tell the object about removal
2029 obj->removingFromEnvironment();
2030 // Deregister in scripting api
2031 m_script->removeObjectReference(obj);
2033 // Delete active object
2034 if(obj->environmentDeletes())
2036 // Id to be removed from m_active_objects
2037 objects_to_remove.push_back(id);
2040 // Remove references from m_active_objects
2041 for(std::list<u16>::iterator i = objects_to_remove.begin();
2042 i != objects_to_remove.end(); ++i)
2044 m_active_objects.erase(*i);
2051 #include "clientsimpleobject.h"
2057 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
2058 ITextureSource *texturesource, IGameDef *gamedef,
2059 IrrlichtDevice *irr):
2062 m_texturesource(texturesource),
2068 ClientEnvironment::~ClientEnvironment()
2070 // delete active objects
2071 for(std::map<u16, ClientActiveObject*>::iterator
2072 i = m_active_objects.begin();
2073 i != m_active_objects.end(); ++i)
2078 for(std::list<ClientSimpleObject*>::iterator
2079 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2088 Map & ClientEnvironment::getMap()
2093 ClientMap & ClientEnvironment::getClientMap()
2098 void ClientEnvironment::addPlayer(Player *player)
2100 DSTACK(__FUNCTION_NAME);
2102 It is a failure if player is local and there already is a local
2105 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2107 Environment::addPlayer(player);
2110 LocalPlayer * ClientEnvironment::getLocalPlayer()
2112 for(std::list<Player*>::iterator i = m_players.begin();
2113 i != m_players.end(); ++i)
2115 Player *player = *i;
2116 if(player->isLocal())
2117 return (LocalPlayer*)player;
2122 void ClientEnvironment::step(float dtime)
2124 DSTACK(__FUNCTION_NAME);
2126 /* Step time of day */
2127 stepTimeOfDay(dtime);
2129 // Get some settings
2130 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2131 bool free_move = fly_allowed && g_settings->getBool("free_move");
2134 LocalPlayer *lplayer = getLocalPlayer();
2136 // collision info queue
2137 std::list<CollisionInfo> player_collisions;
2140 Get the speed the player is going
2142 bool is_climbing = lplayer->is_climbing;
2144 f32 player_speed = lplayer->getSpeed().getLength();
2147 Maximum position increment
2149 //f32 position_max_increment = 0.05*BS;
2150 f32 position_max_increment = 0.1*BS;
2152 // Maximum time increment (for collision detection etc)
2153 // time = distance / speed
2154 f32 dtime_max_increment = 1;
2155 if(player_speed > 0.001)
2156 dtime_max_increment = position_max_increment / player_speed;
2158 // Maximum time increment is 10ms or lower
2159 if(dtime_max_increment > 0.01)
2160 dtime_max_increment = 0.01;
2162 // Don't allow overly huge dtime
2166 f32 dtime_downcount = dtime;
2169 Stuff that has a maximum time increment
2178 if(dtime_downcount > dtime_max_increment)
2180 dtime_part = dtime_max_increment;
2181 dtime_downcount -= dtime_part;
2185 dtime_part = dtime_downcount;
2187 Setting this to 0 (no -=dtime_part) disables an infinite loop
2188 when dtime_part is so small that dtime_downcount -= dtime_part
2191 dtime_downcount = 0;
2200 if(free_move == false && is_climbing == false)
2203 v3f speed = lplayer->getSpeed();
2204 if(lplayer->in_liquid == false)
2205 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2207 // Liquid floating / sinking
2208 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2209 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2211 // Liquid resistance
2212 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2214 // How much the node's viscosity blocks movement, ranges between 0 and 1
2215 // Should match the scale at which viscosity increase affects other liquid attributes
2216 const f32 viscosity_factor = 0.3;
2218 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2219 f32 dl = d_wanted.getLength();
2220 if(dl > lplayer->movement_liquid_fluidity_smooth)
2221 dl = lplayer->movement_liquid_fluidity_smooth;
2222 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2224 v3f d = d_wanted.normalize() * dl;
2228 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2229 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2230 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2231 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2232 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2233 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2237 lplayer->setSpeed(speed);
2242 This also does collision detection.
2244 lplayer->move(dtime_part, this, position_max_increment,
2245 &player_collisions);
2248 while(dtime_downcount > 0.001);
2250 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2252 for(std::list<CollisionInfo>::iterator
2253 i = player_collisions.begin();
2254 i != player_collisions.end(); ++i)
2256 CollisionInfo &info = *i;
2257 v3f speed_diff = info.new_speed - info.old_speed;;
2258 // Handle only fall damage
2259 // (because otherwise walking against something in fast_move kills you)
2260 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2262 // Get rid of other components
2265 f32 pre_factor = 1; // 1 hp per node/s
2266 f32 tolerance = BS*14; // 5 without damage
2267 f32 post_factor = 1; // 1 hp per node/s
2268 if(info.type == COLLISION_NODE)
2270 const ContentFeatures &f = m_gamedef->ndef()->
2271 get(m_map->getNodeNoEx(info.node_p));
2272 // Determine fall damage multiplier
2273 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2274 pre_factor = 1.0 + (float)addp/100.0;
2276 float speed = pre_factor * speed_diff.getLength();
2277 if(speed > tolerance)
2279 f32 damage_f = (speed - tolerance)/BS * post_factor;
2280 u16 damage = (u16)(damage_f+0.5);
2282 damageLocalPlayer(damage, true);
2283 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2284 m_gamedef->event()->put(e);
2290 A quick draft of lava damage
2292 if(m_lava_hurt_interval.step(dtime, 1.0))
2294 v3f pf = lplayer->getPosition();
2296 // Feet, middle and head
2297 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2298 MapNode n1 = m_map->getNodeNoEx(p1);
2299 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2300 MapNode n2 = m_map->getNodeNoEx(p2);
2301 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2302 MapNode n3 = m_map->getNodeNoEx(p3);
2304 u32 damage_per_second = 0;
2305 damage_per_second = MYMAX(damage_per_second,
2306 m_gamedef->ndef()->get(n1).damage_per_second);
2307 damage_per_second = MYMAX(damage_per_second,
2308 m_gamedef->ndef()->get(n2).damage_per_second);
2309 damage_per_second = MYMAX(damage_per_second,
2310 m_gamedef->ndef()->get(n3).damage_per_second);
2312 if(damage_per_second != 0)
2314 damageLocalPlayer(damage_per_second, true);
2321 if(m_drowning_interval.step(dtime, 2.0))
2323 v3f pf = lplayer->getPosition();
2326 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2327 MapNode n = m_map->getNodeNoEx(p);
2328 ContentFeatures c = m_gamedef->ndef()->get(n);
2329 u8 drowning_damage = c.drowning;
2330 if(drowning_damage > 0 && lplayer->hp > 0){
2331 u16 breath = lplayer->getBreath();
2338 lplayer->setBreath(breath);
2339 updateLocalPlayerBreath(breath);
2342 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2343 damageLocalPlayer(drowning_damage, true);
2346 if(m_breathing_interval.step(dtime, 0.5))
2348 v3f pf = lplayer->getPosition();
2351 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2352 MapNode n = m_map->getNodeNoEx(p);
2353 ContentFeatures c = m_gamedef->ndef()->get(n);
2355 lplayer->setBreath(11);
2357 else if(c.drowning == 0){
2358 u16 breath = lplayer->getBreath();
2361 lplayer->setBreath(breath);
2362 updateLocalPlayerBreath(breath);
2368 Stuff that can be done in an arbitarily large dtime
2370 for(std::list<Player*>::iterator i = m_players.begin();
2371 i != m_players.end(); ++i)
2373 Player *player = *i;
2376 Handle non-local players
2378 if(player->isLocal() == false)
2381 player->move(dtime, *m_map, 100*BS);
2385 // Update lighting on all players on client
2389 v3s16 p = player->getLightPosition();
2390 MapNode n = m_map->getNode(p);
2391 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2393 catch(InvalidPositionException &e){
2394 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2396 player->light = light;
2400 Step active objects and update lighting of them
2403 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2404 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2405 for(std::map<u16, ClientActiveObject*>::iterator
2406 i = m_active_objects.begin();
2407 i != m_active_objects.end(); ++i)
2409 ClientActiveObject* obj = i->second;
2411 obj->step(dtime, this);
2419 v3s16 p = obj->getLightPosition();
2420 MapNode n = m_map->getNode(p);
2421 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2423 catch(InvalidPositionException &e){
2424 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2426 obj->updateLight(light);
2431 Step and handle simple objects
2433 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2434 for(std::list<ClientSimpleObject*>::iterator
2435 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2437 ClientSimpleObject *simple = *i;
2438 std::list<ClientSimpleObject*>::iterator cur = i;
2440 simple->step(dtime);
2441 if(simple->m_to_be_removed){
2443 m_simple_objects.erase(cur);
2448 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2450 m_simple_objects.push_back(simple);
2453 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2455 std::map<u16, ClientActiveObject*>::iterator n;
2456 n = m_active_objects.find(id);
2457 if(n == m_active_objects.end())
2462 bool isFreeClientActiveObjectId(u16 id,
2463 std::map<u16, ClientActiveObject*> &objects)
2468 return objects.find(id) == objects.end();
2471 u16 getFreeClientActiveObjectId(
2472 std::map<u16, ClientActiveObject*> &objects)
2474 //try to reuse id's as late as possible
2475 static u16 last_used_id = 0;
2476 u16 startid = last_used_id;
2480 if(isFreeClientActiveObjectId(last_used_id, objects))
2481 return last_used_id;
2483 if(last_used_id == startid)
2488 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2491 if(object->getId() == 0)
2493 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2496 infostream<<"ClientEnvironment::addActiveObject(): "
2497 <<"no free ids available"<<std::endl;
2501 object->setId(new_id);
2503 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2505 infostream<<"ClientEnvironment::addActiveObject(): "
2506 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2510 infostream<<"ClientEnvironment::addActiveObject(): "
2511 <<"added (id="<<object->getId()<<")"<<std::endl;
2512 m_active_objects[object->getId()] = object;
2513 object->addToScene(m_smgr, m_texturesource, m_irr);
2514 { // Update lighting immediately
2518 v3s16 p = object->getLightPosition();
2519 MapNode n = m_map->getNode(p);
2520 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2522 catch(InvalidPositionException &e){
2523 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2525 object->updateLight(light);
2527 return object->getId();
2530 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2531 const std::string &init_data)
2533 ClientActiveObject* obj =
2534 ClientActiveObject::create(type, m_gamedef, this);
2537 infostream<<"ClientEnvironment::addActiveObject(): "
2538 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2547 obj->initialize(init_data);
2549 catch(SerializationError &e)
2551 errorstream<<"ClientEnvironment::addActiveObject():"
2552 <<" id="<<id<<" type="<<type
2553 <<": SerializationError in initialize(): "
2555 <<": init_data="<<serializeJsonString(init_data)
2559 addActiveObject(obj);
2562 void ClientEnvironment::removeActiveObject(u16 id)
2564 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2565 <<"id="<<id<<std::endl;
2566 ClientActiveObject* obj = getActiveObject(id);
2569 infostream<<"ClientEnvironment::removeActiveObject(): "
2570 <<"id="<<id<<" not found"<<std::endl;
2573 obj->removeFromScene(true);
2575 m_active_objects.erase(id);
2578 void ClientEnvironment::processActiveObjectMessage(u16 id,
2579 const std::string &data)
2581 ClientActiveObject* obj = getActiveObject(id);
2584 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2585 <<" got message for id="<<id<<", which doesn't exist."
2591 obj->processMessage(data);
2593 catch(SerializationError &e)
2595 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2596 <<" id="<<id<<" type="<<obj->getType()
2597 <<" SerializationError in processMessage(),"
2598 <<" message="<<serializeJsonString(data)
2604 Callbacks for activeobjects
2607 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2609 LocalPlayer *lplayer = getLocalPlayer();
2613 if(lplayer->hp > damage)
2614 lplayer->hp -= damage;
2619 ClientEnvEvent event;
2620 event.type = CEE_PLAYER_DAMAGE;
2621 event.player_damage.amount = damage;
2622 event.player_damage.send_to_server = handle_hp;
2623 m_client_event_queue.push_back(event);
2626 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2628 ClientEnvEvent event;
2629 event.type = CEE_PLAYER_BREATH;
2630 event.player_breath.amount = breath;
2631 m_client_event_queue.push_back(event);
2635 Client likes to call these
2638 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2639 std::vector<DistanceSortedActiveObject> &dest)
2641 for(std::map<u16, ClientActiveObject*>::iterator
2642 i = m_active_objects.begin();
2643 i != m_active_objects.end(); ++i)
2645 ClientActiveObject* obj = i->second;
2647 f32 d = (obj->getPosition() - origin).getLength();
2652 DistanceSortedActiveObject dso(obj, d);
2654 dest.push_back(dso);
2658 ClientEnvEvent ClientEnvironment::getClientEvent()
2660 ClientEnvEvent event;
2661 if(m_client_event_queue.empty())
2662 event.type = CEE_NONE;
2664 event = m_client_event_queue.front();
2665 m_client_event_queue.pop_front();
2670 #endif // #ifndef SERVER