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 "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, lua_State *L,
324 IGameDef *gamedef, IBackgroundBlockEmerger *emerger):
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(scriptapi_node_on_timer(m_lua,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 scriptapi_node_on_destruct(m_lua, 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 scriptapi_node_after_destruct(m_lua, p, n_old);
859 if(ndef->get(n).has_on_construct)
860 scriptapi_node_on_construct(m_lua, 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 scriptapi_node_on_destruct(m_lua, 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 scriptapi_node_after_destruct(m_lua, 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 scriptapi_rm_object_reference(m_lua, 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 std::list<v3s16> loadable_blocks;
949 infostream<<"ServerEnvironment::clearAllObjects(): "
950 <<"Listing all loadable blocks"<<std::endl;
951 m_map->listAllLoadableBlocks(loadable_blocks);
952 infostream<<"ServerEnvironment::clearAllObjects(): "
953 <<"Done listing all loadable blocks: "
954 <<loadable_blocks.size()
955 <<", now clearing"<<std::endl;
956 u32 report_interval = loadable_blocks.size() / 10;
957 u32 num_blocks_checked = 0;
958 u32 num_blocks_cleared = 0;
959 u32 num_objs_cleared = 0;
960 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
961 i != loadable_blocks.end(); ++i)
964 MapBlock *block = m_map->emergeBlock(p, false);
966 errorstream<<"ServerEnvironment::clearAllObjects(): "
967 <<"Failed to emerge block "<<PP(p)<<std::endl;
970 u32 num_stored = block->m_static_objects.m_stored.size();
971 u32 num_active = block->m_static_objects.m_active.size();
972 if(num_stored != 0 || num_active != 0){
973 block->m_static_objects.m_stored.clear();
974 block->m_static_objects.m_active.clear();
975 block->raiseModified(MOD_STATE_WRITE_NEEDED,
977 num_objs_cleared += num_stored + num_active;
978 num_blocks_cleared++;
980 num_blocks_checked++;
982 if(num_blocks_checked % report_interval == 0){
983 float percent = 100.0 * (float)num_blocks_checked /
984 loadable_blocks.size();
985 infostream<<"ServerEnvironment::clearAllObjects(): "
986 <<"Cleared "<<num_objs_cleared<<" objects"
987 <<" in "<<num_blocks_cleared<<" blocks ("
988 <<percent<<"%)"<<std::endl;
991 infostream<<"ServerEnvironment::clearAllObjects(): "
992 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
993 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
996 void ServerEnvironment::step(float dtime)
998 DSTACK(__FUNCTION_NAME);
1000 //TimeTaker timer("ServerEnv step");
1002 /* Step time of day */
1003 stepTimeOfDay(dtime);
1006 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1007 // really matter that much.
1008 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1014 m_game_time_fraction_counter += dtime;
1015 u32 inc_i = (u32)m_game_time_fraction_counter;
1016 m_game_time += inc_i;
1017 m_game_time_fraction_counter -= (float)inc_i;
1024 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1025 for(std::list<Player*>::iterator i = m_players.begin();
1026 i != m_players.end(); ++i)
1028 Player *player = *i;
1030 // Ignore disconnected players
1031 if(player->peer_id == 0)
1035 player->move(dtime, *m_map, 100*BS);
1040 Manage active block list
1042 if(m_active_blocks_management_interval.step(dtime, 2.0))
1044 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1046 Get player block positions
1048 std::list<v3s16> players_blockpos;
1049 for(std::list<Player*>::iterator
1050 i = m_players.begin();
1051 i != m_players.end(); ++i)
1053 Player *player = *i;
1054 // Ignore disconnected players
1055 if(player->peer_id == 0)
1057 v3s16 blockpos = getNodeBlockPos(
1058 floatToInt(player->getPosition(), BS));
1059 players_blockpos.push_back(blockpos);
1063 Update list of active blocks, collecting changes
1065 const s16 active_block_range = g_settings->getS16("active_block_range");
1066 std::set<v3s16> blocks_removed;
1067 std::set<v3s16> blocks_added;
1068 m_active_blocks.update(players_blockpos, active_block_range,
1069 blocks_removed, blocks_added);
1072 Handle removed blocks
1075 // Convert active objects that are no more in active blocks to static
1076 deactivateFarObjects(false);
1078 for(std::set<v3s16>::iterator
1079 i = blocks_removed.begin();
1080 i != blocks_removed.end(); ++i)
1084 /* infostream<<"Server: Block " << PP(p)
1085 << " became inactive"<<std::endl; */
1087 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1091 // Set current time as timestamp (and let it set ChangedFlag)
1092 block->setTimestamp(m_game_time);
1099 for(std::set<v3s16>::iterator
1100 i = blocks_added.begin();
1101 i != blocks_added.end(); ++i)
1105 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1107 // Block needs to be fetched first
1108 m_emerger->queueBlockEmerge(p, false);
1109 m_active_blocks.m_list.erase(p);
1113 activateBlock(block);
1114 /* infostream<<"Server: Block " << PP(p)
1115 << " became active"<<std::endl; */
1120 Mess around in active blocks
1122 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1124 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1128 for(std::set<v3s16>::iterator
1129 i = m_active_blocks.m_list.begin();
1130 i != m_active_blocks.m_list.end(); ++i)
1134 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1135 <<") being handled"<<std::endl;*/
1137 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1141 // Reset block usage timer
1142 block->resetUsageTimer();
1144 // Set current time as timestamp
1145 block->setTimestampNoChangedFlag(m_game_time);
1146 // If time has changed much from the one on disk,
1147 // set block to be saved when it is unloaded
1148 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1149 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1150 "Timestamp older than 60s (step)");
1153 std::map<v3s16, NodeTimer> elapsed_timers =
1154 block->m_node_timers.step((float)dtime);
1155 if(!elapsed_timers.empty()){
1157 for(std::map<v3s16, NodeTimer>::iterator
1158 i = elapsed_timers.begin();
1159 i != elapsed_timers.end(); i++){
1160 n = block->getNodeNoEx(i->first);
1161 p = i->first + block->getPosRelative();
1162 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1163 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1169 const float abm_interval = 1.0;
1170 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1172 if(m_active_block_interval_overload_skip > 0){
1173 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1174 m_active_block_interval_overload_skip--;
1177 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1178 TimeTaker timer("modify in active blocks");
1180 // Initialize handling of ActiveBlockModifiers
1181 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1183 for(std::set<v3s16>::iterator
1184 i = m_active_blocks.m_list.begin();
1185 i != m_active_blocks.m_list.end(); ++i)
1189 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1190 <<") being handled"<<std::endl;*/
1192 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1196 // Set current time as timestamp
1197 block->setTimestampNoChangedFlag(m_game_time);
1199 /* Handle ActiveBlockModifiers */
1200 abmhandler.apply(block);
1203 u32 time_ms = timer.stop(true);
1204 u32 max_time_ms = 200;
1205 if(time_ms > max_time_ms){
1206 infostream<<"WARNING: active block modifiers took "
1207 <<time_ms<<"ms (longer than "
1208 <<max_time_ms<<"ms)"<<std::endl;
1209 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1214 Step script environment (run global on_step())
1216 scriptapi_environment_step(m_lua, dtime);
1222 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1223 //TimeTaker timer("Step active objects");
1225 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1227 // This helps the objects to send data at the same time
1228 bool send_recommended = false;
1229 m_send_recommended_timer += dtime;
1230 if(m_send_recommended_timer > getSendRecommendedInterval())
1232 m_send_recommended_timer -= getSendRecommendedInterval();
1233 send_recommended = true;
1236 for(std::map<u16, ServerActiveObject*>::iterator
1237 i = m_active_objects.begin();
1238 i != m_active_objects.end(); ++i)
1240 ServerActiveObject* obj = i->second;
1241 // Remove non-peaceful mobs on peaceful mode
1242 if(g_settings->getBool("only_peaceful_mobs")){
1243 if(!obj->isPeaceful())
1244 obj->m_removed = true;
1246 // Don't step if is to be removed or stored statically
1247 if(obj->m_removed || obj->m_pending_deactivation)
1250 obj->step(dtime, send_recommended);
1251 // Read messages from object
1252 while(!obj->m_messages_out.empty())
1254 m_active_object_messages.push_back(
1255 obj->m_messages_out.pop_front());
1261 Manage active objects
1263 if(m_object_management_interval.step(dtime, 0.5))
1265 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1267 Remove objects that satisfy (m_removed && m_known_by_count==0)
1269 removeRemovedObjects();
1273 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1275 std::map<u16, ServerActiveObject*>::iterator n;
1276 n = m_active_objects.find(id);
1277 if(n == m_active_objects.end())
1282 bool isFreeServerActiveObjectId(u16 id,
1283 std::map<u16, ServerActiveObject*> &objects)
1288 return objects.find(id) == objects.end();
1291 u16 getFreeServerActiveObjectId(
1292 std::map<u16, ServerActiveObject*> &objects)
1294 //try to reuse id's as late as possible
1295 static u16 last_used_id = 0;
1296 u16 startid = last_used_id;
1300 if(isFreeServerActiveObjectId(last_used_id, objects))
1301 return last_used_id;
1303 if(last_used_id == startid)
1308 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1311 u16 id = addActiveObjectRaw(object, true, 0);
1316 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1320 v3f objectpos = obj->getBasePosition();
1322 // The block in which the object resides in
1323 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1326 Update the static data
1329 // Create new static object
1330 std::string staticdata = obj->getStaticData();
1331 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1332 // Add to the block where the object is located in
1333 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1334 // Get or generate the block
1335 MapBlock *block = m_map->emergeBlock(blockpos);
1337 bool succeeded = false;
1341 block->m_static_objects.insert(0, s_obj);
1342 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1343 "addActiveObjectAsStatic");
1347 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1348 <<"Could not find or generate "
1349 <<"a block for storing static object"<<std::endl;
1353 if(obj->environmentDeletes())
1361 Finds out what new objects have been added to
1362 inside a radius around a position
1364 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1365 std::set<u16> ¤t_objects,
1366 std::set<u16> &added_objects)
1368 v3f pos_f = intToFloat(pos, BS);
1369 f32 radius_f = radius * BS;
1371 Go through the object list,
1372 - discard m_removed objects,
1373 - discard objects that are too far away,
1374 - discard objects that are found in current_objects.
1375 - add remaining objects to added_objects
1377 for(std::map<u16, ServerActiveObject*>::iterator
1378 i = m_active_objects.begin();
1379 i != m_active_objects.end(); ++i)
1383 ServerActiveObject *object = i->second;
1386 // Discard if removed
1387 if(object->m_removed)
1389 if(object->unlimitedTransferDistance() == false){
1390 // Discard if too far
1391 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1392 if(distance_f > radius_f)
1395 // Discard if already on current_objects
1396 std::set<u16>::iterator n;
1397 n = current_objects.find(id);
1398 if(n != current_objects.end())
1400 // Add to added_objects
1401 added_objects.insert(id);
1406 Finds out what objects have been removed from
1407 inside a radius around a position
1409 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1410 std::set<u16> ¤t_objects,
1411 std::set<u16> &removed_objects)
1413 v3f pos_f = intToFloat(pos, BS);
1414 f32 radius_f = radius * BS;
1416 Go through current_objects; object is removed if:
1417 - object is not found in m_active_objects (this is actually an
1418 error condition; objects should be set m_removed=true and removed
1419 only after all clients have been informed about removal), or
1420 - object has m_removed=true, or
1421 - object is too far away
1423 for(std::set<u16>::iterator
1424 i = current_objects.begin();
1425 i != current_objects.end(); ++i)
1428 ServerActiveObject *object = getActiveObject(id);
1431 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1432 <<" object in current_objects is NULL"<<std::endl;
1433 removed_objects.insert(id);
1437 if(object->m_removed)
1439 removed_objects.insert(id);
1443 // If transfer distance is unlimited, don't remove
1444 if(object->unlimitedTransferDistance())
1447 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1449 if(distance_f >= radius_f)
1451 removed_objects.insert(id);
1459 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1461 if(m_active_object_messages.empty())
1462 return ActiveObjectMessage(0);
1464 return m_active_object_messages.pop_front();
1468 ************ Private methods *************
1471 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1472 bool set_changed, u32 dtime_s)
1475 if(object->getId() == 0){
1476 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1479 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1480 <<"no free ids available"<<std::endl;
1481 if(object->environmentDeletes())
1485 object->setId(new_id);
1488 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1489 <<"supplied with id "<<object->getId()<<std::endl;
1491 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1493 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1494 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1495 if(object->environmentDeletes())
1499 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1500 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1502 m_active_objects[object->getId()] = object;
1504 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1505 <<"Added id="<<object->getId()<<"; there are now "
1506 <<m_active_objects.size()<<" active objects."
1509 // Register reference in scripting api (must be done before post-init)
1510 scriptapi_add_object_reference(m_lua, object);
1511 // Post-initialize object
1512 object->addedToEnvironment(dtime_s);
1514 // Add static data to block
1515 if(object->isStaticAllowed())
1517 // Add static object to active static list of the block
1518 v3f objectpos = object->getBasePosition();
1519 std::string staticdata = object->getStaticData();
1520 StaticObject s_obj(object->getType(), objectpos, staticdata);
1521 // Add to the block where the object is located in
1522 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1523 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1526 block->m_static_objects.m_active[object->getId()] = s_obj;
1527 object->m_static_exists = true;
1528 object->m_static_block = blockpos;
1531 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1532 "addActiveObjectRaw");
1535 v3s16 p = floatToInt(objectpos, BS);
1536 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1537 <<"could not find block for storing id="<<object->getId()
1538 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1542 return object->getId();
1546 Remove objects that satisfy (m_removed && m_known_by_count==0)
1548 void ServerEnvironment::removeRemovedObjects()
1550 std::list<u16> objects_to_remove;
1551 for(std::map<u16, ServerActiveObject*>::iterator
1552 i = m_active_objects.begin();
1553 i != m_active_objects.end(); ++i)
1556 ServerActiveObject* obj = i->second;
1557 // This shouldn't happen but check it
1560 infostream<<"NULL object found in ServerEnvironment"
1561 <<" while finding removed objects. id="<<id<<std::endl;
1562 // Id to be removed from m_active_objects
1563 objects_to_remove.push_back(id);
1568 We will delete objects that are marked as removed or thatare
1569 waiting for deletion after deactivation
1571 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1575 Delete static data from block if is marked as removed
1577 if(obj->m_static_exists && obj->m_removed)
1579 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1581 block->m_static_objects.remove(id);
1582 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1583 "removeRemovedObjects");
1584 obj->m_static_exists = false;
1586 infostream << "failed to emerge block from which "
1587 "an object to be removed was loaded from. id="<<id<<std::endl;
1591 // If m_known_by_count > 0, don't actually remove.
1592 if(obj->m_known_by_count > 0)
1595 // Tell the object about removal
1596 obj->removingFromEnvironment();
1597 // Deregister in scripting api
1598 scriptapi_rm_object_reference(m_lua, obj);
1601 if(obj->environmentDeletes())
1603 // Id to be removed from m_active_objects
1604 objects_to_remove.push_back(id);
1606 // Remove references from m_active_objects
1607 for(std::list<u16>::iterator i = objects_to_remove.begin();
1608 i != objects_to_remove.end(); ++i)
1610 m_active_objects.erase(*i);
1614 static void print_hexdump(std::ostream &o, const std::string &data)
1616 const int linelength = 16;
1617 for(int l=0; ; l++){
1618 int i0 = linelength * l;
1619 bool at_end = false;
1620 int thislinelength = linelength;
1621 if(i0 + thislinelength > (int)data.size()){
1622 thislinelength = data.size() - i0;
1625 for(int di=0; di<linelength; di++){
1628 if(di<thislinelength)
1629 snprintf(buf, 4, "%.2x ", data[i]);
1631 snprintf(buf, 4, " ");
1635 for(int di=0; di<thislinelength; di++){
1649 Convert stored objects from blocks near the players to active.
1651 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1655 // Ignore if no stored objects (to not set changed flag)
1656 if(block->m_static_objects.m_stored.size() == 0)
1658 verbosestream<<"ServerEnvironment::activateObjects(): "
1659 <<"activating objects of block "<<PP(block->getPos())
1660 <<" ("<<block->m_static_objects.m_stored.size()
1661 <<" objects)"<<std::endl;
1662 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1664 errorstream<<"suspiciously large amount of objects detected: "
1665 <<block->m_static_objects.m_stored.size()<<" in "
1666 <<PP(block->getPos())
1667 <<"; removing all of them."<<std::endl;
1668 // Clear stored list
1669 block->m_static_objects.m_stored.clear();
1670 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1671 "stored list cleared in activateObjects due to "
1672 "large amount of objects");
1675 // A list for objects that couldn't be converted to active for some
1676 // reason. They will be stored back.
1677 std::list<StaticObject> new_stored;
1678 // Loop through stored static objects
1679 for(std::list<StaticObject>::iterator
1680 i = block->m_static_objects.m_stored.begin();
1681 i != block->m_static_objects.m_stored.end(); ++i)
1683 /*infostream<<"Server: Creating an active object from "
1684 <<"static data"<<std::endl;*/
1685 StaticObject &s_obj = *i;
1686 // Create an active object from the data
1687 ServerActiveObject *obj = ServerActiveObject::create
1688 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1689 // If couldn't create object, store static data back.
1692 errorstream<<"ServerEnvironment::activateObjects(): "
1693 <<"failed to create active object from static object "
1694 <<"in block "<<PP(s_obj.pos/BS)
1695 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1696 print_hexdump(verbosestream, s_obj.data);
1698 new_stored.push_back(s_obj);
1701 verbosestream<<"ServerEnvironment::activateObjects(): "
1702 <<"activated static object pos="<<PP(s_obj.pos/BS)
1703 <<" type="<<(int)s_obj.type<<std::endl;
1704 // This will also add the object to the active static list
1705 addActiveObjectRaw(obj, false, dtime_s);
1707 // Clear stored list
1708 block->m_static_objects.m_stored.clear();
1709 // Add leftover failed stuff to stored list
1710 for(std::list<StaticObject>::iterator
1711 i = new_stored.begin();
1712 i != new_stored.end(); ++i)
1714 StaticObject &s_obj = *i;
1715 block->m_static_objects.m_stored.push_back(s_obj);
1718 Note: Block hasn't really been modified here.
1719 The objects have just been activated and moved from the stored
1720 static list to the active static list.
1721 As such, the block is essentially the same.
1722 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1723 Otherwise there would be a huge amount of unnecessary I/O.
1728 Convert objects that are not standing inside active blocks to static.
1730 If m_known_by_count != 0, active object is not deleted, but static
1731 data is still updated.
1733 If force_delete is set, active object is deleted nevertheless. It
1734 shall only be set so in the destructor of the environment.
1736 If block wasn't generated (not in memory or on disk),
1738 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1740 std::list<u16> objects_to_remove;
1741 for(std::map<u16, ServerActiveObject*>::iterator
1742 i = m_active_objects.begin();
1743 i != m_active_objects.end(); ++i)
1745 ServerActiveObject* obj = i->second;
1748 // Do not deactivate if static data creation not allowed
1749 if(!force_delete && !obj->isStaticAllowed())
1752 // If pending deactivation, let removeRemovedObjects() do it
1753 if(!force_delete && obj->m_pending_deactivation)
1757 v3f objectpos = obj->getBasePosition();
1759 // The block in which the object resides in
1760 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1762 // If block is active, don't remove
1763 if(!force_delete && m_active_blocks.contains(blockpos_o))
1766 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1767 <<"deactivating object id="<<id<<" on inactive block "
1768 <<PP(blockpos_o)<<std::endl;
1770 // If known by some client, don't immediately delete.
1771 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1774 Update the static data
1777 if(obj->isStaticAllowed())
1779 // Create new static object
1780 std::string staticdata_new = obj->getStaticData();
1781 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1783 bool stays_in_same_block = false;
1784 bool data_changed = true;
1786 if(obj->m_static_exists){
1787 if(obj->m_static_block == blockpos_o)
1788 stays_in_same_block = true;
1790 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1792 std::map<u16, StaticObject>::iterator n =
1793 block->m_static_objects.m_active.find(id);
1794 if(n != block->m_static_objects.m_active.end()){
1795 StaticObject static_old = n->second;
1797 float save_movem = obj->getMinimumSavedMovement();
1799 if(static_old.data == staticdata_new &&
1800 (static_old.pos - objectpos).getLength() < save_movem)
1801 data_changed = false;
1803 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1804 <<"id="<<id<<" m_static_exists=true but "
1805 <<"static data doesn't actually exist in "
1806 <<PP(obj->m_static_block)<<std::endl;
1810 bool shall_be_written = (!stays_in_same_block || data_changed);
1812 // Delete old static object
1813 if(obj->m_static_exists)
1815 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1818 block->m_static_objects.remove(id);
1819 obj->m_static_exists = false;
1820 // Only mark block as modified if data changed considerably
1821 if(shall_be_written)
1822 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1823 "deactivateFarObjects: Static data "
1824 "changed considerably");
1828 // Add to the block where the object is located in
1829 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1830 // Get or generate the block
1831 MapBlock *block = NULL;
1833 block = m_map->emergeBlock(blockpos);
1834 } catch(InvalidPositionException &e){
1835 // Handled via NULL pointer
1840 if(block->m_static_objects.m_stored.size() >= 49){
1841 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1842 <<" statically but block "<<PP(blockpos)
1843 <<" already contains "
1844 <<block->m_static_objects.m_stored.size()
1845 <<" (over 49) objects."
1846 <<" Forcing delete."<<std::endl;
1847 force_delete = true;
1849 // If static counterpart already exists, remove it first.
1850 // This shouldn't happen, but happens rarely for some
1851 // unknown reason. Unsuccessful attempts have been made to
1852 // find said reason.
1853 if(id && block->m_static_objects.m_active.find(id) != block->m_static_objects.m_active.end()){
1854 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1856 block->m_static_objects.remove(id);
1859 block->m_static_objects.insert(0, s_obj);
1861 // Only mark block as modified if data changed considerably
1862 if(shall_be_written)
1863 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1864 "deactivateFarObjects: Static data "
1865 "changed considerably");
1867 obj->m_static_exists = true;
1868 obj->m_static_block = block->getPos();
1873 v3s16 p = floatToInt(objectpos, BS);
1874 errorstream<<"ServerEnv: Could not find or generate "
1875 <<"a block for storing id="<<obj->getId()
1876 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1883 If known by some client, set pending deactivation.
1884 Otherwise delete it immediately.
1887 if(pending_delete && !force_delete)
1889 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1890 <<"object id="<<id<<" is known by clients"
1891 <<"; not deleting yet"<<std::endl;
1893 obj->m_pending_deactivation = true;
1897 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1898 <<"object id="<<id<<" is not known by clients"
1899 <<"; deleting"<<std::endl;
1901 // Tell the object about removal
1902 obj->removingFromEnvironment();
1903 // Deregister in scripting api
1904 scriptapi_rm_object_reference(m_lua, obj);
1906 // Delete active object
1907 if(obj->environmentDeletes())
1909 // Id to be removed from m_active_objects
1910 objects_to_remove.push_back(id);
1913 // Remove references from m_active_objects
1914 for(std::list<u16>::iterator i = objects_to_remove.begin();
1915 i != objects_to_remove.end(); ++i)
1917 m_active_objects.erase(*i);
1924 #include "clientsimpleobject.h"
1930 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1931 ITextureSource *texturesource, IGameDef *gamedef,
1932 IrrlichtDevice *irr):
1935 m_texturesource(texturesource),
1941 ClientEnvironment::~ClientEnvironment()
1943 // delete active objects
1944 for(std::map<u16, ClientActiveObject*>::iterator
1945 i = m_active_objects.begin();
1946 i != m_active_objects.end(); ++i)
1951 for(std::list<ClientSimpleObject*>::iterator
1952 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
1961 Map & ClientEnvironment::getMap()
1966 ClientMap & ClientEnvironment::getClientMap()
1971 void ClientEnvironment::addPlayer(Player *player)
1973 DSTACK(__FUNCTION_NAME);
1975 It is a failure if player is local and there already is a local
1978 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1980 Environment::addPlayer(player);
1983 LocalPlayer * ClientEnvironment::getLocalPlayer()
1985 for(std::list<Player*>::iterator i = m_players.begin();
1986 i != m_players.end(); ++i)
1988 Player *player = *i;
1989 if(player->isLocal())
1990 return (LocalPlayer*)player;
1995 void ClientEnvironment::step(float dtime)
1997 DSTACK(__FUNCTION_NAME);
1999 /* Step time of day */
2000 stepTimeOfDay(dtime);
2002 // Get some settings
2003 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2004 bool free_move = fly_allowed && g_settings->getBool("free_move");
2007 LocalPlayer *lplayer = getLocalPlayer();
2009 // collision info queue
2010 std::list<CollisionInfo> player_collisions;
2013 Get the speed the player is going
2015 bool is_climbing = lplayer->is_climbing;
2017 f32 player_speed = lplayer->getSpeed().getLength();
2020 Maximum position increment
2022 //f32 position_max_increment = 0.05*BS;
2023 f32 position_max_increment = 0.1*BS;
2025 // Maximum time increment (for collision detection etc)
2026 // time = distance / speed
2027 f32 dtime_max_increment = 1;
2028 if(player_speed > 0.001)
2029 dtime_max_increment = position_max_increment / player_speed;
2031 // Maximum time increment is 10ms or lower
2032 if(dtime_max_increment > 0.01)
2033 dtime_max_increment = 0.01;
2035 // Don't allow overly huge dtime
2039 f32 dtime_downcount = dtime;
2042 Stuff that has a maximum time increment
2051 if(dtime_downcount > dtime_max_increment)
2053 dtime_part = dtime_max_increment;
2054 dtime_downcount -= dtime_part;
2058 dtime_part = dtime_downcount;
2060 Setting this to 0 (no -=dtime_part) disables an infinite loop
2061 when dtime_part is so small that dtime_downcount -= dtime_part
2064 dtime_downcount = 0;
2073 if(free_move == false && is_climbing == false)
2076 v3f speed = lplayer->getSpeed();
2077 if(lplayer->in_liquid == false)
2078 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2080 // Liquid floating / sinking
2081 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2082 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2084 // Liquid resistance
2085 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2087 // How much the node's viscosity blocks movement, ranges between 0 and 1
2088 // Should match the scale at which viscosity increase affects other liquid attributes
2089 const f32 viscosity_factor = 0.3;
2091 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2092 f32 dl = d_wanted.getLength();
2093 if(dl > lplayer->movement_liquid_fluidity_smooth)
2094 dl = lplayer->movement_liquid_fluidity_smooth;
2095 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2097 v3f d = d_wanted.normalize() * dl;
2101 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2102 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2103 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2104 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2105 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2106 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2110 lplayer->setSpeed(speed);
2115 This also does collision detection.
2117 lplayer->move(dtime_part, this, position_max_increment,
2118 &player_collisions);
2121 while(dtime_downcount > 0.001);
2123 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2125 for(std::list<CollisionInfo>::iterator
2126 i = player_collisions.begin();
2127 i != player_collisions.end(); ++i)
2129 CollisionInfo &info = *i;
2130 v3f speed_diff = info.new_speed - info.old_speed;;
2131 // Handle only fall damage
2132 // (because otherwise walking against something in fast_move kills you)
2133 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2135 // Get rid of other components
2138 f32 pre_factor = 1; // 1 hp per node/s
2139 f32 tolerance = BS*14; // 5 without damage
2140 f32 post_factor = 1; // 1 hp per node/s
2141 if(info.type == COLLISION_NODE)
2143 const ContentFeatures &f = m_gamedef->ndef()->
2144 get(m_map->getNodeNoEx(info.node_p));
2145 // Determine fall damage multiplier
2146 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2147 pre_factor = 1.0 + (float)addp/100.0;
2149 float speed = pre_factor * speed_diff.getLength();
2150 if(speed > tolerance)
2152 f32 damage_f = (speed - tolerance)/BS * post_factor;
2153 u16 damage = (u16)(damage_f+0.5);
2155 damageLocalPlayer(damage, true);
2160 A quick draft of lava damage
2162 if(m_lava_hurt_interval.step(dtime, 1.0))
2164 v3f pf = lplayer->getPosition();
2166 // Feet, middle and head
2167 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2168 MapNode n1 = m_map->getNodeNoEx(p1);
2169 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2170 MapNode n2 = m_map->getNodeNoEx(p2);
2171 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2172 MapNode n3 = m_map->getNodeNoEx(p2);
2174 u32 damage_per_second = 0;
2175 damage_per_second = MYMAX(damage_per_second,
2176 m_gamedef->ndef()->get(n1).damage_per_second);
2177 damage_per_second = MYMAX(damage_per_second,
2178 m_gamedef->ndef()->get(n2).damage_per_second);
2179 damage_per_second = MYMAX(damage_per_second,
2180 m_gamedef->ndef()->get(n3).damage_per_second);
2182 if(damage_per_second != 0)
2184 damageLocalPlayer(damage_per_second, true);
2189 Stuff that can be done in an arbitarily large dtime
2191 for(std::list<Player*>::iterator i = m_players.begin();
2192 i != m_players.end(); ++i)
2194 Player *player = *i;
2197 Handle non-local players
2199 if(player->isLocal() == false)
2202 player->move(dtime, *m_map, 100*BS);
2206 // Update lighting on all players on client
2210 v3s16 p = player->getLightPosition();
2211 MapNode n = m_map->getNode(p);
2212 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2214 catch(InvalidPositionException &e){
2215 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2217 player->light = light;
2221 Step active objects and update lighting of them
2224 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2225 for(std::map<u16, ClientActiveObject*>::iterator
2226 i = m_active_objects.begin();
2227 i != m_active_objects.end(); ++i)
2229 ClientActiveObject* obj = i->second;
2231 obj->step(dtime, this);
2239 v3s16 p = obj->getLightPosition();
2240 MapNode n = m_map->getNode(p);
2241 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2243 catch(InvalidPositionException &e){
2244 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2246 obj->updateLight(light);
2251 Step and handle simple objects
2253 for(std::list<ClientSimpleObject*>::iterator
2254 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2256 ClientSimpleObject *simple = *i;
2257 std::list<ClientSimpleObject*>::iterator cur = i;
2259 simple->step(dtime);
2260 if(simple->m_to_be_removed){
2262 m_simple_objects.erase(cur);
2267 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2269 m_simple_objects.push_back(simple);
2272 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2274 std::map<u16, ClientActiveObject*>::iterator n;
2275 n = m_active_objects.find(id);
2276 if(n == m_active_objects.end())
2281 bool isFreeClientActiveObjectId(u16 id,
2282 std::map<u16, ClientActiveObject*> &objects)
2287 return objects.find(id) == objects.end();
2290 u16 getFreeClientActiveObjectId(
2291 std::map<u16, ClientActiveObject*> &objects)
2293 //try to reuse id's as late as possible
2294 static u16 last_used_id = 0;
2295 u16 startid = last_used_id;
2299 if(isFreeClientActiveObjectId(last_used_id, objects))
2300 return last_used_id;
2302 if(last_used_id == startid)
2307 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2310 if(object->getId() == 0)
2312 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2315 infostream<<"ClientEnvironment::addActiveObject(): "
2316 <<"no free ids available"<<std::endl;
2320 object->setId(new_id);
2322 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2324 infostream<<"ClientEnvironment::addActiveObject(): "
2325 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2329 infostream<<"ClientEnvironment::addActiveObject(): "
2330 <<"added (id="<<object->getId()<<")"<<std::endl;
2331 m_active_objects[object->getId()] = object;
2332 object->addToScene(m_smgr, m_texturesource, m_irr);
2333 { // Update lighting immediately
2337 v3s16 p = object->getLightPosition();
2338 MapNode n = m_map->getNode(p);
2339 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2341 catch(InvalidPositionException &e){
2342 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2344 object->updateLight(light);
2346 return object->getId();
2349 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2350 const std::string &init_data)
2352 ClientActiveObject* obj =
2353 ClientActiveObject::create(type, m_gamedef, this);
2356 infostream<<"ClientEnvironment::addActiveObject(): "
2357 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2366 obj->initialize(init_data);
2368 catch(SerializationError &e)
2370 errorstream<<"ClientEnvironment::addActiveObject():"
2371 <<" id="<<id<<" type="<<type
2372 <<": SerializationError in initialize(): "
2374 <<": init_data="<<serializeJsonString(init_data)
2378 addActiveObject(obj);
2381 void ClientEnvironment::removeActiveObject(u16 id)
2383 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2384 <<"id="<<id<<std::endl;
2385 ClientActiveObject* obj = getActiveObject(id);
2388 infostream<<"ClientEnvironment::removeActiveObject(): "
2389 <<"id="<<id<<" not found"<<std::endl;
2392 obj->removeFromScene(true);
2394 m_active_objects.erase(id);
2397 void ClientEnvironment::processActiveObjectMessage(u16 id,
2398 const std::string &data)
2400 ClientActiveObject* obj = getActiveObject(id);
2403 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2404 <<" got message for id="<<id<<", which doesn't exist."
2410 obj->processMessage(data);
2412 catch(SerializationError &e)
2414 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2415 <<" id="<<id<<" type="<<obj->getType()
2416 <<" SerializationError in processMessage(),"
2417 <<" message="<<serializeJsonString(data)
2423 Callbacks for activeobjects
2426 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2428 LocalPlayer *lplayer = getLocalPlayer();
2432 if(lplayer->hp > damage)
2433 lplayer->hp -= damage;
2438 ClientEnvEvent event;
2439 event.type = CEE_PLAYER_DAMAGE;
2440 event.player_damage.amount = damage;
2441 event.player_damage.send_to_server = handle_hp;
2442 m_client_event_queue.push_back(event);
2446 Client likes to call these
2449 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2450 std::vector<DistanceSortedActiveObject> &dest)
2452 for(std::map<u16, ClientActiveObject*>::iterator
2453 i = m_active_objects.begin();
2454 i != m_active_objects.end(); ++i)
2456 ClientActiveObject* obj = i->second;
2458 f32 d = (obj->getPosition() - origin).getLength();
2463 DistanceSortedActiveObject dso(obj, d);
2465 dest.push_back(dso);
2469 ClientEnvEvent ClientEnvironment::getClientEvent()
2471 if(m_client_event_queue.empty())
2473 ClientEnvEvent event;
2474 event.type = CEE_NONE;
2477 return m_client_event_queue.pop_front();
2480 #endif // #ifndef SERVER