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(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 v3s16 p = i->first + block->getPosRelative();
808 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
809 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
813 /* Handle ActiveBlockModifiers */
814 ABMHandler abmhandler(m_abms, dtime_s, this, false);
815 abmhandler.apply(block);
818 void ServerEnvironment::addActiveBlockModifier(ActiveBlockModifier *abm)
820 m_abms.push_back(ABMWithState(abm));
823 bool ServerEnvironment::setNode(v3s16 p, const MapNode &n)
825 INodeDefManager *ndef = m_gamedef->ndef();
826 MapNode n_old = m_map->getNodeNoEx(p);
828 if(ndef->get(n_old).has_on_destruct)
829 scriptapi_node_on_destruct(m_lua, p, n_old);
831 bool succeeded = m_map->addNodeWithEvent(p, n);
834 // Call post-destructor
835 if(ndef->get(n_old).has_after_destruct)
836 scriptapi_node_after_destruct(m_lua, p, n_old);
838 if(ndef->get(n).has_on_construct)
839 scriptapi_node_on_construct(m_lua, p, n);
843 bool ServerEnvironment::removeNode(v3s16 p)
845 INodeDefManager *ndef = m_gamedef->ndef();
846 MapNode n_old = m_map->getNodeNoEx(p);
848 if(ndef->get(n_old).has_on_destruct)
849 scriptapi_node_on_destruct(m_lua, p, n_old);
851 // This is slightly optimized compared to addNodeWithEvent(air)
852 bool succeeded = m_map->removeNodeWithEvent(p);
855 // Call post-destructor
856 if(ndef->get(n_old).has_after_destruct)
857 scriptapi_node_after_destruct(m_lua, p, n_old);
858 // Air doesn't require constructor
862 std::set<u16> ServerEnvironment::getObjectsInsideRadius(v3f pos, float radius)
864 std::set<u16> objects;
865 for(core::map<u16, ServerActiveObject*>::Iterator
866 i = m_active_objects.getIterator();
867 i.atEnd()==false; i++)
869 ServerActiveObject* obj = i.getNode()->getValue();
870 u16 id = i.getNode()->getKey();
871 v3f objectpos = obj->getBasePosition();
872 if(objectpos.getDistanceFrom(pos) > radius)
879 void ServerEnvironment::clearAllObjects()
881 infostream<<"ServerEnvironment::clearAllObjects(): "
882 <<"Removing all active objects"<<std::endl;
883 core::list<u16> objects_to_remove;
884 for(core::map<u16, ServerActiveObject*>::Iterator
885 i = m_active_objects.getIterator();
886 i.atEnd()==false; i++)
888 ServerActiveObject* obj = i.getNode()->getValue();
889 if(obj->getType() == ACTIVEOBJECT_TYPE_PLAYER)
891 u16 id = i.getNode()->getKey();
892 v3f objectpos = obj->getBasePosition();
893 // Delete static object if block is loaded
894 if(obj->m_static_exists){
895 MapBlock *block = m_map->getBlockNoCreateNoEx(obj->m_static_block);
897 block->m_static_objects.remove(id);
898 block->raiseModified(MOD_STATE_WRITE_NEEDED,
900 obj->m_static_exists = false;
903 // If known by some client, don't delete immediately
904 if(obj->m_known_by_count > 0){
905 obj->m_pending_deactivation = true;
906 obj->m_removed = true;
910 // Tell the object about removal
911 obj->removingFromEnvironment();
912 // Deregister in scripting api
913 scriptapi_rm_object_reference(m_lua, obj);
915 // Delete active object
916 if(obj->environmentDeletes())
918 // Id to be removed from m_active_objects
919 objects_to_remove.push_back(id);
921 // Remove references from m_active_objects
922 for(core::list<u16>::Iterator i = objects_to_remove.begin();
923 i != objects_to_remove.end(); i++)
925 m_active_objects.remove(*i);
928 core::list<v3s16> loadable_blocks;
929 infostream<<"ServerEnvironment::clearAllObjects(): "
930 <<"Listing all loadable blocks"<<std::endl;
931 m_map->listAllLoadableBlocks(loadable_blocks);
932 infostream<<"ServerEnvironment::clearAllObjects(): "
933 <<"Done listing all loadable blocks: "
934 <<loadable_blocks.size()
935 <<", now clearing"<<std::endl;
936 u32 report_interval = loadable_blocks.size() / 10;
937 u32 num_blocks_checked = 0;
938 u32 num_blocks_cleared = 0;
939 u32 num_objs_cleared = 0;
940 for(core::list<v3s16>::Iterator i = loadable_blocks.begin();
941 i != loadable_blocks.end(); i++)
944 MapBlock *block = m_map->emergeBlock(p, false);
946 errorstream<<"ServerEnvironment::clearAllObjects(): "
947 <<"Failed to emerge block "<<PP(p)<<std::endl;
950 u32 num_stored = block->m_static_objects.m_stored.size();
951 u32 num_active = block->m_static_objects.m_active.size();
952 if(num_stored != 0 || num_active != 0){
953 block->m_static_objects.m_stored.clear();
954 block->m_static_objects.m_active.clear();
955 block->raiseModified(MOD_STATE_WRITE_NEEDED,
957 num_objs_cleared += num_stored + num_active;
958 num_blocks_cleared++;
960 num_blocks_checked++;
962 if(num_blocks_checked % report_interval == 0){
963 float percent = 100.0 * (float)num_blocks_checked /
964 loadable_blocks.size();
965 infostream<<"ServerEnvironment::clearAllObjects(): "
966 <<"Cleared "<<num_objs_cleared<<" objects"
967 <<" in "<<num_blocks_cleared<<" blocks ("
968 <<percent<<"%)"<<std::endl;
971 infostream<<"ServerEnvironment::clearAllObjects(): "
972 <<"Finished: Cleared "<<num_objs_cleared<<" objects"
973 <<" in "<<num_blocks_cleared<<" blocks"<<std::endl;
976 void ServerEnvironment::step(float dtime)
978 DSTACK(__FUNCTION_NAME);
980 //TimeTaker timer("ServerEnv step");
982 /* Step time of day */
983 stepTimeOfDay(dtime);
986 // NOTE: This is kind of funny on a singleplayer game, but doesn't
987 // really matter that much.
988 m_recommended_send_interval = g_settings->getFloat("dedicated_server_step");
994 m_game_time_fraction_counter += dtime;
995 u32 inc_i = (u32)m_game_time_fraction_counter;
996 m_game_time += inc_i;
997 m_game_time_fraction_counter -= (float)inc_i;
1004 ScopeProfiler sp(g_profiler, "SEnv: handle players avg", SPT_AVG);
1005 for(core::list<Player*>::Iterator i = m_players.begin();
1006 i != m_players.end(); i++)
1008 Player *player = *i;
1010 // Ignore disconnected players
1011 if(player->peer_id == 0)
1014 v3f playerpos = player->getPosition();
1017 player->move(dtime, *m_map, 100*BS);
1022 Manage active block list
1024 if(m_active_blocks_management_interval.step(dtime, 2.0))
1026 ScopeProfiler sp(g_profiler, "SEnv: manage act. block list avg /2s", SPT_AVG);
1028 Get player block positions
1030 core::list<v3s16> players_blockpos;
1031 for(core::list<Player*>::Iterator
1032 i = m_players.begin();
1033 i != m_players.end(); i++)
1035 Player *player = *i;
1036 // Ignore disconnected players
1037 if(player->peer_id == 0)
1039 v3s16 blockpos = getNodeBlockPos(
1040 floatToInt(player->getPosition(), BS));
1041 players_blockpos.push_back(blockpos);
1045 Update list of active blocks, collecting changes
1047 const s16 active_block_range = g_settings->getS16("active_block_range");
1048 core::map<v3s16, bool> blocks_removed;
1049 core::map<v3s16, bool> blocks_added;
1050 m_active_blocks.update(players_blockpos, active_block_range,
1051 blocks_removed, blocks_added);
1054 Handle removed blocks
1057 // Convert active objects that are no more in active blocks to static
1058 deactivateFarObjects(false);
1060 for(core::map<v3s16, bool>::Iterator
1061 i = blocks_removed.getIterator();
1062 i.atEnd()==false; i++)
1064 v3s16 p = i.getNode()->getKey();
1066 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1067 <<") became inactive"<<std::endl;*/
1069 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1073 // Set current time as timestamp (and let it set ChangedFlag)
1074 block->setTimestamp(m_game_time);
1081 for(core::map<v3s16, bool>::Iterator
1082 i = blocks_added.getIterator();
1083 i.atEnd()==false; i++)
1085 v3s16 p = i.getNode()->getKey();
1087 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1088 <<") became active"<<std::endl;*/
1090 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1092 // Block needs to be fetched first
1093 m_emerger->queueBlockEmerge(p, false);
1094 m_active_blocks.m_list.remove(p);
1098 activateBlock(block);
1103 Mess around in active blocks
1105 if(m_active_blocks_nodemetadata_interval.step(dtime, 1.0))
1107 ScopeProfiler sp(g_profiler, "SEnv: mess in act. blocks avg /1s", SPT_AVG);
1111 for(core::map<v3s16, bool>::Iterator
1112 i = m_active_blocks.m_list.getIterator();
1113 i.atEnd()==false; i++)
1115 v3s16 p = i.getNode()->getKey();
1117 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1118 <<") being handled"<<std::endl;*/
1120 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1124 // Reset block usage timer
1125 block->resetUsageTimer();
1127 // Set current time as timestamp
1128 block->setTimestampNoChangedFlag(m_game_time);
1129 // If time has changed much from the one on disk,
1130 // set block to be saved when it is unloaded
1131 if(block->getTimestamp() > block->getDiskTimestamp() + 60)
1132 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1133 "Timestamp older than 60s (step)");
1136 std::map<v3s16, NodeTimer> elapsed_timers =
1137 block->m_node_timers.step((float)dtime);
1138 if(!elapsed_timers.empty()){
1140 for(std::map<v3s16, NodeTimer>::iterator
1141 i = elapsed_timers.begin();
1142 i != elapsed_timers.end(); i++){
1143 n = block->getNodeNoEx(i->first);
1144 p = i->first + block->getPosRelative();
1145 if(scriptapi_node_on_timer(m_lua,p,n,i->second.elapsed))
1146 block->setNodeTimer(i->first,NodeTimer(i->second.timeout,0));
1152 const float abm_interval = 1.0;
1153 if(m_active_block_modifier_interval.step(dtime, abm_interval))
1155 if(m_active_block_interval_overload_skip > 0){
1156 ScopeProfiler sp(g_profiler, "SEnv: ABM overload skips");
1157 m_active_block_interval_overload_skip--;
1160 ScopeProfiler sp(g_profiler, "SEnv: modify in blocks avg /1s", SPT_AVG);
1161 TimeTaker timer("modify in active blocks");
1163 // Initialize handling of ActiveBlockModifiers
1164 ABMHandler abmhandler(m_abms, abm_interval, this, true);
1166 for(core::map<v3s16, bool>::Iterator
1167 i = m_active_blocks.m_list.getIterator();
1168 i.atEnd()==false; i++)
1170 v3s16 p = i.getNode()->getKey();
1172 /*infostream<<"Server: Block ("<<p.X<<","<<p.Y<<","<<p.Z
1173 <<") being handled"<<std::endl;*/
1175 MapBlock *block = m_map->getBlockNoCreateNoEx(p);
1179 // Set current time as timestamp
1180 block->setTimestampNoChangedFlag(m_game_time);
1182 /* Handle ActiveBlockModifiers */
1183 abmhandler.apply(block);
1186 u32 time_ms = timer.stop(true);
1187 u32 max_time_ms = 200;
1188 if(time_ms > max_time_ms){
1189 infostream<<"WARNING: active block modifiers took "
1190 <<time_ms<<"ms (longer than "
1191 <<max_time_ms<<"ms)"<<std::endl;
1192 m_active_block_interval_overload_skip = (time_ms / max_time_ms) + 1;
1197 Step script environment (run global on_step())
1199 scriptapi_environment_step(m_lua, dtime);
1205 ScopeProfiler sp(g_profiler, "SEnv: step act. objs avg", SPT_AVG);
1206 //TimeTaker timer("Step active objects");
1208 g_profiler->avg("SEnv: num of objects", m_active_objects.size());
1210 // This helps the objects to send data at the same time
1211 bool send_recommended = false;
1212 m_send_recommended_timer += dtime;
1213 if(m_send_recommended_timer > getSendRecommendedInterval())
1215 m_send_recommended_timer -= getSendRecommendedInterval();
1216 send_recommended = true;
1219 for(core::map<u16, ServerActiveObject*>::Iterator
1220 i = m_active_objects.getIterator();
1221 i.atEnd()==false; i++)
1223 ServerActiveObject* obj = i.getNode()->getValue();
1224 // Remove non-peaceful mobs on peaceful mode
1225 if(g_settings->getBool("only_peaceful_mobs")){
1226 if(!obj->isPeaceful())
1227 obj->m_removed = true;
1229 // Don't step if is to be removed or stored statically
1230 if(obj->m_removed || obj->m_pending_deactivation)
1233 obj->step(dtime, send_recommended);
1234 // Read messages from object
1235 while(obj->m_messages_out.size() > 0)
1237 m_active_object_messages.push_back(
1238 obj->m_messages_out.pop_front());
1244 Manage active objects
1246 if(m_object_management_interval.step(dtime, 0.5))
1248 ScopeProfiler sp(g_profiler, "SEnv: remove removed objs avg /.5s", SPT_AVG);
1250 Remove objects that satisfy (m_removed && m_known_by_count==0)
1252 removeRemovedObjects();
1256 ServerActiveObject* ServerEnvironment::getActiveObject(u16 id)
1258 core::map<u16, ServerActiveObject*>::Node *n;
1259 n = m_active_objects.find(id);
1262 return n->getValue();
1265 bool isFreeServerActiveObjectId(u16 id,
1266 core::map<u16, ServerActiveObject*> &objects)
1271 for(core::map<u16, ServerActiveObject*>::Iterator
1272 i = objects.getIterator();
1273 i.atEnd()==false; i++)
1275 if(i.getNode()->getKey() == id)
1281 u16 getFreeServerActiveObjectId(
1282 core::map<u16, ServerActiveObject*> &objects)
1287 if(isFreeServerActiveObjectId(new_id, objects))
1297 u16 ServerEnvironment::addActiveObject(ServerActiveObject *object)
1300 u16 id = addActiveObjectRaw(object, true, 0);
1305 bool ServerEnvironment::addActiveObjectAsStatic(ServerActiveObject *obj)
1309 v3f objectpos = obj->getBasePosition();
1311 // The block in which the object resides in
1312 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1315 Update the static data
1318 // Create new static object
1319 std::string staticdata = obj->getStaticData();
1320 StaticObject s_obj(obj->getType(), objectpos, staticdata);
1321 // Add to the block where the object is located in
1322 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1323 // Get or generate the block
1324 MapBlock *block = m_map->emergeBlock(blockpos);
1326 bool succeeded = false;
1330 block->m_static_objects.insert(0, s_obj);
1331 block->raiseModified(MOD_STATE_WRITE_AT_UNLOAD,
1332 "addActiveObjectAsStatic");
1336 infostream<<"ServerEnvironment::addActiveObjectAsStatic: "
1337 <<"Could not find or generate "
1338 <<"a block for storing static object"<<std::endl;
1342 if(obj->environmentDeletes())
1350 Finds out what new objects have been added to
1351 inside a radius around a position
1353 void ServerEnvironment::getAddedActiveObjects(v3s16 pos, s16 radius,
1354 core::map<u16, bool> ¤t_objects,
1355 core::map<u16, bool> &added_objects)
1357 v3f pos_f = intToFloat(pos, BS);
1358 f32 radius_f = radius * BS;
1360 Go through the object list,
1361 - discard m_removed objects,
1362 - discard objects that are too far away,
1363 - discard objects that are found in current_objects.
1364 - add remaining objects to added_objects
1366 for(core::map<u16, ServerActiveObject*>::Iterator
1367 i = m_active_objects.getIterator();
1368 i.atEnd()==false; i++)
1370 u16 id = i.getNode()->getKey();
1372 ServerActiveObject *object = i.getNode()->getValue();
1375 // Discard if removed
1376 if(object->m_removed)
1378 if(object->unlimitedTransferDistance() == false){
1379 // Discard if too far
1380 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1381 if(distance_f > radius_f)
1384 // Discard if already on current_objects
1385 core::map<u16, bool>::Node *n;
1386 n = current_objects.find(id);
1389 // Add to added_objects
1390 added_objects.insert(id, false);
1395 Finds out what objects have been removed from
1396 inside a radius around a position
1398 void ServerEnvironment::getRemovedActiveObjects(v3s16 pos, s16 radius,
1399 core::map<u16, bool> ¤t_objects,
1400 core::map<u16, bool> &removed_objects)
1402 v3f pos_f = intToFloat(pos, BS);
1403 f32 radius_f = radius * BS;
1405 Go through current_objects; object is removed if:
1406 - object is not found in m_active_objects (this is actually an
1407 error condition; objects should be set m_removed=true and removed
1408 only after all clients have been informed about removal), or
1409 - object has m_removed=true, or
1410 - object is too far away
1412 for(core::map<u16, bool>::Iterator
1413 i = current_objects.getIterator();
1414 i.atEnd()==false; i++)
1416 u16 id = i.getNode()->getKey();
1417 ServerActiveObject *object = getActiveObject(id);
1420 infostream<<"ServerEnvironment::getRemovedActiveObjects():"
1421 <<" object in current_objects is NULL"<<std::endl;
1422 removed_objects.insert(id, false);
1426 if(object->m_removed)
1428 removed_objects.insert(id, false);
1432 // If transfer distance is unlimited, don't remove
1433 if(object->unlimitedTransferDistance())
1436 f32 distance_f = object->getBasePosition().getDistanceFrom(pos_f);
1438 if(distance_f >= radius_f)
1440 removed_objects.insert(id, false);
1448 ActiveObjectMessage ServerEnvironment::getActiveObjectMessage()
1450 if(m_active_object_messages.size() == 0)
1451 return ActiveObjectMessage(0);
1453 return m_active_object_messages.pop_front();
1457 ************ Private methods *************
1460 u16 ServerEnvironment::addActiveObjectRaw(ServerActiveObject *object,
1461 bool set_changed, u32 dtime_s)
1464 if(object->getId() == 0){
1465 u16 new_id = getFreeServerActiveObjectId(m_active_objects);
1468 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1469 <<"no free ids available"<<std::endl;
1470 if(object->environmentDeletes())
1474 object->setId(new_id);
1477 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1478 <<"supplied with id "<<object->getId()<<std::endl;
1480 if(isFreeServerActiveObjectId(object->getId(), m_active_objects) == false)
1482 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1483 <<"id is not free ("<<object->getId()<<")"<<std::endl;
1484 if(object->environmentDeletes())
1488 /*infostream<<"ServerEnvironment::addActiveObjectRaw(): "
1489 <<"added (id="<<object->getId()<<")"<<std::endl;*/
1491 m_active_objects.insert(object->getId(), object);
1493 verbosestream<<"ServerEnvironment::addActiveObjectRaw(): "
1494 <<"Added id="<<object->getId()<<"; there are now "
1495 <<m_active_objects.size()<<" active objects."
1498 // Register reference in scripting api (must be done before post-init)
1499 scriptapi_add_object_reference(m_lua, object);
1500 // Post-initialize object
1501 object->addedToEnvironment(dtime_s);
1503 // Add static data to block
1504 if(object->isStaticAllowed())
1506 // Add static object to active static list of the block
1507 v3f objectpos = object->getBasePosition();
1508 std::string staticdata = object->getStaticData();
1509 StaticObject s_obj(object->getType(), objectpos, staticdata);
1510 // Add to the block where the object is located in
1511 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1512 MapBlock *block = m_map->getBlockNoCreateNoEx(blockpos);
1515 block->m_static_objects.m_active.insert(object->getId(), s_obj);
1516 object->m_static_exists = true;
1517 object->m_static_block = blockpos;
1520 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1521 "addActiveObjectRaw");
1524 v3s16 p = floatToInt(objectpos, BS);
1525 errorstream<<"ServerEnvironment::addActiveObjectRaw(): "
1526 <<"could not find block for storing id="<<object->getId()
1527 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1531 return object->getId();
1535 Remove objects that satisfy (m_removed && m_known_by_count==0)
1537 void ServerEnvironment::removeRemovedObjects()
1539 core::list<u16> objects_to_remove;
1540 for(core::map<u16, ServerActiveObject*>::Iterator
1541 i = m_active_objects.getIterator();
1542 i.atEnd()==false; i++)
1544 u16 id = i.getNode()->getKey();
1545 ServerActiveObject* obj = i.getNode()->getValue();
1546 // This shouldn't happen but check it
1549 infostream<<"NULL object found in ServerEnvironment"
1550 <<" while finding removed objects. id="<<id<<std::endl;
1551 // Id to be removed from m_active_objects
1552 objects_to_remove.push_back(id);
1557 We will delete objects that are marked as removed or thatare
1558 waiting for deletion after deactivation
1560 if(obj->m_removed == false && obj->m_pending_deactivation == false)
1564 Delete static data from block if is marked as removed
1566 if(obj->m_static_exists && obj->m_removed)
1568 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1570 block->m_static_objects.remove(id);
1571 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1572 "removeRemovedObjects");
1573 obj->m_static_exists = false;
1575 infostream << "failed to emerge block from which "
1576 "an object to be removed was loaded from. id="<<id<<std::endl;
1580 // If m_known_by_count > 0, don't actually remove.
1581 if(obj->m_known_by_count > 0)
1584 // Tell the object about removal
1585 obj->removingFromEnvironment();
1586 // Deregister in scripting api
1587 scriptapi_rm_object_reference(m_lua, obj);
1590 if(obj->environmentDeletes())
1592 // Id to be removed from m_active_objects
1593 objects_to_remove.push_back(id);
1595 // Remove references from m_active_objects
1596 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1597 i != objects_to_remove.end(); i++)
1599 m_active_objects.remove(*i);
1603 static void print_hexdump(std::ostream &o, const std::string &data)
1605 const int linelength = 16;
1606 for(int l=0; ; l++){
1607 int i0 = linelength * l;
1608 bool at_end = false;
1609 int thislinelength = linelength;
1610 if(i0 + thislinelength > (int)data.size()){
1611 thislinelength = data.size() - i0;
1614 for(int di=0; di<linelength; di++){
1617 if(di<thislinelength)
1618 snprintf(buf, 4, "%.2x ", data[i]);
1620 snprintf(buf, 4, " ");
1624 for(int di=0; di<thislinelength; di++){
1638 Convert stored objects from blocks near the players to active.
1640 void ServerEnvironment::activateObjects(MapBlock *block, u32 dtime_s)
1644 // Ignore if no stored objects (to not set changed flag)
1645 if(block->m_static_objects.m_stored.size() == 0)
1647 verbosestream<<"ServerEnvironment::activateObjects(): "
1648 <<"activating objects of block "<<PP(block->getPos())
1649 <<" ("<<block->m_static_objects.m_stored.size()
1650 <<" objects)"<<std::endl;
1651 bool large_amount = (block->m_static_objects.m_stored.size() > 49);
1653 errorstream<<"suspiciously large amount of objects detected: "
1654 <<block->m_static_objects.m_stored.size()<<" in "
1655 <<PP(block->getPos())
1656 <<"; removing all of them."<<std::endl;
1657 // Clear stored list
1658 block->m_static_objects.m_stored.clear();
1659 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1660 "stored list cleared in activateObjects due to "
1661 "large amount of objects");
1664 // A list for objects that couldn't be converted to active for some
1665 // reason. They will be stored back.
1666 core::list<StaticObject> new_stored;
1667 // Loop through stored static objects
1668 for(core::list<StaticObject>::Iterator
1669 i = block->m_static_objects.m_stored.begin();
1670 i != block->m_static_objects.m_stored.end(); i++)
1672 /*infostream<<"Server: Creating an active object from "
1673 <<"static data"<<std::endl;*/
1674 StaticObject &s_obj = *i;
1675 // Create an active object from the data
1676 ServerActiveObject *obj = ServerActiveObject::create
1677 (s_obj.type, this, 0, s_obj.pos, s_obj.data);
1678 // If couldn't create object, store static data back.
1681 errorstream<<"ServerEnvironment::activateObjects(): "
1682 <<"failed to create active object from static object "
1683 <<"in block "<<PP(s_obj.pos/BS)
1684 <<" type="<<(int)s_obj.type<<" data:"<<std::endl;
1685 print_hexdump(verbosestream, s_obj.data);
1687 new_stored.push_back(s_obj);
1690 verbosestream<<"ServerEnvironment::activateObjects(): "
1691 <<"activated static object pos="<<PP(s_obj.pos/BS)
1692 <<" type="<<(int)s_obj.type<<std::endl;
1693 // This will also add the object to the active static list
1694 addActiveObjectRaw(obj, false, dtime_s);
1696 // Clear stored list
1697 block->m_static_objects.m_stored.clear();
1698 // Add leftover failed stuff to stored list
1699 for(core::list<StaticObject>::Iterator
1700 i = new_stored.begin();
1701 i != new_stored.end(); i++)
1703 StaticObject &s_obj = *i;
1704 block->m_static_objects.m_stored.push_back(s_obj);
1707 Note: Block hasn't really been modified here.
1708 The objects have just been activated and moved from the stored
1709 static list to the active static list.
1710 As such, the block is essentially the same.
1711 Thus, do not call block->raiseModified(MOD_STATE_WRITE_NEEDED).
1712 Otherwise there would be a huge amount of unnecessary I/O.
1717 Convert objects that are not standing inside active blocks to static.
1719 If m_known_by_count != 0, active object is not deleted, but static
1720 data is still updated.
1722 If force_delete is set, active object is deleted nevertheless. It
1723 shall only be set so in the destructor of the environment.
1725 If block wasn't generated (not in memory or on disk),
1727 void ServerEnvironment::deactivateFarObjects(bool force_delete)
1729 core::list<u16> objects_to_remove;
1730 for(core::map<u16, ServerActiveObject*>::Iterator
1731 i = m_active_objects.getIterator();
1732 i.atEnd()==false; i++)
1734 ServerActiveObject* obj = i.getNode()->getValue();
1737 // Do not deactivate if static data creation not allowed
1738 if(!force_delete && !obj->isStaticAllowed())
1741 // If pending deactivation, let removeRemovedObjects() do it
1742 if(!force_delete && obj->m_pending_deactivation)
1745 u16 id = i.getNode()->getKey();
1746 v3f objectpos = obj->getBasePosition();
1748 // The block in which the object resides in
1749 v3s16 blockpos_o = getNodeBlockPos(floatToInt(objectpos, BS));
1751 // If block is active, don't remove
1752 if(!force_delete && m_active_blocks.contains(blockpos_o))
1755 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1756 <<"deactivating object id="<<id<<" on inactive block "
1757 <<PP(blockpos_o)<<std::endl;
1759 // If known by some client, don't immediately delete.
1760 bool pending_delete = (obj->m_known_by_count > 0 && !force_delete);
1763 Update the static data
1766 if(obj->isStaticAllowed())
1768 // Create new static object
1769 std::string staticdata_new = obj->getStaticData();
1770 StaticObject s_obj(obj->getType(), objectpos, staticdata_new);
1772 bool stays_in_same_block = false;
1773 bool data_changed = true;
1775 if(obj->m_static_exists){
1776 if(obj->m_static_block == blockpos_o)
1777 stays_in_same_block = true;
1779 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1781 core::map<u16, StaticObject>::Node *n =
1782 block->m_static_objects.m_active.find(id);
1784 StaticObject static_old = n->getValue();
1786 float save_movem = obj->getMinimumSavedMovement();
1788 if(static_old.data == staticdata_new &&
1789 (static_old.pos - objectpos).getLength() < save_movem)
1790 data_changed = false;
1792 errorstream<<"ServerEnvironment::deactivateFarObjects(): "
1793 <<"id="<<id<<" m_static_exists=true but "
1794 <<"static data doesn't actually exist in "
1795 <<PP(obj->m_static_block)<<std::endl;
1799 bool shall_be_written = (!stays_in_same_block || data_changed);
1801 // Delete old static object
1802 if(obj->m_static_exists)
1804 MapBlock *block = m_map->emergeBlock(obj->m_static_block, false);
1807 block->m_static_objects.remove(id);
1808 obj->m_static_exists = false;
1809 // Only mark block as modified if data changed considerably
1810 if(shall_be_written)
1811 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1812 "deactivateFarObjects: Static data "
1813 "changed considerably");
1817 // Add to the block where the object is located in
1818 v3s16 blockpos = getNodeBlockPos(floatToInt(objectpos, BS));
1819 // Get or generate the block
1820 MapBlock *block = NULL;
1822 block = m_map->emergeBlock(blockpos);
1823 } catch(InvalidPositionException &e){
1824 // Handled via NULL pointer
1829 if(block->m_static_objects.m_stored.size() >= 49){
1830 errorstream<<"ServerEnv: Trying to store id="<<obj->getId()
1831 <<" statically but block "<<PP(blockpos)
1832 <<" already contains "
1833 <<block->m_static_objects.m_stored.size()
1834 <<" (over 49) objects."
1835 <<" Forcing delete."<<std::endl;
1836 force_delete = true;
1838 u16 new_id = pending_delete ? id : 0;
1839 // If static counterpart already exists, remove it first.
1840 // This shouldn't happen, but happens rarely for some
1841 // unknown reason. Unsuccessful attempts have been made to
1842 // find said reason.
1843 if(new_id && block->m_static_objects.m_active.find(new_id)){
1844 infostream<<"ServerEnv: WARNING: Performing hack #83274"
1846 block->m_static_objects.remove(new_id);
1848 block->m_static_objects.insert(new_id, s_obj);
1850 // Only mark block as modified if data changed considerably
1851 if(shall_be_written)
1852 block->raiseModified(MOD_STATE_WRITE_NEEDED,
1853 "deactivateFarObjects: Static data "
1854 "changed considerably");
1856 obj->m_static_exists = true;
1857 obj->m_static_block = block->getPos();
1862 v3s16 p = floatToInt(objectpos, BS);
1863 errorstream<<"ServerEnv: Could not find or generate "
1864 <<"a block for storing id="<<obj->getId()
1865 <<" statically (pos="<<PP(p)<<")"<<std::endl;
1872 If known by some client, set pending deactivation.
1873 Otherwise delete it immediately.
1876 if(pending_delete && !force_delete)
1878 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1879 <<"object id="<<id<<" is known by clients"
1880 <<"; not deleting yet"<<std::endl;
1882 obj->m_pending_deactivation = true;
1886 verbosestream<<"ServerEnvironment::deactivateFarObjects(): "
1887 <<"object id="<<id<<" is not known by clients"
1888 <<"; deleting"<<std::endl;
1890 // Tell the object about removal
1891 obj->removingFromEnvironment();
1892 // Deregister in scripting api
1893 scriptapi_rm_object_reference(m_lua, obj);
1895 // Delete active object
1896 if(obj->environmentDeletes())
1898 // Id to be removed from m_active_objects
1899 objects_to_remove.push_back(id);
1902 // Remove references from m_active_objects
1903 for(core::list<u16>::Iterator i = objects_to_remove.begin();
1904 i != objects_to_remove.end(); i++)
1906 m_active_objects.remove(*i);
1913 #include "clientsimpleobject.h"
1919 ClientEnvironment::ClientEnvironment(ClientMap *map, scene::ISceneManager *smgr,
1920 ITextureSource *texturesource, IGameDef *gamedef,
1921 IrrlichtDevice *irr):
1924 m_texturesource(texturesource),
1930 ClientEnvironment::~ClientEnvironment()
1932 // delete active objects
1933 for(core::map<u16, ClientActiveObject*>::Iterator
1934 i = m_active_objects.getIterator();
1935 i.atEnd()==false; i++)
1937 delete i.getNode()->getValue();
1940 for(core::list<ClientSimpleObject*>::Iterator
1941 i = m_simple_objects.begin(); i != m_simple_objects.end(); i++)
1950 Map & ClientEnvironment::getMap()
1955 ClientMap & ClientEnvironment::getClientMap()
1960 void ClientEnvironment::addPlayer(Player *player)
1962 DSTACK(__FUNCTION_NAME);
1964 It is a failure if player is local and there already is a local
1967 assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
1969 Environment::addPlayer(player);
1972 LocalPlayer * ClientEnvironment::getLocalPlayer()
1974 for(core::list<Player*>::Iterator i = m_players.begin();
1975 i != m_players.end(); i++)
1977 Player *player = *i;
1978 if(player->isLocal())
1979 return (LocalPlayer*)player;
1984 void ClientEnvironment::step(float dtime)
1986 DSTACK(__FUNCTION_NAME);
1988 /* Step time of day */
1989 stepTimeOfDay(dtime);
1991 // Get some settings
1992 bool fly_allowed = m_gamedef->checkLocalPrivilege("fly");
1993 bool free_move = fly_allowed && g_settings->getBool("free_move");
1996 LocalPlayer *lplayer = getLocalPlayer();
1998 // collision info queue
1999 core::list<CollisionInfo> player_collisions;
2002 Get the speed the player is going
2004 bool is_climbing = lplayer->is_climbing;
2006 f32 player_speed = lplayer->getSpeed().getLength();
2009 Maximum position increment
2011 //f32 position_max_increment = 0.05*BS;
2012 f32 position_max_increment = 0.1*BS;
2014 // Maximum time increment (for collision detection etc)
2015 // time = distance / speed
2016 f32 dtime_max_increment = 1;
2017 if(player_speed > 0.001)
2018 dtime_max_increment = position_max_increment / player_speed;
2020 // Maximum time increment is 10ms or lower
2021 if(dtime_max_increment > 0.01)
2022 dtime_max_increment = 0.01;
2024 // Don't allow overly huge dtime
2028 f32 dtime_downcount = dtime;
2031 Stuff that has a maximum time increment
2040 if(dtime_downcount > dtime_max_increment)
2042 dtime_part = dtime_max_increment;
2043 dtime_downcount -= dtime_part;
2047 dtime_part = dtime_downcount;
2049 Setting this to 0 (no -=dtime_part) disables an infinite loop
2050 when dtime_part is so small that dtime_downcount -= dtime_part
2053 dtime_downcount = 0;
2061 v3f lplayerpos = lplayer->getPosition();
2064 if(free_move == false && is_climbing == false)
2067 v3f speed = lplayer->getSpeed();
2068 if(lplayer->in_liquid == false)
2069 speed.Y -= lplayer->movement_gravity * dtime_part * 2;
2071 // Liquid floating / sinking
2072 if(lplayer->in_liquid && !lplayer->swimming_vertical)
2073 speed.Y -= lplayer->movement_liquid_sink * dtime_part * 2;
2075 // Liquid resistance
2076 if(lplayer->in_liquid_stable || lplayer->in_liquid)
2078 // How much the node's viscosity blocks movement, ranges between 0 and 1
2079 // Should match the scale at which viscosity increase affects other liquid attributes
2080 const f32 viscosity_factor = 0.3;
2082 v3f d_wanted = -speed / lplayer->movement_liquid_fluidity;
2083 f32 dl = d_wanted.getLength();
2084 if(dl > lplayer->movement_liquid_fluidity_smooth)
2085 dl = lplayer->movement_liquid_fluidity_smooth;
2086 dl *= (lplayer->liquid_viscosity * viscosity_factor) + (1 - viscosity_factor);
2088 v3f d = d_wanted.normalize() * dl;
2092 if(speed.X > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.X -= lplayer->movement_liquid_fluidity_smooth;
2093 if(speed.X < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.X += lplayer->movement_liquid_fluidity_smooth;
2094 if(speed.Y > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Y -= lplayer->movement_liquid_fluidity_smooth;
2095 if(speed.Y < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Y += lplayer->movement_liquid_fluidity_smooth;
2096 if(speed.Z > lplayer->movement_liquid_fluidity + lplayer->movement_liquid_fluidity_smooth) speed.Z -= lplayer->movement_liquid_fluidity_smooth;
2097 if(speed.Z < -lplayer->movement_liquid_fluidity - lplayer->movement_liquid_fluidity_smooth) speed.Z += lplayer->movement_liquid_fluidity_smooth;
2101 lplayer->setSpeed(speed);
2106 This also does collision detection.
2108 lplayer->move(dtime_part, *m_map, position_max_increment,
2109 &player_collisions);
2112 while(dtime_downcount > 0.001);
2114 //std::cout<<"Looped "<<loopcount<<" times."<<std::endl;
2116 for(core::list<CollisionInfo>::Iterator
2117 i = player_collisions.begin();
2118 i != player_collisions.end(); i++)
2120 CollisionInfo &info = *i;
2121 v3f speed_diff = info.new_speed - info.old_speed;;
2122 // Handle only fall damage
2123 // (because otherwise walking against something in fast_move kills you)
2124 if(speed_diff.Y < 0 || info.old_speed.Y >= 0)
2126 // Get rid of other components
2129 f32 pre_factor = 1; // 1 hp per node/s
2130 f32 tolerance = BS*14; // 5 without damage
2131 f32 post_factor = 1; // 1 hp per node/s
2132 if(info.type == COLLISION_NODE)
2134 const ContentFeatures &f = m_gamedef->ndef()->
2135 get(m_map->getNodeNoEx(info.node_p));
2136 // Determine fall damage multiplier
2137 int addp = itemgroup_get(f.groups, "fall_damage_add_percent");
2138 pre_factor = 1.0 + (float)addp/100.0;
2140 float speed = pre_factor * speed_diff.getLength();
2141 if(speed > tolerance)
2143 f32 damage_f = (speed - tolerance)/BS * post_factor;
2144 u16 damage = (u16)(damage_f+0.5);
2146 damageLocalPlayer(damage, true);
2151 A quick draft of lava damage
2153 if(m_lava_hurt_interval.step(dtime, 1.0))
2155 v3f pf = lplayer->getPosition();
2157 // Feet, middle and head
2158 v3s16 p1 = floatToInt(pf + v3f(0, BS*0.1, 0), BS);
2159 MapNode n1 = m_map->getNodeNoEx(p1);
2160 v3s16 p2 = floatToInt(pf + v3f(0, BS*0.8, 0), BS);
2161 MapNode n2 = m_map->getNodeNoEx(p2);
2162 v3s16 p3 = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
2163 MapNode n3 = m_map->getNodeNoEx(p2);
2165 u32 damage_per_second = 0;
2166 damage_per_second = MYMAX(damage_per_second,
2167 m_gamedef->ndef()->get(n1).damage_per_second);
2168 damage_per_second = MYMAX(damage_per_second,
2169 m_gamedef->ndef()->get(n2).damage_per_second);
2170 damage_per_second = MYMAX(damage_per_second,
2171 m_gamedef->ndef()->get(n3).damage_per_second);
2173 if(damage_per_second != 0)
2175 damageLocalPlayer(damage_per_second, true);
2180 Stuff that can be done in an arbitarily large dtime
2182 for(core::list<Player*>::Iterator i = m_players.begin();
2183 i != m_players.end(); i++)
2185 Player *player = *i;
2186 v3f playerpos = player->getPosition();
2189 Handle non-local players
2191 if(player->isLocal() == false)
2194 player->move(dtime, *m_map, 100*BS);
2198 // Update lighting on all players on client
2202 v3s16 p = player->getLightPosition();
2203 MapNode n = m_map->getNode(p);
2204 light = n.getLightBlendF1((float)getDayNightRatio()/1000, m_gamedef->ndef());
2206 catch(InvalidPositionException &e){
2207 light = blend_light_f1((float)getDayNightRatio()/1000, LIGHT_SUN, 0);
2209 player->light = light;
2213 Step active objects and update lighting of them
2216 bool update_lighting = m_active_object_light_update_interval.step(dtime, 0.21);
2217 for(core::map<u16, ClientActiveObject*>::Iterator
2218 i = m_active_objects.getIterator();
2219 i.atEnd()==false; i++)
2221 ClientActiveObject* obj = i.getNode()->getValue();
2223 obj->step(dtime, this);
2231 v3s16 p = obj->getLightPosition();
2232 MapNode n = m_map->getNode(p);
2233 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2235 catch(InvalidPositionException &e){
2236 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2238 obj->updateLight(light);
2243 Step and handle simple objects
2245 for(core::list<ClientSimpleObject*>::Iterator
2246 i = m_simple_objects.begin(); i != m_simple_objects.end();)
2248 ClientSimpleObject *simple = *i;
2249 core::list<ClientSimpleObject*>::Iterator cur = i;
2251 simple->step(dtime);
2252 if(simple->m_to_be_removed){
2254 m_simple_objects.erase(cur);
2259 void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
2261 m_simple_objects.push_back(simple);
2264 ClientActiveObject* ClientEnvironment::getActiveObject(u16 id)
2266 core::map<u16, ClientActiveObject*>::Node *n;
2267 n = m_active_objects.find(id);
2270 return n->getValue();
2273 bool isFreeClientActiveObjectId(u16 id,
2274 core::map<u16, ClientActiveObject*> &objects)
2279 for(core::map<u16, ClientActiveObject*>::Iterator
2280 i = objects.getIterator();
2281 i.atEnd()==false; i++)
2283 if(i.getNode()->getKey() == id)
2289 u16 getFreeClientActiveObjectId(
2290 core::map<u16, ClientActiveObject*> &objects)
2295 if(isFreeClientActiveObjectId(new_id, objects))
2305 u16 ClientEnvironment::addActiveObject(ClientActiveObject *object)
2308 if(object->getId() == 0)
2310 u16 new_id = getFreeClientActiveObjectId(m_active_objects);
2313 infostream<<"ClientEnvironment::addActiveObject(): "
2314 <<"no free ids available"<<std::endl;
2318 object->setId(new_id);
2320 if(isFreeClientActiveObjectId(object->getId(), m_active_objects) == false)
2322 infostream<<"ClientEnvironment::addActiveObject(): "
2323 <<"id is not free ("<<object->getId()<<")"<<std::endl;
2327 infostream<<"ClientEnvironment::addActiveObject(): "
2328 <<"added (id="<<object->getId()<<")"<<std::endl;
2329 m_active_objects.insert(object->getId(), object);
2330 object->addToScene(m_smgr, m_texturesource, m_irr);
2331 { // Update lighting immediately
2335 v3s16 p = object->getLightPosition();
2336 MapNode n = m_map->getNode(p);
2337 light = n.getLightBlend(getDayNightRatio(), m_gamedef->ndef());
2339 catch(InvalidPositionException &e){
2340 light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
2342 object->updateLight(light);
2344 return object->getId();
2347 void ClientEnvironment::addActiveObject(u16 id, u8 type,
2348 const std::string &init_data)
2350 ClientActiveObject* obj =
2351 ClientActiveObject::create(type, m_gamedef, this);
2354 infostream<<"ClientEnvironment::addActiveObject(): "
2355 <<"id="<<id<<" type="<<type<<": Couldn't create object"
2364 obj->initialize(init_data);
2366 catch(SerializationError &e)
2368 errorstream<<"ClientEnvironment::addActiveObject():"
2369 <<" id="<<id<<" type="<<type
2370 <<": SerializationError in initialize(): "
2372 <<": init_data="<<serializeJsonString(init_data)
2376 addActiveObject(obj);
2379 void ClientEnvironment::removeActiveObject(u16 id)
2381 verbosestream<<"ClientEnvironment::removeActiveObject(): "
2382 <<"id="<<id<<std::endl;
2383 ClientActiveObject* obj = getActiveObject(id);
2386 infostream<<"ClientEnvironment::removeActiveObject(): "
2387 <<"id="<<id<<" not found"<<std::endl;
2390 obj->removeFromScene(true);
2392 m_active_objects.remove(id);
2395 void ClientEnvironment::processActiveObjectMessage(u16 id,
2396 const std::string &data)
2398 ClientActiveObject* obj = getActiveObject(id);
2401 infostream<<"ClientEnvironment::processActiveObjectMessage():"
2402 <<" got message for id="<<id<<", which doesn't exist."
2408 obj->processMessage(data);
2410 catch(SerializationError &e)
2412 errorstream<<"ClientEnvironment::processActiveObjectMessage():"
2413 <<" id="<<id<<" type="<<obj->getType()
2414 <<" SerializationError in processMessage(),"
2415 <<" message="<<serializeJsonString(data)
2421 Callbacks for activeobjects
2424 void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
2426 LocalPlayer *lplayer = getLocalPlayer();
2430 if(lplayer->hp > damage)
2431 lplayer->hp -= damage;
2436 ClientEnvEvent event;
2437 event.type = CEE_PLAYER_DAMAGE;
2438 event.player_damage.amount = damage;
2439 event.player_damage.send_to_server = handle_hp;
2440 m_client_event_queue.push_back(event);
2444 Client likes to call these
2447 void ClientEnvironment::getActiveObjects(v3f origin, f32 max_d,
2448 core::array<DistanceSortedActiveObject> &dest)
2450 for(core::map<u16, ClientActiveObject*>::Iterator
2451 i = m_active_objects.getIterator();
2452 i.atEnd()==false; i++)
2454 ClientActiveObject* obj = i.getNode()->getValue();
2456 f32 d = (obj->getPosition() - origin).getLength();
2461 DistanceSortedActiveObject dso(obj, d);
2463 dest.push_back(dso);
2467 ClientEnvEvent ClientEnvironment::getClientEvent()
2469 if(m_client_event_queue.size() == 0)
2471 ClientEnvEvent event;
2472 event.type = CEE_NONE;
2475 return m_client_event_queue.pop_front();
2478 #endif // #ifndef SERVER