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.
23 #include "environment.h"
26 #include "collision.h"
27 #include "content_mapnode.h"
29 #include "serverobject.h"
30 #include "content_sao.h"
35 #include "cpp_api/scriptapi.h"
37 #include "nodemetadata.h"
38 #include "main.h" // For g_settings, g_profiler
41 #include "clientmap.h"
42 #include "localplayer.h"
44 #include "daynightratio.h"
46 #include "util/serialize.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 Environment::Environment():
52 m_time_of_day_f(9000./24000),
53 m_time_of_day_speed(0),
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 void Environment::printPlayers(std::ostream &o)
195 o<<"Players in environment:"<<std::endl;
196 for(std::list<Player*>::iterator i = m_players.begin();
197 i != m_players.end(); i++)
200 o<<"Player peer_id="<<player->peer_id<<std::endl;
204 u32 Environment::getDayNightRatio()
206 bool smooth = (g_settings->getS32("enable_shaders") != 0);
207 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
210 void Environment::stepTimeOfDay(float dtime)
212 m_time_counter += dtime;
213 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
214 u32 units = (u32)(m_time_counter*speed);
215 m_time_counter -= (f32)units / speed;
219 if(m_time_of_day + units >= 24000)
221 m_time_of_day = (m_time_of_day + units) % 24000;
223 m_time_of_day_f = (float)m_time_of_day / 24000.0;
226 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
227 if(m_time_of_day_f > 1.0)
228 m_time_of_day_f -= 1.0;
229 if(m_time_of_day_f < 0.0)
230 m_time_of_day_f += 1.0;
238 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
242 // Initialize timer to random value to spread processing
243 float itv = abm->getTriggerInterval();
244 itv = MYMAX(0.001, itv); // No less than 1ms
245 int minval = MYMAX(-0.51*itv, -60); // Clamp to
246 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
247 timer = myrand_range(minval, maxval);
254 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
257 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
258 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
259 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
266 void ActiveBlockList::update(std::list<v3s16> &active_positions,
268 std::set<v3s16> &blocks_removed,
269 std::set<v3s16> &blocks_added)
274 std::set<v3s16> newlist;
275 for(std::list<v3s16>::iterator i = active_positions.begin();
276 i != active_positions.end(); ++i)
278 fillRadiusBlock(*i, radius, newlist);
282 Find out which blocks on the old list are not on the new list
284 // Go through old list
285 for(std::set<v3s16>::iterator i = m_list.begin();
286 i != m_list.end(); ++i)
289 // If not on new list, it's been removed
290 if(newlist.find(p) == newlist.end())
291 blocks_removed.insert(p);
295 Find out which blocks on the new list are not on the old list
297 // Go through new list
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
302 // If not on old list, it's been added
303 if(m_list.find(p) == m_list.end())
304 blocks_added.insert(p);
311 for(std::set<v3s16>::iterator i = newlist.begin();
312 i != newlist.end(); ++i)
323 ServerEnvironment::ServerEnvironment(ServerMap *map, ScriptApi *scriptIface,
324 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
326 m_script(scriptIface),
329 m_random_spawn_timer(3),
330 m_send_recommended_timer(0),
331 m_active_block_interval_overload_skip(0),
333 m_game_time_fraction_counter(0),
334 m_recommended_send_interval(0.1)
338 ServerEnvironment::~ServerEnvironment()
340 // Clear active block list.
341 // This makes the next one delete all active objects.
342 m_active_blocks.clear();
344 // Convert all objects to static and delete the active objects
345 deactivateFarObjects(true);
350 // Delete ActiveBlockModifiers
351 for(std::list<ABMWithState>::iterator
352 i = m_abms.begin(); i != m_abms.end(); ++i){
357 Map & ServerEnvironment::getMap()
362 ServerMap & ServerEnvironment::getServerMap()
367 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
369 float distance = pos1.getDistanceFrom(pos2);
371 //calculate normalized direction vector
372 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
373 (pos2.Y - pos1.Y)/distance,
374 (pos2.Z - pos1.Z)/distance);
376 //find out if there's a node on path between pos1 and pos2
377 for (float i = 1; i < distance; i += stepsize) {
378 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
379 normalized_vector.Y * i,
380 normalized_vector.Z * i) +pos1,BS);
382 MapNode n = getMap().getNodeNoEx(pos);
384 if(n.param0 != CONTENT_AIR) {
391 void ServerEnvironment::serializePlayers(const std::string &savedir)
393 std::string players_path = savedir + "/players";
394 fs::CreateDir(players_path);
396 std::set<Player*> saved_players;
398 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
399 for(u32 i=0; i<player_files.size(); i++)
401 if(player_files[i].dir)
404 // Full path to this file
405 std::string path = players_path + "/" + player_files[i].name;
407 //infostream<<"Checking player file "<<path<<std::endl;
409 // Load player to see what is its name
410 RemotePlayer testplayer(m_gamedef);
412 // Open file and deserialize
413 std::ifstream is(path.c_str(), std::ios_base::binary);
414 if(is.good() == false)
416 infostream<<"Failed to read "<<path<<std::endl;
419 testplayer.deSerialize(is);
422 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
424 // Search for the player
425 std::string playername = testplayer.getName();
426 Player *player = getPlayer(playername.c_str());
429 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
433 //infostream<<"Found matching player, overwriting."<<std::endl;
435 // OK, found. Save player there.
437 // Open file and serialize
438 std::ofstream os(path.c_str(), std::ios_base::binary);
439 if(os.good() == false)
441 infostream<<"Failed to overwrite "<<path<<std::endl;
444 player->serialize(os);
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::ofstream os(path.c_str(), std::ios_base::binary);
493 if(os.good() == false)
495 infostream<<"Failed to overwrite "<<path<<std::endl;
498 player->serialize(os);
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);
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);
575 void ServerEnvironment::saveMeta(const std::string &savedir)
577 std::string path = savedir + "/env_meta.txt";
579 // Open file and serialize
580 std::ofstream os(path.c_str(), std::ios_base::binary);
581 if(os.good() == false)
583 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
585 throw SerializationError("Couldn't save env meta");
589 args.setU64("game_time", m_game_time);
590 args.setU64("time_of_day", getTimeOfDay());
595 void ServerEnvironment::loadMeta(const std::string &savedir)
597 std::string path = savedir + "/env_meta.txt";
599 // Open file and deserialize
600 std::ifstream is(path.c_str(), std::ios_base::binary);
601 if(is.good() == false)
603 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
605 throw SerializationError("Couldn't load env meta");
613 throw SerializationError
614 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
616 std::getline(is, line);
617 std::string trimmedline = trim(line);
618 if(trimmedline == "EnvArgsEnd")
620 args.parseConfigLine(line);
624 m_game_time = args.getU64("game_time");
625 }catch(SettingNotFoundException &e){
626 // Getting this is crucial, otherwise timestamps are useless
627 throw SerializationError("Couldn't load env meta game_time");
631 m_time_of_day = args.getU64("time_of_day");
632 }catch(SettingNotFoundException &e){
633 // This is not as important
634 m_time_of_day = 9000;
640 ActiveBlockModifier *abm;
642 std::set<content_t> required_neighbors;
648 ServerEnvironment *m_env;
649 std::map<content_t, std::list<ActiveABM> > m_aabms;
651 ABMHandler(std::list<ABMWithState> &abms,
652 float dtime_s, ServerEnvironment *env,
658 INodeDefManager *ndef = env->getGameDef()->ndef();
659 for(std::list<ABMWithState>::iterator
660 i = abms.begin(); i != abms.end(); ++i){
661 ActiveBlockModifier *abm = i->abm;
662 float trigger_interval = abm->getTriggerInterval();
663 if(trigger_interval < 0.001)
664 trigger_interval = 0.001;
665 float actual_interval = dtime_s;
668 if(i->timer < trigger_interval)
670 i->timer -= trigger_interval;
671 actual_interval = trigger_interval;
673 float intervals = actual_interval / trigger_interval;
676 float chance = abm->getTriggerChance();
681 aabm.chance = chance / intervals;
685 std::set<std::string> required_neighbors_s
686 = abm->getRequiredNeighbors();
687 for(std::set<std::string>::iterator
688 i = required_neighbors_s.begin();
689 i != required_neighbors_s.end(); i++)
691 ndef->getIds(*i, aabm.required_neighbors);
694 std::set<std::string> contents_s = abm->getTriggerContents();
695 for(std::set<std::string>::iterator
696 i = contents_s.begin(); i != contents_s.end(); i++)
698 std::set<content_t> ids;
699 ndef->getIds(*i, ids);
700 for(std::set<content_t>::const_iterator k = ids.begin();
704 std::map<content_t, std::list<ActiveABM> >::iterator j;
706 if(j == m_aabms.end()){
707 std::list<ActiveABM> aabmlist;
708 m_aabms[c] = aabmlist;
711 j->second.push_back(aabm);
716 void apply(MapBlock *block)
721 ServerMap *map = &m_env->getServerMap();
724 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
725 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
726 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
728 MapNode n = block->getNodeNoEx(p0);
729 content_t c = n.getContent();
730 v3s16 p = p0 + block->getPosRelative();
732 std::map<content_t, std::list<ActiveABM> >::iterator j;
734 if(j == m_aabms.end())
737 for(std::list<ActiveABM>::iterator
738 i = j->second.begin(); i != j->second.end(); i++)
740 if(myrand() % i->chance != 0)
744 if(!i->required_neighbors.empty())
747 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
748 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
749 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
753 MapNode n = map->getNodeNoEx(p1);
754 content_t c = n.getContent();
755 std::set<content_t>::const_iterator k;
756 k = i->required_neighbors.find(c);
757 if(k != i->required_neighbors.end()){
761 // No required neighbor found
766 // Find out how many objects the block contains
767 u32 active_object_count = block->m_static_objects.m_active.size();
768 // Find out how many objects this and all the neighbors contain
769 u32 active_object_count_wider = 0;
770 u32 wider_unknown_count = 0;
771 for(s16 x=-1; x<=1; x++)
772 for(s16 y=-1; y<=1; y++)
773 for(s16 z=-1; z<=1; z++)
775 MapBlock *block2 = map->getBlockNoCreateNoEx(
776 block->getPos() + v3s16(x,y,z));
778 wider_unknown_count = 0;
781 active_object_count_wider +=
782 block2->m_static_objects.m_active.size()
783 + block2->m_static_objects.m_stored.size();
786 u32 wider_known_count = 3*3*3 - wider_unknown_count;
787 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
789 // Call all the trigger variations
790 i->abm->trigger(m_env, p, n);
791 i->abm->trigger(m_env, p, n,
792 active_object_count, active_object_count_wider);
798 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
800 // Get time difference
802 u32 stamp = block->getTimestamp();
803 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
804 dtime_s = m_game_time - block->getTimestamp();
805 dtime_s += additional_dtime;
807 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
808 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
810 // Set current time as timestamp
811 block->setTimestampNoChangedFlag(m_game_time);
813 /*infostream<<"ServerEnvironment::activateBlock(): block is "
814 <<dtime_s<<" seconds old."<<std::endl;*/
816 // Activate stored objects
817 activateObjects(block, dtime_s);
820 std::map<v3s16, NodeTimer> elapsed_timers =
821 block->m_node_timers.step((float)dtime_s);
822 if(!elapsed_timers.empty()){
824 for(std::map<v3s16, NodeTimer>::iterator
825 i = elapsed_timers.begin();
826 i != elapsed_timers.end(); i++){
827 n = block->getNodeNoEx(i->first);
828 v3s16 p = i->first + block->getPosRelative();
829 if(m_script->node_on_timer(p,n,i->second.elapsed))
830 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
834 /* Handle ActiveBlockModifiers */
835 ABMHandler abmhandler(m_abms, dtime_s, this, false);
836 abmhandler.apply(block);
839 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
841 m_abms.push_back(ABMWithState(abm));
844 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
846 INodeDefManager *ndef = m_gamedef->ndef();
847 MapNode n_old = m_map->getNodeNoEx(p);
849 if(ndef->get(n_old).has_on_destruct)
850 m_script->node_on_destruct(p, n_old);
852 bool succeeded = m_map->addNodeWithEvent(p, n);
855 // Call post-destructor
856 if(ndef->get(n_old).has_after_destruct)
857 m_script->node_after_destruct(p, n_old);
859 if(ndef->get(n).has_on_construct)
860 m_script->node_on_construct(p, n);
864 bool ServerEnvironment::removeNode(v3s16 p)
866 INodeDefManager *ndef = m_gamedef->ndef();
867 MapNode n_old = m_map->getNodeNoEx(p);
869 if(ndef->get(n_old).has_on_destruct)
870 m_script->node_on_destruct(p, n_old);
872 // This is slightly optimized compared to addNodeWithEvent(air)
873 bool succeeded = m_map->removeNodeWithEvent(p);
876 // Call post-destructor
877 if(ndef->get(n_old).has_after_destruct)
878 m_script->node_after_destruct(p, n_old);
879 // Air doesn't require constructor
883 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
885 std::set<u16> objects;
886 for(std::map<u16, ServerActiveObject*>::iterator
887 i = m_active_objects.begin();
888 i != m_active_objects.end(); ++i)
890 ServerActiveObject* obj = i->second;
892 v3f objectpos = obj->getBasePosition();
893 if(objectpos.getDistanceFrom(pos) > radius)
900 void ServerEnvironment::clearAllObjects()
902 infostream<<"ServerEnvironment::clearAllObjects(): "
903 <<"Removing all active objects"<<std::endl;
904 std::list<u16> objects_to_remove;
905 for(std::map<u16, ServerActiveObject*>::iterator
906 i = m_active_objects.begin();
907 i != m_active_objects.end(); ++i)
909 ServerActiveObject* obj = i->second;
910 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
913 // Delete static object if block is loaded
914 if(obj->m_static_exists){
915 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
917 block->m_static_objects.remove(id);
918 block->raiseModified(MOD_STATE_WRITE_NEEDED,
920 obj->m_static_exists = false;
923 // If known by some client, don't delete immediately
924 if(obj->m_known_by_count > 0){
925 obj->m_pending_deactivation = true;
926 obj->m_removed = true;
930 // Tell the object about removal
931 obj->removingFromEnvironment();
932 // Deregister in scripting api
933 m_script->removeObjectReference(obj);
935 // Delete active object
936 if(obj->environmentDeletes())
938 // Id to be removed from m_active_objects
939 objects_to_remove.push_back(id);
941 // Remove references from m_active_objects
942 for(std::list<u16>::iterator i = objects_to_remove.begin();
943 i != objects_to_remove.end(); ++i)
945 m_active_objects.erase(*i);
948 // Get list of loaded blocks
949 std::list<v3s16> loaded_blocks;
950 infostream<<"ServerEnvironment::clearAllObjects(): "
951 <<"Listing all loaded blocks"<<std::endl;
952 m_map->listAllLoadedBlocks(loaded_blocks);
953 infostream<<"ServerEnvironment::clearAllObjects(): "
954 <<"Done listing all loaded blocks: "
955 <<loaded_blocks.size()<<std::endl;
957 // Get list of loadable blocks
958 std::list<v3s16> loadable_blocks;
959 infostream<<"ServerEnvironment::clearAllObjects(): "
960 <<"Listing all loadable blocks"<<std::endl;
961 m_map->listAllLoadableBlocks(loadable_blocks);
962 infostream<<"ServerEnvironment::clearAllObjects(): "
963 <<"Done listing all loadable blocks: "
964 <<loadable_blocks.size()
965 <<", now clearing"<<std::endl;
967 // Grab a reference on each loaded block to avoid unloading it
968 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
969 i != loaded_blocks.end(); ++i)
972 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
977 // Remove objects in all loadable blocks
978 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
979 unload_interval = MYMAX(unload_interval, 1);
980 u32 report_interval = loadable_blocks.size() / 10;
981 u32 num_blocks_checked = 0;
982 u32 num_blocks_cleared = 0;
983 u32 num_objs_cleared = 0;
984 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
985 i != loadable_blocks.end(); ++i)
988 MapBlock *block = m_map->emergeBlock(p, false);
990 errorstream<<"ServerEnvironment::clearAllObjects(): "
991 <<"Failed to emerge block "<<PP(p)<<std::endl;
994 u32 num_stored = block->m_static_objects.m_stored.size();
995 u32 num_active = block->m_static_objects.m_active.size();
996 if(num_stored != 0 || num_active != 0){
997 block->m_static_objects.m_stored.clear();
998 block->m_static_objects.m_active.clear();
999 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1001 num_objs_cleared += num_stored + num_active;
1002 num_blocks_cleared++;
1004 num_blocks_checked++;
1006 if(num_blocks_checked % report_interval == 0){
1007 float percent = 100.0 * (float)num_blocks_checked /
1008 loadable_blocks.size();
1009 infostream<<"ServerEnvironment::clearAllObjects(): "
1010 <<"Cleared "<<num_objs_cleared<<" objects"
1011 <<" in "<<num_blocks_cleared<<" blocks ("
1012 <<percent<<"%)"<<std::endl;
1014 if(num_blocks_checked % unload_interval == 0){
1015 m_map->unloadUnreferencedBlocks();
1018 m_map->unloadUnreferencedBlocks();
1020 // Drop references that were added above
1021 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1022 i != loaded_blocks.end(); ++i)
1025 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1030 infostream<<"ServerEnvironment::clearAllObjects(): "
1031 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1032 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1035 void ServerEnvironment::step(float dtime)
1037 DSTACK(__FUNCTION_NAME);
1039 //TimeTaker timer("ServerEnv step");
1041 /* Step time of day */
1042 stepTimeOfDay(dtime);
1045 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1046 // really matter that much.
1047 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1053 m_game_time_fraction_counter += dtime;
1054 u32 inc_i = (u32)m_game_time_fraction_counter;
1055 m_game_time += inc_i;
1056 m_game_time_fraction_counter -= (float)inc_i;
1063 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1064 for(std::list<Player*>::iterator i = m_players.begin();
1065 i != m_players.end(); ++i)
1067 Player *player = *i;
1069 // Ignore disconnected players
1070 if(player->peer_id == 0)
1074 player->move(dtime, *m_map, 100*BS);
1079 Manage active block list
1081 if(m_active_blocks_management_interval.step(dtime, 2.0))
1083 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1085 Get player block positions
1087 std::list<v3s16> players_blockpos;
1088 for(std::list<Player*>::iterator
1089 i = m_players.begin();
1090 i != m_players.end(); ++i)
1092 Player *player = *i;
1093 // Ignore disconnected players
1094 if(player->peer_id == 0)
1096 v3s16 blockpos = getNodeBlockPos(
1097 floatToInt(player->getPosition(), BS));
1098 players_blockpos.push_back(blockpos);
1102 Update list of active blocks, collecting changes
1104 const s16 active_block_range = g_settings->getS16("active_block_range");
1105 std::set<v3s16> blocks_removed;
1106 std::set<v3s16> blocks_added;
1107 m_active_blocks.update(players_blockpos, active_block_range,
1108 blocks_removed, blocks_added);
1111 Handle removed blocks
1114 // Convert active objects that are no more in active blocks to static
1115 deactivateFarObjects(false);
1117 for(std::set<v3s16>::iterator
1118 i = blocks_removed.begin();
1119 i != blocks_removed.end(); ++i)
1123 /* infostream<<"Server: Block " << PP(p)
1124 << " became inactive"<<std::endl; */
1126 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1130 // Set current time as timestamp (and let it set ChangedFlag)
1131 block->setTimestamp(m_game_time);
1138 for(std::set<v3s16>::iterator
1139 i = blocks_added.begin();
1140 i != blocks_added.end(); ++i)
1144 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1146 // Block needs to be fetched first
1147 m_emerger->queueBlockEmerge(p, false);
1148 m_active_blocks.m_list.erase(p);
1152 activateBlock(block);
1153 /* infostream<<"Server: Block " << PP(p)
1154 << " became active"<<std::endl; */
1159 Mess around in active blocks
1161 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1163 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1167 for(std::set<v3s16>::iterator
1168 i = m_active_blocks.m_list.begin();
1169 i != m_active_blocks.m_list.end(); ++i)
1173 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1174 <<") being handled"<<std::endl;*/
1176 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1180 // Reset block usage timer
1181 block->resetUsageTimer();
1183 // Set current time as timestamp
1184 block->setTimestampNoChangedFlag(m_game_time);
1185 // If time has changed much from the one on disk,
1186 // set block to be saved when it is unloaded
1187 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1188 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1189 "Timestamp older than 60s (step)");
1192 std::map<v3s16, NodeTimer> elapsed_timers =
1193 block->m_node_timers.step((float)dtime);
1194 if(!elapsed_timers.empty()){
1196 for(std::map<v3s16, NodeTimer>::iterator
1197 i = elapsed_timers.begin();
1198 i != elapsed_timers.end(); i++){
1199 n = block->getNodeNoEx(i->first);
1200 p = i->first + block->getPosRelative();
1201 if(m_script->node_on_timer(p,n,i->second.elapsed))
1202 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1208 const float abm_interval = 1.0;
1209 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1211 if(m_active_block_interval_overload_skip > 0){
1212 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1213 m_active_block_interval_overload_skip--;
1216 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1217 TimeTaker timer("modify in active blocks");
1219 // Initialize handling of ActiveBlockModifiers
1220 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1222 for(std::set<v3s16>::iterator
1223 i = m_active_blocks.m_list.begin();
1224 i != m_active_blocks.m_list.end(); ++i)
1228 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1229 <<") being handled"<<std::endl;*/
1231 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1235 // Set current time as timestamp
1236 block->setTimestampNoChangedFlag(m_game_time);
1238 /* Handle ActiveBlockModifiers */
1239 abmhandler.apply(block);
1242 u32 time_ms = timer.stop(true);
1243 u32 max_time_ms = 200;
1244 if(time_ms > max_time_ms){
1245 infostream<<"WARNING: active block modifiers took "
1246 <<time_ms<<"ms (longer than "
1247 <<max_time_ms<<"ms)"<<std::endl;
1248 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1253 Step script environment (run global on_step())
1255 m_script->environment_Step(dtime);
1261 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1262 //TimeTaker timer("Step active objects");
1264 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1266 // This helps the objects to send data at the same time
1267 bool send_recommended = false;
1268 m_send_recommended_timer += dtime;
1269 if(m_send_recommended_timer > getSendRecommendedInterval())
1271 m_send_recommended_timer -= getSendRecommendedInterval();
1272 send_recommended = true;
1275 for(std::map<u16, ServerActiveObject*>::iterator
1276 i = m_active_objects.begin();
1277 i != m_active_objects.end(); ++i)
1279 ServerActiveObject* obj = i->second;
1280 // Remove non-peaceful mobs on peaceful mode
1281 if(g_settings->getBool("only_peaceful_mobs")){
1282 if(!obj->isPeaceful())
1283 obj->m_removed = true;
1285 // Don't step if is to be removed or stored statically
1286 if(obj->m_removed || obj->m_pending_deactivation)
1289 obj->step(dtime, send_recommended);
1290 // Read messages from object
1291 while(!obj->m_messages_out.empty())
1293 m_active_object_messages.push_back(
1294 obj->m_messages_out.pop_front());
1300 Manage active objects
1302 if(m_object_management_interval.step(dtime, 0.5))
1304 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1306 Remove objects that satisfy (m_removed && m_known_by_count==0)
1308 removeRemovedObjects();
1312 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1314 std::map<u16, ServerActiveObject*>::iterator n;
1315 n = m_active_objects.find(id);
1316 if(n == m_active_objects.end())
1321 bool isFreeServerActiveObjectId(u16 id,
1322 std::map<u16, ServerActiveObject*> &objects)
1327 return objects.find(id) == objects.end();
1330 u16 getFreeServerActiveObjectId(
1331 std::map<u16, ServerActiveObject*> &objects)
1333 //try to reuse id's as late as possible
1334 static u16 last_used_id = 0;
1335 u16 startid = last_used_id;
1339 if(isFreeServerActiveObjectId(last_used_id, objects))
1340 return last_used_id;
1342 if(last_used_id == startid)
1347 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1350 u16 id = addActiveObjectRaw(object, true, 0);
1355 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1359 v3f objectpos = obj->getBasePosition();
1361 // The block in which the object resides in
1362 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1365 Update the static data
1368 // Create new static object
1369 std::string staticdata = obj->getStaticData();
1370 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1371 // Add to the block where the object is located in
1372 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1373 // Get or generate the block
1374 MapBlock *block = m_map->emergeBlock(blockpos);
1376 bool succeeded = false;
1380 block->m_static_objects.insert(0, s_obj);
1381 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1382 "addActiveObjectAsStatic");
1386 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1387 <<"Could not find or generate "
1388 <<"a block for storing static object"<<std::endl;
1392 if(obj->environmentDeletes())
1400 Finds out what new objects have been added to
1401 inside a radius around a position
1403 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1404 std::set<u16> ¤t_objects,
1405 std::set<u16> &added_objects)
1407 v3f pos_f = intToFloat(pos, BS);
1408 f32 radius_f = radius * BS;
1410 Go through the object list,
1411 - discard m_removed objects,
1412 - discard objects that are too far away,
1413 - discard objects that are found in current_objects.
1414 - add remaining objects to added_objects
1416 for(std::map<u16, ServerActiveObject*>::iterator
1417 i = m_active_objects.begin();
1418 i != m_active_objects.end(); ++i)
1422 ServerActiveObject *object = i->second;
1425 // Discard if removed
1426 if(object->m_removed)
1428 if(object->unlimitedTransferDistance() == false){
1429 // Discard if too far
1430 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1431 if(distance_f > radius_f)
1434 // Discard if already on current_objects
1435 std::set<u16>::iterator n;
1436 n = current_objects.find(id);
1437 if(n != current_objects.end())
1439 // Add to added_objects
1440 added_objects.insert(id);
1445 Finds out what objects have been removed from
1446 inside a radius around a position
1448 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1449 std::set<u16> ¤t_objects,
1450 std::set<u16> &removed_objects)
1452 v3f pos_f = intToFloat(pos, BS);
1453 f32 radius_f = radius * BS;
1455 Go through current_objects; object is removed if:
1456 - object is not found in m_active_objects (this is actually an
1457 error condition; objects should be set m_removed=true and removed
1458 only after all clients have been informed about removal), or
1459 - object has m_removed=true, or
1460 - object is too far away
1462 for(std::set<u16>::iterator
1463 i = current_objects.begin();
1464 i != current_objects.end(); ++i)
1467 ServerActiveObject *object = getActiveObject(id);
1470 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1471 <<" object in current_objects is NULL"<<std::endl;
1472 removed_objects.insert(id);
1476 if(object->m_removed)
1478 removed_objects.insert(id);
1482 // If transfer distance is unlimited, don't remove
1483 if(object->unlimitedTransferDistance())
1486 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1488 if(distance_f >= radius_f)
1490 removed_objects.insert(id);
1498 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1500 if(m_active_object_messages.empty())
1501 return ActiveObjectMessage(0);
1503 return m_active_object_messages.pop_front();
1507 ************ Private methods *************
1510 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1511 bool set_changed, u32 dtime_s)
1514 if(object->getId() == 0){
1515 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1518 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1519 <<"no free ids available"<<std::endl;
1520 if(object->environmentDeletes())
1524 object->setId(new_id);
1527 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1528 <<"supplied with id "<<object->getId()<<std::endl;
1530 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1532 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1533 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1534 if(object->environmentDeletes())
1538 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1539 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1541 m_active_objects[object->getId()] = object;
1543 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1544 <<"Added id="<<object->getId()<<"; there are now "
1545 <<m_active_objects.size()<<" active objects."
1548 // Register reference in scripting api (must be done before post-init)
1549 m_script->addObjectReference(object);
1550 // Post-initialize object
1551 object->addedToEnvironment(dtime_s);
1553 // Add static data to block
1554 if(object->isStaticAllowed())
1556 // Add static object to active static list of the block
1557 v3f objectpos = object->getBasePosition();
1558 std::string staticdata = object->getStaticData();
1559 StaticObject s_obj(object->getType(), objectpos, staticdata);
1560 // Add to the block where the object is located in
1561 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1562 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1565 block->m_static_objects.m_active[object->getId()] = s_obj;
1566 object->m_static_exists = true;
1567 object->m_static_block = blockpos;
1570 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1571 "addActiveObjectRaw");
1574 v3s16 p = floatToInt(objectpos, BS);
1575 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1576 <<"could not find block for storing id="<<object->getId()
1577 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1581 return object->getId();
1585 Remove objects that satisfy (m_removed && m_known_by_count==0)
1587 void ServerEnvironment::removeRemovedObjects()
1589 std::list<u16> objects_to_remove;
1590 for(std::map<u16, ServerActiveObject*>::iterator
1591 i = m_active_objects.begin();
1592 i != m_active_objects.end(); ++i)
1595 ServerActiveObject* obj = i->second;
1596 // This shouldn't happen but check it
1599 infostream<<"NULL object found in ServerEnvironment"
1600 <<" while finding removed objects. id="<<id<<std::endl;
1601 // Id to be removed from m_active_objects
1602 objects_to_remove.push_back(id);
1607 We will delete objects that are marked as removed or thatare
1608 waiting for deletion after deactivation
1610 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1614 Delete static data from block if is marked as removed
1616 if(obj->m_static_exists && obj->m_removed)
1618 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1620 block->m_static_objects.remove(id);
1621 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1622 "removeRemovedObjects");
1623 obj->m_static_exists = false;
1625 infostream << "failed to emerge block from which "
1626 "an object to be removed was loaded from. id="<<id<<std::endl;
1630 // If m_known_by_count > 0, don't actually remove.
1631 if(obj->m_known_by_count > 0)
1634 // Tell the object about removal
1635 obj->removingFromEnvironment();
1636 // Deregister in scripting api
1637 m_script->removeObjectReference(obj);
1640 if(obj->environmentDeletes())
1642 // Id to be removed from m_active_objects
1643 objects_to_remove.push_back(id);
1645 // Remove references from m_active_objects
1646 for(std::list<u16>::iterator i = objects_to_remove.begin();
1647 i != objects_to_remove.end(); ++i)
1649 m_active_objects.erase(*i);
1653 static void print_hexdump(std::ostream &o, const std::string &data)
1655 const int linelength = 16;
1656 for(int l=0; ; l++){
1657 int i0 = linelength * l;
1658 bool at_end = false;
1659 int thislinelength = linelength;
1660 if(i0 + thislinelength > (int)data.size()){
1661 thislinelength = data.size() - i0;
1664 for(int di=0; di<linelength; di++){
1667 if(di<thislinelength)
1668 snprintf(buf, 4, "%.2x ", data[i]);
1670 snprintf(buf, 4, " ");
1674 for(int di=0; di<thislinelength; di++){
1688 Convert stored objects from blocks near the players to active.
1690 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1694 // Ignore if no stored objects (to not set changed flag)
1695 if(block->m_static_objects.m_stored.size() == 0)
1697 verbosestream<<"ServerEnvironment::activateObjects(): "
1698 <<"activating objects of block "<<PP(block->getPos())
1699 <<" ("<<block->m_static_objects.m_stored.size()
1700 <<" objects)"<<std::endl;
1701 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1703 errorstream<<"suspiciously large amount of objects detected: "
1704 <<block->m_static_objects.m_stored.size()<<" in "
1705 <<PP(block->getPos())
1706 <<"; removing all of them."<<std::endl;
1707 // Clear stored list
1708 block->m_static_objects.m_stored.clear();
1709 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1710 "stored list cleared in activateObjects due to "
1711 "large amount of objects");
1714 // A list for objects that couldn't be converted to active for some
1715 // reason. They will be stored back.
1716 std::list<StaticObject> new_stored;
1717 // Loop through stored static objects
1718 for(std::list<StaticObject>::iterator
1719 i = block->m_static_objects.m_stored.begin();
1720 i != block->m_static_objects.m_stored.end(); ++i)
1722 /*infostream<<"Server: Creating an active object from "
1723 <<"static data"<<std::endl;*/
1724 StaticObject &s_obj = *i;
1725 // Create an active object from the data
1726 ServerActiveObject *obj = ServerActiveObject::create
1727 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1728 // If couldn't create object, store static data back.
1731 errorstream<<"ServerEnvironment::activateObjects(): "
1732 <<"failed to create active object from static object "
1733 <<"in block "<<PP(s_obj.pos/BS)
1734 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1735 print_hexdump(verbosestream, s_obj.data);
1737 new_stored.push_back(s_obj);
1740 verbosestream<<"ServerEnvironment::activateObjects(): "
1741 <<"activated static object pos="<<PP(s_obj.pos/BS)
1742 <<" type="<<(int)s_obj.type<<std::endl;
1743 // This will also add the object to the active static list
1744 addActiveObjectRaw(obj, false, dtime_s);
1746 // Clear stored list
1747 block->m_static_objects.m_stored.clear();
1748 // Add leftover failed stuff to stored list
1749 for(std::list<StaticObject>::iterator
1750 i = new_stored.begin();
1751 i != new_stored.end(); ++i)
1753 StaticObject &s_obj = *i;
1754 block->m_static_objects.m_stored.push_back(s_obj);
1757 Note: Block hasn't really been modified here.
1758 The objects have just been activated and moved from the stored
1759 static list to the active static list.
1760 As such, the block is essentially the same.
1761 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1762 Otherwise there would be a huge amount of unnecessary I/O.
1767 Convert objects that are not standing inside active blocks to static.
1769 If m_known_by_count != 0, active object is not deleted, but static
1770 data is still updated.
1772 If force_delete is set, active object is deleted nevertheless. It
1773 shall only be set so in the destructor of the environment.
1775 If block wasn't generated (not in memory or on disk),
1777 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1779 std::list<u16> objects_to_remove;
1780 for(std::map<u16, ServerActiveObject*>::iterator
1781 i = m_active_objects.begin();
1782 i != m_active_objects.end(); ++i)
1784 ServerActiveObject* obj = i->second;
1787 // Do not deactivate if static data creation not allowed
1788 if(!force_delete && !obj->isStaticAllowed())
1791 // If pending deactivation, let removeRemovedObjects() do it
1792 if(!force_delete && obj->m_pending_deactivation)
1796 v3f objectpos = obj->getBasePosition();
1798 // The block in which the object resides in
1799 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1801 // If block is active, don't remove
1802 if(!force_delete && m_active_blocks.contains(blockpos_o))
1805 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1806 <<"deactivating object id="<<id<<" on inactive block "
1807 <<PP(blockpos_o)<<std::endl;
1809 // If known by some client, don't immediately delete.
1810 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1813 Update the static data
1816 if(obj->isStaticAllowed())
1818 // Create new static object
1819 std::string staticdata_new = obj->getStaticData();
1820 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1822 bool stays_in_same_block = false;
1823 bool data_changed = true;
1825 if(obj->m_static_exists){
1826 if(obj->m_static_block == blockpos_o)
1827 stays_in_same_block = true;
1829 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1831 std::map<u16, StaticObject>::iterator n =
1832 block->m_static_objects.m_active.find(id);
1833 if(n != block->m_static_objects.m_active.end()){
1834 StaticObject static_old = n->second;
1836 float save_movem = obj->getMinimumSavedMovement();
1838 if(static_old.data == staticdata_new &&
1839 (static_old.pos - objectpos).getLength() < save_movem)
1840 data_changed = false;
1842 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1843 <<"id="<<id<<" m_static_exists=true but "
1844 <<"static data doesn't actually exist in "
1845 <<PP(obj->m_static_block)<<std::endl;
1849 bool shall_be_written = (!stays_in_same_block || data_changed);
1851 // Delete old static object
1852 if(obj->m_static_exists)
1854 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1857 block->m_static_objects.remove(id);
1858 obj->m_static_exists = false;
1859 // Only mark block as modified if data changed considerably
1860 if(shall_be_written)
1861 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1862 "deactivateFarObjects: Static data "
1863 "changed considerably");
1867 // Add to the block where the object is located in
1868 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1869 // Get or generate the block
1870 MapBlock *block = NULL;
1872 block = m_map->emergeBlock(blockpos);
1873 } catch(InvalidPositionException &e){
1874 // Handled via NULL pointer
1879 if(block->m_static_objects.m_stored.size() >= 49){
1880 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1881 <<" statically but block "<<PP(blockpos)
1882 <<" already contains "
1883 <<block->m_static_objects.m_stored.size()
1884 <<" (over 49) objects."
1885 <<" Forcing delete."<<std::endl;
1886 force_delete = true;
1888 // If static counterpart already exists, remove it first.
1889 // This shouldn't happen, but happens rarely for some
1890 // unknown reason. Unsuccessful attempts have been made to
1891 // find said reason.
1892 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1893 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1895 block->m_static_objects.remove(id);
1898 block->m_static_objects.insert(0, s_obj);
1900 // Only mark block as modified if data changed considerably
1901 if(shall_be_written)
1902 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1903 "deactivateFarObjects: Static data "
1904 "changed considerably");
1906 obj->m_static_exists = true;
1907 obj->m_static_block = block->getPos();
1912 v3s16 p = floatToInt(objectpos, BS);
1913 errorstream<<"ServerEnv: Could not find or generate "
1914 <<"a block for storing id="<<obj->getId()
1915 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1922 If known by some client, set pending deactivation.
1923 Otherwise delete it immediately.
1926 if(pending_delete && !force_delete)
1928 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1929 <<"object id="<<id<<" is known by clients"
1930 <<"; not deleting yet"<<std::endl;
1932 obj->m_pending_deactivation = true;
1936 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1937 <<"object id="<<id<<" is not known by clients"
1938 <<"; deleting"<<std::endl;
1940 // Tell the object about removal
1941 obj->removingFromEnvironment();
1942 // Deregister in scripting api
1943 m_script->removeObjectReference(obj);
1945 // Delete active object
1946 if(obj->environmentDeletes())
1948 // Id to be removed from m_active_objects
1949 objects_to_remove.push_back(id);
1952 // Remove references from m_active_objects
1953 for(std::list<u16>::iterator i = objects_to_remove.begin();
1954 i != objects_to_remove.end(); ++i)
1956 m_active_objects.erase(*i);
1963 #include "clientsimpleobject.h"
1969 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1970 ITextureSource *texturesource, IGameDef *gamedef,
1971 IrrlichtDevice *irr):
1974 m_texturesource(texturesource),
1980 ClientEnvironment::~ClientEnvironment()
1982 // delete active objects
1983 for(std::map<u16, ClientActiveObject*>::iterator
1984 i = m_active_objects.begin();
1985 i != m_active_objects.end(); ++i)
1990 for(std::list<ClientSimpleObject*>::iterator
1991 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
2000 Map & ClientEnvironment::getMap()
2005 ClientMap & ClientEnvironment::getClientMap()
2010 void ClientEnvironment::addPlayer(Player *player)
2012 DSTACK(__FUNCTION_NAME);
2014 It is a failure if player is local and there already is a local
2017 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2019 Environment::addPlayer(player);
2022 LocalPlayer * ClientEnvironment::getLocalPlayer()
2024 for(std::list<Player*>::iterator i = m_players.begin();
2025 i != m_players.end(); ++i)
2027 Player *player = *i;
2028 if(player->isLocal())
2029 return (LocalPlayer*)player;
2034 void ClientEnvironment::step(float dtime)
2036 DSTACK(__FUNCTION_NAME);
2038 /* Step time of day */
2039 stepTimeOfDay(dtime);
2041 // Get some settings
2042 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2043 bool free_move = fly_allowed && g_settings->getBool("free_move");
2046 LocalPlayer *lplayer = getLocalPlayer();
2048 // collision info queue
2049 std::list<CollisionInfo> player_collisions;
2052 Get the speed the player is going
2054 bool is_climbing = lplayer->is_climbing;
2056 f32 player_speed = lplayer->getSpeed().getLength();
2059 Maximum position increment
2061 //f32 position_max_increment = 0.05*BS;
2062 f32 position_max_increment = 0.1*BS;
2064 // Maximum time increment (for collision detection etc)
2065 // time = distance / speed
2066 f32 dtime_max_increment = 1;
2067 if(player_speed > 0.001)
2068 dtime_max_increment = position_max_increment / player_speed;
2070 // Maximum time increment is 10ms or lower
2071 if(dtime_max_increment > 0.01)
2072 dtime_max_increment = 0.01;
2074 // Don't allow overly huge dtime
2078 f32 dtime_downcount = dtime;
2081 Stuff that has a maximum time increment
2090 if(dtime_downcount > dtime_max_increment)
2092 dtime_part = dtime_max_increment;
2093 dtime_downcount -= dtime_part;
2097 dtime_part = dtime_downcount;
2099 Setting this to 0 (no -=dtime_part) disables an infinite loop
2100 when dtime_part is so small that dtime_downcount -= dtime_part
2103 dtime_downcount = 0;
2112 if(free_move == false && is_climbing == false)
2115 v3f speed = lplayer->getSpeed();
2116 if(lplayer->in_liquid == false)
2117 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2119 // Liquid floating / sinking
2120 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2121 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2123 // Liquid resistance
2124 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2126 // How much the node's viscosity blocks movement, ranges between 0 and 1
2127 // Should match the scale at which viscosity increase affects other liquid attributes
2128 const f32 viscosity_factor = 0.3;
2130 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2131 f32 dl = d_wanted.getLength();
2132 if(dl > lplayer->movement_liquid_fluidity_smooth)
2133 dl = lplayer->movement_liquid_fluidity_smooth;
2134 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2136 v3f d = d_wanted.normalize() * dl;
2140 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2141 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2142 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2143 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2144 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2145 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2149 lplayer->setSpeed(speed);
2154 This also does collision detection.
2156 lplayer->move(dtime_part, this, position_max_increment,
2157 &player_collisions);
2160 while(dtime_downcount > 0.001);
2162 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2164 for(std::list<CollisionInfo>::iterator
2165 i = player_collisions.begin();
2166 i != player_collisions.end(); ++i)
2168 CollisionInfo &info = *i;
2169 v3f speed_diff = info.new_speed - info.old_speed;;
2170 // Handle only fall damage
2171 // (because otherwise walking against something in fast_move kills you)
2172 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2174 // Get rid of other components
2177 f32 pre_factor = 1; // 1 hp per node/s
2178 f32 tolerance = BS*14; // 5 without damage
2179 f32 post_factor = 1; // 1 hp per node/s
2180 if(info.type == COLLISION_NODE)
2182 const ContentFeatures &f = m_gamedef->ndef()->
2183 get(m_map->getNodeNoEx(info.node_p));
2184 // Determine fall damage multiplier
2185 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2186 pre_factor = 1.0 + (float)addp/100.0;
2188 float speed = pre_factor * speed_diff.getLength();
2189 if(speed > tolerance)
2191 f32 damage_f = (speed - tolerance)/BS * post_factor;
2192 u16 damage = (u16)(damage_f+0.5);
2194 damageLocalPlayer(damage, true);
2199 A quick draft of lava damage
2201 if(m_lava_hurt_interval.step(dtime, 1.0))
2203 v3f pf = lplayer->getPosition();
2205 // Feet, middle and head
2206 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2207 MapNode n1 = m_map->getNodeNoEx(p1);
2208 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2209 MapNode n2 = m_map->getNodeNoEx(p2);
2210 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2211 MapNode n3 = m_map->getNodeNoEx(p3);
2213 u32 damage_per_second = 0;
2214 damage_per_second = MYMAX(damage_per_second,
2215 m_gamedef->ndef()->get(n1).damage_per_second);
2216 damage_per_second = MYMAX(damage_per_second,
2217 m_gamedef->ndef()->get(n2).damage_per_second);
2218 damage_per_second = MYMAX(damage_per_second,
2219 m_gamedef->ndef()->get(n3).damage_per_second);
2221 if(damage_per_second != 0)
2223 damageLocalPlayer(damage_per_second, true);
2228 Stuff that can be done in an arbitarily large dtime
2230 for(std::list<Player*>::iterator i = m_players.begin();
2231 i != m_players.end(); ++i)
2233 Player *player = *i;
2236 Handle non-local players
2238 if(player->isLocal() == false)
2241 player->move(dtime, *m_map, 100*BS);
2245 // Update lighting on all players on client
2249 v3s16 p = player->getLightPosition();
2250 MapNode n = m_map->getNode(p);
2251 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2253 catch(InvalidPositionException &e){
2254 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2256 player->light = light;
2260 Step active objects and update lighting of them
2263 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2264 for(std::map<u16, ClientActiveObject*>::iterator
2265 i = m_active_objects.begin();
2266 i != m_active_objects.end(); ++i)
2268 ClientActiveObject* obj = i->second;
2270 obj->step(dtime, this);
2278 v3s16 p = obj->getLightPosition();
2279 MapNode n = m_map->getNode(p);
2280 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2282 catch(InvalidPositionException &e){
2283 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2285 obj->updateLight(light);
2290 Step and handle simple objects
2292 for(std::list<ClientSimpleObject*>::iterator
2293 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2295 ClientSimpleObject *simple = *i;
2296 std::list<ClientSimpleObject*>::iterator cur = i;
2298 simple->step(dtime);
2299 if(simple->m_to_be_removed){
2301 m_simple_objects.erase(cur);
2306 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2308 m_simple_objects.push_back(simple);
2311 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2313 std::map<u16, ClientActiveObject*>::iterator n;
2314 n = m_active_objects.find(id);
2315 if(n == m_active_objects.end())
2320 bool isFreeClientActiveObjectId(u16 id,
2321 std::map<u16, ClientActiveObject*> &objects)
2326 return objects.find(id) == objects.end();
2329 u16 getFreeClientActiveObjectId(
2330 std::map<u16, ClientActiveObject*> &objects)
2332 //try to reuse id's as late as possible
2333 static u16 last_used_id = 0;
2334 u16 startid = last_used_id;
2338 if(isFreeClientActiveObjectId(last_used_id, objects))
2339 return last_used_id;
2341 if(last_used_id == startid)
2346 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2349 if(object->getId() == 0)
2351 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2354 infostream<<"ClientEnvironment::addActiveObject(): "
2355 <<"no free ids available"<<std::endl;
2359 object->setId(new_id);
2361 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2363 infostream<<"ClientEnvironment::addActiveObject(): "
2364 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2368 infostream<<"ClientEnvironment::addActiveObject(): "
2369 <<"added (id="<<object->getId()<<")"<<std::endl;
2370 m_active_objects[object->getId()] = object;
2371 object->addToScene(m_smgr, m_texturesource, m_irr);
2372 { // Update lighting immediately
2376 v3s16 p = object->getLightPosition();
2377 MapNode n = m_map->getNode(p);
2378 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2380 catch(InvalidPositionException &e){
2381 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2383 object->updateLight(light);
2385 return object->getId();
2388 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2389 const std::string &init_data)
2391 ClientActiveObject* obj =
2392 ClientActiveObject::create(type, m_gamedef, this);
2395 infostream<<"ClientEnvironment::addActiveObject(): "
2396 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2405 obj->initialize(init_data);
2407 catch(SerializationError &e)
2409 errorstream<<"ClientEnvironment::addActiveObject():"
2410 <<" id="<<id<<" type="<<type
2411 <<": SerializationError in initialize(): "
2413 <<": init_data="<<serializeJsonString(init_data)
2417 addActiveObject(obj);
2420 void ClientEnvironment::removeActiveObject(u16 id)
2422 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2423 <<"id="<<id<<std::endl;
2424 ClientActiveObject* obj = getActiveObject(id);
2427 infostream<<"ClientEnvironment::removeActiveObject(): "
2428 <<"id="<<id<<" not found"<<std::endl;
2431 obj->removeFromScene(true);
2433 m_active_objects.erase(id);
2436 void ClientEnvironment::processActiveObjectMessage(u16 id,
2437 const std::string &data)
2439 ClientActiveObject* obj = getActiveObject(id);
2442 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2443 <<" got message for id="<<id<<", which doesn't exist."
2449 obj->processMessage(data);
2451 catch(SerializationError &e)
2453 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2454 <<" id="<<id<<" type="<<obj->getType()
2455 <<" SerializationError in processMessage(),"
2456 <<" message="<<serializeJsonString(data)
2462 Callbacks for activeobjects
2465 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2467 LocalPlayer *lplayer = getLocalPlayer();
2471 if(lplayer->hp > damage)
2472 lplayer->hp -= damage;
2477 ClientEnvEvent event;
2478 event.type = CEE_PLAYER_DAMAGE;
2479 event.player_damage.amount = damage;
2480 event.player_damage.send_to_server = handle_hp;
2481 m_client_event_queue.push_back(event);
2485 Client likes to call these
2488 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2489 std::vector<DistanceSortedActiveObject> &dest)
2491 for(std::map<u16, ClientActiveObject*>::iterator
2492 i = m_active_objects.begin();
2493 i != m_active_objects.end(); ++i)
2495 ClientActiveObject* obj = i->second;
2497 f32 d = (obj->getPosition() - origin).getLength();
2502 DistanceSortedActiveObject dso(obj, d);
2504 dest.push_back(dso);
2508 ClientEnvEvent ClientEnvironment::getClientEvent()
2510 if(m_client_event_queue.empty())
2512 ClientEnvEvent event;
2513 event.type = CEE_NONE;
2516 return m_client_event_queue.pop_front();
2519 #endif // #ifndef SERVER