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),
56 Environment::~Environment()
59 for(std::list<Player*>::iterator i = m_players.begin();
60 i != m_players.end(); ++i)
66 void Environment::addPlayer(Player *player)
68 DSTACK(__FUNCTION_NAME);
70 Check that peer_ids are unique.
71 Also check that names are unique.
72 Exception: there can be multiple players with peer_id=0
74 // If peer id is non-zero, it has to be unique.
75 if(player->peer_id != 0)
76 assert(getPlayer(player->peer_id) == NULL);
77 // Name has to be unique.
78 assert(getPlayer(player->getName()) == NULL);
80 m_players.push_back(player);
83 void Environment::removePlayer(u16 peer_id)
85 DSTACK(__FUNCTION_NAME);
87 for(std::list<Player*>::iterator i = m_players.begin();
88 i != m_players.end(); ++i)
91 if(player->peer_id != peer_id)
96 // See if there is an another one
97 // (shouldn't be, but just to be sure)
102 Player * Environment::getPlayer(u16 peer_id)
104 for(std::list<Player*>::iterator i = m_players.begin();
105 i != m_players.end(); ++i)
108 if(player->peer_id == peer_id)
114 Player * Environment::getPlayer(const char *name)
116 for(std::list<Player*>::iterator i = m_players.begin();
117 i != m_players.end(); ++i)
120 if(strcmp(player->getName(), name) == 0)
126 Player * Environment::getRandomConnectedPlayer()
128 std::list<Player*> connected_players = getPlayers(true);
129 u32 chosen_one = myrand() % connected_players.size();
131 for(std::list<Player*>::iterator
132 i = connected_players.begin();
133 i != connected_players.end(); ++i)
145 Player * Environment::getNearestConnectedPlayer(v3f pos)
147 std::list<Player*> connected_players = getPlayers(true);
149 Player *nearest_player = NULL;
150 for(std::list<Player*>::iterator
151 i = connected_players.begin();
152 i != connected_players.end(); ++i)
155 f32 d = player->getPosition().getDistanceFrom(pos);
156 if(d < nearest_d || nearest_player == NULL)
159 nearest_player = player;
162 return nearest_player;
165 std::list<Player*> Environment::getPlayers()
170 std::list<Player*> Environment::getPlayers(bool ignore_disconnected)
172 std::list<Player*> newlist;
173 for(std::list<Player*>::iterator
174 i = m_players.begin();
175 i != m_players.end(); ++i)
179 if(ignore_disconnected)
181 // Ignore disconnected players
182 if(player->peer_id == 0)
186 newlist.push_back(player);
191 u32 Environment::getDayNightRatio()
193 bool smooth = g_settings->getBool("enable_shaders");
194 return time_to_daynight_ratio(m_time_of_day_f*24000, smooth);
197 void Environment::stepTimeOfDay(float dtime)
199 m_time_counter += dtime;
200 f32 speed = m_time_of_day_speed * 24000./(24.*3600);
201 u32 units = (u32)(m_time_counter*speed);
202 m_time_counter -= (f32)units / speed;
206 if(m_time_of_day + units >= 24000)
208 m_time_of_day = (m_time_of_day + units) % 24000;
210 m_time_of_day_f = (float)m_time_of_day / 24000.0;
213 m_time_of_day_f += m_time_of_day_speed/24/3600*dtime;
214 if(m_time_of_day_f > 1.0)
215 m_time_of_day_f -= 1.0;
216 if(m_time_of_day_f < 0.0)
217 m_time_of_day_f += 1.0;
225 ABMWithState::ABMWithState(ActiveBlockModifier *abm_):
229 // Initialize timer to random value to spread processing
230 float itv = abm->getTriggerInterval();
231 itv = MYMAX(0.001, itv); // No less than 1ms
232 int minval = MYMAX(-0.51*itv, -60); // Clamp to
233 int maxval = MYMIN(0.51*itv, 60); // +-60 seconds
234 timer = myrand_range(minval, maxval);
241 void fillRadiusBlock(v3s16 p0, s16 r, std::set<v3s16> &list)
244 for(p.X=p0.X-r; p.X<=p0.X+r; p.X++)
245 for(p.Y=p0.Y-r; p.Y<=p0.Y+r; p.Y++)
246 for(p.Z=p0.Z-r; p.Z<=p0.Z+r; p.Z++)
253 void ActiveBlockList::update(std::list<v3s16> &active_positions,
255 std::set<v3s16> &blocks_removed,
256 std::set<v3s16> &blocks_added)
261 std::set<v3s16> newlist;
262 for(std::list<v3s16>::iterator i = active_positions.begin();
263 i != active_positions.end(); ++i)
265 fillRadiusBlock(*i, radius, newlist);
269 Find out which blocks on the old list are not on the new list
271 // Go through old list
272 for(std::set<v3s16>::iterator i = m_list.begin();
273 i != m_list.end(); ++i)
276 // If not on new list, it's been removed
277 if(newlist.find(p) == newlist.end())
278 blocks_removed.insert(p);
282 Find out which blocks on the new list are not on the old list
284 // Go through new list
285 for(std::set<v3s16>::iterator i = newlist.begin();
286 i != newlist.end(); ++i)
289 // If not on old list, it's been added
290 if(m_list.find(p) == m_list.end())
291 blocks_added.insert(p);
298 for(std::set<v3s16>::iterator i = newlist.begin();
299 i != newlist.end(); ++i)
310 ServerEnvironment::ServerEnvironment(ServerMap *map,
311 GameScripting *scriptIface,
312 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
314 m_script(scriptIface),
317 m_random_spawn_timer(3),
318 m_send_recommended_timer(0),
319 m_active_block_interval_overload_skip(0),
321 m_game_time_fraction_counter(0),
322 m_recommended_send_interval(0.1),
323 m_max_lag_estimate(0.1)
327 ServerEnvironment::~ServerEnvironment()
329 // Clear active block list.
330 // This makes the next one delete all active objects.
331 m_active_blocks.clear();
333 // Convert all objects to static and delete the active objects
334 deactivateFarObjects(true);
339 // Delete ActiveBlockModifiers
340 for(std::list<ABMWithState>::iterator
341 i = m_abms.begin(); i != m_abms.end(); ++i){
346 Map & ServerEnvironment::getMap()
351 ServerMap & ServerEnvironment::getServerMap()
356 bool ServerEnvironment::line_of_sight(v3f pos1, v3f pos2, float stepsize)
358 float distance = pos1.getDistanceFrom(pos2);
360 //calculate normalized direction vector
361 v3f normalized_vector = v3f((pos2.X - pos1.X)/distance,
362 (pos2.Y - pos1.Y)/distance,
363 (pos2.Z - pos1.Z)/distance);
365 //find out if there's a node on path between pos1 and pos2
366 for (float i = 1; i < distance; i += stepsize) {
367 v3s16 pos = floatToInt(v3f(normalized_vector.X * i,
368 normalized_vector.Y * i,
369 normalized_vector.Z * i) +pos1,BS);
371 MapNode n = getMap().getNodeNoEx(pos);
373 if(n.param0 != CONTENT_AIR) {
380 void ServerEnvironment::serializePlayers(const std::string &savedir)
382 std::string players_path = savedir + "/players";
383 fs::CreateDir(players_path);
385 std::set<Player*> saved_players;
387 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
388 for(u32 i=0; i<player_files.size(); i++)
390 if(player_files[i].dir || player_files[i].name[0] == '.')
393 // Full path to this file
394 std::string path = players_path + "/" + player_files[i].name;
396 //infostream<<"Checking player file "<<path<<std::endl;
398 // Load player to see what is its name
399 RemotePlayer testplayer(m_gamedef);
401 // Open file and deserialize
402 std::ifstream is(path.c_str(), std::ios_base::binary);
403 if(is.good() == false)
405 infostream<<"Failed to read "<<path<<std::endl;
408 testplayer.deSerialize(is, player_files[i].name);
411 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
413 // Search for the player
414 std::string playername = testplayer.getName();
415 Player *player = getPlayer(playername.c_str());
418 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
422 //infostream<<"Found matching player, overwriting."<<std::endl;
424 // OK, found. Save player there.
425 if(player->checkModified())
427 // Open file and serialize
428 std::ostringstream ss(std::ios_base::binary);
429 player->serialize(ss);
430 if(!fs::safeWriteToFile(path, ss.str()))
432 infostream<<"Failed to write "<<path<<std::endl;
435 saved_players.insert(player);
437 saved_players.insert(player);
441 for(std::list<Player*>::iterator i = m_players.begin();
442 i != m_players.end(); ++i)
445 if(saved_players.find(player) != saved_players.end())
447 /*infostream<<"Player "<<player->getName()
448 <<" was already saved."<<std::endl;*/
451 std::string playername = player->getName();
452 // Don't save unnamed player
455 //infostream<<"Not saving unnamed player."<<std::endl;
461 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
462 playername = "player";
463 std::string path = players_path + "/" + playername;
465 for(u32 i=0; i<1000; i++)
467 if(fs::PathExists(path) == false)
472 path = players_path + "/" + playername + itos(i);
476 infostream<<"Didn't find free file for player"<<std::endl;
481 /*infostream<<"Saving player "<<player->getName()<<" to "
483 // Open file and serialize
484 std::ostringstream ss(std::ios_base::binary);
485 player->serialize(ss);
486 if(!fs::safeWriteToFile(path, ss.str()))
488 infostream<<"Failed to write "<<path<<std::endl;
491 saved_players.insert(player);
495 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
498 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
500 std::string players_path = savedir + "/players";
502 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
503 for(u32 i=0; i<player_files.size(); i++)
505 if(player_files[i].dir)
508 // Full path to this file
509 std::string path = players_path + "/" + player_files[i].name;
511 //infostream<<"Checking player file "<<path<<std::endl;
513 // Load player to see what is its name
514 RemotePlayer testplayer(m_gamedef);
516 // Open file and deserialize
517 std::ifstream is(path.c_str(), std::ios_base::binary);
518 if(is.good() == false)
520 infostream<<"Failed to read "<<path<<std::endl;
523 testplayer.deSerialize(is, player_files[i].name);
526 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
528 infostream<<"Not loading player with invalid name: "
529 <<testplayer.getName()<<std::endl;
532 /*infostream<<"Loaded test player with name "<<testplayer.getName()
535 // Search for the player
536 std::string playername = testplayer.getName();
537 Player *player = getPlayer(playername.c_str());
538 bool newplayer = false;
541 //infostream<<"Is a new player"<<std::endl;
542 player = new RemotePlayer(m_gamedef);
548 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
550 // Open file and deserialize
551 std::ifstream is(path.c_str(), std::ios_base::binary);
552 if(is.good() == false)
554 infostream<<"Failed to read "<<path<<std::endl;
557 player->deSerialize(is, player_files[i].name);
567 void ServerEnvironment::saveMeta(const std::string &savedir)
569 std::string path = savedir + "/env_meta.txt";
571 // Open file and serialize
572 std::ostringstream ss(std::ios_base::binary);
575 args.setU64("game_time", m_game_time);
576 args.setU64("time_of_day", getTimeOfDay());
580 if(!fs::safeWriteToFile(path, ss.str()))
582 infostream<<"ServerEnvironment::saveMeta(): Failed to write "
584 throw SerializationError("Couldn't save env meta");
588 void ServerEnvironment::loadMeta(const std::string &savedir)
590 std::string path = savedir + "/env_meta.txt";
592 // Open file and deserialize
593 std::ifstream is(path.c_str(), std::ios_base::binary);
594 if(is.good() == false)
596 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
598 throw SerializationError("Couldn't load env meta");
606 throw SerializationError
607 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
609 std::getline(is, line);
610 std::string trimmedline = trim(line);
611 if(trimmedline == "EnvArgsEnd")
613 args.parseConfigLine(line);
617 m_game_time = args.getU64("game_time");
618 }catch(SettingNotFoundException &e){
619 // Getting this is crucial, otherwise timestamps are useless
620 throw SerializationError("Couldn't load env meta game_time");
624 m_time_of_day = args.getU64("time_of_day");
625 }catch(SettingNotFoundException &e){
626 // This is not as important
627 m_time_of_day = 9000;
633 ActiveBlockModifier *abm;
635 std::set<content_t> required_neighbors;
641 ServerEnvironment *m_env;
642 std::map<content_t, std::list<ActiveABM> > m_aabms;
644 ABMHandler(std::list<ABMWithState> &abms,
645 float dtime_s, ServerEnvironment *env,
651 INodeDefManager *ndef = env->getGameDef()->ndef();
652 for(std::list<ABMWithState>::iterator
653 i = abms.begin(); i != abms.end(); ++i){
654 ActiveBlockModifier *abm = i->abm;
655 float trigger_interval = abm->getTriggerInterval();
656 if(trigger_interval < 0.001)
657 trigger_interval = 0.001;
658 float actual_interval = dtime_s;
661 if(i->timer < trigger_interval)
663 i->timer -= trigger_interval;
664 actual_interval = trigger_interval;
666 float intervals = actual_interval / trigger_interval;
669 float chance = abm->getTriggerChance();
674 aabm.chance = chance / intervals;
678 std::set<std::string> required_neighbors_s
679 = abm->getRequiredNeighbors();
680 for(std::set<std::string>::iterator
681 i = required_neighbors_s.begin();
682 i != required_neighbors_s.end(); i++)
684 ndef->getIds(*i, aabm.required_neighbors);
687 std::set<std::string> contents_s = abm->getTriggerContents();
688 for(std::set<std::string>::iterator
689 i = contents_s.begin(); i != contents_s.end(); i++)
691 std::set<content_t> ids;
692 ndef->getIds(*i, ids);
693 for(std::set<content_t>::const_iterator k = ids.begin();
697 std::map<content_t, std::list<ActiveABM> >::iterator j;
699 if(j == m_aabms.end()){
700 std::list<ActiveABM> aabmlist;
701 m_aabms[c] = aabmlist;
704 j->second.push_back(aabm);
709 void apply(MapBlock *block)
714 ServerMap *map = &m_env->getServerMap();
717 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
718 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
719 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
721 MapNode n = block->getNodeNoEx(p0);
722 content_t c = n.getContent();
723 v3s16 p = p0 + block->getPosRelative();
725 std::map<content_t, std::list<ActiveABM> >::iterator j;
727 if(j == m_aabms.end())
730 for(std::list<ActiveABM>::iterator
731 i = j->second.begin(); i != j->second.end(); i++)
733 if(myrand() % i->chance != 0)
737 if(!i->required_neighbors.empty())
740 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
741 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
742 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
746 MapNode n = map->getNodeNoEx(p1);
747 content_t c = n.getContent();
748 std::set<content_t>::const_iterator k;
749 k = i->required_neighbors.find(c);
750 if(k != i->required_neighbors.end()){
754 // No required neighbor found
759 // Find out how many objects the block contains
760 u32 active_object_count = block->m_static_objects.m_active.size();
761 // Find out how many objects this and all the neighbors contain
762 u32 active_object_count_wider = 0;
763 u32 wider_unknown_count = 0;
764 for(s16 x=-1; x<=1; x++)
765 for(s16 y=-1; y<=1; y++)
766 for(s16 z=-1; z<=1; z++)
768 MapBlock *block2 = map->getBlockNoCreateNoEx(
769 block->getPos() + v3s16(x,y,z));
771 wider_unknown_count = 0;
774 active_object_count_wider +=
775 block2->m_static_objects.m_active.size()
776 + block2->m_static_objects.m_stored.size();
779 u32 wider_known_count = 3*3*3 - wider_unknown_count;
780 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
782 // Call all the trigger variations
783 i->abm->trigger(m_env, p, n);
784 i->abm->trigger(m_env, p, n,
785 active_object_count, active_object_count_wider);
791 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
793 // Get time difference
795 u32 stamp = block->getTimestamp();
796 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
797 dtime_s = m_game_time - block->getTimestamp();
798 dtime_s += additional_dtime;
800 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
801 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
803 // Set current time as timestamp
804 block->setTimestampNoChangedFlag(m_game_time);
806 /*infostream<<"ServerEnvironment::activateBlock(): block is "
807 <<dtime_s<<" seconds old."<<std::endl;*/
809 // Activate stored objects
810 activateObjects(block, dtime_s);
813 std::map<v3s16, NodeTimer> elapsed_timers =
814 block->m_node_timers.step((float)dtime_s);
815 if(!elapsed_timers.empty()){
817 for(std::map<v3s16, NodeTimer>::iterator
818 i = elapsed_timers.begin();
819 i != elapsed_timers.end(); i++){
820 n = block->getNodeNoEx(i->first);
821 v3s16 p = i->first + block->getPosRelative();
822 if(m_script->node_on_timer(p,n,i->second.elapsed))
823 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
827 /* Handle ActiveBlockModifiers */
828 ABMHandler abmhandler(m_abms, dtime_s, this, false);
829 abmhandler.apply(block);
832 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
834 m_abms.push_back(ABMWithState(abm));
837 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
839 INodeDefManager *ndef = m_gamedef->ndef();
840 MapNode n_old = m_map->getNodeNoEx(p);
842 if(ndef->get(n_old).has_on_destruct)
843 m_script->node_on_destruct(p, n_old);
845 bool succeeded = m_map->addNodeWithEvent(p, n);
848 // Call post-destructor
849 if(ndef->get(n_old).has_after_destruct)
850 m_script->node_after_destruct(p, n_old);
852 if(ndef->get(n).has_on_construct)
853 m_script->node_on_construct(p, n);
857 bool ServerEnvironment::removeNode(v3s16 p)
859 INodeDefManager *ndef = m_gamedef->ndef();
860 MapNode n_old = m_map->getNodeNoEx(p);
862 if(ndef->get(n_old).has_on_destruct)
863 m_script->node_on_destruct(p, n_old);
865 // This is slightly optimized compared to addNodeWithEvent(air)
866 bool succeeded = m_map->removeNodeWithEvent(p);
869 // Call post-destructor
870 if(ndef->get(n_old).has_after_destruct)
871 m_script->node_after_destruct(p, n_old);
872 // Air doesn't require constructor
876 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
878 std::set<u16> objects;
879 for(std::map<u16, ServerActiveObject*>::iterator
880 i = m_active_objects.begin();
881 i != m_active_objects.end(); ++i)
883 ServerActiveObject* obj = i->second;
885 v3f objectpos = obj->getBasePosition();
886 if(objectpos.getDistanceFrom(pos) > radius)
893 void ServerEnvironment::clearAllObjects()
895 infostream<<"ServerEnvironment::clearAllObjects(): "
896 <<"Removing all active objects"<<std::endl;
897 std::list<u16> objects_to_remove;
898 for(std::map<u16, ServerActiveObject*>::iterator
899 i = m_active_objects.begin();
900 i != m_active_objects.end(); ++i)
902 ServerActiveObject* obj = i->second;
903 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
906 // Delete static object if block is loaded
907 if(obj->m_static_exists){
908 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
910 block->m_static_objects.remove(id);
911 block->raiseModified(MOD_STATE_WRITE_NEEDED,
913 obj->m_static_exists = false;
916 // If known by some client, don't delete immediately
917 if(obj->m_known_by_count > 0){
918 obj->m_pending_deactivation = true;
919 obj->m_removed = true;
923 // Tell the object about removal
924 obj->removingFromEnvironment();
925 // Deregister in scripting api
926 m_script->removeObjectReference(obj);
928 // Delete active object
929 if(obj->environmentDeletes())
931 // Id to be removed from m_active_objects
932 objects_to_remove.push_back(id);
934 // Remove references from m_active_objects
935 for(std::list<u16>::iterator i = objects_to_remove.begin();
936 i != objects_to_remove.end(); ++i)
938 m_active_objects.erase(*i);
941 // Get list of loaded blocks
942 std::list<v3s16> loaded_blocks;
943 infostream<<"ServerEnvironment::clearAllObjects(): "
944 <<"Listing all loaded blocks"<<std::endl;
945 m_map->listAllLoadedBlocks(loaded_blocks);
946 infostream<<"ServerEnvironment::clearAllObjects(): "
947 <<"Done listing all loaded blocks: "
948 <<loaded_blocks.size()<<std::endl;
950 // Get list of loadable blocks
951 std::list<v3s16> loadable_blocks;
952 infostream<<"ServerEnvironment::clearAllObjects(): "
953 <<"Listing all loadable blocks"<<std::endl;
954 m_map->listAllLoadableBlocks(loadable_blocks);
955 infostream<<"ServerEnvironment::clearAllObjects(): "
956 <<"Done listing all loadable blocks: "
957 <<loadable_blocks.size()
958 <<", now clearing"<<std::endl;
960 // Grab a reference on each loaded block to avoid unloading it
961 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
962 i != loaded_blocks.end(); ++i)
965 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
970 // Remove objects in all loadable blocks
971 u32 unload_interval = g_settings->getS32("max_clearobjects_extra_loaded_blocks");
972 unload_interval = MYMAX(unload_interval, 1);
973 u32 report_interval = loadable_blocks.size() / 10;
974 u32 num_blocks_checked = 0;
975 u32 num_blocks_cleared = 0;
976 u32 num_objs_cleared = 0;
977 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
978 i != loadable_blocks.end(); ++i)
981 MapBlock *block = m_map->emergeBlock(p, false);
983 errorstream<<"ServerEnvironment::clearAllObjects(): "
984 <<"Failed to emerge block "<<PP(p)<<std::endl;
987 u32 num_stored = block->m_static_objects.m_stored.size();
988 u32 num_active = block->m_static_objects.m_active.size();
989 if(num_stored != 0 || num_active != 0){
990 block->m_static_objects.m_stored.clear();
991 block->m_static_objects.m_active.clear();
992 block->raiseModified(MOD_STATE_WRITE_NEEDED,
994 num_objs_cleared += num_stored + num_active;
995 num_blocks_cleared++;
997 num_blocks_checked++;
999 if(num_blocks_checked % report_interval == 0){
1000 float percent = 100.0 * (float)num_blocks_checked /
1001 loadable_blocks.size();
1002 infostream<<"ServerEnvironment::clearAllObjects(): "
1003 <<"Cleared "<<num_objs_cleared<<" objects"
1004 <<" in "<<num_blocks_cleared<<" blocks ("
1005 <<percent<<"%)"<<std::endl;
1007 if(num_blocks_checked % unload_interval == 0){
1008 m_map->unloadUnreferencedBlocks();
1011 m_map->unloadUnreferencedBlocks();
1013 // Drop references that were added above
1014 for(std::list<v3s16>::iterator i = loaded_blocks.begin();
1015 i != loaded_blocks.end(); ++i)
1018 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1023 infostream<<"ServerEnvironment::clearAllObjects(): "
1024 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
1025 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
1028 void ServerEnvironment::step(float dtime)
1030 DSTACK(__FUNCTION_NAME);
1032 //TimeTaker timer("ServerEnv step");
1034 /* Step time of day */
1035 stepTimeOfDay(dtime);
1038 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1039 // really matter that much.
1040 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1046 m_game_time_fraction_counter += dtime;
1047 u32 inc_i = (u32)m_game_time_fraction_counter;
1048 m_game_time += inc_i;
1049 m_game_time_fraction_counter -= (float)inc_i;
1056 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1057 for(std::list<Player*>::iterator i = m_players.begin();
1058 i != m_players.end(); ++i)
1060 Player *player = *i;
1062 // Ignore disconnected players
1063 if(player->peer_id == 0)
1067 player->move(dtime, *m_map, 100*BS);
1072 Manage active block list
1074 if(m_active_blocks_management_interval.step(dtime, 2.0))
1076 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1078 Get player block positions
1080 std::list<v3s16> players_blockpos;
1081 for(std::list<Player*>::iterator
1082 i = m_players.begin();
1083 i != m_players.end(); ++i)
1085 Player *player = *i;
1086 // Ignore disconnected players
1087 if(player->peer_id == 0)
1089 v3s16 blockpos = getNodeBlockPos(
1090 floatToInt(player->getPosition(), BS));
1091 players_blockpos.push_back(blockpos);
1095 Update list of active blocks, collecting changes
1097 const s16 active_block_range = g_settings->getS16("active_block_range");
1098 std::set<v3s16> blocks_removed;
1099 std::set<v3s16> blocks_added;
1100 m_active_blocks.update(players_blockpos, active_block_range,
1101 blocks_removed, blocks_added);
1104 Handle removed blocks
1107 // Convert active objects that are no more in active blocks to static
1108 deactivateFarObjects(false);
1110 for(std::set<v3s16>::iterator
1111 i = blocks_removed.begin();
1112 i != blocks_removed.end(); ++i)
1116 /* infostream<<"Server: Block " << PP(p)
1117 << " became inactive"<<std::endl; */
1119 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1123 // Set current time as timestamp (and let it set ChangedFlag)
1124 block->setTimestamp(m_game_time);
1131 for(std::set<v3s16>::iterator
1132 i = blocks_added.begin();
1133 i != blocks_added.end(); ++i)
1137 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1139 // Block needs to be fetched first
1140 m_emerger->enqueueBlockEmerge(
1141 PEER_ID_INEXISTENT, p, false);
1142 m_active_blocks.m_list.erase(p);
1146 activateBlock(block);
1147 /* infostream<<"Server: Block " << PP(p)
1148 << " became active"<<std::endl; */
1153 Mess around in active blocks
1155 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1157 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1161 for(std::set<v3s16>::iterator
1162 i = m_active_blocks.m_list.begin();
1163 i != m_active_blocks.m_list.end(); ++i)
1167 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1168 <<") being handled"<<std::endl;*/
1170 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1174 // Reset block usage timer
1175 block->resetUsageTimer();
1177 // Set current time as timestamp
1178 block->setTimestampNoChangedFlag(m_game_time);
1179 // If time has changed much from the one on disk,
1180 // set block to be saved when it is unloaded
1181 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1182 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1183 "Timestamp older than 60s (step)");
1186 std::map<v3s16, NodeTimer> elapsed_timers =
1187 block->m_node_timers.step((float)dtime);
1188 if(!elapsed_timers.empty()){
1190 for(std::map<v3s16, NodeTimer>::iterator
1191 i = elapsed_timers.begin();
1192 i != elapsed_timers.end(); i++){
1193 n = block->getNodeNoEx(i->first);
1194 p = i->first + block->getPosRelative();
1195 if(m_script->node_on_timer(p,n,i->second.elapsed))
1196 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1202 const float abm_interval = 1.0;
1203 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1205 if(m_active_block_interval_overload_skip > 0){
1206 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1207 m_active_block_interval_overload_skip--;
1210 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1211 TimeTaker timer("modify in active blocks");
1213 // Initialize handling of ActiveBlockModifiers
1214 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1216 for(std::set<v3s16>::iterator
1217 i = m_active_blocks.m_list.begin();
1218 i != m_active_blocks.m_list.end(); ++i)
1222 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1223 <<") being handled"<<std::endl;*/
1225 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1229 // Set current time as timestamp
1230 block->setTimestampNoChangedFlag(m_game_time);
1232 /* Handle ActiveBlockModifiers */
1233 abmhandler.apply(block);
1236 u32 time_ms = timer.stop(true);
1237 u32 max_time_ms = 200;
1238 if(time_ms > max_time_ms){
1239 infostream<<"WARNING: active block modifiers took "
1240 <<time_ms<<"ms (longer than "
1241 <<max_time_ms<<"ms)"<<std::endl;
1242 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1247 Step script environment (run global on_step())
1249 m_script->environment_Step(dtime);
1255 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1256 //TimeTaker timer("Step active objects");
1258 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1260 // This helps the objects to send data at the same time
1261 bool send_recommended = false;
1262 m_send_recommended_timer += dtime;
1263 if(m_send_recommended_timer > getSendRecommendedInterval())
1265 m_send_recommended_timer -= getSendRecommendedInterval();
1266 send_recommended = true;
1269 for(std::map<u16, ServerActiveObject*>::iterator
1270 i = m_active_objects.begin();
1271 i != m_active_objects.end(); ++i)
1273 ServerActiveObject* obj = i->second;
1274 // Remove non-peaceful mobs on peaceful mode
1275 if(g_settings->getBool("only_peaceful_mobs")){
1276 if(!obj->isPeaceful())
1277 obj->m_removed = true;
1279 // Don't step if is to be removed or stored statically
1280 if(obj->m_removed || obj->m_pending_deactivation)
1283 obj->step(dtime, send_recommended);
1284 // Read messages from object
1285 while(!obj->m_messages_out.empty())
1287 m_active_object_messages.push_back(
1288 obj->m_messages_out.pop_front());
1294 Manage active objects
1296 if(m_object_management_interval.step(dtime, 0.5))
1298 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1300 Remove objects that satisfy (m_removed && m_known_by_count==0)
1302 removeRemovedObjects();
1306 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1308 std::map<u16, ServerActiveObject*>::iterator n;
1309 n = m_active_objects.find(id);
1310 if(n == m_active_objects.end())
1315 bool isFreeServerActiveObjectId(u16 id,
1316 std::map<u16, ServerActiveObject*> &objects)
1321 return objects.find(id) == objects.end();
1324 u16 getFreeServerActiveObjectId(
1325 std::map<u16, ServerActiveObject*> &objects)
1327 //try to reuse id's as late as possible
1328 static u16 last_used_id = 0;
1329 u16 startid = last_used_id;
1333 if(isFreeServerActiveObjectId(last_used_id, objects))
1334 return last_used_id;
1336 if(last_used_id == startid)
1341 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1344 u16 id = addActiveObjectRaw(object, true, 0);
1349 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1353 v3f objectpos = obj->getBasePosition();
1355 // The block in which the object resides in
1356 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1359 Update the static data
1362 // Create new static object
1363 std::string staticdata = obj->getStaticData();
1364 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1365 // Add to the block where the object is located in
1366 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1367 // Get or generate the block
1368 MapBlock *block = m_map->emergeBlock(blockpos);
1370 bool succeeded = false;
1374 block->m_static_objects.insert(0, s_obj);
1375 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1376 "addActiveObjectAsStatic");
1380 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1381 <<"Could not find or generate "
1382 <<"a block for storing static object"<<std::endl;
1386 if(obj->environmentDeletes())
1394 Finds out what new objects have been added to
1395 inside a radius around a position
1397 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1398 std::set<u16> ¤t_objects,
1399 std::set<u16> &added_objects)
1401 v3f pos_f = intToFloat(pos, BS);
1402 f32 radius_f = radius * BS;
1404 Go through the object list,
1405 - discard m_removed objects,
1406 - discard objects that are too far away,
1407 - discard objects that are found in current_objects.
1408 - add remaining objects to added_objects
1410 for(std::map<u16, ServerActiveObject*>::iterator
1411 i = m_active_objects.begin();
1412 i != m_active_objects.end(); ++i)
1416 ServerActiveObject *object = i->second;
1419 // Discard if removed
1420 if(object->m_removed)
1422 if(object->unlimitedTransferDistance() == false){
1423 // Discard if too far
1424 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1425 if(distance_f > radius_f)
1428 // Discard if already on current_objects
1429 std::set<u16>::iterator n;
1430 n = current_objects.find(id);
1431 if(n != current_objects.end())
1433 // Add to added_objects
1434 added_objects.insert(id);
1439 Finds out what objects have been removed from
1440 inside a radius around a position
1442 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1443 std::set<u16> ¤t_objects,
1444 std::set<u16> &removed_objects)
1446 v3f pos_f = intToFloat(pos, BS);
1447 f32 radius_f = radius * BS;
1449 Go through current_objects; object is removed if:
1450 - object is not found in m_active_objects (this is actually an
1451 error condition; objects should be set m_removed=true and removed
1452 only after all clients have been informed about removal), or
1453 - object has m_removed=true, or
1454 - object is too far away
1456 for(std::set<u16>::iterator
1457 i = current_objects.begin();
1458 i != current_objects.end(); ++i)
1461 ServerActiveObject *object = getActiveObject(id);
1464 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1465 <<" object in current_objects is NULL"<<std::endl;
1466 removed_objects.insert(id);
1470 if(object->m_removed)
1472 removed_objects.insert(id);
1476 // If transfer distance is unlimited, don't remove
1477 if(object->unlimitedTransferDistance())
1480 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1482 if(distance_f >= radius_f)
1484 removed_objects.insert(id);
1492 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1494 if(m_active_object_messages.empty())
1495 return ActiveObjectMessage(0);
1497 ActiveObjectMessage message = m_active_object_messages.front();
1498 m_active_object_messages.pop_front();
1503 ************ Private methods *************
1506 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1507 bool set_changed, u32 dtime_s)
1510 if(object->getId() == 0){
1511 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1514 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1515 <<"no free ids available"<<std::endl;
1516 if(object->environmentDeletes())
1520 object->setId(new_id);
1523 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1524 <<"supplied with id "<<object->getId()<<std::endl;
1526 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1528 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1529 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1530 if(object->environmentDeletes())
1534 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1535 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1537 m_active_objects[object->getId()] = object;
1539 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1540 <<"Added id="<<object->getId()<<"; there are now "
1541 <<m_active_objects.size()<<" active objects."
1544 // Register reference in scripting api (must be done before post-init)
1545 m_script->addObjectReference(object);
1546 // Post-initialize object
1547 object->addedToEnvironment(dtime_s);
1549 // Add static data to block
1550 if(object->isStaticAllowed())
1552 // Add static object to active static list of the block
1553 v3f objectpos = object->getBasePosition();
1554 std::string staticdata = object->getStaticData();
1555 StaticObject s_obj(object->getType(), objectpos, staticdata);
1556 // Add to the block where the object is located in
1557 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1558 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1561 block->m_static_objects.m_active[object->getId()] = s_obj;
1562 object->m_static_exists = true;
1563 object->m_static_block = blockpos;
1566 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1567 "addActiveObjectRaw");
1570 v3s16 p = floatToInt(objectpos, BS);
1571 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1572 <<"could not find block for storing id="<<object->getId()
1573 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1577 return object->getId();
1581 Remove objects that satisfy (m_removed && m_known_by_count==0)
1583 void ServerEnvironment::removeRemovedObjects()
1585 std::list<u16> objects_to_remove;
1586 for(std::map<u16, ServerActiveObject*>::iterator
1587 i = m_active_objects.begin();
1588 i != m_active_objects.end(); ++i)
1591 ServerActiveObject* obj = i->second;
1592 // This shouldn't happen but check it
1595 infostream<<"NULL object found in ServerEnvironment"
1596 <<" while finding removed objects. id="<<id<<std::endl;
1597 // Id to be removed from m_active_objects
1598 objects_to_remove.push_back(id);
1603 We will delete objects that are marked as removed or thatare
1604 waiting for deletion after deactivation
1606 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1610 Delete static data from block if is marked as removed
1612 if(obj->m_static_exists && obj->m_removed)
1614 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1616 block->m_static_objects.remove(id);
1617 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1618 "removeRemovedObjects");
1619 obj->m_static_exists = false;
1621 infostream << "failed to emerge block from which "
1622 "an object to be removed was loaded from. id="<<id<<std::endl;
1626 // If m_known_by_count > 0, don't actually remove.
1627 if(obj->m_known_by_count > 0)
1630 // Tell the object about removal
1631 obj->removingFromEnvironment();
1632 // Deregister in scripting api
1633 m_script->removeObjectReference(obj);
1636 if(obj->environmentDeletes())
1638 // Id to be removed from m_active_objects
1639 objects_to_remove.push_back(id);
1641 // Remove references from m_active_objects
1642 for(std::list<u16>::iterator i = objects_to_remove.begin();
1643 i != objects_to_remove.end(); ++i)
1645 m_active_objects.erase(*i);
1649 static void print_hexdump(std::ostream &o, const std::string &data)
1651 const int linelength = 16;
1652 for(int l=0; ; l++){
1653 int i0 = linelength * l;
1654 bool at_end = false;
1655 int thislinelength = linelength;
1656 if(i0 + thislinelength > (int)data.size()){
1657 thislinelength = data.size() - i0;
1660 for(int di=0; di<linelength; di++){
1663 if(di<thislinelength)
1664 snprintf(buf, 4, "%.2x ", data[i]);
1666 snprintf(buf, 4, " ");
1670 for(int di=0; di<thislinelength; di++){
1684 Convert stored objects from blocks near the players to active.
1686 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1690 // Ignore if no stored objects (to not set changed flag)
1691 if(block->m_static_objects.m_stored.size() == 0)
1693 verbosestream<<"ServerEnvironment::activateObjects(): "
1694 <<"activating objects of block "<<PP(block->getPos())
1695 <<" ("<<block->m_static_objects.m_stored.size()
1696 <<" objects)"<<std::endl;
1697 bool large_amount = (block->m_static_objects.m_stored.size() > g_settings->getU16("max_objects_per_block"));
1699 errorstream<<"suspiciously large amount of objects detected: "
1700 <<block->m_static_objects.m_stored.size()<<" in "
1701 <<PP(block->getPos())
1702 <<"; removing all of them."<<std::endl;
1703 // Clear stored list
1704 block->m_static_objects.m_stored.clear();
1705 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1706 "stored list cleared in activateObjects due to "
1707 "large amount of objects");
1710 // A list for objects that couldn't be converted to active for some
1711 // reason. They will be stored back.
1712 std::list<StaticObject> new_stored;
1713 // Loop through stored static objects
1714 for(std::list<StaticObject>::iterator
1715 i = block->m_static_objects.m_stored.begin();
1716 i != block->m_static_objects.m_stored.end(); ++i)
1718 /*infostream<<"Server: Creating an active object from "
1719 <<"static data"<<std::endl;*/
1720 StaticObject &s_obj = *i;
1721 // Create an active object from the data
1722 ServerActiveObject *obj = ServerActiveObject::create
1723 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1724 // If couldn't create object, store static data back.
1727 errorstream<<"ServerEnvironment::activateObjects(): "
1728 <<"failed to create active object from static object "
1729 <<"in block "<<PP(s_obj.pos/BS)
1730 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1731 print_hexdump(verbosestream, s_obj.data);
1733 new_stored.push_back(s_obj);
1736 verbosestream<<"ServerEnvironment::activateObjects(): "
1737 <<"activated static object pos="<<PP(s_obj.pos/BS)
1738 <<" type="<<(int)s_obj.type<<std::endl;
1739 // This will also add the object to the active static list
1740 addActiveObjectRaw(obj, false, dtime_s);
1742 // Clear stored list
1743 block->m_static_objects.m_stored.clear();
1744 // Add leftover failed stuff to stored list
1745 for(std::list<StaticObject>::iterator
1746 i = new_stored.begin();
1747 i != new_stored.end(); ++i)
1749 StaticObject &s_obj = *i;
1750 block->m_static_objects.m_stored.push_back(s_obj);
1753 Note: Block hasn't really been modified here.
1754 The objects have just been activated and moved from the stored
1755 static list to the active static list.
1756 As such, the block is essentially the same.
1757 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1758 Otherwise there would be a huge amount of unnecessary I/O.
1763 Convert objects that are not standing inside active blocks to static.
1765 If m_known_by_count != 0, active object is not deleted, but static
1766 data is still updated.
1768 If force_delete is set, active object is deleted nevertheless. It
1769 shall only be set so in the destructor of the environment.
1771 If block wasn't generated (not in memory or on disk),
1773 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1775 std::list<u16> objects_to_remove;
1776 for(std::map<u16, ServerActiveObject*>::iterator
1777 i = m_active_objects.begin();
1778 i != m_active_objects.end(); ++i)
1780 ServerActiveObject* obj = i->second;
1783 // Do not deactivate if static data creation not allowed
1784 if(!force_delete && !obj->isStaticAllowed())
1787 // If pending deactivation, let removeRemovedObjects() do it
1788 if(!force_delete && obj->m_pending_deactivation)
1792 v3f objectpos = obj->getBasePosition();
1794 // The block in which the object resides in
1795 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1797 // If block is active, don't remove
1798 if(!force_delete && m_active_blocks.contains(blockpos_o))
1801 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1802 <<"deactivating object id="<<id<<" on inactive block "
1803 <<PP(blockpos_o)<<std::endl;
1805 // If known by some client, don't immediately delete.
1806 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1809 Update the static data
1812 if(obj->isStaticAllowed())
1814 // Create new static object
1815 std::string staticdata_new = obj->getStaticData();
1816 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1818 bool stays_in_same_block = false;
1819 bool data_changed = true;
1821 if(obj->m_static_exists){
1822 if(obj->m_static_block == blockpos_o)
1823 stays_in_same_block = true;
1825 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1827 std::map<u16, StaticObject>::iterator n =
1828 block->m_static_objects.m_active.find(id);
1829 if(n != block->m_static_objects.m_active.end()){
1830 StaticObject static_old = n->second;
1832 float save_movem = obj->getMinimumSavedMovement();
1834 if(static_old.data == staticdata_new &&
1835 (static_old.pos - objectpos).getLength() < save_movem)
1836 data_changed = false;
1838 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1839 <<"id="<<id<<" m_static_exists=true but "
1840 <<"static data doesn't actually exist in "
1841 <<PP(obj->m_static_block)<<std::endl;
1845 bool shall_be_written = (!stays_in_same_block || data_changed);
1847 // Delete old static object
1848 if(obj->m_static_exists)
1850 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1853 block->m_static_objects.remove(id);
1854 obj->m_static_exists = false;
1855 // Only mark block as modified if data changed considerably
1856 if(shall_be_written)
1857 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1858 "deactivateFarObjects: Static data "
1859 "changed considerably");
1863 // Add to the block where the object is located in
1864 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1865 // Get or generate the block
1866 MapBlock *block = NULL;
1868 block = m_map->emergeBlock(blockpos);
1869 } catch(InvalidPositionException &e){
1870 // Handled via NULL pointer
1875 if(block->m_static_objects.m_stored.size() >= g_settings->getU16("max_objects_per_block")){
1876 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1877 <<" statically but block "<<PP(blockpos)
1878 <<" already contains "
1879 <<block->m_static_objects.m_stored.size()
1881 <<" Forcing delete."<<std::endl;
1882 force_delete = true;
1884 // If static counterpart already exists, remove it first.
1885 // This shouldn't happen, but happens rarely for some
1886 // unknown reason. Unsuccessful attempts have been made to
1887 // find said reason.
1888 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1889 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1891 block->m_static_objects.remove(id);
1894 block->m_static_objects.insert(0, s_obj);
1896 // Only mark block as modified if data changed considerably
1897 if(shall_be_written)
1898 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1899 "deactivateFarObjects: Static data "
1900 "changed considerably");
1902 obj->m_static_exists = true;
1903 obj->m_static_block = block->getPos();
1908 v3s16 p = floatToInt(objectpos, BS);
1909 errorstream<<"ServerEnv: Could not find or generate "
1910 <<"a block for storing id="<<obj->getId()
1911 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1918 If known by some client, set pending deactivation.
1919 Otherwise delete it immediately.
1922 if(pending_delete && !force_delete)
1924 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1925 <<"object id="<<id<<" is known by clients"
1926 <<"; not deleting yet"<<std::endl;
1928 obj->m_pending_deactivation = true;
1932 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1933 <<"object id="<<id<<" is not known by clients"
1934 <<"; deleting"<<std::endl;
1936 // Tell the object about removal
1937 obj->removingFromEnvironment();
1938 // Deregister in scripting api
1939 m_script->removeObjectReference(obj);
1941 // Delete active object
1942 if(obj->environmentDeletes())
1944 // Id to be removed from m_active_objects
1945 objects_to_remove.push_back(id);
1948 // Remove references from m_active_objects
1949 for(std::list<u16>::iterator i = objects_to_remove.begin();
1950 i != objects_to_remove.end(); ++i)
1952 m_active_objects.erase(*i);
1959 #include "clientsimpleobject.h"
1965 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1966 ITextureSource *texturesource, IGameDef *gamedef,
1967 IrrlichtDevice *irr):
1970 m_texturesource(texturesource),
1976 ClientEnvironment::~ClientEnvironment()
1978 // delete active objects
1979 for(std::map<u16, ClientActiveObject*>::iterator
1980 i = m_active_objects.begin();
1981 i != m_active_objects.end(); ++i)
1986 for(std::list<ClientSimpleObject*>::iterator
1987 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
1996 Map & ClientEnvironment::getMap()
2001 ClientMap & ClientEnvironment::getClientMap()
2006 void ClientEnvironment::addPlayer(Player *player)
2008 DSTACK(__FUNCTION_NAME);
2010 It is a failure if player is local and there already is a local
2013 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
2015 Environment::addPlayer(player);
2018 LocalPlayer * ClientEnvironment::getLocalPlayer()
2020 for(std::list<Player*>::iterator i = m_players.begin();
2021 i != m_players.end(); ++i)
2023 Player *player = *i;
2024 if(player->isLocal())
2025 return (LocalPlayer*)player;
2030 void ClientEnvironment::step(float dtime)
2032 DSTACK(__FUNCTION_NAME);
2034 /* Step time of day */
2035 stepTimeOfDay(dtime);
2037 // Get some settings
2038 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2039 bool free_move = fly_allowed && g_settings->getBool("free_move");
2042 LocalPlayer *lplayer = getLocalPlayer();
2044 // collision info queue
2045 std::list<CollisionInfo> player_collisions;
2048 Get the speed the player is going
2050 bool is_climbing = lplayer->is_climbing;
2052 f32 player_speed = lplayer->getSpeed().getLength();
2055 Maximum position increment
2057 //f32 position_max_increment = 0.05*BS;
2058 f32 position_max_increment = 0.1*BS;
2060 // Maximum time increment (for collision detection etc)
2061 // time = distance / speed
2062 f32 dtime_max_increment = 1;
2063 if(player_speed > 0.001)
2064 dtime_max_increment = position_max_increment / player_speed;
2066 // Maximum time increment is 10ms or lower
2067 if(dtime_max_increment > 0.01)
2068 dtime_max_increment = 0.01;
2070 // Don't allow overly huge dtime
2074 f32 dtime_downcount = dtime;
2077 Stuff that has a maximum time increment
2086 if(dtime_downcount > dtime_max_increment)
2088 dtime_part = dtime_max_increment;
2089 dtime_downcount -= dtime_part;
2093 dtime_part = dtime_downcount;
2095 Setting this to 0 (no -=dtime_part) disables an infinite loop
2096 when dtime_part is so small that dtime_downcount -= dtime_part
2099 dtime_downcount = 0;
2108 if(free_move == false && is_climbing == false)
2111 v3f speed = lplayer->getSpeed();
2112 if(lplayer->in_liquid == false)
2113 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2115 // Liquid floating / sinking
2116 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2117 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2119 // Liquid resistance
2120 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2122 // How much the node's viscosity blocks movement, ranges between 0 and 1
2123 // Should match the scale at which viscosity increase affects other liquid attributes
2124 const f32 viscosity_factor = 0.3;
2126 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2127 f32 dl = d_wanted.getLength();
2128 if(dl > lplayer->movement_liquid_fluidity_smooth)
2129 dl = lplayer->movement_liquid_fluidity_smooth;
2130 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2132 v3f d = d_wanted.normalize() * dl;
2136 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2137 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2138 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2139 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2140 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2141 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2145 lplayer->setSpeed(speed);
2150 This also does collision detection.
2152 lplayer->move(dtime_part, this, position_max_increment,
2153 &player_collisions);
2156 while(dtime_downcount > 0.001);
2158 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2160 for(std::list<CollisionInfo>::iterator
2161 i = player_collisions.begin();
2162 i != player_collisions.end(); ++i)
2164 CollisionInfo &info = *i;
2165 v3f speed_diff = info.new_speed - info.old_speed;;
2166 // Handle only fall damage
2167 // (because otherwise walking against something in fast_move kills you)
2168 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2170 // Get rid of other components
2173 f32 pre_factor = 1; // 1 hp per node/s
2174 f32 tolerance = BS*14; // 5 without damage
2175 f32 post_factor = 1; // 1 hp per node/s
2176 if(info.type == COLLISION_NODE)
2178 const ContentFeatures &f = m_gamedef->ndef()->
2179 get(m_map->getNodeNoEx(info.node_p));
2180 // Determine fall damage multiplier
2181 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2182 pre_factor = 1.0 + (float)addp/100.0;
2184 float speed = pre_factor * speed_diff.getLength();
2185 if(speed > tolerance)
2187 f32 damage_f = (speed - tolerance)/BS * post_factor;
2188 u16 damage = (u16)(damage_f+0.5);
2190 damageLocalPlayer(damage, true);
2191 MtEvent *e = new SimpleTriggerEvent("PlayerFallingDamage");
2192 m_gamedef->event()->put(e);
2198 A quick draft of lava damage
2200 if(m_lava_hurt_interval.step(dtime, 1.0))
2202 v3f pf = lplayer->getPosition();
2204 // Feet, middle and head
2205 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2206 MapNode n1 = m_map->getNodeNoEx(p1);
2207 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2208 MapNode n2 = m_map->getNodeNoEx(p2);
2209 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2210 MapNode n3 = m_map->getNodeNoEx(p3);
2212 u32 damage_per_second = 0;
2213 damage_per_second = MYMAX(damage_per_second,
2214 m_gamedef->ndef()->get(n1).damage_per_second);
2215 damage_per_second = MYMAX(damage_per_second,
2216 m_gamedef->ndef()->get(n2).damage_per_second);
2217 damage_per_second = MYMAX(damage_per_second,
2218 m_gamedef->ndef()->get(n3).damage_per_second);
2220 if(damage_per_second != 0)
2222 damageLocalPlayer(damage_per_second, true);
2229 if(m_drowning_interval.step(dtime, 2.0))
2231 v3f pf = lplayer->getPosition();
2234 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2235 MapNode n = m_map->getNodeNoEx(p);
2236 ContentFeatures c = m_gamedef->ndef()->get(n);
2237 u8 drowning_damage = c.drowning;
2238 if(drowning_damage > 0 && lplayer->hp > 0){
2239 u16 breath = lplayer->getBreath();
2246 lplayer->setBreath(breath);
2247 updateLocalPlayerBreath(breath);
2250 if(lplayer->getBreath() == 0 && drowning_damage > 0){
2251 damageLocalPlayer(drowning_damage, true);
2254 if(m_breathing_interval.step(dtime, 0.5))
2256 v3f pf = lplayer->getPosition();
2259 v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2260 MapNode n = m_map->getNodeNoEx(p);
2261 ContentFeatures c = m_gamedef->ndef()->get(n);
2263 lplayer->setBreath(11);
2265 else if(c.drowning == 0){
2266 u16 breath = lplayer->getBreath();
2269 lplayer->setBreath(breath);
2270 updateLocalPlayerBreath(breath);
2276 Stuff that can be done in an arbitarily large dtime
2278 for(std::list<Player*>::iterator i = m_players.begin();
2279 i != m_players.end(); ++i)
2281 Player *player = *i;
2284 Handle non-local players
2286 if(player->isLocal() == false)
2289 player->move(dtime, *m_map, 100*BS);
2293 // Update lighting on all players on client
2297 v3s16 p = player->getLightPosition();
2298 MapNode n = m_map->getNode(p);
2299 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2301 catch(InvalidPositionException &e){
2302 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2304 player->light = light;
2308 Step active objects and update lighting of them
2311 g_profiler->avg("CEnv: num of objects", m_active_objects.size());
2312 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2313 for(std::map<u16, ClientActiveObject*>::iterator
2314 i = m_active_objects.begin();
2315 i != m_active_objects.end(); ++i)
2317 ClientActiveObject* obj = i->second;
2319 obj->step(dtime, this);
2327 v3s16 p = obj->getLightPosition();
2328 MapNode n = m_map->getNode(p);
2329 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2331 catch(InvalidPositionException &e){
2332 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2334 obj->updateLight(light);
2339 Step and handle simple objects
2341 g_profiler->avg("CEnv: num of simple objects", m_simple_objects.size());
2342 for(std::list<ClientSimpleObject*>::iterator
2343 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2345 ClientSimpleObject *simple = *i;
2346 std::list<ClientSimpleObject*>::iterator cur = i;
2348 simple->step(dtime);
2349 if(simple->m_to_be_removed){
2351 m_simple_objects.erase(cur);
2356 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2358 m_simple_objects.push_back(simple);
2361 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2363 std::map<u16, ClientActiveObject*>::iterator n;
2364 n = m_active_objects.find(id);
2365 if(n == m_active_objects.end())
2370 bool isFreeClientActiveObjectId(u16 id,
2371 std::map<u16, ClientActiveObject*> &objects)
2376 return objects.find(id) == objects.end();
2379 u16 getFreeClientActiveObjectId(
2380 std::map<u16, ClientActiveObject*> &objects)
2382 //try to reuse id's as late as possible
2383 static u16 last_used_id = 0;
2384 u16 startid = last_used_id;
2388 if(isFreeClientActiveObjectId(last_used_id, objects))
2389 return last_used_id;
2391 if(last_used_id == startid)
2396 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2399 if(object->getId() == 0)
2401 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2404 infostream<<"ClientEnvironment::addActiveObject(): "
2405 <<"no free ids available"<<std::endl;
2409 object->setId(new_id);
2411 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2413 infostream<<"ClientEnvironment::addActiveObject(): "
2414 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2418 infostream<<"ClientEnvironment::addActiveObject(): "
2419 <<"added (id="<<object->getId()<<")"<<std::endl;
2420 m_active_objects[object->getId()] = object;
2421 object->addToScene(m_smgr, m_texturesource, m_irr);
2422 { // Update lighting immediately
2426 v3s16 p = object->getLightPosition();
2427 MapNode n = m_map->getNode(p);
2428 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2430 catch(InvalidPositionException &e){
2431 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2433 object->updateLight(light);
2435 return object->getId();
2438 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2439 const std::string &init_data)
2441 ClientActiveObject* obj =
2442 ClientActiveObject::create(type, m_gamedef, this);
2445 infostream<<"ClientEnvironment::addActiveObject(): "
2446 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2455 obj->initialize(init_data);
2457 catch(SerializationError &e)
2459 errorstream<<"ClientEnvironment::addActiveObject():"
2460 <<" id="<<id<<" type="<<type
2461 <<": SerializationError in initialize(): "
2463 <<": init_data="<<serializeJsonString(init_data)
2467 addActiveObject(obj);
2470 void ClientEnvironment::removeActiveObject(u16 id)
2472 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2473 <<"id="<<id<<std::endl;
2474 ClientActiveObject* obj = getActiveObject(id);
2477 infostream<<"ClientEnvironment::removeActiveObject(): "
2478 <<"id="<<id<<" not found"<<std::endl;
2481 obj->removeFromScene(true);
2483 m_active_objects.erase(id);
2486 void ClientEnvironment::processActiveObjectMessage(u16 id,
2487 const std::string &data)
2489 ClientActiveObject* obj = getActiveObject(id);
2492 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2493 <<" got message for id="<<id<<", which doesn't exist."
2499 obj->processMessage(data);
2501 catch(SerializationError &e)
2503 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2504 <<" id="<<id<<" type="<<obj->getType()
2505 <<" SerializationError in processMessage(),"
2506 <<" message="<<serializeJsonString(data)
2512 Callbacks for activeobjects
2515 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2517 LocalPlayer *lplayer = getLocalPlayer();
2521 if(lplayer->hp > damage)
2522 lplayer->hp -= damage;
2527 ClientEnvEvent event;
2528 event.type = CEE_PLAYER_DAMAGE;
2529 event.player_damage.amount = damage;
2530 event.player_damage.send_to_server = handle_hp;
2531 m_client_event_queue.push_back(event);
2534 void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
2536 ClientEnvEvent event;
2537 event.type = CEE_PLAYER_BREATH;
2538 event.player_breath.amount = breath;
2539 m_client_event_queue.push_back(event);
2543 Client likes to call these
2546 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2547 std::vector<DistanceSortedActiveObject> &dest)
2549 for(std::map<u16, ClientActiveObject*>::iterator
2550 i = m_active_objects.begin();
2551 i != m_active_objects.end(); ++i)
2553 ClientActiveObject* obj = i->second;
2555 f32 d = (obj->getPosition() - origin).getLength();
2560 DistanceSortedActiveObject dso(obj, d);
2562 dest.push_back(dso);
2566 ClientEnvEvent ClientEnvironment::getClientEvent()
2568 ClientEnvEvent event;
2569 if(m_client_event_queue.empty())
2570 event.type = CEE_NONE;
2572 event = m_client_event_queue.front();
2573 m_client_event_queue.pop_front();
2578 #endif // #ifndef SERVER