3 Copyright (C) 2010-2011 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(core::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(core::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(core::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(core::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 core::list<Player*> connected_players = getPlayers(true);
131 u32 chosen_one = myrand() % connected_players.size();
133 for(core::list<Player*>::Iterator
134 i = connected_players.begin();
135 i != connected_players.end(); i++)
147 Player * Environment::getNearestConnectedPlayer(v3f pos)
149 core::list<Player*> connected_players = getPlayers(true);
151 Player *nearest_player = NULL;
152 for(core::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 core::list<Player*> Environment::getPlayers()
172 core::list<Player*> Environment::getPlayers(bool ignore_disconnected)
174 core::list<Player*> newlist;
175 for(core::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(core::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, core::map<v3s16, bool> &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(core::list<v3s16> &active_positions,
268 core::map<v3s16, bool> &blocks_removed,
269 core::map<v3s16, bool> &blocks_added)
274 core::map<v3s16, bool> newlist;
275 for(core::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(core::map<v3s16, bool>::Iterator i = m_list.getIterator();
286 i.atEnd()==false; i++)
288 v3s16 p = i.getNode()->getKey();
289 // If not on new list, it's been removed
290 if(newlist.find(p) == NULL)
291 blocks_removed.insert(p, true);
295 Find out which blocks on the new list are not on the old list
297 // Go through new list
298 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
299 i.atEnd()==false; i++)
301 v3s16 p = i.getNode()->getKey();
302 // If not on old list, it's been added
303 if(m_list.find(p) == NULL)
304 blocks_added.insert(p, true);
311 for(core::map<v3s16, bool>::Iterator i = newlist.getIterator();
312 i.atEnd()==false; i++)
314 v3s16 p = i.getNode()->getKey();
315 m_list.insert(p, true);
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(core::list<ABMWithState>::Iterator
352 i = m_abms.begin(); i != m_abms.end(); i++){
357 Map & ServerEnvironment::getMap()
362 ServerMap & ServerEnvironment::getServerMap()
368 void ServerEnvironment::serializePlayers(const std::string &savedir)
370 std::string players_path = savedir + "/players";
371 fs::CreateDir(players_path);
373 core::map<Player*, bool> saved_players;
375 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
376 for(u32 i=0; i<player_files.size(); i++)
378 if(player_files[i].dir)
381 // Full path to this file
382 std::string path = players_path + "/" + player_files[i].name;
384 //infostream<<"Checking player file "<<path<<std::endl;
386 // Load player to see what is its name
387 RemotePlayer testplayer(m_gamedef);
389 // Open file and deserialize
390 std::ifstream is(path.c_str(), std::ios_base::binary);
391 if(is.good() == false)
393 infostream<<"Failed to read "<<path<<std::endl;
396 testplayer.deSerialize(is);
399 //infostream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
401 // Search for the player
402 std::string playername = testplayer.getName();
403 Player *player = getPlayer(playername.c_str());
406 infostream<<"Didn't find matching player, ignoring file "<<path<<std::endl;
410 //infostream<<"Found matching player, overwriting."<<std::endl;
412 // OK, found. Save player there.
414 // Open file and serialize
415 std::ofstream os(path.c_str(), std::ios_base::binary);
416 if(os.good() == false)
418 infostream<<"Failed to overwrite "<<path<<std::endl;
421 player->serialize(os);
422 saved_players.insert(player, true);
426 for(core::list<Player*>::Iterator i = m_players.begin();
427 i != m_players.end(); i++)
430 if(saved_players.find(player) != NULL)
432 /*infostream<<"Player "<<player->getName()
433 <<" was already saved."<<std::endl;*/
436 std::string playername = player->getName();
437 // Don't save unnamed player
440 //infostream<<"Not saving unnamed player."<<std::endl;
446 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
447 playername = "player";
448 std::string path = players_path + "/" + playername;
450 for(u32 i=0; i<1000; i++)
452 if(fs::PathExists(path) == false)
457 path = players_path + "/" + playername + itos(i);
461 infostream<<"Didn't find free file for player"<<std::endl;
466 /*infostream<<"Saving player "<<player->getName()<<" to "
468 // Open file and serialize
469 std::ofstream os(path.c_str(), std::ios_base::binary);
470 if(os.good() == false)
472 infostream<<"Failed to overwrite "<<path<<std::endl;
475 player->serialize(os);
476 saved_players.insert(player, true);
480 //infostream<<"Saved "<<saved_players.size()<<" players."<<std::endl;
483 void ServerEnvironment::deSerializePlayers(const std::string &savedir)
485 std::string players_path = savedir + "/players";
487 core::map<Player*, bool> saved_players;
489 std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
490 for(u32 i=0; i<player_files.size(); i++)
492 if(player_files[i].dir)
495 // Full path to this file
496 std::string path = players_path + "/" + player_files[i].name;
498 //infostream<<"Checking player file "<<path<<std::endl;
500 // Load player to see what is its name
501 RemotePlayer testplayer(m_gamedef);
503 // Open file and deserialize
504 std::ifstream is(path.c_str(), std::ios_base::binary);
505 if(is.good() == false)
507 infostream<<"Failed to read "<<path<<std::endl;
510 testplayer.deSerialize(is);
513 if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
515 infostream<<"Not loading player with invalid name: "
516 <<testplayer.getName()<<std::endl;
519 /*infostream<<"Loaded test player with name "<<testplayer.getName()
522 // Search for the player
523 std::string playername = testplayer.getName();
524 Player *player = getPlayer(playername.c_str());
525 bool newplayer = false;
528 //infostream<<"Is a new player"<<std::endl;
529 player = new RemotePlayer(m_gamedef);
535 verbosestream<<"Reading player "<<testplayer.getName()<<" from "
537 // Open file and deserialize
538 std::ifstream is(path.c_str(), std::ios_base::binary);
539 if(is.good() == false)
541 infostream<<"Failed to read "<<path<<std::endl;
544 player->deSerialize(is);
554 void ServerEnvironment::saveMeta(const std::string &savedir)
556 std::string path = savedir + "/env_meta.txt";
558 // Open file and serialize
559 std::ofstream os(path.c_str(), std::ios_base::binary);
560 if(os.good() == false)
562 infostream<<"ServerEnvironment::saveMeta(): Failed to open "
564 throw SerializationError("Couldn't save env meta");
568 args.setU64("game_time", m_game_time);
569 args.setU64("time_of_day", getTimeOfDay());
574 void ServerEnvironment::loadMeta(const std::string &savedir)
576 std::string path = savedir + "/env_meta.txt";
578 // Open file and deserialize
579 std::ifstream is(path.c_str(), std::ios_base::binary);
580 if(is.good() == false)
582 infostream<<"ServerEnvironment::loadMeta(): Failed to open "
584 throw SerializationError("Couldn't load env meta");
592 throw SerializationError
593 ("ServerEnvironment::loadMeta(): EnvArgsEnd not found");
595 std::getline(is, line);
596 std::string trimmedline = trim(line);
597 if(trimmedline == "EnvArgsEnd")
599 args.parseConfigLine(line);
603 m_game_time = args.getU64("game_time");
604 }catch(SettingNotFoundException &e){
605 // Getting this is crucial, otherwise timestamps are useless
606 throw SerializationError("Couldn't load env meta game_time");
610 m_time_of_day = args.getU64("time_of_day");
611 }catch(SettingNotFoundException &e){
612 // This is not as important
613 m_time_of_day = 9000;
619 ActiveBlockModifier *abm;
621 std::set<content_t> required_neighbors;
627 ServerEnvironment *m_env;
628 std::map<content_t, std::list<ActiveABM> > m_aabms;
630 ABMHandler(core::list<ABMWithState> &abms,
631 float dtime_s, ServerEnvironment *env,
637 INodeDefManager *ndef = env->getGameDef()->ndef();
638 for(core::list<ABMWithState>::Iterator
639 i = abms.begin(); i != abms.end(); i++){
640 ActiveBlockModifier *abm = i->abm;
641 float trigger_interval = abm->getTriggerInterval();
642 if(trigger_interval < 0.001)
643 trigger_interval = 0.001;
644 float actual_interval = dtime_s;
647 if(i->timer < trigger_interval)
649 i->timer -= trigger_interval;
650 actual_interval = trigger_interval;
652 float intervals = actual_interval / trigger_interval;
655 float chance = abm->getTriggerChance();
660 aabm.chance = chance / intervals;
664 std::set<std::string> required_neighbors_s
665 = abm->getRequiredNeighbors();
666 for(std::set<std::string>::iterator
667 i = required_neighbors_s.begin();
668 i != required_neighbors_s.end(); i++)
670 ndef->getIds(*i, aabm.required_neighbors);
673 std::set<std::string> contents_s = abm->getTriggerContents();
674 for(std::set<std::string>::iterator
675 i = contents_s.begin(); i != contents_s.end(); i++)
677 std::set<content_t> ids;
678 ndef->getIds(*i, ids);
679 for(std::set<content_t>::const_iterator k = ids.begin();
683 std::map<content_t, std::list<ActiveABM> >::iterator j;
685 if(j == m_aabms.end()){
686 std::list<ActiveABM> aabmlist;
687 m_aabms[c] = aabmlist;
690 j->second.push_back(aabm);
695 void apply(MapBlock *block)
700 ServerMap *map = &m_env->getServerMap();
703 for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
704 for(p0.Y=0; p0.Y<MAP_BLOCKSIZE; p0.Y++)
705 for(p0.Z=0; p0.Z<MAP_BLOCKSIZE; p0.Z++)
707 MapNode n = block->getNodeNoEx(p0);
708 content_t c = n.getContent();
709 v3s16 p = p0 + block->getPosRelative();
711 std::map<content_t, std::list<ActiveABM> >::iterator j;
713 if(j == m_aabms.end())
716 for(std::list<ActiveABM>::iterator
717 i = j->second.begin(); i != j->second.end(); i++)
719 if(myrand() % i->chance != 0)
723 if(!i->required_neighbors.empty())
726 for(p1.X = p.X-1; p1.X <= p.X+1; p1.X++)
727 for(p1.Y = p.Y-1; p1.Y <= p.Y+1; p1.Y++)
728 for(p1.Z = p.Z-1; p1.Z <= p.Z+1; p1.Z++)
732 MapNode n = map->getNodeNoEx(p1);
733 content_t c = n.getContent();
734 std::set<content_t>::const_iterator k;
735 k = i->required_neighbors.find(c);
736 if(k != i->required_neighbors.end()){
740 // No required neighbor found
745 // Find out how many objects the block contains
746 u32 active_object_count = block->m_static_objects.m_active.size();
747 // Find out how many objects this and all the neighbors contain
748 u32 active_object_count_wider = 0;
749 u32 wider_unknown_count = 0;
750 for(s16 x=-1; x<=1; x++)
751 for(s16 y=-1; y<=1; y++)
752 for(s16 z=-1; z<=1; z++)
754 MapBlock *block2 = map->getBlockNoCreateNoEx(
755 block->getPos() + v3s16(x,y,z));
757 wider_unknown_count = 0;
760 active_object_count_wider +=
761 block2->m_static_objects.m_active.size()
762 + block2->m_static_objects.m_stored.size();
765 u32 wider_known_count = 3*3*3 - wider_unknown_count;
766 active_object_count_wider += wider_unknown_count * active_object_count_wider / wider_known_count;
768 // Call all the trigger variations
769 i->abm->trigger(m_env, p, n);
770 i->abm->trigger(m_env, p, n,
771 active_object_count, active_object_count_wider);
777 void ServerEnvironment::activateBlock(MapBlock *block, u32 additional_dtime)
779 // Get time difference
781 u32 stamp = block->getTimestamp();
782 if(m_game_time > stamp && stamp != BLOCK_TIMESTAMP_UNDEFINED)
783 dtime_s = m_game_time - block->getTimestamp();
784 dtime_s += additional_dtime;
786 /*infostream<<"ServerEnvironment::activateBlock(): block timestamp: "
787 <<stamp<<", game time: "<<m_game_time<<std::endl;*/
789 // Set current time as timestamp
790 block->setTimestampNoChangedFlag(m_game_time);
792 /*infostream<<"ServerEnvironment::activateBlock(): block is "
793 <<dtime_s<<" seconds old."<<std::endl;*/
795 // Activate stored objects
796 activateObjects(block, dtime_s);
799 std::map<v3s16, NodeTimer> elapsed_timers =
800 block->m_node_timers.step((float)dtime_s);
801 if(!elapsed_timers.empty()){
803 for(std::map<v3s16, NodeTimer>::iterator
804 i = elapsed_timers.begin();
805 i != elapsed_timers.end(); i++){
806 n = block->getNodeNoEx(i->first);
807 if(scriptapi_node_on_timer(m_lua,i->first,n,i->second.elapsed))
808 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
812 /* Handle ActiveBlockModifiers */
813 ABMHandler abmhandler(m_abms, dtime_s, this, false);
814 abmhandler.apply(block);
817 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
819 m_abms.push_back(ABMWithState(abm));
822 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
824 INodeDefManager *ndef = m_gamedef->ndef();
825 MapNode n_old = m_map->getNodeNoEx(p);
827 if(ndef->get(n_old).has_on_destruct)
828 scriptapi_node_on_destruct(m_lua, p, n_old);
830 bool succeeded = m_map->addNodeWithEvent(p, n);
833 // Call post-destructor
834 if(ndef->get(n_old).has_after_destruct)
835 scriptapi_node_after_destruct(m_lua, p, n_old);
837 if(ndef->get(n).has_on_construct)
838 scriptapi_node_on_construct(m_lua, p, n);
842 bool ServerEnvironment::removeNode(v3s16 p)
844 INodeDefManager *ndef = m_gamedef->ndef();
845 MapNode n_old = m_map->getNodeNoEx(p);
847 if(ndef->get(n_old).has_on_destruct)
848 scriptapi_node_on_destruct(m_lua, p, n_old);
850 // This is slightly optimized compared to addNodeWithEvent(air)
851 bool succeeded = m_map->removeNodeWithEvent(p);
854 // Call post-destructor
855 if(ndef->get(n_old).has_after_destruct)
856 scriptapi_node_after_destruct(m_lua, p, n_old);
857 // Air doesn't require constructor
861 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
863 std::set<u16> objects;
864 for(core::map<u16, ServerActiveObject*>::Iterator
865 i = m_active_objects.getIterator();
866 i.atEnd()==false; i++)
868 ServerActiveObject* obj = i.getNode()->getValue();
869 u16 id = i.getNode()->getKey();
870 v3f objectpos = obj->getBasePosition();
871 if(objectpos.getDistanceFrom(pos) > radius)
878 void ServerEnvironment::clearAllObjects()
880 infostream<<"ServerEnvironment::clearAllObjects(): "
881 <<"Removing all active objects"<<std::endl;
882 core::list<u16> objects_to_remove;
883 for(core::map<u16, ServerActiveObject*>::Iterator
884 i = m_active_objects.getIterator();
885 i.atEnd()==false; i++)
887 ServerActiveObject* obj = i.getNode()->getValue();
888 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
890 u16 id = i.getNode()->getKey();
891 v3f objectpos = obj->getBasePosition();
892 // Delete static object if block is loaded
893 if(obj->m_static_exists){
894 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
896 block->m_static_objects.remove(id);
897 block->raiseModified(MOD_STATE_WRITE_NEEDED,
899 obj->m_static_exists = false;
902 // If known by some client, don't delete immediately
903 if(obj->m_known_by_count > 0){
904 obj->m_pending_deactivation = true;
905 obj->m_removed = true;
909 // Tell the object about removal
910 obj->removingFromEnvironment();
911 // Deregister in scripting api
912 scriptapi_rm_object_reference(m_lua, obj);
914 // Delete active object
915 if(obj->environmentDeletes())
917 // Id to be removed from m_active_objects
918 objects_to_remove.push_back(id);
920 // Remove references from m_active_objects
921 for(core::list<u16>::Iterator i = objects_to_remove.begin();
922 i != objects_to_remove.end(); i++)
924 m_active_objects.remove(*i);
927 core::list<v3s16> loadable_blocks;
928 infostream<<"ServerEnvironment::clearAllObjects(): "
929 <<"Listing all loadable blocks"<<std::endl;
930 m_map->listAllLoadableBlocks(loadable_blocks);
931 infostream<<"ServerEnvironment::clearAllObjects(): "
932 <<"Done listing all loadable blocks: "
933 <<loadable_blocks.size()
934 <<", now clearing"<<std::endl;
935 u32 report_interval = loadable_blocks.size() / 10;
936 u32 num_blocks_checked = 0;
937 u32 num_blocks_cleared = 0;
938 u32 num_objs_cleared = 0;
939 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
940 i != loadable_blocks.end(); i++)
943 MapBlock *block = m_map->emergeBlock(p, false);
945 errorstream<<"ServerEnvironment::clearAllObjects(): "
946 <<"Failed to emerge block "<<PP(p)<<std::endl;
949 u32 num_stored = block->m_static_objects.m_stored.size();
950 u32 num_active = block->m_static_objects.m_active.size();
951 if(num_stored != 0 || num_active != 0){
952 block->m_static_objects.m_stored.clear();
953 block->m_static_objects.m_active.clear();
954 block->raiseModified(MOD_STATE_WRITE_NEEDED,
956 num_objs_cleared += num_stored + num_active;
957 num_blocks_cleared++;
959 num_blocks_checked++;
961 if(num_blocks_checked % report_interval == 0){
962 float percent = 100.0 * (float)num_blocks_checked /
963 loadable_blocks.size();
964 infostream<<"ServerEnvironment::clearAllObjects(): "
965 <<"Cleared "<<num_objs_cleared<<" objects"
966 <<" in "<<num_blocks_cleared<<" blocks ("
967 <<percent<<"%)"<<std::endl;
970 infostream<<"ServerEnvironment::clearAllObjects(): "
971 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
972 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
975 void ServerEnvironment::step(float dtime)
977 DSTACK(__FUNCTION_NAME);
979 //TimeTaker timer("ServerEnv step");
981 /* Step time of day */
982 stepTimeOfDay(dtime);
985 // NOTE: This is kind of funny on a singleplayer game, but doesn't
986 // really matter that much.
987 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
993 m_game_time_fraction_counter += dtime;
994 u32 inc_i = (u32)m_game_time_fraction_counter;
995 m_game_time += inc_i;
996 m_game_time_fraction_counter -= (float)inc_i;
1003 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1004 for(core::list<Player*>::Iterator i = m_players.begin();
1005 i != m_players.end(); i++)
1007 Player *player = *i;
1009 // Ignore disconnected players
1010 if(player->peer_id == 0)
1013 v3f playerpos = player->getPosition();
1016 player->move(dtime, *m_map, 100*BS);
1021 Manage active block list
1023 if(m_active_blocks_management_interval.step(dtime, 2.0))
1025 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1027 Get player block positions
1029 core::list<v3s16> players_blockpos;
1030 for(core::list<Player*>::Iterator
1031 i = m_players.begin();
1032 i != m_players.end(); i++)
1034 Player *player = *i;
1035 // Ignore disconnected players
1036 if(player->peer_id == 0)
1038 v3s16 blockpos = getNodeBlockPos(
1039 floatToInt(player->getPosition(), BS));
1040 players_blockpos.push_back(blockpos);
1044 Update list of active blocks, collecting changes
1046 const s16 active_block_range = g_settings->getS16("active_block_range");
1047 core::map<v3s16, bool> blocks_removed;
1048 core::map<v3s16, bool> blocks_added;
1049 m_active_blocks.update(players_blockpos, active_block_range,
1050 blocks_removed, blocks_added);
1053 Handle removed blocks
1056 // Convert active objects that are no more in active blocks to static
1057 deactivateFarObjects(false);
1059 for(core::map<v3s16, bool>::Iterator
1060 i = blocks_removed.getIterator();
1061 i.atEnd()==false; i++)
1063 v3s16 p = i.getNode()->getKey();
1065 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1066 <<") became inactive"<<std::endl;*/
1068 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1072 // Set current time as timestamp (and let it set ChangedFlag)
1073 block->setTimestamp(m_game_time);
1080 for(core::map<v3s16, bool>::Iterator
1081 i = blocks_added.getIterator();
1082 i.atEnd()==false; i++)
1084 v3s16 p = i.getNode()->getKey();
1086 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1087 <<") became active"<<std::endl;*/
1089 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1091 // Block needs to be fetched first
1092 m_emerger->queueBlockEmerge(p, false);
1093 m_active_blocks.m_list.remove(p);
1097 activateBlock(block);
1102 Mess around in active blocks
1104 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1106 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1110 for(core::map<v3s16, bool>::Iterator
1111 i = m_active_blocks.m_list.getIterator();
1112 i.atEnd()==false; i++)
1114 v3s16 p = i.getNode()->getKey();
1116 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1117 <<") being handled"<<std::endl;*/
1119 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1123 // Reset block usage timer
1124 block->resetUsageTimer();
1126 // Set current time as timestamp
1127 block->setTimestampNoChangedFlag(m_game_time);
1128 // If time has changed much from the one on disk,
1129 // set block to be saved when it is unloaded
1130 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1131 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1132 "Timestamp older than 60s (step)");
1135 std::map<v3s16, NodeTimer> elapsed_timers =
1136 block->m_node_timers.step((float)dtime);
1137 if(!elapsed_timers.empty()){
1139 for(std::map<v3s16, NodeTimer>::iterator
1140 i = elapsed_timers.begin();
1141 i != elapsed_timers.end(); i++){
1142 n = block->getNodeNoEx(i->first);
1143 p = i->first + block->getPosRelative();
1144 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1145 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1151 const float abm_interval = 1.0;
1152 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1154 if(m_active_block_interval_overload_skip > 0){
1155 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1156 m_active_block_interval_overload_skip--;
1159 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1160 TimeTaker timer("modify in active blocks");
1162 // Initialize handling of ActiveBlockModifiers
1163 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1165 for(core::map<v3s16, bool>::Iterator
1166 i = m_active_blocks.m_list.getIterator();
1167 i.atEnd()==false; i++)
1169 v3s16 p = i.getNode()->getKey();
1171 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1172 <<") being handled"<<std::endl;*/
1174 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1178 // Set current time as timestamp
1179 block->setTimestampNoChangedFlag(m_game_time);
1181 /* Handle ActiveBlockModifiers */
1182 abmhandler.apply(block);
1185 u32 time_ms = timer.stop(true);
1186 u32 max_time_ms = 200;
1187 if(time_ms > max_time_ms){
1188 infostream<<"WARNING: active block modifiers took "
1189 <<time_ms<<"ms (longer than "
1190 <<max_time_ms<<"ms)"<<std::endl;
1191 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1196 Step script environment (run global on_step())
1198 scriptapi_environment_step(m_lua, dtime);
1204 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1205 //TimeTaker timer("Step active objects");
1207 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1209 // This helps the objects to send data at the same time
1210 bool send_recommended = false;
1211 m_send_recommended_timer += dtime;
1212 if(m_send_recommended_timer > getSendRecommendedInterval())
1214 m_send_recommended_timer -= getSendRecommendedInterval();
1215 send_recommended = true;
1218 for(core::map<u16, ServerActiveObject*>::Iterator
1219 i = m_active_objects.getIterator();
1220 i.atEnd()==false; i++)
1222 ServerActiveObject* obj = i.getNode()->getValue();
1223 // Remove non-peaceful mobs on peaceful mode
1224 if(g_settings->getBool("only_peaceful_mobs")){
1225 if(!obj->isPeaceful())
1226 obj->m_removed = true;
1228 // Don't step if is to be removed or stored statically
1229 if(obj->m_removed || obj->m_pending_deactivation)
1232 obj->step(dtime, send_recommended);
1233 // Read messages from object
1234 while(obj->m_messages_out.size() > 0)
1236 m_active_object_messages.push_back(
1237 obj->m_messages_out.pop_front());
1243 Manage active objects
1245 if(m_object_management_interval.step(dtime, 0.5))
1247 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1249 Remove objects that satisfy (m_removed && m_known_by_count==0)
1251 removeRemovedObjects();
1255 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1257 core::map<u16, ServerActiveObject*>::Node *n;
1258 n = m_active_objects.find(id);
1261 return n->getValue();
1264 bool isFreeServerActiveObjectId(u16 id,
1265 core::map<u16, ServerActiveObject*> &objects)
1270 for(core::map<u16, ServerActiveObject*>::Iterator
1271 i = objects.getIterator();
1272 i.atEnd()==false; i++)
1274 if(i.getNode()->getKey() == id)
1280 u16 getFreeServerActiveObjectId(
1281 core::map<u16, ServerActiveObject*> &objects)
1286 if(isFreeServerActiveObjectId(new_id, objects))
1296 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1299 u16 id = addActiveObjectRaw(object, true, 0);
1303 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1307 v3f objectpos = obj->getBasePosition();
1309 // The block in which the object resides in
1310 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1313 Update the static data
1316 // Create new static object
1317 std::string staticdata = obj->getStaticData();
1318 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1319 // Add to the block where the object is located in
1320 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1321 // Get or generate the block
1322 MapBlock *block = m_map->emergeBlock(blockpos);
1324 bool succeeded = false;
1328 block->m_static_objects.insert(0, s_obj);
1329 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1330 "addActiveObjectAsStatic");
1334 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1335 <<"Could not find or generate "
1336 <<"a block for storing static object"<<std::endl;
1340 if(obj->environmentDeletes())
1347 Finds out what new objects have been added to
1348 inside a radius around a position
1350 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1351 core::map<u16, bool> ¤t_objects,
1352 core::map<u16, bool> &added_objects)
1354 v3f pos_f = intToFloat(pos, BS);
1355 f32 radius_f = radius * BS;
1357 Go through the object list,
1358 - discard m_removed objects,
1359 - discard objects that are too far away,
1360 - discard objects that are found in current_objects.
1361 - add remaining objects to added_objects
1363 for(core::map<u16, ServerActiveObject*>::Iterator
1364 i = m_active_objects.getIterator();
1365 i.atEnd()==false; i++)
1367 u16 id = i.getNode()->getKey();
1369 ServerActiveObject *object = i.getNode()->getValue();
1372 // Discard if removed
1373 if(object->m_removed)
1375 if(object->unlimitedTransferDistance() == false){
1376 // Discard if too far
1377 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1378 if(distance_f > radius_f)
1381 // Discard if already on current_objects
1382 core::map<u16, bool>::Node *n;
1383 n = current_objects.find(id);
1386 // Add to added_objects
1387 added_objects.insert(id, false);
1392 Finds out what objects have been removed from
1393 inside a radius around a position
1395 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1396 core::map<u16, bool> ¤t_objects,
1397 core::map<u16, bool> &removed_objects)
1399 v3f pos_f = intToFloat(pos, BS);
1400 f32 radius_f = radius * BS;
1402 Go through current_objects; object is removed if:
1403 - object is not found in m_active_objects (this is actually an
1404 error condition; objects should be set m_removed=true and removed
1405 only after all clients have been informed about removal), or
1406 - object has m_removed=true, or
1407 - object is too far away
1409 for(core::map<u16, bool>::Iterator
1410 i = current_objects.getIterator();
1411 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 ServerActiveObject *object = getActiveObject(id);
1417 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1418 <<" object in current_objects is NULL"<<std::endl;
1419 removed_objects.insert(id, false);
1423 if(object->m_removed)
1425 removed_objects.insert(id, false);
1429 // If transfer distance is unlimited, don't remove
1430 if(object->unlimitedTransferDistance())
1433 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1435 if(distance_f >= radius_f)
1437 removed_objects.insert(id, false);
1445 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1447 if(m_active_object_messages.size() == 0)
1448 return ActiveObjectMessage(0);
1450 return m_active_object_messages.pop_front();
1454 ************ Private methods *************
1457 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1458 bool set_changed, u32 dtime_s)
1461 if(object->getId() == 0){
1462 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1465 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1466 <<"no free ids available"<<std::endl;
1467 if(object->environmentDeletes())
1471 object->setId(new_id);
1474 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1475 <<"supplied with id "<<object->getId()<<std::endl;
1477 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1479 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1480 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1481 if(object->environmentDeletes())
1485 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1486 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1488 m_active_objects.insert(object->getId(), object);
1490 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1491 <<"Added id="<<object->getId()<<"; there are now "
1492 <<m_active_objects.size()<<" active objects."
1495 // Register reference in scripting api (must be done before post-init)
1496 scriptapi_add_object_reference(m_lua, object);
1497 // Post-initialize object
1498 object->addedToEnvironment(dtime_s);
1500 // Add static data to block
1501 if(object->isStaticAllowed())
1503 // Add static object to active static list of the block
1504 v3f objectpos = object->getBasePosition();
1505 std::string staticdata = object->getStaticData();
1506 StaticObject s_obj(object->getType(), objectpos, staticdata);
1507 // Add to the block where the object is located in
1508 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1509 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1512 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1513 object->m_static_exists = true;
1514 object->m_static_block = blockpos;
1517 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1518 "addActiveObjectRaw");
1521 v3s16 p = floatToInt(objectpos, BS);
1522 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1523 <<"could not find block for storing id="<<object->getId()
1524 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1528 return object->getId();
1532 Remove objects that satisfy (m_removed && m_known_by_count==0)
1534 void ServerEnvironment::removeRemovedObjects()
1536 core::list<u16> objects_to_remove;
1537 for(core::map<u16, ServerActiveObject*>::Iterator
1538 i = m_active_objects.getIterator();
1539 i.atEnd()==false; i++)
1541 u16 id = i.getNode()->getKey();
1542 ServerActiveObject* obj = i.getNode()->getValue();
1543 // This shouldn't happen but check it
1546 infostream<<"NULL object found in ServerEnvironment"
1547 <<" while finding removed objects. id="<<id<<std::endl;
1548 // Id to be removed from m_active_objects
1549 objects_to_remove.push_back(id);
1554 We will delete objects that are marked as removed or thatare
1555 waiting for deletion after deactivation
1557 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1561 Delete static data from block if is marked as removed
1563 if(obj->m_static_exists && obj->m_removed)
1565 MapBlock *block = m_map->emergeBlock(obj->m_static_block);
1568 block->m_static_objects.remove(id);
1569 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1570 "removeRemovedObjects");
1571 obj->m_static_exists = false;
1575 // If m_known_by_count > 0, don't actually remove.
1576 if(obj->m_known_by_count > 0)
1579 // Tell the object about removal
1580 obj->removingFromEnvironment();
1581 // Deregister in scripting api
1582 scriptapi_rm_object_reference(m_lua, obj);
1585 if(obj->environmentDeletes())
1587 // Id to be removed from m_active_objects
1588 objects_to_remove.push_back(id);
1590 // Remove references from m_active_objects
1591 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1592 i != objects_to_remove.end(); i++)
1594 m_active_objects.remove(*i);
1598 static void print_hexdump(std::ostream &o, const std::string &data)
1600 const int linelength = 16;
1601 for(int l=0; ; l++){
1602 int i0 = linelength * l;
1603 bool at_end = false;
1604 int thislinelength = linelength;
1605 if(i0 + thislinelength > (int)data.size()){
1606 thislinelength = data.size() - i0;
1609 for(int di=0; di<linelength; di++){
1612 if(di<thislinelength)
1613 snprintf(buf, 4, "%.2x ", data[i]);
1615 snprintf(buf, 4, " ");
1619 for(int di=0; di<thislinelength; di++){
1633 Convert stored objects from blocks near the players to active.
1635 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1639 // Ignore if no stored objects (to not set changed flag)
1640 if(block->m_static_objects.m_stored.size() == 0)
1642 verbosestream<<"ServerEnvironment::activateObjects(): "
1643 <<"activating objects of block "<<PP(block->getPos())
1644 <<" ("<<block->m_static_objects.m_stored.size()
1645 <<" objects)"<<std::endl;
1646 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1648 errorstream<<"suspiciously large amount of objects detected: "
1649 <<block->m_static_objects.m_stored.size()<<" in "
1650 <<PP(block->getPos())
1651 <<"; removing all of them."<<std::endl;
1652 // Clear stored list
1653 block->m_static_objects.m_stored.clear();
1654 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1655 "stored list cleared in activateObjects due to "
1656 "large amount of objects");
1659 // A list for objects that couldn't be converted to active for some
1660 // reason. They will be stored back.
1661 core::list<StaticObject> new_stored;
1662 // Loop through stored static objects
1663 for(core::list<StaticObject>::Iterator
1664 i = block->m_static_objects.m_stored.begin();
1665 i != block->m_static_objects.m_stored.end(); i++)
1667 /*infostream<<"Server: Creating an active object from "
1668 <<"static data"<<std::endl;*/
1669 StaticObject &s_obj = *i;
1670 // Create an active object from the data
1671 ServerActiveObject *obj = ServerActiveObject::create
1672 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1673 // If couldn't create object, store static data back.
1676 errorstream<<"ServerEnvironment::activateObjects(): "
1677 <<"failed to create active object from static object "
1678 <<"in block "<<PP(s_obj.pos/BS)
1679 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1680 print_hexdump(verbosestream, s_obj.data);
1682 new_stored.push_back(s_obj);
1685 verbosestream<<"ServerEnvironment::activateObjects(): "
1686 <<"activated static object pos="<<PP(s_obj.pos/BS)
1687 <<" type="<<(int)s_obj.type<<std::endl;
1688 // This will also add the object to the active static list
1689 addActiveObjectRaw(obj, false, dtime_s);
1691 // Clear stored list
1692 block->m_static_objects.m_stored.clear();
1693 // Add leftover failed stuff to stored list
1694 for(core::list<StaticObject>::Iterator
1695 i = new_stored.begin();
1696 i != new_stored.end(); i++)
1698 StaticObject &s_obj = *i;
1699 block->m_static_objects.m_stored.push_back(s_obj);
1702 Note: Block hasn't really been modified here.
1703 The objects have just been activated and moved from the stored
1704 static list to the active static list.
1705 As such, the block is essentially the same.
1706 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1707 Otherwise there would be a huge amount of unnecessary I/O.
1712 Convert objects that are not standing inside active blocks to static.
1714 If m_known_by_count != 0, active object is not deleted, but static
1715 data is still updated.
1717 If force_delete is set, active object is deleted nevertheless. It
1718 shall only be set so in the destructor of the environment.
1720 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1722 core::list<u16> objects_to_remove;
1723 for(core::map<u16, ServerActiveObject*>::Iterator
1724 i = m_active_objects.getIterator();
1725 i.atEnd()==false; i++)
1727 ServerActiveObject* obj = i.getNode()->getValue();
1730 // Do not deactivate if static data creation not allowed
1731 if(!force_delete && !obj->isStaticAllowed())
1734 // If pending deactivation, let removeRemovedObjects() do it
1735 if(!force_delete && obj->m_pending_deactivation)
1738 u16 id = i.getNode()->getKey();
1739 v3f objectpos = obj->getBasePosition();
1741 // The block in which the object resides in
1742 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1744 // If block is active, don't remove
1745 if(!force_delete && m_active_blocks.contains(blockpos_o))
1748 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1749 <<"deactivating object id="<<id<<" on inactive block "
1750 <<PP(blockpos_o)<<std::endl;
1752 // If known by some client, don't immediately delete.
1753 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1756 Update the static data
1759 if(obj->isStaticAllowed())
1761 // Create new static object
1762 std::string staticdata_new = obj->getStaticData();
1763 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1765 bool stays_in_same_block = false;
1766 bool data_changed = true;
1768 if(obj->m_static_exists){
1769 if(obj->m_static_block == blockpos_o)
1770 stays_in_same_block = true;
1772 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1774 core::map<u16, StaticObject>::Node *n =
1775 block->m_static_objects.m_active.find(id);
1777 StaticObject static_old = n->getValue();
1779 float save_movem = obj->getMinimumSavedMovement();
1781 if(static_old.data == staticdata_new &&
1782 (static_old.pos - objectpos).getLength() < save_movem)
1783 data_changed = false;
1785 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1786 <<"id="<<id<<" m_static_exists=true but "
1787 <<"static data doesn't actually exist in "
1788 <<PP(obj->m_static_block)<<std::endl;
1792 bool shall_be_written = (!stays_in_same_block || data_changed);
1794 // Delete old static object
1795 if(obj->m_static_exists)
1797 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1800 block->m_static_objects.remove(id);
1801 obj->m_static_exists = false;
1802 // Only mark block as modified if data changed considerably
1803 if(shall_be_written)
1804 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1805 "deactivateFarObjects: Static data "
1806 "changed considerably");
1810 // Add to the block where the object is located in
1811 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1812 // Get or generate the block
1813 MapBlock *block = NULL;
1815 block = m_map->emergeBlock(blockpos);
1816 } catch(InvalidPositionException &e){
1817 // Handled via NULL pointer
1822 if(block->m_static_objects.m_stored.size() >= 49){
1823 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1824 <<" statically but block "<<PP(blockpos)
1825 <<" already contains "
1826 <<block->m_static_objects.m_stored.size()
1827 <<" (over 49) objects."
1828 <<" Forcing delete."<<std::endl;
1829 force_delete = true;
1831 u16 new_id = pending_delete ? id : 0;
1832 // If static counterpart already exists, remove it first.
1833 // This shouldn't happen, but happens rarely for some
1834 // unknown reason. Unsuccessful attempts have been made to
1835 // find said reason.
1836 if(new_id && block->m_static_objects.m_active.find(new_id)){
1837 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1839 block->m_static_objects.remove(new_id);
1841 block->m_static_objects.insert(new_id, s_obj);
1843 // Only mark block as modified if data changed considerably
1844 if(shall_be_written)
1845 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1846 "deactivateFarObjects: Static data "
1847 "changed considerably");
1849 obj->m_static_exists = true;
1850 obj->m_static_block = block->getPos();
1855 v3s16 p = floatToInt(objectpos, BS);
1856 errorstream<<"ServerEnv: Could not find or generate "
1857 <<"a block for storing id="<<obj->getId()
1858 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1865 If known by some client, set pending deactivation.
1866 Otherwise delete it immediately.
1869 if(pending_delete && !force_delete)
1871 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1872 <<"object id="<<id<<" is known by clients"
1873 <<"; not deleting yet"<<std::endl;
1875 obj->m_pending_deactivation = true;
1879 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1880 <<"object id="<<id<<" is not known by clients"
1881 <<"; deleting"<<std::endl;
1883 // Tell the object about removal
1884 obj->removingFromEnvironment();
1885 // Deregister in scripting api
1886 scriptapi_rm_object_reference(m_lua, obj);
1888 // Delete active object
1889 if(obj->environmentDeletes())
1891 // Id to be removed from m_active_objects
1892 objects_to_remove.push_back(id);
1895 // Remove references from m_active_objects
1896 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1897 i != objects_to_remove.end(); i++)
1899 m_active_objects.remove(*i);
1906 #include "clientsimpleobject.h"
1912 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1913 ITextureSource *texturesource, IGameDef *gamedef,
1914 IrrlichtDevice *irr):
1917 m_texturesource(texturesource),
1923 ClientEnvironment::~ClientEnvironment()
1925 // delete active objects
1926 for(core::map<u16, ClientActiveObject*>::Iterator
1927 i = m_active_objects.getIterator();
1928 i.atEnd()==false; i++)
1930 delete i.getNode()->getValue();
1933 for(core::list<ClientSimpleObject*>::Iterator
1934 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1943 Map & ClientEnvironment::getMap()
1948 ClientMap & ClientEnvironment::getClientMap()
1953 void ClientEnvironment::addPlayer(Player *player)
1955 DSTACK(__FUNCTION_NAME);
1957 It is a failure if player is local and there already is a local
1960 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1962 Environment::addPlayer(player);
1965 LocalPlayer * ClientEnvironment::getLocalPlayer()
1967 for(core::list<Player*>::Iterator i = m_players.begin();
1968 i != m_players.end(); i++)
1970 Player *player = *i;
1971 if(player->isLocal())
1972 return (LocalPlayer*)player;
1977 void ClientEnvironment::step(float dtime)
1979 DSTACK(__FUNCTION_NAME);
1981 /* Step time of day */
1982 stepTimeOfDay(dtime);
1984 // Get some settings
1985 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1986 bool free_move = fly_allowed && g_settings->getBool("free_move");
1989 LocalPlayer *lplayer = getLocalPlayer();
1991 // collision info queue
1992 core::list<CollisionInfo> player_collisions;
1995 Get the speed the player is going
1997 bool is_climbing = lplayer->is_climbing;
1999 f32 player_speed = lplayer->getSpeed().getLength();
2002 Maximum position increment
2004 //f32 position_max_increment = 0.05*BS;
2005 f32 position_max_increment = 0.1*BS;
2007 // Maximum time increment (for collision detection etc)
2008 // time = distance / speed
2009 f32 dtime_max_increment = 1;
2010 if(player_speed > 0.001)
2011 dtime_max_increment = position_max_increment / player_speed;
2013 // Maximum time increment is 10ms or lower
2014 if(dtime_max_increment > 0.01)
2015 dtime_max_increment = 0.01;
2017 // Don't allow overly huge dtime
2021 f32 dtime_downcount = dtime;
2024 Stuff that has a maximum time increment
2033 if(dtime_downcount > dtime_max_increment)
2035 dtime_part = dtime_max_increment;
2036 dtime_downcount -= dtime_part;
2040 dtime_part = dtime_downcount;
2042 Setting this to 0 (no -=dtime_part) disables an infinite loop
2043 when dtime_part is so small that dtime_downcount -= dtime_part
2046 dtime_downcount = 0;
2054 v3f lplayerpos = lplayer->getPosition();
2057 if(free_move == false && is_climbing == false)
2060 v3f speed = lplayer->getSpeed();
2061 if(lplayer->swimming_up == false)
2062 speed.Y -= 9.81 * BS * dtime_part * 2;
2065 if(lplayer->in_water_stable || lplayer->in_water)
2067 f32 max_down = 2.0*BS;
2068 if(speed.Y < -max_down) speed.Y = -max_down;
2071 if(speed.getLength() > max)
2073 speed = speed / speed.getLength() * max;
2077 lplayer->setSpeed(speed);
2082 This also does collision detection.
2084 lplayer->move(dtime_part, *m_map, position_max_increment,
2085 &player_collisions);
2088 while(dtime_downcount > 0.001);
2090 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2092 for(core::list<CollisionInfo>::Iterator
2093 i = player_collisions.begin();
2094 i != player_collisions.end(); i++)
2096 CollisionInfo &info = *i;
2097 v3f speed_diff = info.new_speed - info.old_speed;;
2098 // Handle only fall damage
2099 // (because otherwise walking against something in fast_move kills you)
2100 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2102 // Get rid of other components
2105 f32 pre_factor = 1; // 1 hp per node/s
2106 f32 tolerance = BS*14; // 5 without damage
2107 f32 post_factor = 1; // 1 hp per node/s
2108 if(info.type == COLLISION_NODE)
2110 const ContentFeatures &f = m_gamedef->ndef()->
2111 get(m_map->getNodeNoEx(info.node_p));
2112 // Determine fall damage multiplier
2113 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2114 pre_factor = 1.0 + (float)addp/100.0;
2116 float speed = pre_factor * speed_diff.getLength();
2117 if(speed > tolerance)
2119 f32 damage_f = (speed - tolerance)/BS * post_factor;
2120 u16 damage = (u16)(damage_f+0.5);
2122 damageLocalPlayer(damage, true);
2127 A quick draft of lava damage
2129 if(m_lava_hurt_interval.step(dtime, 1.0))
2131 v3f pf = lplayer->getPosition();
2133 // Feet, middle and head
2134 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2135 MapNode n1 = m_map->getNodeNoEx(p1);
2136 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2137 MapNode n2 = m_map->getNodeNoEx(p2);
2138 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2139 MapNode n3 = m_map->getNodeNoEx(p2);
2141 u32 damage_per_second = 0;
2142 damage_per_second = MYMAX(damage_per_second,
2143 m_gamedef->ndef()->get(n1).damage_per_second);
2144 damage_per_second = MYMAX(damage_per_second,
2145 m_gamedef->ndef()->get(n2).damage_per_second);
2146 damage_per_second = MYMAX(damage_per_second,
2147 m_gamedef->ndef()->get(n3).damage_per_second);
2149 if(damage_per_second != 0)
2151 damageLocalPlayer(damage_per_second, true);
2156 Stuff that can be done in an arbitarily large dtime
2158 for(core::list<Player*>::Iterator i = m_players.begin();
2159 i != m_players.end(); i++)
2161 Player *player = *i;
2162 v3f playerpos = player->getPosition();
2165 Handle non-local players
2167 if(player->isLocal() == false)
2170 player->move(dtime, *m_map, 100*BS);
2174 // Update lighting on all players on client
2178 v3s16 p = player->getLightPosition();
2179 MapNode n = m_map->getNode(p);
2180 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2182 catch(InvalidPositionException &e){
2183 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2185 player->light = light;
2189 Step active objects and update lighting of them
2192 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2193 for(core::map<u16, ClientActiveObject*>::Iterator
2194 i = m_active_objects.getIterator();
2195 i.atEnd()==false; i++)
2197 ClientActiveObject* obj = i.getNode()->getValue();
2199 obj->step(dtime, this);
2207 v3s16 p = obj->getLightPosition();
2208 MapNode n = m_map->getNode(p);
2209 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2211 catch(InvalidPositionException &e){
2212 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2214 obj->updateLight(light);
2219 Step and handle simple objects
2221 for(core::list<ClientSimpleObject*>::Iterator
2222 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2224 ClientSimpleObject *simple = *i;
2225 core::list<ClientSimpleObject*>::Iterator cur = i;
2227 simple->step(dtime);
2228 if(simple->m_to_be_removed){
2230 m_simple_objects.erase(cur);
2235 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2237 m_simple_objects.push_back(simple);
2240 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2242 core::map<u16, ClientActiveObject*>::Node *n;
2243 n = m_active_objects.find(id);
2246 return n->getValue();
2249 bool isFreeClientActiveObjectId(u16 id,
2250 core::map<u16, ClientActiveObject*> &objects)
2255 for(core::map<u16, ClientActiveObject*>::Iterator
2256 i = objects.getIterator();
2257 i.atEnd()==false; i++)
2259 if(i.getNode()->getKey() == id)
2265 u16 getFreeClientActiveObjectId(
2266 core::map<u16, ClientActiveObject*> &objects)
2271 if(isFreeClientActiveObjectId(new_id, objects))
2281 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2284 if(object->getId() == 0)
2286 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2289 infostream<<"ClientEnvironment::addActiveObject(): "
2290 <<"no free ids available"<<std::endl;
2294 object->setId(new_id);
2296 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2298 infostream<<"ClientEnvironment::addActiveObject(): "
2299 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2303 infostream<<"ClientEnvironment::addActiveObject(): "
2304 <<"added (id="<<object->getId()<<")"<<std::endl;
2305 m_active_objects.insert(object->getId(), object);
2306 object->addToScene(m_smgr, m_texturesource, m_irr);
2307 { // Update lighting immediately
2311 v3s16 p = object->getLightPosition();
2312 MapNode n = m_map->getNode(p);
2313 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2315 catch(InvalidPositionException &e){
2316 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2318 object->updateLight(light);
2320 return object->getId();
2323 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2324 const std::string &init_data)
2326 ClientActiveObject* obj =
2327 ClientActiveObject::create(type, m_gamedef, this);
2330 infostream<<"ClientEnvironment::addActiveObject(): "
2331 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2340 obj->initialize(init_data);
2342 catch(SerializationError &e)
2344 errorstream<<"ClientEnvironment::addActiveObject():"
2345 <<" id="<<id<<" type="<<type
2346 <<": SerializationError in initialize(): "
2348 <<": init_data="<<serializeJsonString(init_data)
2352 addActiveObject(obj);
2355 void ClientEnvironment::removeActiveObject(u16 id)
2357 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2358 <<"id="<<id<<std::endl;
2359 ClientActiveObject* obj = getActiveObject(id);
2362 infostream<<"ClientEnvironment::removeActiveObject(): "
2363 <<"id="<<id<<" not found"<<std::endl;
2366 obj->removeFromScene(true);
2368 m_active_objects.remove(id);
2371 void ClientEnvironment::processActiveObjectMessage(u16 id,
2372 const std::string &data)
2374 ClientActiveObject* obj = getActiveObject(id);
2377 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2378 <<" got message for id="<<id<<", which doesn't exist."
2384 obj->processMessage(data);
2386 catch(SerializationError &e)
2388 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2389 <<" id="<<id<<" type="<<obj->getType()
2390 <<" SerializationError in processMessage(),"
2391 <<" message="<<serializeJsonString(data)
2397 Callbacks for activeobjects
2400 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2402 LocalPlayer *lplayer = getLocalPlayer();
2406 if(lplayer->hp > damage)
2407 lplayer->hp -= damage;
2412 ClientEnvEvent event;
2413 event.type = CEE_PLAYER_DAMAGE;
2414 event.player_damage.amount = damage;
2415 event.player_damage.send_to_server = handle_hp;
2416 m_client_event_queue.push_back(event);
2420 Client likes to call these
2423 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2424 core::array<DistanceSortedActiveObject> &dest)
2426 for(core::map<u16, ClientActiveObject*>::Iterator
2427 i = m_active_objects.getIterator();
2428 i.atEnd()==false; i++)
2430 ClientActiveObject* obj = i.getNode()->getValue();
2432 f32 d = (obj->getPosition() - origin).getLength();
2437 DistanceSortedActiveObject dso(obj, d);
2439 dest.push_back(dso);
2443 ClientEnvEvent ClientEnvironment::getClientEvent()
2445 if(m_client_event_queue.size() == 0)
2447 ClientEnvEvent event;
2448 event.type = CEE_NONE;
2451 return m_client_event_queue.pop_front();
2454 #endif // #ifndef SERVER