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)
1298 //try to reuse id's as late as possible
1299 static u16 last_used_id = 0;
1300 u16 startid = last_used_id;
1304 if(isFreeServerActiveObjectId(last_used_id, objects))
1305 return last_used_id;
1307 if(last_used_id == startid)
1312 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1315 u16 id = addActiveObjectRaw(object, true, 0);
1320 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1324 v3f objectpos = obj->getBasePosition();
1326 // The block in which the object resides in
1327 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1330 Update the static data
1333 // Create new static object
1334 std::string staticdata = obj->getStaticData();
1335 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1336 // Add to the block where the object is located in
1337 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1338 // Get or generate the block
1339 MapBlock *block = m_map->emergeBlock(blockpos);
1341 bool succeeded = false;
1345 block->m_static_objects.insert(0, s_obj);
1346 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1347 "addActiveObjectAsStatic");
1351 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1352 <<"Could not find or generate "
1353 <<"a block for storing static object"<<std::endl;
1357 if(obj->environmentDeletes())
1365 Finds out what new objects have been added to
1366 inside a radius around a position
1368 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1369 std::set<u16> ¤t_objects,
1370 std::set<u16> &added_objects)
1372 v3f pos_f = intToFloat(pos, BS);
1373 f32 radius_f = radius * BS;
1375 Go through the object list,
1376 - discard m_removed objects,
1377 - discard objects that are too far away,
1378 - discard objects that are found in current_objects.
1379 - add remaining objects to added_objects
1381 for(std::map<u16, ServerActiveObject*>::iterator
1382 i = m_active_objects.begin();
1383 i != m_active_objects.end(); ++i)
1387 ServerActiveObject *object = i->second;
1390 // Discard if removed
1391 if(object->m_removed)
1393 if(object->unlimitedTransferDistance() == false){
1394 // Discard if too far
1395 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1396 if(distance_f > radius_f)
1399 // Discard if already on current_objects
1400 std::set<u16>::iterator n;
1401 n = current_objects.find(id);
1402 if(n != current_objects.end())
1404 // Add to added_objects
1405 added_objects.insert(id);
1410 Finds out what objects have been removed from
1411 inside a radius around a position
1413 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1414 std::set<u16> ¤t_objects,
1415 std::set<u16> &removed_objects)
1417 v3f pos_f = intToFloat(pos, BS);
1418 f32 radius_f = radius * BS;
1420 Go through current_objects; object is removed if:
1421 - object is not found in m_active_objects (this is actually an
1422 error condition; objects should be set m_removed=true and removed
1423 only after all clients have been informed about removal), or
1424 - object has m_removed=true, or
1425 - object is too far away
1427 for(std::set<u16>::iterator
1428 i = current_objects.begin();
1429 i != current_objects.end(); ++i)
1432 ServerActiveObject *object = getActiveObject(id);
1435 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1436 <<" object in current_objects is NULL"<<std::endl;
1437 removed_objects.insert(id);
1441 if(object->m_removed)
1443 removed_objects.insert(id);
1447 // If transfer distance is unlimited, don't remove
1448 if(object->unlimitedTransferDistance())
1451 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1453 if(distance_f >= radius_f)
1455 removed_objects.insert(id);
1463 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1465 if(m_active_object_messages.empty())
1466 return ActiveObjectMessage(0);
1468 return m_active_object_messages.pop_front();
1472 ************ Private methods *************
1475 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1476 bool set_changed, u32 dtime_s)
1479 if(object->getId() == 0){
1480 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1483 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1484 <<"no free ids available"<<std::endl;
1485 if(object->environmentDeletes())
1489 object->setId(new_id);
1492 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1493 <<"supplied with id "<<object->getId()<<std::endl;
1495 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1497 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1498 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1499 if(object->environmentDeletes())
1503 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1504 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1506 m_active_objects[object->getId()] = object;
1508 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1509 <<"Added id="<<object->getId()<<"; there are now "
1510 <<m_active_objects.size()<<" active objects."
1513 // Register reference in scripting api (must be done before post-init)
1514 scriptapi_add_object_reference(m_lua, object);
1515 // Post-initialize object
1516 object->addedToEnvironment(dtime_s);
1518 // Add static data to block
1519 if(object->isStaticAllowed())
1521 // Add static object to active static list of the block
1522 v3f objectpos = object->getBasePosition();
1523 std::string staticdata = object->getStaticData();
1524 StaticObject s_obj(object->getType(), objectpos, staticdata);
1525 // Add to the block where the object is located in
1526 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1527 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1530 block->m_static_objects.m_active[object->getId()] = s_obj;
1531 object->m_static_exists = true;
1532 object->m_static_block = blockpos;
1535 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1536 "addActiveObjectRaw");
1539 v3s16 p = floatToInt(objectpos, BS);
1540 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1541 <<"could not find block for storing id="<<object->getId()
1542 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1546 return object->getId();
1550 Remove objects that satisfy (m_removed && m_known_by_count==0)
1552 void ServerEnvironment::removeRemovedObjects()
1554 std::list<u16> objects_to_remove;
1555 for(std::map<u16, ServerActiveObject*>::iterator
1556 i = m_active_objects.begin();
1557 i != m_active_objects.end(); ++i)
1560 ServerActiveObject* obj = i->second;
1561 // This shouldn't happen but check it
1564 infostream<<"NULL object found in ServerEnvironment"
1565 <<" while finding removed objects. id="<<id<<std::endl;
1566 // Id to be removed from m_active_objects
1567 objects_to_remove.push_back(id);
1572 We will delete objects that are marked as removed or thatare
1573 waiting for deletion after deactivation
1575 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1579 Delete static data from block if is marked as removed
1581 if(obj->m_static_exists && obj->m_removed)
1583 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1585 block->m_static_objects.remove(id);
1586 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1587 "removeRemovedObjects");
1588 obj->m_static_exists = false;
1590 infostream << "failed to emerge block from which "
1591 "an object to be removed was loaded from. id="<<id<<std::endl;
1595 // If m_known_by_count > 0, don't actually remove.
1596 if(obj->m_known_by_count > 0)
1599 // Tell the object about removal
1600 obj->removingFromEnvironment();
1601 // Deregister in scripting api
1602 scriptapi_rm_object_reference(m_lua, obj);
1605 if(obj->environmentDeletes())
1607 // Id to be removed from m_active_objects
1608 objects_to_remove.push_back(id);
1610 // Remove references from m_active_objects
1611 for(std::list<u16>::iterator i = objects_to_remove.begin();
1612 i != objects_to_remove.end(); ++i)
1614 m_active_objects.erase(*i);
1618 static void print_hexdump(std::ostream &o, const std::string &data)
1620 const int linelength = 16;
1621 for(int l=0; ; l++){
1622 int i0 = linelength * l;
1623 bool at_end = false;
1624 int thislinelength = linelength;
1625 if(i0 + thislinelength > (int)data.size()){
1626 thislinelength = data.size() - i0;
1629 for(int di=0; di<linelength; di++){
1632 if(di<thislinelength)
1633 snprintf(buf, 4, "%.2x ", data[i]);
1635 snprintf(buf, 4, " ");
1639 for(int di=0; di<thislinelength; di++){
1653 Convert stored objects from blocks near the players to active.
1655 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1659 // Ignore if no stored objects (to not set changed flag)
1660 if(block->m_static_objects.m_stored.size() == 0)
1662 verbosestream<<"ServerEnvironment::activateObjects(): "
1663 <<"activating objects of block "<<PP(block->getPos())
1664 <<" ("<<block->m_static_objects.m_stored.size()
1665 <<" objects)"<<std::endl;
1666 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1668 errorstream<<"suspiciously large amount of objects detected: "
1669 <<block->m_static_objects.m_stored.size()<<" in "
1670 <<PP(block->getPos())
1671 <<"; removing all of them."<<std::endl;
1672 // Clear stored list
1673 block->m_static_objects.m_stored.clear();
1674 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1675 "stored list cleared in activateObjects due to "
1676 "large amount of objects");
1679 // A list for objects that couldn't be converted to active for some
1680 // reason. They will be stored back.
1681 std::list<StaticObject> new_stored;
1682 // Loop through stored static objects
1683 for(std::list<StaticObject>::iterator
1684 i = block->m_static_objects.m_stored.begin();
1685 i != block->m_static_objects.m_stored.end(); ++i)
1687 /*infostream<<"Server: Creating an active object from "
1688 <<"static data"<<std::endl;*/
1689 StaticObject &s_obj = *i;
1690 // Create an active object from the data
1691 ServerActiveObject *obj = ServerActiveObject::create
1692 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1693 // If couldn't create object, store static data back.
1696 errorstream<<"ServerEnvironment::activateObjects(): "
1697 <<"failed to create active object from static object "
1698 <<"in block "<<PP(s_obj.pos/BS)
1699 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1700 print_hexdump(verbosestream, s_obj.data);
1702 new_stored.push_back(s_obj);
1705 verbosestream<<"ServerEnvironment::activateObjects(): "
1706 <<"activated static object pos="<<PP(s_obj.pos/BS)
1707 <<" type="<<(int)s_obj.type<<std::endl;
1708 // This will also add the object to the active static list
1709 addActiveObjectRaw(obj, false, dtime_s);
1711 // Clear stored list
1712 block->m_static_objects.m_stored.clear();
1713 // Add leftover failed stuff to stored list
1714 for(std::list<StaticObject>::iterator
1715 i = new_stored.begin();
1716 i != new_stored.end(); ++i)
1718 StaticObject &s_obj = *i;
1719 block->m_static_objects.m_stored.push_back(s_obj);
1722 Note: Block hasn't really been modified here.
1723 The objects have just been activated and moved from the stored
1724 static list to the active static list.
1725 As such, the block is essentially the same.
1726 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1727 Otherwise there would be a huge amount of unnecessary I/O.
1732 Convert objects that are not standing inside active blocks to static.
1734 If m_known_by_count != 0, active object is not deleted, but static
1735 data is still updated.
1737 If force_delete is set, active object is deleted nevertheless. It
1738 shall only be set so in the destructor of the environment.
1740 If block wasn't generated (not in memory or on disk),
1742 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1744 std::list<u16> objects_to_remove;
1745 for(std::map<u16, ServerActiveObject*>::iterator
1746 i = m_active_objects.begin();
1747 i != m_active_objects.end(); ++i)
1749 ServerActiveObject* obj = i->second;
1752 // Do not deactivate if static data creation not allowed
1753 if(!force_delete && !obj->isStaticAllowed())
1756 // If pending deactivation, let removeRemovedObjects() do it
1757 if(!force_delete && obj->m_pending_deactivation)
1761 v3f objectpos = obj->getBasePosition();
1763 // The block in which the object resides in
1764 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1766 // If block is active, don't remove
1767 if(!force_delete && m_active_blocks.contains(blockpos_o))
1770 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1771 <<"deactivating object id="<<id<<" on inactive block "
1772 <<PP(blockpos_o)<<std::endl;
1774 // If known by some client, don't immediately delete.
1775 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1778 Update the static data
1781 if(obj->isStaticAllowed())
1783 // Create new static object
1784 std::string staticdata_new = obj->getStaticData();
1785 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1787 bool stays_in_same_block = false;
1788 bool data_changed = true;
1790 if(obj->m_static_exists){
1791 if(obj->m_static_block == blockpos_o)
1792 stays_in_same_block = true;
1794 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1796 std::map<u16, StaticObject>::iterator n =
1797 block->m_static_objects.m_active.find(id);
1798 if(n != block->m_static_objects.m_active.end()){
1799 StaticObject static_old = n->second;
1801 float save_movem = obj->getMinimumSavedMovement();
1803 if(static_old.data == staticdata_new &&
1804 (static_old.pos - objectpos).getLength() < save_movem)
1805 data_changed = false;
1807 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1808 <<"id="<<id<<" m_static_exists=true but "
1809 <<"static data doesn't actually exist in "
1810 <<PP(obj->m_static_block)<<std::endl;
1814 bool shall_be_written = (!stays_in_same_block || data_changed);
1816 // Delete old static object
1817 if(obj->m_static_exists)
1819 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1822 block->m_static_objects.remove(id);
1823 obj->m_static_exists = false;
1824 // Only mark block as modified if data changed considerably
1825 if(shall_be_written)
1826 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1827 "deactivateFarObjects: Static data "
1828 "changed considerably");
1832 // Add to the block where the object is located in
1833 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1834 // Get or generate the block
1835 MapBlock *block = NULL;
1837 block = m_map->emergeBlock(blockpos);
1838 } catch(InvalidPositionException &e){
1839 // Handled via NULL pointer
1844 if(block->m_static_objects.m_stored.size() >= 49){
1845 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1846 <<" statically but block "<<PP(blockpos)
1847 <<" already contains "
1848 <<block->m_static_objects.m_stored.size()
1849 <<" (over 49) objects."
1850 <<" Forcing delete."<<std::endl;
1851 force_delete = true;
1853 u16 new_id = pending_delete ? id : 0;
1854 // If static counterpart already exists, remove it first.
1855 // This shouldn't happen, but happens rarely for some
1856 // unknown reason. Unsuccessful attempts have been made to
1857 // find said reason.
1858 if(new_id && block->m_static_objects.m_active.find(new_id) != block->m_static_objects.m_active.end()){
1859 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1861 block->m_static_objects.remove(new_id);
1863 block->m_static_objects.insert(new_id, s_obj);
1865 // Only mark block as modified if data changed considerably
1866 if(shall_be_written)
1867 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1868 "deactivateFarObjects: Static data "
1869 "changed considerably");
1871 obj->m_static_exists = true;
1872 obj->m_static_block = block->getPos();
1877 v3s16 p = floatToInt(objectpos, BS);
1878 errorstream<<"ServerEnv: Could not find or generate "
1879 <<"a block for storing id="<<obj->getId()
1880 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1887 If known by some client, set pending deactivation.
1888 Otherwise delete it immediately.
1891 if(pending_delete && !force_delete)
1893 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1894 <<"object id="<<id<<" is known by clients"
1895 <<"; not deleting yet"<<std::endl;
1897 obj->m_pending_deactivation = true;
1901 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1902 <<"object id="<<id<<" is not known by clients"
1903 <<"; deleting"<<std::endl;
1905 // Tell the object about removal
1906 obj->removingFromEnvironment();
1907 // Deregister in scripting api
1908 scriptapi_rm_object_reference(m_lua, obj);
1910 // Delete active object
1911 if(obj->environmentDeletes())
1913 // Id to be removed from m_active_objects
1914 objects_to_remove.push_back(id);
1917 // Remove references from m_active_objects
1918 for(std::list<u16>::iterator i = objects_to_remove.begin();
1919 i != objects_to_remove.end(); ++i)
1921 m_active_objects.erase(*i);
1928 #include "clientsimpleobject.h"
1934 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1935 ITextureSource *texturesource, IGameDef *gamedef,
1936 IrrlichtDevice *irr):
1939 m_texturesource(texturesource),
1945 ClientEnvironment::~ClientEnvironment()
1947 // delete active objects
1948 for(std::map<u16, ClientActiveObject*>::iterator
1949 i = m_active_objects.begin();
1950 i != m_active_objects.end(); ++i)
1955 for(std::list<ClientSimpleObject*>::iterator
1956 i = m_simple_objects.begin(); i != m_simple_objects.end(); ++i)
1965 Map & ClientEnvironment::getMap()
1970 ClientMap & ClientEnvironment::getClientMap()
1975 void ClientEnvironment::addPlayer(Player *player)
1977 DSTACK(__FUNCTION_NAME);
1979 It is a failure if player is local and there already is a local
1982 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1984 Environment::addPlayer(player);
1987 LocalPlayer * ClientEnvironment::getLocalPlayer()
1989 for(std::list<Player*>::iterator i = m_players.begin();
1990 i != m_players.end(); ++i)
1992 Player *player = *i;
1993 if(player->isLocal())
1994 return (LocalPlayer*)player;
1999 void ClientEnvironment::step(float dtime)
2001 DSTACK(__FUNCTION_NAME);
2003 /* Step time of day */
2004 stepTimeOfDay(dtime);
2006 // Get some settings
2007 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
2008 bool free_move = fly_allowed && g_settings->getBool("free_move");
2011 LocalPlayer *lplayer = getLocalPlayer();
2013 // collision info queue
2014 std::list<CollisionInfo> player_collisions;
2017 Get the speed the player is going
2019 bool is_climbing = lplayer->is_climbing;
2021 f32 player_speed = lplayer->getSpeed().getLength();
2024 Maximum position increment
2026 //f32 position_max_increment = 0.05*BS;
2027 f32 position_max_increment = 0.1*BS;
2029 // Maximum time increment (for collision detection etc)
2030 // time = distance / speed
2031 f32 dtime_max_increment = 1;
2032 if(player_speed > 0.001)
2033 dtime_max_increment = position_max_increment / player_speed;
2035 // Maximum time increment is 10ms or lower
2036 if(dtime_max_increment > 0.01)
2037 dtime_max_increment = 0.01;
2039 // Don't allow overly huge dtime
2043 f32 dtime_downcount = dtime;
2046 Stuff that has a maximum time increment
2055 if(dtime_downcount > dtime_max_increment)
2057 dtime_part = dtime_max_increment;
2058 dtime_downcount -= dtime_part;
2062 dtime_part = dtime_downcount;
2064 Setting this to 0 (no -=dtime_part) disables an infinite loop
2065 when dtime_part is so small that dtime_downcount -= dtime_part
2068 dtime_downcount = 0;
2076 v3f lplayerpos = lplayer->getPosition();
2079 if(free_move == false && is_climbing == false)
2082 v3f speed = lplayer->getSpeed();
2083 if(lplayer->in_liquid == false)
2084 speed.Y -= lplayer->movement_gravity * lplayer->physics_override_gravity * dtime_part * 2;
2086 // Liquid floating / sinking
2087 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2088 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2090 // Liquid resistance
2091 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2093 // How much the node's viscosity blocks movement, ranges between 0 and 1
2094 // Should match the scale at which viscosity increase affects other liquid attributes
2095 const f32 viscosity_factor = 0.3;
2097 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2098 f32 dl = d_wanted.getLength();
2099 if(dl > lplayer->movement_liquid_fluidity_smooth)
2100 dl = lplayer->movement_liquid_fluidity_smooth;
2101 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2103 v3f d = d_wanted.normalize() * dl;
2107 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2108 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += 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.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += 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;
2112 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2116 lplayer->setSpeed(speed);
2121 This also does collision detection.
2123 lplayer->move(dtime_part, this, position_max_increment,
2124 &player_collisions);
2127 while(dtime_downcount > 0.001);
2129 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2131 for(std::list<CollisionInfo>::iterator
2132 i = player_collisions.begin();
2133 i != player_collisions.end(); ++i)
2135 CollisionInfo &info = *i;
2136 v3f speed_diff = info.new_speed - info.old_speed;;
2137 // Handle only fall damage
2138 // (because otherwise walking against something in fast_move kills you)
2139 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2141 // Get rid of other components
2144 f32 pre_factor = 1; // 1 hp per node/s
2145 f32 tolerance = BS*14; // 5 without damage
2146 f32 post_factor = 1; // 1 hp per node/s
2147 if(info.type == COLLISION_NODE)
2149 const ContentFeatures &f = m_gamedef->ndef()->
2150 get(m_map->getNodeNoEx(info.node_p));
2151 // Determine fall damage multiplier
2152 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2153 pre_factor = 1.0 + (float)addp/100.0;
2155 float speed = pre_factor * speed_diff.getLength();
2156 if(speed > tolerance)
2158 f32 damage_f = (speed - tolerance)/BS * post_factor;
2159 u16 damage = (u16)(damage_f+0.5);
2161 damageLocalPlayer(damage, true);
2166 A quick draft of lava damage
2168 if(m_lava_hurt_interval.step(dtime, 1.0))
2170 v3f pf = lplayer->getPosition();
2172 // Feet, middle and head
2173 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2174 MapNode n1 = m_map->getNodeNoEx(p1);
2175 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2176 MapNode n2 = m_map->getNodeNoEx(p2);
2177 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2178 MapNode n3 = m_map->getNodeNoEx(p2);
2180 u32 damage_per_second = 0;
2181 damage_per_second = MYMAX(damage_per_second,
2182 m_gamedef->ndef()->get(n1).damage_per_second);
2183 damage_per_second = MYMAX(damage_per_second,
2184 m_gamedef->ndef()->get(n2).damage_per_second);
2185 damage_per_second = MYMAX(damage_per_second,
2186 m_gamedef->ndef()->get(n3).damage_per_second);
2188 if(damage_per_second != 0)
2190 damageLocalPlayer(damage_per_second, true);
2195 Stuff that can be done in an arbitarily large dtime
2197 for(std::list<Player*>::iterator i = m_players.begin();
2198 i != m_players.end(); ++i)
2200 Player *player = *i;
2201 v3f playerpos = player->getPosition();
2204 Handle non-local players
2206 if(player->isLocal() == false)
2209 player->move(dtime, *m_map, 100*BS);
2213 // Update lighting on all players on client
2217 v3s16 p = player->getLightPosition();
2218 MapNode n = m_map->getNode(p);
2219 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2221 catch(InvalidPositionException &e){
2222 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2224 player->light = light;
2228 Step active objects and update lighting of them
2231 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2232 for(std::map<u16, ClientActiveObject*>::iterator
2233 i = m_active_objects.begin();
2234 i != m_active_objects.end(); ++i)
2236 ClientActiveObject* obj = i->second;
2238 obj->step(dtime, this);
2246 v3s16 p = obj->getLightPosition();
2247 MapNode n = m_map->getNode(p);
2248 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2250 catch(InvalidPositionException &e){
2251 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2253 obj->updateLight(light);
2258 Step and handle simple objects
2260 for(std::list<ClientSimpleObject*>::iterator
2261 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2263 ClientSimpleObject *simple = *i;
2264 std::list<ClientSimpleObject*>::iterator cur = i;
2266 simple->step(dtime);
2267 if(simple->m_to_be_removed){
2269 m_simple_objects.erase(cur);
2274 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2276 m_simple_objects.push_back(simple);
2279 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2281 std::map<u16, ClientActiveObject*>::iterator n;
2282 n = m_active_objects.find(id);
2283 if(n == m_active_objects.end())
2288 bool isFreeClientActiveObjectId(u16 id,
2289 std::map<u16, ClientActiveObject*> &objects)
2294 return objects.find(id) == objects.end();
2297 u16 getFreeClientActiveObjectId(
2298 std::map<u16, ClientActiveObject*> &objects)
2300 //try to reuse id's as late as possible
2301 static u16 last_used_id = 0;
2302 u16 startid = last_used_id;
2306 if(isFreeClientActiveObjectId(last_used_id, objects))
2307 return last_used_id;
2309 if(last_used_id == startid)
2314 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2317 if(object->getId() == 0)
2319 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2322 infostream<<"ClientEnvironment::addActiveObject(): "
2323 <<"no free ids available"<<std::endl;
2327 object->setId(new_id);
2329 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2331 infostream<<"ClientEnvironment::addActiveObject(): "
2332 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2336 infostream<<"ClientEnvironment::addActiveObject(): "
2337 <<"added (id="<<object->getId()<<")"<<std::endl;
2338 m_active_objects[object->getId()] = object;
2339 object->addToScene(m_smgr, m_texturesource, m_irr);
2340 { // Update lighting immediately
2344 v3s16 p = object->getLightPosition();
2345 MapNode n = m_map->getNode(p);
2346 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2348 catch(InvalidPositionException &e){
2349 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2351 object->updateLight(light);
2353 return object->getId();
2356 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2357 const std::string &init_data)
2359 ClientActiveObject* obj =
2360 ClientActiveObject::create(type, m_gamedef, this);
2363 infostream<<"ClientEnvironment::addActiveObject(): "
2364 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2373 obj->initialize(init_data);
2375 catch(SerializationError &e)
2377 errorstream<<"ClientEnvironment::addActiveObject():"
2378 <<" id="<<id<<" type="<<type
2379 <<": SerializationError in initialize(): "
2381 <<": init_data="<<serializeJsonString(init_data)
2385 addActiveObject(obj);
2388 void ClientEnvironment::removeActiveObject(u16 id)
2390 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2391 <<"id="<<id<<std::endl;
2392 ClientActiveObject* obj = getActiveObject(id);
2395 infostream<<"ClientEnvironment::removeActiveObject(): "
2396 <<"id="<<id<<" not found"<<std::endl;
2399 obj->removeFromScene(true);
2401 m_active_objects.erase(id);
2404 void ClientEnvironment::processActiveObjectMessage(u16 id,
2405 const std::string &data)
2407 ClientActiveObject* obj = getActiveObject(id);
2410 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2411 <<" got message for id="<<id<<", which doesn't exist."
2417 obj->processMessage(data);
2419 catch(SerializationError &e)
2421 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2422 <<" id="<<id<<" type="<<obj->getType()
2423 <<" SerializationError in processMessage(),"
2424 <<" message="<<serializeJsonString(data)
2430 Callbacks for activeobjects
2433 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2435 LocalPlayer *lplayer = getLocalPlayer();
2439 if(lplayer->hp > damage)
2440 lplayer->hp -= damage;
2445 ClientEnvEvent event;
2446 event.type = CEE_PLAYER_DAMAGE;
2447 event.player_damage.amount = damage;
2448 event.player_damage.send_to_server = handle_hp;
2449 m_client_event_queue.push_back(event);
2453 Client likes to call these
2456 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2457 std::vector<DistanceSortedActiveObject> &dest)
2459 for(std::map<u16, ClientActiveObject*>::iterator
2460 i = m_active_objects.begin();
2461 i != m_active_objects.end(); ++i)
2463 ClientActiveObject* obj = i->second;
2465 f32 d = (obj->getPosition() - origin).getLength();
2470 DistanceSortedActiveObject dso(obj, d);
2472 dest.push_back(dso);
2476 ClientEnvEvent ClientEnvironment::getClientEvent()
2478 if(m_client_event_queue.empty())
2480 ClientEnvEvent event;
2481 event.type = CEE_NONE;
2484 return m_client_event_queue.pop_front();
2487 #endif // #ifndef SERVER