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 v3f objectpos = obj->getBasePosition();
914 // Delete static object if block is loaded
915 if(obj->m_static_exists){
916 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
918 block->m_static_objects.remove(id);
919 block->raiseModified(MOD_STATE_WRITE_NEEDED,
921 obj->m_static_exists = false;
924 // If known by some client, don't delete immediately
925 if(obj->m_known_by_count > 0){
926 obj->m_pending_deactivation = true;
927 obj->m_removed = true;
931 // Tell the object about removal
932 obj->removingFromEnvironment();
933 // Deregister in scripting api
934 scriptapi_rm_object_reference(m_lua, obj);
936 // Delete active object
937 if(obj->environmentDeletes())
939 // Id to be removed from m_active_objects
940 objects_to_remove.push_back(id);
942 // Remove references from m_active_objects
943 for(std::list<u16>::iterator i = objects_to_remove.begin();
944 i != objects_to_remove.end(); ++i)
946 m_active_objects.erase(*i);
949 std::list<v3s16> loadable_blocks;
950 infostream<<"ServerEnvironment::clearAllObjects(): "
951 <<"Listing all loadable blocks"<<std::endl;
952 m_map->listAllLoadableBlocks(loadable_blocks);
953 infostream<<"ServerEnvironment::clearAllObjects(): "
954 <<"Done listing all loadable blocks: "
955 <<loadable_blocks.size()
956 <<", now clearing"<<std::endl;
957 u32 report_interval = loadable_blocks.size() / 10;
958 u32 num_blocks_checked = 0;
959 u32 num_blocks_cleared = 0;
960 u32 num_objs_cleared = 0;
961 for(std::list<v3s16>::iterator i = loadable_blocks.begin();
962 i != loadable_blocks.end(); ++i)
965 MapBlock *block = m_map->emergeBlock(p, false);
967 errorstream<<"ServerEnvironment::clearAllObjects(): "
968 <<"Failed to emerge block "<<PP(p)<<std::endl;
971 u32 num_stored = block->m_static_objects.m_stored.size();
972 u32 num_active = block->m_static_objects.m_active.size();
973 if(num_stored != 0 || num_active != 0){
974 block->m_static_objects.m_stored.clear();
975 block->m_static_objects.m_active.clear();
976 block->raiseModified(MOD_STATE_WRITE_NEEDED,
978 num_objs_cleared += num_stored + num_active;
979 num_blocks_cleared++;
981 num_blocks_checked++;
983 if(num_blocks_checked % report_interval == 0){
984 float percent = 100.0 * (float)num_blocks_checked /
985 loadable_blocks.size();
986 infostream<<"ServerEnvironment::clearAllObjects(): "
987 <<"Cleared "<<num_objs_cleared<<" objects"
988 <<" in "<<num_blocks_cleared<<" blocks ("
989 <<percent<<"%)"<<std::endl;
992 infostream<<"ServerEnvironment::clearAllObjects(): "
993 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
994 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
997 void ServerEnvironment::step(float dtime)
999 DSTACK(__FUNCTION_NAME);
1001 //TimeTaker timer("ServerEnv step");
1003 /* Step time of day */
1004 stepTimeOfDay(dtime);
1007 // NOTE: This is kind of funny on a singleplayer game, but doesn't
1008 // really matter that much.
1009 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
1015 m_game_time_fraction_counter += dtime;
1016 u32 inc_i = (u32)m_game_time_fraction_counter;
1017 m_game_time += inc_i;
1018 m_game_time_fraction_counter -= (float)inc_i;
1025 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1026 for(std::list<Player*>::iterator i = m_players.begin();
1027 i != m_players.end(); ++i)
1029 Player *player = *i;
1031 // Ignore disconnected players
1032 if(player->peer_id == 0)
1035 v3f playerpos = player->getPosition();
1038 player->move(dtime, *m_map, 100*BS);
1043 Manage active block list
1045 if(m_active_blocks_management_interval.step(dtime, 2.0))
1047 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1049 Get player block positions
1051 std::list<v3s16> players_blockpos;
1052 for(std::list<Player*>::iterator
1053 i = m_players.begin();
1054 i != m_players.end(); ++i)
1056 Player *player = *i;
1057 // Ignore disconnected players
1058 if(player->peer_id == 0)
1060 v3s16 blockpos = getNodeBlockPos(
1061 floatToInt(player->getPosition(), BS));
1062 players_blockpos.push_back(blockpos);
1066 Update list of active blocks, collecting changes
1068 const s16 active_block_range = g_settings->getS16("active_block_range");
1069 std::set<v3s16> blocks_removed;
1070 std::set<v3s16> blocks_added;
1071 m_active_blocks.update(players_blockpos, active_block_range,
1072 blocks_removed, blocks_added);
1075 Handle removed blocks
1078 // Convert active objects that are no more in active blocks to static
1079 deactivateFarObjects(false);
1081 for(std::set<v3s16>::iterator
1082 i = blocks_removed.begin();
1083 i != blocks_removed.end(); ++i)
1087 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1088 <<") became inactive"<<std::endl;*/
1090 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1094 // Set current time as timestamp (and let it set ChangedFlag)
1095 block->setTimestamp(m_game_time);
1102 for(std::set<v3s16>::iterator
1103 i = blocks_added.begin();
1104 i != blocks_added.end(); ++i)
1108 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1109 <<") became active"<<std::endl;*/
1111 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1113 // Block needs to be fetched first
1114 m_emerger->queueBlockEmerge(p, false);
1115 m_active_blocks.m_list.erase(p);
1119 activateBlock(block);
1124 Mess around in active blocks
1126 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1128 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1132 for(std::set<v3s16>::iterator
1133 i = m_active_blocks.m_list.begin();
1134 i != m_active_blocks.m_list.end(); ++i)
1138 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1139 <<") being handled"<<std::endl;*/
1141 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1145 // Reset block usage timer
1146 block->resetUsageTimer();
1148 // Set current time as timestamp
1149 block->setTimestampNoChangedFlag(m_game_time);
1150 // If time has changed much from the one on disk,
1151 // set block to be saved when it is unloaded
1152 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1153 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1154 "Timestamp older than 60s (step)");
1157 std::map<v3s16, NodeTimer> elapsed_timers =
1158 block->m_node_timers.step((float)dtime);
1159 if(!elapsed_timers.empty()){
1161 for(std::map<v3s16, NodeTimer>::iterator
1162 i = elapsed_timers.begin();
1163 i != elapsed_timers.end(); i++){
1164 n = block->getNodeNoEx(i->first);
1165 p = i->first + block->getPosRelative();
1166 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1167 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1173 const float abm_interval = 1.0;
1174 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1176 if(m_active_block_interval_overload_skip > 0){
1177 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1178 m_active_block_interval_overload_skip--;
1181 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1182 TimeTaker timer("modify in active blocks");
1184 // Initialize handling of ActiveBlockModifiers
1185 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1187 for(std::set<v3s16>::iterator
1188 i = m_active_blocks.m_list.begin();
1189 i != m_active_blocks.m_list.end(); ++i)
1193 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1194 <<") being handled"<<std::endl;*/
1196 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1200 // Set current time as timestamp
1201 block->setTimestampNoChangedFlag(m_game_time);
1203 /* Handle ActiveBlockModifiers */
1204 abmhandler.apply(block);
1207 u32 time_ms = timer.stop(true);
1208 u32 max_time_ms = 200;
1209 if(time_ms > max_time_ms){
1210 infostream<<"WARNING: active block modifiers took "
1211 <<time_ms<<"ms (longer than "
1212 <<max_time_ms<<"ms)"<<std::endl;
1213 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1218 Step script environment (run global on_step())
1220 scriptapi_environment_step(m_lua, dtime);
1226 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1227 //TimeTaker timer("Step active objects");
1229 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1231 // This helps the objects to send data at the same time
1232 bool send_recommended = false;
1233 m_send_recommended_timer += dtime;
1234 if(m_send_recommended_timer > getSendRecommendedInterval())
1236 m_send_recommended_timer -= getSendRecommendedInterval();
1237 send_recommended = true;
1240 for(std::map<u16, ServerActiveObject*>::iterator
1241 i = m_active_objects.begin();
1242 i != m_active_objects.end(); ++i)
1244 ServerActiveObject* obj = i->second;
1245 // Remove non-peaceful mobs on peaceful mode
1246 if(g_settings->getBool("only_peaceful_mobs")){
1247 if(!obj->isPeaceful())
1248 obj->m_removed = true;
1250 // Don't step if is to be removed or stored statically
1251 if(obj->m_removed || obj->m_pending_deactivation)
1254 obj->step(dtime, send_recommended);
1255 // Read messages from object
1256 while(!obj->m_messages_out.empty())
1258 m_active_object_messages.push_back(
1259 obj->m_messages_out.pop_front());
1265 Manage active objects
1267 if(m_object_management_interval.step(dtime, 0.5))
1269 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1271 Remove objects that satisfy (m_removed && m_known_by_count==0)
1273 removeRemovedObjects();
1277 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1279 std::map<u16, ServerActiveObject*>::iterator n;
1280 n = m_active_objects.find(id);
1281 if(n == m_active_objects.end())
1286 bool isFreeServerActiveObjectId(u16 id,
1287 std::map<u16, ServerActiveObject*> &objects)
1292 return objects.find(id) == objects.end();
1295 u16 getFreeServerActiveObjectId(
1296 std::map<u16, ServerActiveObject*> &objects)
1301 if(isFreeServerActiveObjectId(new_id, objects))
1311 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1314 u16 id = addActiveObjectRaw(object, true, 0);
1319 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1323 v3f objectpos = obj->getBasePosition();
1325 // The block in which the object resides in
1326 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1329 Update the static data
1332 // Create new static object
1333 std::string staticdata = obj->getStaticData();
1334 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1335 // Add to the block where the object is located in
1336 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1337 // Get or generate the block
1338 MapBlock *block = m_map->emergeBlock(blockpos);
1340 bool succeeded = false;
1344 block->m_static_objects.insert(0, s_obj);
1345 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1346 "addActiveObjectAsStatic");
1350 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1351 <<"Could not find or generate "
1352 <<"a block for storing static object"<<std::endl;
1356 if(obj->environmentDeletes())
1364 Finds out what new objects have been added to
1365 inside a radius around a position
1367 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1368 std::set<u16> ¤t_objects,
1369 std::set<u16> &added_objects)
1371 v3f pos_f = intToFloat(pos, BS);
1372 f32 radius_f = radius * BS;
1374 Go through the object list,
1375 - discard m_removed objects,
1376 - discard objects that are too far away,
1377 - discard objects that are found in current_objects.
1378 - add remaining objects to added_objects
1380 for(std::map<u16, ServerActiveObject*>::iterator
1381 i = m_active_objects.begin();
1382 i != m_active_objects.end(); ++i)
1386 ServerActiveObject *object = i->second;
1389 // Discard if removed
1390 if(object->m_removed)
1392 if(object->unlimitedTransferDistance() == false){
1393 // Discard if too far
1394 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1395 if(distance_f > radius_f)
1398 // Discard if already on current_objects
1399 std::set<u16>::iterator n;
1400 n = current_objects.find(id);
1401 if(n != current_objects.end())
1403 // Add to added_objects
1404 added_objects.insert(id);
1409 Finds out what objects have been removed from
1410 inside a radius around a position
1412 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1413 std::set<u16> ¤t_objects,
1414 std::set<u16> &removed_objects)
1416 v3f pos_f = intToFloat(pos, BS);
1417 f32 radius_f = radius * BS;
1419 Go through current_objects; object is removed if:
1420 - object is not found in m_active_objects (this is actually an
1421 error condition; objects should be set m_removed=true and removed
1422 only after all clients have been informed about removal), or
1423 - object has m_removed=true, or
1424 - object is too far away
1426 for(std::set<u16>::iterator
1427 i = current_objects.begin();
1428 i != current_objects.end(); ++i)
1431 ServerActiveObject *object = getActiveObject(id);
1434 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1435 <<" object in current_objects is NULL"<<std::endl;
1436 removed_objects.insert(id);
1440 if(object->m_removed)
1442 removed_objects.insert(id);
1446 // If transfer distance is unlimited, don't remove
1447 if(object->unlimitedTransferDistance())
1450 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1452 if(distance_f >= radius_f)
1454 removed_objects.insert(id);
1462 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1464 if(m_active_object_messages.empty())
1465 return ActiveObjectMessage(0);
1467 return m_active_object_messages.pop_front();
1471 ************ Private methods *************
1474 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1475 bool set_changed, u32 dtime_s)
1478 if(object->getId() == 0){
1479 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1482 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"no free ids available"<<std::endl;
1484 if(object->environmentDeletes())
1488 object->setId(new_id);
1491 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1492 <<"supplied with id "<<object->getId()<<std::endl;
1494 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1496 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1497 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1498 if(object->environmentDeletes())
1502 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1503 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1505 m_active_objects[object->getId()] = object;
1507 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1508 <<"Added id="<<object->getId()<<"; there are now "
1509 <<m_active_objects.size()<<" active objects."
1512 // Register reference in scripting api (must be done before post-init)
1513 scriptapi_add_object_reference(m_lua, object);
1514 // Post-initialize object
1515 object->addedToEnvironment(dtime_s);
1517 // Add static data to block
1518 if(object->isStaticAllowed())
1520 // Add static object to active static list of the block
1521 v3f objectpos = object->getBasePosition();
1522 std::string staticdata = object->getStaticData();
1523 StaticObject s_obj(object->getType(), objectpos, staticdata);
1524 // Add to the block where the object is located in
1525 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1526 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1529 block->m_static_objects.m_active[object->getId()] = s_obj;
1530 object->m_static_exists = true;
1531 object->m_static_block = blockpos;
1534 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1535 "addActiveObjectRaw");
1538 v3s16 p = floatToInt(objectpos, BS);
1539 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1540 <<"could not find block for storing id="<<object->getId()
1541 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1545 return object->getId();
1549 Remove objects that satisfy (m_removed && m_known_by_count==0)
1551 void ServerEnvironment::removeRemovedObjects()
1553 std::list<u16> objects_to_remove;
1554 for(std::map<u16, ServerActiveObject*>::iterator
1555 i = m_active_objects.begin();
1556 i != m_active_objects.end(); ++i)
1559 ServerActiveObject* obj = i->second;
1560 // This shouldn't happen but check it
1563 infostream<<"NULL object found in ServerEnvironment"
1564 <<" while finding removed objects. id="<<id<<std::endl;
1565 // Id to be removed from m_active_objects
1566 objects_to_remove.push_back(id);
1571 We will delete objects that are marked as removed or thatare
1572 waiting for deletion after deactivation
1574 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1578 Delete static data from block if is marked as removed
1580 if(obj->m_static_exists && obj->m_removed)
1582 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1584 block->m_static_objects.remove(id);
1585 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1586 "removeRemovedObjects");
1587 obj->m_static_exists = false;
1589 infostream << "failed to emerge block from which "
1590 "an object to be removed was loaded from. id="<<id<<std::endl;
1594 // If m_known_by_count > 0, don't actually remove.
1595 if(obj->m_known_by_count > 0)
1598 // Tell the object about removal
1599 obj->removingFromEnvironment();
1600 // Deregister in scripting api
1601 scriptapi_rm_object_reference(m_lua, obj);
1604 if(obj->environmentDeletes())
1606 // Id to be removed from m_active_objects
1607 objects_to_remove.push_back(id);
1609 // Remove references from m_active_objects
1610 for(std::list<u16>::iterator i = objects_to_remove.begin();
1611 i != objects_to_remove.end(); ++i)
1613 m_active_objects.erase(*i);
1617 static void print_hexdump(std::ostream &o, const std::string &data)
1619 const int linelength = 16;
1620 for(int l=0; ; l++){
1621 int i0 = linelength * l;
1622 bool at_end = false;
1623 int thislinelength = linelength;
1624 if(i0 + thislinelength > (int)data.size()){
1625 thislinelength = data.size() - i0;
1628 for(int di=0; di<linelength; di++){
1631 if(di<thislinelength)
1632 snprintf(buf, 4, "%.2x ", data[i]);
1634 snprintf(buf, 4, " ");
1638 for(int di=0; di<thislinelength; di++){
1652 Convert stored objects from blocks near the players to active.
1654 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1658 // Ignore if no stored objects (to not set changed flag)
1659 if(block->m_static_objects.m_stored.size() == 0)
1661 verbosestream<<"ServerEnvironment::activateObjects(): "
1662 <<"activating objects of block "<<PP(block->getPos())
1663 <<" ("<<block->m_static_objects.m_stored.size()
1664 <<" objects)"<<std::endl;
1665 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1667 errorstream<<"suspiciously large amount of objects detected: "
1668 <<block->m_static_objects.m_stored.size()<<" in "
1669 <<PP(block->getPos())
1670 <<"; removing all of them."<<std::endl;
1671 // Clear stored list
1672 block->m_static_objects.m_stored.clear();
1673 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1674 "stored list cleared in activateObjects due to "
1675 "large amount of objects");
1678 // A list for objects that couldn't be converted to active for some
1679 // reason. They will be stored back.
1680 std::list<StaticObject> new_stored;
1681 // Loop through stored static objects
1682 for(std::list<StaticObject>::iterator
1683 i = block->m_static_objects.m_stored.begin();
1684 i != block->m_static_objects.m_stored.end(); ++i)
1686 /*infostream<<"Server: Creating an active object from "
1687 <<"static data"<<std::endl;*/
1688 StaticObject &s_obj = *i;
1689 // Create an active object from the data
1690 ServerActiveObject *obj = ServerActiveObject::create
1691 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1692 // If couldn't create object, store static data back.
1695 errorstream<<"ServerEnvironment::activateObjects(): "
1696 <<"failed to create active object from static object "
1697 <<"in block "<<PP(s_obj.pos/BS)
1698 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1699 print_hexdump(verbosestream, s_obj.data);
1701 new_stored.push_back(s_obj);
1704 verbosestream<<"ServerEnvironment::activateObjects(): "
1705 <<"activated static object pos="<<PP(s_obj.pos/BS)
1706 <<" type="<<(int)s_obj.type<<std::endl;
1707 // This will also add the object to the active static list
1708 addActiveObjectRaw(obj, false, dtime_s);
1710 // Clear stored list
1711 block->m_static_objects.m_stored.clear();
1712 // Add leftover failed stuff to stored list
1713 for(std::list<StaticObject>::iterator
1714 i = new_stored.begin();
1715 i != new_stored.end(); ++i)
1717 StaticObject &s_obj = *i;
1718 block->m_static_objects.m_stored.push_back(s_obj);
1721 Note: Block hasn't really been modified here.
1722 The objects have just been activated and moved from the stored
1723 static list to the active static list.
1724 As such, the block is essentially the same.
1725 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1726 Otherwise there would be a huge amount of unnecessary I/O.
1731 Convert objects that are not standing inside active blocks to static.
1733 If m_known_by_count != 0, active object is not deleted, but static
1734 data is still updated.
1736 If force_delete is set, active object is deleted nevertheless. It
1737 shall only be set so in the destructor of the environment.
1739 If block wasn't generated (not in memory or on disk),
1741 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1743 std::list<u16> objects_to_remove;
1744 for(std::map<u16, ServerActiveObject*>::iterator
1745 i = m_active_objects.begin();
1746 i != m_active_objects.end(); ++i)
1748 ServerActiveObject* obj = i->second;
1751 // Do not deactivate if static data creation not allowed
1752 if(!force_delete && !obj->isStaticAllowed())
1755 // If pending deactivation, let removeRemovedObjects() do it
1756 if(!force_delete && obj->m_pending_deactivation)
1760 v3f objectpos = obj->getBasePosition();
1762 // The block in which the object resides in
1763 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1765 // If block is active, don't remove
1766 if(!force_delete && m_active_blocks.contains(blockpos_o))
1769 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1770 <<"deactivating object id="<<id<<" on inactive block "
1771 <<PP(blockpos_o)<<std::endl;
1773 // If known by some client, don't immediately delete.
1774 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1777 Update the static data
1780 if(obj->isStaticAllowed())
1782 // Create new static object
1783 std::string staticdata_new = obj->getStaticData();
1784 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1786 bool stays_in_same_block = false;
1787 bool data_changed = true;
1789 if(obj->m_static_exists){
1790 if(obj->m_static_block == blockpos_o)
1791 stays_in_same_block = true;
1793 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1795 std::map<u16, StaticObject>::iterator n =
1796 block->m_static_objects.m_active.find(id);
1797 if(n != block->m_static_objects.m_active.end()){
1798 StaticObject static_old = n->second;
1800 float save_movem = obj->getMinimumSavedMovement();
1802 if(static_old.data == staticdata_new &&
1803 (static_old.pos - objectpos).getLength() < save_movem)
1804 data_changed = false;
1806 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1807 <<"id="<<id<<" m_static_exists=true but "
1808 <<"static data doesn't actually exist in "
1809 <<PP(obj->m_static_block)<<std::endl;
1813 bool shall_be_written = (!stays_in_same_block || data_changed);
1815 // Delete old static object
1816 if(obj->m_static_exists)
1818 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1821 block->m_static_objects.remove(id);
1822 obj->m_static_exists = false;
1823 // Only mark block as modified if data changed considerably
1824 if(shall_be_written)
1825 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1826 "deactivateFarObjects: Static data "
1827 "changed considerably");
1831 // Add to the block where the object is located in
1832 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1833 // Get or generate the block
1834 MapBlock *block = NULL;
1836 block = m_map->emergeBlock(blockpos);
1837 } catch(InvalidPositionException &e){
1838 // Handled via NULL pointer
1843 if(block->m_static_objects.m_stored.size() >= 49){
1844 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1845 <<" statically but block "<<PP(blockpos)
1846 <<" already contains "
1847 <<block->m_static_objects.m_stored.size()
1848 <<" (over 49) objects."
1849 <<" Forcing delete."<<std::endl;
1850 force_delete = true;
1852 u16 new_id = pending_delete ? id : 0;
1853 // If static counterpart already exists, remove it first.
1854 // This shouldn't happen, but happens rarely for some
1855 // unknown reason. Unsuccessful attempts have been made to
1856 // find said reason.
1857 if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
1858 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1860 block->m_static_objects.remove(new_id);
1862 block->m_static_objects.insert(new_id, s_obj);
1864 // Only mark block as modified if data changed considerably
1865 if(shall_be_written)
1866 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1867 "deactivateFarObjects: Static data "
1868 "changed considerably");
1870 obj->m_static_exists = true;
1871 obj->m_static_block = block->getPos();
1876 v3s16 p = floatToInt(objectpos, BS);
1877 errorstream<<"ServerEnv: Could not find or generate "
1878 <<"a block for storing id="<<obj->getId()
1879 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1886 If known by some client, set pending deactivation.
1887 Otherwise delete it immediately.
1890 if(pending_delete && !force_delete)
1892 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1893 <<"object id="<<id<<" is known by clients"
1894 <<"; not deleting yet"<<std::endl;
1896 obj->m_pending_deactivation = true;
1900 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1901 <<"object id="<<id<<" is not known by clients"
1902 <<"; deleting"<<std::endl;
1904 // Tell the object about removal
1905 obj->removingFromEnvironment();
1906 // Deregister in scripting api
1907 scriptapi_rm_object_reference(m_lua, obj);
1909 // Delete active object
1910 if(obj->environmentDeletes())
1912 // Id to be removed from m_active_objects
1913 objects_to_remove.push_back(id);
1916 // Remove references from m_active_objects
1917 for(std::list<u16>::iterator i = objects_to_remove.begin();
1918 i != objects_to_remove.end(); ++i)
1920 m_active_objects.erase(*i);
1927 #include "clientsimpleobject.h"
1933 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1934 ITextureSource *texturesource, IGameDef *gamedef,
1935 IrrlichtDevice *irr):
1938 m_texturesource(texturesource),
1944 ClientEnvironment::~ClientEnvironment()
1946 // delete active objects
1947 for(std::map<u16, ClientActiveObject*>::iterator
1948 i = m_active_objects.begin();
1949 i != m_active_objects.end(); ++i)
1954 for(std::list<ClientSimpleObject*>::iterator
1955 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
1964 Map & ClientEnvironment::getMap()
1969 ClientMap & ClientEnvironment::getClientMap()
1974 void ClientEnvironment::addPlayer(Player *player)
1976 DSTACK(__FUNCTION_NAME);
1978 It is a failure if player is local and there already is a local
1981 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1983 Environment::addPlayer(player);
1986 LocalPlayer * ClientEnvironment::getLocalPlayer()
1988 for(std::list<Player*>::iterator i = m_players.begin();
1989 i != m_players.end(); ++i)
1991 Player *player = *i;
1992 if(player->isLocal())
1993 return (LocalPlayer*)player;
1998 void ClientEnvironment::step(float dtime)
2000 DSTACK(__FUNCTION_NAME);
2002 /* Step time of day */
2003 stepTimeOfDay(dtime);
2005 // Get some settings
2006 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2007 bool free_move = fly_allowed && g_settings->getBool("free_move");
2010 LocalPlayer *lplayer = getLocalPlayer();
2012 // collision info queue
2013 std::list<CollisionInfo> player_collisions;
2016 Get the speed the player is going
2018 bool is_climbing = lplayer->is_climbing;
2020 f32 player_speed = lplayer->getSpeed().getLength();
2023 Maximum position increment
2025 //f32 position_max_increment = 0.05*BS;
2026 f32 position_max_increment = 0.1*BS;
2028 // Maximum time increment (for collision detection etc)
2029 // time = distance / speed
2030 f32 dtime_max_increment = 1;
2031 if(player_speed > 0.001)
2032 dtime_max_increment = position_max_increment / player_speed;
2034 // Maximum time increment is 10ms or lower
2035 if(dtime_max_increment > 0.01)
2036 dtime_max_increment = 0.01;
2038 // Don't allow overly huge dtime
2042 f32 dtime_downcount = dtime;
2045 Stuff that has a maximum time increment
2054 if(dtime_downcount > dtime_max_increment)
2056 dtime_part = dtime_max_increment;
2057 dtime_downcount -= dtime_part;
2061 dtime_part = dtime_downcount;
2063 Setting this to 0 (no -=dtime_part) disables an infinite loop
2064 when dtime_part is so small that dtime_downcount -= dtime_part
2067 dtime_downcount = 0;
2075 v3f lplayerpos = lplayer->getPosition();
2078 if(free_move == false && is_climbing == false)
2081 v3f speed = lplayer->getSpeed();
2082 if(lplayer->in_liquid == false)
2083 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2085 // Liquid floating / sinking
2086 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2087 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2089 // Liquid resistance
2090 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2092 // How much the node's viscosity blocks movement, ranges between 0 and 1
2093 // Should match the scale at which viscosity increase affects other liquid attributes
2094 const f32 viscosity_factor = 0.3;
2096 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2097 f32 dl = d_wanted.getLength();
2098 if(dl > lplayer->movement_liquid_fluidity_smooth)
2099 dl = lplayer->movement_liquid_fluidity_smooth;
2100 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2102 v3f d = d_wanted.normalize() * dl;
2106 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2107 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2108 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2109 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2110 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2111 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2115 lplayer->setSpeed(speed);
2120 This also does collision detection.
2122 lplayer->move(dtime_part, this, position_max_increment,
2123 &player_collisions);
2126 while(dtime_downcount > 0.001);
2128 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2130 for(std::list<CollisionInfo>::iterator
2131 i = player_collisions.begin();
2132 i != player_collisions.end(); ++i)
2134 CollisionInfo &info = *i;
2135 v3f speed_diff = info.new_speed - info.old_speed;;
2136 // Handle only fall damage
2137 // (because otherwise walking against something in fast_move kills you)
2138 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2140 // Get rid of other components
2143 f32 pre_factor = 1; // 1 hp per node/s
2144 f32 tolerance = BS*14; // 5 without damage
2145 f32 post_factor = 1; // 1 hp per node/s
2146 if(info.type == COLLISION_NODE)
2148 const ContentFeatures &f = m_gamedef->ndef()->
2149 get(m_map->getNodeNoEx(info.node_p));
2150 // Determine fall damage multiplier
2151 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2152 pre_factor = 1.0 + (float)addp/100.0;
2154 float speed = pre_factor * speed_diff.getLength();
2155 if(speed > tolerance)
2157 f32 damage_f = (speed - tolerance)/BS * post_factor;
2158 u16 damage = (u16)(damage_f+0.5);
2160 damageLocalPlayer(damage, true);
2165 A quick draft of lava damage
2167 if(m_lava_hurt_interval.step(dtime, 1.0))
2169 v3f pf = lplayer->getPosition();
2171 // Feet, middle and head
2172 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2173 MapNode n1 = m_map->getNodeNoEx(p1);
2174 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2175 MapNode n2 = m_map->getNodeNoEx(p2);
2176 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2177 MapNode n3 = m_map->getNodeNoEx(p2);
2179 u32 damage_per_second = 0;
2180 damage_per_second = MYMAX(damage_per_second,
2181 m_gamedef->ndef()->get(n1).damage_per_second);
2182 damage_per_second = MYMAX(damage_per_second,
2183 m_gamedef->ndef()->get(n2).damage_per_second);
2184 damage_per_second = MYMAX(damage_per_second,
2185 m_gamedef->ndef()->get(n3).damage_per_second);
2187 if(damage_per_second != 0)
2189 damageLocalPlayer(damage_per_second, true);
2194 Stuff that can be done in an arbitarily large dtime
2196 for(std::list<Player*>::iterator i = m_players.begin();
2197 i != m_players.end(); ++i)
2199 Player *player = *i;
2200 v3f playerpos = player->getPosition();
2203 Handle non-local players
2205 if(player->isLocal() == false)
2208 player->move(dtime, *m_map, 100*BS);
2212 // Update lighting on all players on client
2216 v3s16 p = player->getLightPosition();
2217 MapNode n = m_map->getNode(p);
2218 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2220 catch(InvalidPositionException &e){
2221 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2223 player->light = light;
2227 Step active objects and update lighting of them
2230 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2231 for(std::map<u16, ClientActiveObject*>::iterator
2232 i = m_active_objects.begin();
2233 i != m_active_objects.end(); ++i)
2235 ClientActiveObject* obj = i->second;
2237 obj->step(dtime, this);
2245 v3s16 p = obj->getLightPosition();
2246 MapNode n = m_map->getNode(p);
2247 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2249 catch(InvalidPositionException &e){
2250 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2252 obj->updateLight(light);
2257 Step and handle simple objects
2259 for(std::list<ClientSimpleObject*>::iterator
2260 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2262 ClientSimpleObject *simple = *i;
2263 std::list<ClientSimpleObject*>::iterator cur = i;
2265 simple->step(dtime);
2266 if(simple->m_to_be_removed){
2268 m_simple_objects.erase(cur);
2273 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2275 m_simple_objects.push_back(simple);
2278 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2280 std::map<u16, ClientActiveObject*>::iterator n;
2281 n = m_active_objects.find(id);
2282 if(n == m_active_objects.end())
2287 bool isFreeClientActiveObjectId(u16 id,
2288 std::map<u16, ClientActiveObject*> &objects)
2293 return objects.find(id) == objects.end();
2296 u16 getFreeClientActiveObjectId(
2297 std::map<u16, ClientActiveObject*> &objects)
2302 if(isFreeClientActiveObjectId(new_id, objects))
2312 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2315 if(object->getId() == 0)
2317 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2320 infostream<<"ClientEnvironment::addActiveObject(): "
2321 <<"no free ids available"<<std::endl;
2325 object->setId(new_id);
2327 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2329 infostream<<"ClientEnvironment::addActiveObject(): "
2330 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2334 infostream<<"ClientEnvironment::addActiveObject(): "
2335 <<"added (id="<<object->getId()<<")"<<std::endl;
2336 m_active_objects[object->getId()] = object;
2337 object->addToScene(m_smgr, m_texturesource, m_irr);
2338 { // Update lighting immediately
2342 v3s16 p = object->getLightPosition();
2343 MapNode n = m_map->getNode(p);
2344 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2346 catch(InvalidPositionException &e){
2347 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2349 object->updateLight(light);
2351 return object->getId();
2354 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2355 const std::string &init_data)
2357 ClientActiveObject* obj =
2358 ClientActiveObject::create(type, m_gamedef, this);
2361 infostream<<"ClientEnvironment::addActiveObject(): "
2362 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2371 obj->initialize(init_data);
2373 catch(SerializationError &e)
2375 errorstream<<"ClientEnvironment::addActiveObject():"
2376 <<" id="<<id<<" type="<<type
2377 <<": SerializationError in initialize(): "
2379 <<": init_data="<<serializeJsonString(init_data)
2383 addActiveObject(obj);
2386 void ClientEnvironment::removeActiveObject(u16 id)
2388 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2389 <<"id="<<id<<std::endl;
2390 ClientActiveObject* obj = getActiveObject(id);
2393 infostream<<"ClientEnvironment::removeActiveObject(): "
2394 <<"id="<<id<<" not found"<<std::endl;
2397 obj->removeFromScene(true);
2399 m_active_objects.erase(id);
2402 void ClientEnvironment::processActiveObjectMessage(u16 id,
2403 const std::string &data)
2405 ClientActiveObject* obj = getActiveObject(id);
2408 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2409 <<" got message for id="<<id<<", which doesn't exist."
2415 obj->processMessage(data);
2417 catch(SerializationError &e)
2419 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2420 <<" id="<<id<<" type="<<obj->getType()
2421 <<" SerializationError in processMessage(),"
2422 <<" message="<<serializeJsonString(data)
2428 Callbacks for activeobjects
2431 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2433 LocalPlayer *lplayer = getLocalPlayer();
2437 if(lplayer->hp > damage)
2438 lplayer->hp -= damage;
2443 ClientEnvEvent event;
2444 event.type = CEE_PLAYER_DAMAGE;
2445 event.player_damage.amount = damage;
2446 event.player_damage.send_to_server = handle_hp;
2447 m_client_event_queue.push_back(event);
2451 Client likes to call these
2454 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2455 std::vector<DistanceSortedActiveObject> &dest)
2457 for(std::map<u16, ClientActiveObject*>::iterator
2458 i = m_active_objects.begin();
2459 i != m_active_objects.end(); ++i)
2461 ClientActiveObject* obj = i->second;
2463 f32 d = (obj->getPosition() - origin).getLength();
2468 DistanceSortedActiveObject dso(obj, d);
2470 dest.push_back(dso);
2474 ClientEnvEvent ClientEnvironment::getClientEvent()
2476 if(m_client_event_queue.empty())
2478 ClientEnvEvent event;
2479 event.type = CEE_NONE;
2482 return m_client_event_queue.pop_front();
2485 #endif // #ifndef SERVER